From ed71fcbe9864fd082cf26f435df1ee7ba8b4ebb1 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 4 Feb 2014 13:54:57 +0100 Subject: ssl: Fix broken appup from ssl-5.2.1.1 that was released together with inets-5.9.2.2 --- lib/ssl/src/ssl.appup.src | 29 ++++++----------------------- lib/ssl/vsn.mk | 2 +- 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index 664f588003..c04d29bfc6 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,34 +1,17 @@ %% -*- erlang -*- {"%VSN%", [ - {"5.1.2", - [ - {restart_application, inets} - ] - }, - {"5.1.1", [{restart_application, ssl}] - }, - {"5.1", [ - {load_module, ssl_connection, soft_purge, soft_purge, []} - ] - }, + {"5.1.2", [{restart_application, ssl}]}, + {"5.1.1", [{restart_application, ssl}]}, + {"5.1", [{restart_application, ssl}]}, {<<"5.0\\*">>, [{restart_application, ssl}]}, {<<"4\\.*">>, [{restart_application, ssl}]}, {<<"3\\.*">>, [{restart_application, ssl}]} ], [ - {"5.1.2", - [ - {restart_application, inets} - ] - }, - {"5.1.1", [{restart_application, ssl}] - }, - {"5.1", [ - {load_module, ssl_connection, soft_purge, soft_purge, []} - ] - }, - {"5.1", [{restart_application, ssl}]}, + {"5.1.2", [{restart_application, ssl}]}, + {"5.1.1", [{restart_application, ssl}]}, + {"5.1", [{restart_application, ssl}]}, {<<"5.0\\*">>, [{restart_application, ssl}]}, {<<"4\\.*">>, [{restart_application, ssl}]}, {<<"3\\.*">>, [{restart_application, ssl}]} diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 84728fd311..7b9b6d03bc 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 5.1.2.1 +SSL_VSN = 5.1.2.2 -- cgit v1.2.3 From 5c3c8fc3c64ccf73985add8c123d41134dec7966 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Fri, 31 Jan 2014 15:54:46 +0100 Subject: inets: Corrected appup --- lib/inets/src/inets_app/inets.appup.src | 129 +------------------------------- 1 file changed, 4 insertions(+), 125 deletions(-) diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 3e005379bb..d2475a0e48 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -1,7 +1,7 @@ %% This is an -*- erlang -*- file. %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2012. All Rights Reserved. +%% Copyright Ericsson AB 1999-2014. 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 @@ -18,129 +18,8 @@ {"%VSN%", [ - {"5.9.2.1", - [ - {restart_application, inets} - ] - }, - {"5.9.2", - [ - {load_module, httpd_manager, soft_purge, soft_purge, []} - ] - }, - {"5.9.1", - [ - {load_module, httpd_request_handler, soft_purge, soft_purge, []} - ] - }, - {"5.9", - [ - {load_module, httpd_request_handler, soft_purge, soft_purge, []}, - {load_module, tftp, soft_purge, soft_purge, [inets_service]}, - {load_module, inets_service, soft_purge, soft_purge, []}, - {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}, - {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, - {update, httpc_manager, soft, soft_purge, soft_purge, []} - ] - }, - {"5.8.1", - [ - {load_module, httpd_request_handler, soft_purge, soft_purge, []}, - {load_module, tftp, soft_purge, soft_purge, [inets_service]}, - {load_module, inets_service, soft_purge, soft_purge, []}, - - {load_module, http_uri, soft_purge, soft_purge, []}, - {load_module, httpc_response, soft_purge, soft_purge, [http_uri]}, - - {load_module, httpc, soft_purge, soft_purge, - [http_uri, httpc_manager]}, - - {load_module, inets_app, soft_purge, soft_purge, [inets_sup]}, - {update, inets_sup, soft, soft_purge, soft_purge, []}, - - {load_module, httpd_conf, soft_purge, soft_purge, []}, - {load_module, httpd_response, soft_purge, soft_purge, []}, - {load_module, httpd_script_env, soft_purge, soft_purge, []}, - - {load_module, inets, soft_purge, soft_purge, [inets_trace]}, - {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]}, - {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, - {update, httpd_sup, soft, soft_purge, soft_purge, []}, - {add_module, inets_trace} - ] - }, - {"5.8", - [ - {restart_application, inets} - ] - }, - {"5.7.2", - [ - {restart_application, inets} - ] - } + {<<"5\\.*">>, [{restart_application, inets}]} ], [ - {"5.9.2.1", - [ - {restart_application, inets} - ] - }, - {"5.9.2", - [ - {load_module, httpd_manager, soft_purge, soft_purge, []} - ] - }, - {"5.9.1", - [ - {load_module, httpd_request_handler, soft_purge, soft_purge, []} - ] - }, - {"5.9", - [ - {load_module, httpd_request_handler, soft_purge, soft_purge, []}, - {load_module, tftp, soft_purge, soft_purge, [inets_service]}, - {load_module, inets_service, soft_purge, soft_purge, []}, - {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}, - {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, - {update, httpc_manager, soft, soft_purge, soft_purge, []} - ] - }, - {"5.8.1", - [ - {load_module, httpd_request_handler, soft_purge, soft_purge, []}, - {load_module, tftp, soft_purge, soft_purge, [inets_service]}, - {load_module, inets_service, soft_purge, soft_purge, []}, - - {load_module, http_uri, soft_purge, soft_purge, []}, - {load_module, httpc_response, soft_purge, soft_purge, [http_uri]}, - - {load_module, httpc, soft_purge, soft_purge, - [http_uri, httpc_manager]}, - - {load_module, inets_app, soft_purge, soft_purge, [inets_sup]}, - {update, inets_sup, soft, soft_purge, soft_purge, []}, - - {load_module, httpd_conf, soft_purge, soft_purge, []}, - {load_module, httpd_response, soft_purge, soft_purge, []}, - {load_module, httpd_script_env, soft_purge, soft_purge, []}, - - {load_module, inets, soft_purge, soft_purge, []}, - {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]}, - {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, - {update, httpd_sup, soft, soft_purge, soft_purge, []}, - {remove, {inets_trace, soft_purge, brutal_purge}} - ] - }, - {"5.8", - [ - {restart_application, inets} - ] - }, - {"5.7.2", - [ - {restart_application, inets} - ] - } - ] -}. + {<<"5\\.*">>, [{restart_application, inets}]} + ]}. -- cgit v1.2.3 From bf0c0533cdb86da99d0fafb2a123ff883d6923a0 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Mon, 17 Sep 2012 15:46:33 +0200 Subject: inets: Add tests using external proxy running on localhost --- lib/inets/test/Makefile | 4 +- lib/inets/test/erl_make_certs.erl | 429 +++++++++++++++ lib/inets/test/httpc_proxy_SUITE.erl | 575 +++++++++++++++++++++ .../httpc_proxy_SUITE_data/apache2/apache2.conf | 87 ++++ .../apache2/htdocs/index.html | 4 + .../test/httpc_proxy_SUITE_data/server_proxy.sh | 198 +++++++ 6 files changed, 1296 insertions(+), 1 deletion(-) create mode 100644 lib/inets/test/erl_make_certs.erl create mode 100644 lib/inets/test/httpc_proxy_SUITE.erl create mode 100644 lib/inets/test/httpc_proxy_SUITE_data/apache2/apache2.conf create mode 100644 lib/inets/test/httpc_proxy_SUITE_data/apache2/htdocs/index.html create mode 100755 lib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile index 0fc98eff6f..0ca99e8692 100644 --- a/lib/inets/test/Makefile +++ b/lib/inets/test/Makefile @@ -149,6 +149,7 @@ INETS_ROOT = ../../inets MODULES = \ inets_test_lib \ + erl_make_certs \ ftp_SUITE \ ftp_format_SUITE \ ftp_solaris8_sparc_test \ @@ -169,6 +170,7 @@ MODULES = \ http_format_SUITE \ httpc_SUITE \ httpc_cookie_SUITE \ + httpc_proxy_SUITE \ httpd_SUITE \ httpd_basic_SUITE \ httpd_mod \ @@ -213,7 +215,7 @@ INETS_FILES = inets.config $(INETS_SPECS) INETS_DATADIRS = inets_SUITE_data inets_sup_SUITE_data HTTPD_DATADIRS = httpd_test_data httpd_SUITE_data -HTTPC_DATADIRS = httpc_SUITE_data +HTTPC_DATADIRS = httpc_SUITE_data httpc_proxy_SUITE_data FTP_DATADIRS = ftp_SUITE_data DATADIRS = $(INETS_DATADIRS) $(HTTPD_DATADIRS) $(HTTPC_DATADIRS) $(FTP_DATADIRS) diff --git a/lib/inets/test/erl_make_certs.erl b/lib/inets/test/erl_make_certs.erl new file mode 100644 index 0000000000..bac88dc277 --- /dev/null +++ b/lib/inets/test/erl_make_certs.erl @@ -0,0 +1,429 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. 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. +%% +%% %CopyrightEnd% +%% + +%% Create test certificates + +-module(erl_make_certs). +-include_lib("public_key/include/public_key.hrl"). + +-export([make_cert/1, gen_rsa/1, verify_signature/3, write_pem/3]). +-compile(export_all). + +%%-------------------------------------------------------------------- +%% @doc Create and return a der encoded certificate +%% Option Default +%% ------------------------------------------------------- +%% digest sha1 +%% validity {date(), date() + week()} +%% version 3 +%% subject [] list of the following content +%% {name, Name} +%% {email, Email} +%% {city, City} +%% {state, State} +%% {org, Org} +%% {org_unit, OrgUnit} +%% {country, Country} +%% {serial, Serial} +%% {title, Title} +%% {dnQualifer, DnQ} +%% issuer = {Issuer, IssuerKey} true (i.e. a ca cert is created) +%% (obs IssuerKey migth be {Key, Password} +%% key = KeyFile|KeyBin|rsa|dsa Subject PublicKey rsa or dsa generates key +%% +%% +%% (OBS: The generated keys are for testing only) +%% @spec ([{::atom(), ::term()}]) -> {Cert::binary(), Key::binary()} +%% @end +%%-------------------------------------------------------------------- + +make_cert(Opts) -> + SubjectPrivateKey = get_key(Opts), + {TBSCert, IssuerKey} = make_tbs(SubjectPrivateKey, Opts), + Cert = public_key:pkix_sign(TBSCert, IssuerKey), + true = verify_signature(Cert, IssuerKey, undef), %% verify that the keys where ok + {Cert, encode_key(SubjectPrivateKey)}. + +%%-------------------------------------------------------------------- +%% @doc Writes pem files in Dir with FileName ++ ".pem" and FileName ++ "_key.pem" +%% @spec (::string(), ::string(), {Cert,Key}) -> ok +%% @end +%%-------------------------------------------------------------------- +write_pem(Dir, FileName, {Cert, Key = {_,_,not_encrypted}}) when is_binary(Cert) -> + ok = der_to_pem(filename:join(Dir, FileName ++ ".pem"), + [{'Certificate', Cert, not_encrypted}]), + ok = der_to_pem(filename:join(Dir, FileName ++ "_key.pem"), [Key]). + +%%-------------------------------------------------------------------- +%% @doc Creates a rsa key (OBS: for testing only) +%% the size are in bytes +%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()} +%% @end +%%-------------------------------------------------------------------- +gen_rsa(Size) when is_integer(Size) -> + Key = gen_rsa2(Size), + {Key, encode_key(Key)}. + +%%-------------------------------------------------------------------- +%% @doc Creates a dsa key (OBS: for testing only) +%% the sizes are in bytes +%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()} +%% @end +%%-------------------------------------------------------------------- +gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) -> + Key = gen_dsa2(LSize, NSize), + {Key, encode_key(Key)}. + +%%-------------------------------------------------------------------- +%% @doc Verifies cert signatures +%% @spec (::binary(), ::tuple()) -> ::boolean() +%% @end +%%-------------------------------------------------------------------- +verify_signature(DerEncodedCert, DerKey, _KeyParams) -> + Key = decode_key(DerKey), + case Key of + #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} -> + public_key:pkix_verify(DerEncodedCert, + #'RSAPublicKey'{modulus=Mod, publicExponent=Exp}); + #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} -> + public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}) + end. + +%%%%%%%%%%%%%%%%%%%%%%%%% Implementation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +get_key(Opts) -> + case proplists:get_value(key, Opts) of + undefined -> make_key(rsa, Opts); + rsa -> make_key(rsa, Opts); + dsa -> make_key(dsa, Opts); + Key -> + Password = proplists:get_value(password, Opts, no_passwd), + decode_key(Key, Password) + end. + +decode_key({Key, Pw}) -> + decode_key(Key, Pw); +decode_key(Key) -> + decode_key(Key, no_passwd). + + +decode_key(#'RSAPublicKey'{} = Key,_) -> + Key; +decode_key(#'RSAPrivateKey'{} = Key,_) -> + Key; +decode_key(#'DSAPrivateKey'{} = Key,_) -> + Key; +decode_key(PemEntry = {_,_,_}, Pw) -> + public_key:pem_entry_decode(PemEntry, Pw); +decode_key(PemBin, Pw) -> + [KeyInfo] = public_key:pem_decode(PemBin), + decode_key(KeyInfo, Pw). + +encode_key(Key = #'RSAPrivateKey'{}) -> + {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key), + {'RSAPrivateKey', list_to_binary(Der), not_encrypted}; +encode_key(Key = #'DSAPrivateKey'{}) -> + {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key), + {'DSAPrivateKey', list_to_binary(Der), not_encrypted}. + +make_tbs(SubjectKey, Opts) -> + Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))), + + IssuerProp = proplists:get_value(issuer, Opts, true), + {Issuer, IssuerKey} = issuer(IssuerProp, Opts, SubjectKey), + + {Algo, Parameters} = sign_algorithm(IssuerKey, Opts), + + SignAlgo = #'SignatureAlgorithm'{algorithm = Algo, + parameters = Parameters}, + Subject = case IssuerProp of + true -> %% Is a Root Ca + Issuer; + _ -> + subject(proplists:get_value(subject, Opts),false) + end, + + {#'OTPTBSCertificate'{serialNumber = trunc(random:uniform()*100000000)*10000 + 1, + signature = SignAlgo, + issuer = Issuer, + validity = validity(Opts), + subject = Subject, + subjectPublicKeyInfo = publickey(SubjectKey), + version = Version, + extensions = extensions(Opts) + }, IssuerKey}. + +issuer(true, Opts, SubjectKey) -> + %% Self signed + {subject(proplists:get_value(subject, Opts), true), SubjectKey}; +issuer({Issuer, IssuerKey}, _Opts, _SubjectKey) when is_binary(Issuer) -> + {issuer_der(Issuer), decode_key(IssuerKey)}; +issuer({File, IssuerKey}, _Opts, _SubjectKey) when is_list(File) -> + {ok, [{cert, Cert, _}|_]} = pem_to_der(File), + {issuer_der(Cert), decode_key(IssuerKey)}. + +issuer_der(Issuer) -> + Decoded = public_key:pkix_decode_cert(Issuer, otp), + #'OTPCertificate'{tbsCertificate=Tbs} = Decoded, + #'OTPTBSCertificate'{subject=Subject} = Tbs, + Subject. + +subject(undefined, IsRootCA) -> + User = if IsRootCA -> "RootCA"; true -> user() end, + Opts = [{email, User ++ "@erlang.org"}, + {name, User}, + {city, "Stockholm"}, + {country, "SE"}, + {org, "erlang"}, + {org_unit, "testing dep"}], + subject(Opts); +subject(Opts, _) -> + subject(Opts). + +user() -> + case os:getenv("USER") of + false -> + "test_user"; + User -> + User + end. + +subject(SubjectOpts) when is_list(SubjectOpts) -> + Encode = fun(Opt) -> + {Type,Value} = subject_enc(Opt), + [#'AttributeTypeAndValue'{type=Type, value=Value}] + end, + {rdnSequence, [Encode(Opt) || Opt <- SubjectOpts]}. + +%% Fill in the blanks +subject_enc({name, Name}) -> {?'id-at-commonName', {printableString, Name}}; +subject_enc({email, Email}) -> {?'id-emailAddress', Email}; +subject_enc({city, City}) -> {?'id-at-localityName', {printableString, City}}; +subject_enc({state, State}) -> {?'id-at-stateOrProvinceName', {printableString, State}}; +subject_enc({org, Org}) -> {?'id-at-organizationName', {printableString, Org}}; +subject_enc({org_unit, OrgUnit}) -> {?'id-at-organizationalUnitName', {printableString, OrgUnit}}; +subject_enc({country, Country}) -> {?'id-at-countryName', Country}; +subject_enc({serial, Serial}) -> {?'id-at-serialNumber', Serial}; +subject_enc({title, Title}) -> {?'id-at-title', {printableString, Title}}; +subject_enc({dnQualifer, DnQ}) -> {?'id-at-dnQualifier', DnQ}; +subject_enc(Other) -> Other. + + +extensions(Opts) -> + case proplists:get_value(extensions, Opts, []) of + false -> + asn1_NOVALUE; + Exts -> + lists:flatten([extension(Ext) || Ext <- default_extensions(Exts)]) + end. + +default_extensions(Exts) -> + Def = [{key_usage,undefined}, + {subject_altname, undefined}, + {issuer_altname, undefined}, + {basic_constraints, default}, + {name_constraints, undefined}, + {policy_constraints, undefined}, + {ext_key_usage, undefined}, + {inhibit_any, undefined}, + {auth_key_id, undefined}, + {subject_key_id, undefined}, + {policy_mapping, undefined}], + Filter = fun({Key, _}, D) -> lists:keydelete(Key, 1, D) end, + Exts ++ lists:foldl(Filter, Def, Exts). + +extension({_, undefined}) -> []; +extension({basic_constraints, Data}) -> + case Data of + default -> + #'Extension'{extnID = ?'id-ce-basicConstraints', + extnValue = #'BasicConstraints'{cA=true}, + critical=true}; + false -> + []; + Len when is_integer(Len) -> + #'Extension'{extnID = ?'id-ce-basicConstraints', + extnValue = #'BasicConstraints'{cA=true, pathLenConstraint=Len}, + critical=true}; + _ -> + #'Extension'{extnID = ?'id-ce-basicConstraints', + extnValue = Data} + end; +extension({Id, Data, Critical}) -> + #'Extension'{extnID = Id, extnValue = Data, critical = Critical}. + + +publickey(#'RSAPrivateKey'{modulus=N, publicExponent=E}) -> + Public = #'RSAPublicKey'{modulus=N, publicExponent=E}, + Algo = #'PublicKeyAlgorithm'{algorithm= ?rsaEncryption, parameters='NULL'}, + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, + subjectPublicKey = Public}; +publickey(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) -> + Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-dsa', + parameters={params, #'Dss-Parms'{p=P, q=Q, g=G}}}, + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}. + +validity(Opts) -> + DefFrom0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())-1), + DefTo0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())+7), + {DefFrom, DefTo} = proplists:get_value(validity, Opts, {DefFrom0, DefTo0}), + Format = fun({Y,M,D}) -> lists:flatten(io_lib:format("~w~2..0w~2..0w000000Z",[Y,M,D])) end, + #'Validity'{notBefore={generalTime, Format(DefFrom)}, + notAfter ={generalTime, Format(DefTo)}}. + +sign_algorithm(#'RSAPrivateKey'{}, Opts) -> + Type = case proplists:get_value(digest, Opts, sha1) of + sha1 -> ?'sha1WithRSAEncryption'; + sha512 -> ?'sha512WithRSAEncryption'; + sha384 -> ?'sha384WithRSAEncryption'; + sha256 -> ?'sha256WithRSAEncryption'; + md5 -> ?'md5WithRSAEncryption'; + md2 -> ?'md2WithRSAEncryption' + end, + {Type, 'NULL'}; +sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) -> + {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}}. + +make_key(rsa, _Opts) -> + %% (OBS: for testing only) + gen_rsa2(64); +make_key(dsa, _Opts) -> + gen_dsa2(128, 20). %% Bytes i.e. {1024, 160} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% RSA key generation (OBS: for testing only) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(SMALL_PRIMES, [65537,97,89,83,79,73,71,67,61,59,53, + 47,43,41,37,31,29,23,19,17,13,11,7,5,3]). + +gen_rsa2(Size) -> + P = prime(Size), + Q = prime(Size), + N = P*Q, + Tot = (P - 1) * (Q - 1), + [E|_] = lists:dropwhile(fun(Candidate) -> (Tot rem Candidate) == 0 end, ?SMALL_PRIMES), + {D1,D2} = extended_gcd(E, Tot), + D = erlang:max(D1,D2), + case D < E of + true -> + gen_rsa2(Size); + false -> + {Co1,Co2} = extended_gcd(Q, P), + Co = erlang:max(Co1,Co2), + #'RSAPrivateKey'{version = 'two-prime', + modulus = N, + publicExponent = E, + privateExponent = D, + prime1 = P, + prime2 = Q, + exponent1 = D rem (P-1), + exponent2 = D rem (Q-1), + coefficient = Co + } + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% DSA key generation (OBS: for testing only) +%% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm +%% and the fips_186-3.pdf +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +gen_dsa2(LSize, NSize) -> + Q = prime(NSize), %% Choose N-bit prime Q + X0 = prime(LSize), + P0 = prime((LSize div 2) +1), + + %% Choose L-bit prime modulus P such that p–1 is a multiple of q. + case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of + error -> + gen_dsa2(LSize, NSize); + P -> + G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q. + %% such that This may be done by setting g = h^(p–1)/q mod p, commonly h=2 is used. + + X = prime(20), %% Choose x by some random method, where 0 < x < q. + Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p. + + #'DSAPrivateKey'{version=0, p=P, q=Q, g=G, y=Y, x=X} + end. + +%% See fips_186-3.pdf +dsa_search(T, P0, Q, Iter) when Iter > 0 -> + P = 2*T*Q*P0 + 1, + case is_prime(crypto:mpint(P), 50) of + true -> P; + false -> dsa_search(T+1, P0, Q, Iter-1) + end; +dsa_search(_,_,_,_) -> + error. + + +%%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +prime(ByteSize) -> + Rand = odd_rand(ByteSize), + crypto:erlint(prime_odd(Rand, 0)). + +prime_odd(Rand, N) -> + case is_prime(Rand, 50) of + true -> + Rand; + false -> + NotPrime = crypto:erlint(Rand), + prime_odd(crypto:mpint(NotPrime+2), N+1) + end. + +%% see http://en.wikipedia.org/wiki/Fermat_primality_test +is_prime(_, 0) -> true; +is_prime(Candidate, Test) -> + CoPrime = odd_rand(<<0,0,0,4, 10000:32>>, Candidate), + case crypto:mod_exp(CoPrime, Candidate, Candidate) of + CoPrime -> is_prime(Candidate, Test-1); + _ -> false + end. + +odd_rand(Size) -> + Min = 1 bsl (Size*8-1), + Max = (1 bsl (Size*8))-1, + odd_rand(crypto:mpint(Min), crypto:mpint(Max)). + +odd_rand(Min,Max) -> + Rand = <> = crypto:rand_uniform(Min,Max), + BitSkip = (Sz+4)*8-1, + case Rand of + Odd = <<_:BitSkip, 1:1>> -> Odd; + Even = <<_:BitSkip, 0:1>> -> + crypto:mpint(crypto:erlint(Even)+1) + end. + +extended_gcd(A, B) -> + case A rem B of + 0 -> + {0, 1}; + N -> + {X, Y} = extended_gcd(B, N), + {Y, X-Y*(A div B)} + end. + +pem_to_der(File) -> + {ok, PemBin} = file:read_file(File), + public_key:pem_decode(PemBin). + +der_to_pem(File, Entries) -> + PemBin = public_key:pem_encode(Entries), + file:write_file(File, PemBin). diff --git a/lib/inets/test/httpc_proxy_SUITE.erl b/lib/inets/test/httpc_proxy_SUITE.erl new file mode 100644 index 0000000000..2475489bb3 --- /dev/null +++ b/lib/inets/test/httpc_proxy_SUITE.erl @@ -0,0 +1,575 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. 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. +%% +%% %CopyrightEnd% +%% +%% + +%% +%% ts:run(inets, httpc_proxy_SUITE, [batch]). +%% ct:run("../inets_test", httpc_proxy_SUITE). +%% + +-module(httpc_proxy_SUITE). + +-include_lib("common_test/include/ct.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). + +-define(LOCAL_PROXY_SCRIPT, "server_proxy.sh"). +-define(p(F, A), % Debug printout + begin + io:format( + "~w ~w: " ++ begin F end, + [self(),?MODULE] ++ begin A end) + end). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + +suite() -> + [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group,local_proxy}, + {group,local_proxy_https}]. + +groups() -> + [{local_proxy,[], + [http_emulate_lower_versions + |local_proxy_cases()]}, + {local_proxy_https,[], + local_proxy_cases()}]. + +%% internal functions + +local_proxy_cases() -> + [http_head, + http_get, + http_options, + http_trace, + http_post, + http_put, + http_delete, + http_headers, + http_proxy_auth, + http_doesnotexist, + http_stream, + http_not_modified_otp_6821]. + +%%-------------------------------------------------------------------- + +init_per_suite(Config0) -> + case init_apps([crypto,public_key], Config0) of + Config when is_list(Config) -> + make_cert_files(dsa, "server-", Config), + Config; + Other -> + Other + end. + +end_per_suite(_Config) -> + [app_stop(App) || App <- r(suite_apps())], + ok. + +%% internal functions + +suite_apps() -> + [crypto,public_key]. + +%%-------------------------------------------------------------------- + +init_per_group(local_proxy, Config) -> + init_local_proxy([{protocol,http}|Config]); +init_per_group(local_proxy_https, Config) -> + init_local_proxy([{protocol,https}|Config]). + +end_per_group(Group, Config) + when + Group =:= local_proxy; + Group =:= local_proxy_https -> + rcmd_local_proxy(["stop"], Config), + Config; +end_per_group(_, Config) -> + Config. + +%%-------------------------------------------------------------------- + +init_per_testcase(Case, Config0) -> + ct:timetrap({seconds,30}), + Apps = apps(Case, Config0), + case init_apps(Apps, Config0) of + Config when is_list(Config) -> + case app_start(inets, Config) of + ok -> + Config; + Error -> + [app_stop(N) || N <- [inets|r(Apps)]], + ct:fail({could_not_init_inets,Error}) + end; + E3 -> + E3 + end. + +end_per_testcase(_Case, Config) -> + app_stop(inets), + Config. + +%% internal functions + +apps(_Case, Config) -> + case ?config(protocol, Config) of + https -> + [ssl]; + _ -> + [] + end. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +http_head(doc) -> + ["Test http/https HEAD request."]; +http_head(Config) when is_list(Config) -> + Method = head, + URL = url("/index.html", Config), + Request = {URL,[]}, + HttpOpts = [], + Opts = [], + {ok,{{_,200,_},[_|_],[]}} = + httpc:request(Method, Request, HttpOpts, Opts), + ok. + +%%-------------------------------------------------------------------- + +http_get(doc) -> + ["Test http/https GET request."]; +http_get(Config) when is_list(Config) -> + Method = get, + URL = url("/index.html", Config), + Request = {URL,[]}, + Timeout = timer:seconds(1), + ConnTimeout = Timeout + timer:seconds(1), + + HttpOpts1 = [{timeout,Timeout},{connect_timeout,ConnTimeout}], + Opts1 = [], + {ok,{{_,200,_},[_|_],[_|_]=B1}} = + httpc:request(Method, Request, HttpOpts1, Opts1), + inets_test_lib:check_body(B1), + + HttpOpts2 = [], + Opts2 = [{body_format,binary}], + {ok,{{_,200,_},[_|_],B2}} = + httpc:request(Method, Request, HttpOpts2, Opts2), + inets_test_lib:check_body(binary_to_list(B2)). + +%%-------------------------------------------------------------------- + +http_options(doc) -> + ["Perform an OPTIONS request."]; +http_options(Config) when is_list(Config) -> + Method = options, + URL = url("/index.html", Config), + Request = {URL,[]}, + HttpOpts = [], + Opts = [], + {ok,{{_,200,_},Headers,_}} = + httpc:request(Method, Request, HttpOpts, Opts), + {value,_} = lists:keysearch("allow", 1, Headers), + ok. + +%%-------------------------------------------------------------------- + +http_trace(doc) -> + ["Perform a TRACE request."]; +http_trace(Config) when is_list(Config) -> + Method = trace, + URL = url("/index.html", Config), + Request = {URL,[]}, + HttpOpts = [], + Opts = [], + {ok,{{_,200,_},[_|_],"TRACE "++_}} = + httpc:request(Method, Request, HttpOpts, Opts), + ok. + +%%-------------------------------------------------------------------- + +http_post(doc) -> + ["Perform a POST request that goes through a proxy. When the " + "request goes to an ordinary file it seems the POST data " + "is ignored."]; +http_post(Config) when is_list(Config) -> + Method = post, + URL = url("/index.html", Config), + Request = {URL,[],"text/plain","foobar"}, + HttpOpts = [], + Opts = [], + {ok,{{_,200,_},[_|_],[_|_]}} = + httpc:request(Method, Request, HttpOpts, Opts), + ok. + +%%-------------------------------------------------------------------- + +http_put(doc) -> + ["Perform a PUT request. The server will not allow it " + "but we only test sending the request."]; +http_put(Config) when is_list(Config) -> + Method = put, + URL = url("/put.html", Config), + Content = + "

foo

bar

", + Request = {URL,[],"html",Content}, + HttpOpts = [], + Opts = [], + {ok,{{_,405,_},[_|_],[_|_]}} = + httpc:request(Method, Request, HttpOpts, Opts), + ok. + +%%-------------------------------------------------------------------- + +http_delete(doc) -> + ["Perform a DELETE request that goes through a proxy. Note the server " + "will reject the request with a 405 Method Not Allowed," + "but this is just a test of sending the request."]; +http_delete(Config) when is_list(Config) -> + Method = delete, + URL = url("/delete.html", Config), + Request = {URL,[]}, + HttpOpts = [], + Opts = [], + {ok,{{_,405,_},[_|_],[_|_]}} = + httpc:request(Method, Request, HttpOpts, Opts), + ok. + +%%-------------------------------------------------------------------- + +http_headers(doc) -> + ["Use as many request headers as possible"]; +http_headers(Config) when is_list(Config) -> + Method = get, + URL = url("/index.html", Config), + Headers = + [{"Accept", + "text/*, text/html, text/html;level=1, */*"}, + {"Accept-Charset", + "iso-8859-5, unicode-1-1;q=0.8"}, + {"Accept-Encoding", "*"}, + {"Accept-Language", + "sv, en-gb;q=0.8, en;q=0.7"}, + {"User-Agent", "inets"}, + {"Max-Forwards","5"}, + {"Referer", + "http://otp.ericsson.se:8000/product/internal"}], + Request = {URL,Headers}, + HttpOpts = [], + Opts = [], + {ok,{{_,200,_},[_|_],[_|_]}} = + httpc:request(Method, Request, HttpOpts, Opts), + ok. + +%%-------------------------------------------------------------------- + +http_proxy_auth(doc) -> + ["Test the code for sending of proxy authorization."]; +http_proxy_auth(Config) when is_list(Config) -> + %% Our proxy seems to ignore the header, however our proxy + %% does not requirer an auth header, but we want to know + %% atleast the code for sending the header does not crash! + Method = get, + URL = url("/index.html", Config), + Request = {URL,[]}, + HttpOpts = [{proxy_auth,{"foo","bar"}}], + Opts = [], + {ok,{{_,200,_},[_|_],[_|_]}} = + httpc:request(Method, Request, HttpOpts, Opts), + ok. + +%%-------------------------------------------------------------------- + +http_doesnotexist(doc) -> + ["Test that we get a 404 when the page is not found."]; +http_doesnotexist(Config) when is_list(Config) -> + Method = get, + URL = url("/doesnotexist.html", Config), + Request = {URL,[]}, + HttpOpts = [{proxy_auth,{"foo","bar"}}], + Opts = [], + {ok,{{_,404,_},[_|_],[_|_]}} = + httpc:request(Method, Request, HttpOpts, Opts), + ok. + +%%-------------------------------------------------------------------- + +http_stream(doc) -> + ["Test the option stream for asynchronous requests"]; +http_stream(Config) when is_list(Config) -> + Method = get, + URL = url("/index.html", Config), + Request = {URL,[]}, + HttpOpts = [], + + Opts1 = [{body_format,binary}], + {ok,{{_,200,_},[_|_],Body}} = + httpc:request(Method, Request, HttpOpts, Opts1), + + Opts2 = [{sync,false},{stream,self}], + {ok,RequestId} = + httpc:request(Method, Request, HttpOpts, Opts2), + receive + {http,{RequestId,stream_start,[_|_]}} -> + ok + end, + case http_stream(RequestId, <<>>) of + Body -> ok + end. + %% StreamedBody = http_stream(RequestId, <<>>), + %% Body =:= StreamedBody, + %% ok. + +http_stream(RequestId, Body) -> + receive + {http,{RequestId,stream,Bin}} -> + http_stream(RequestId, <>); + {http,{RequestId,stream_end,_Headers}} -> + Body + end. + +%%-------------------------------------------------------------------- + +http_emulate_lower_versions(doc) -> + ["Perform requests as 0.9 and 1.0 clients."]; +http_emulate_lower_versions(Config) when is_list(Config) -> + Method = get, + URL = url("/index.html", Config), + Request = {URL,[]}, + Opts = [], + + HttpOpts1 = [{version,"HTTP/0.9"}], + {ok,[_|_]=B1} = + httpc:request(Method, Request, HttpOpts1, Opts), + inets_test_lib:check_body(B1), + + HttpOpts2 = [{version,"HTTP/1.0"}], + {ok,{{_,200,_},[_|_],[_|_]=B2}} = + httpc:request(Method, Request, HttpOpts2, Opts), + inets_test_lib:check_body(B2), + + HttpOpts3 = [{version,"HTTP/1.1"}], + {ok,{{_,200,_},[_|_],[_|_]=B3}} = + httpc:request(Method, Request, HttpOpts3, Opts), + inets_test_lib:check_body(B3), + + ok. + +%%-------------------------------------------------------------------- +http_not_modified_otp_6821(doc) -> + ["If unmodified no body should be returned"]; +http_not_modified_otp_6821(Config) when is_list(Config) -> + Method = get, + URL = url("/index.html", Config), + Opts = [], + + Request1 = {URL,[]}, + HttpOpts1 = [], + {ok,{{_,200,_},ReplyHeaders,[_|_]}} = + httpc:request(Method, Request1, HttpOpts1, Opts), + ETag = header_value("etag", ReplyHeaders), + LastModified = header_value("last-modified", ReplyHeaders), + + Request2 = + {URL, + [{"If-None-Match",ETag}, + {"If-Modified-Since",LastModified}]}, + HttpOpts2 = [{timeout,15000}], % Limit wait for bug result + {ok,{{_,304,_},_,[]}} = % Page Unchanged + httpc:request(Method, Request2, HttpOpts2, Opts), + + ok. + +header_value(Name, [{HeaderName,HeaderValue}|Headers]) -> + case string:to_lower(HeaderName) of + Name -> + HeaderValue; + _ -> + header_value(Name, Headers) + end. + +%%-------------------------------------------------------------------- +%% Internal Functions ------------------------------------------------ +%%-------------------------------------------------------------------- + +init_apps([], Config) -> + Config; +init_apps([App|Apps], Config) -> + case app_start(App, Config) of + ok -> + init_apps(Apps, Config); + Error -> + Msg = + lists:flatten( + io_lib:format( + "Could not start ~p due to ~p.~n", + [App, Error])), + {skip,Msg} + end. + +app_start(App, Config) -> + try + case App of + crypto -> + crypto:stop(), + ok = crypto:start(); + inets -> + application:stop(App), + ok = application:start(App), + case ?config(proxy, Config) of + undefined -> ok; + {_,ProxySpec} -> + ok = httpc:set_options([{proxy,ProxySpec}]) + end; + _ -> + application:stop(App), + ok = application:start(App) + end + catch + Class:Reason -> + {exception,Class,Reason} + end. + +app_stop(App) -> + application:stop(App). + +make_cert_files(Alg, Prefix, Config) -> + PrivDir = ?config(priv_dir, Config), + CaInfo = {CaCert,_} = erl_make_certs:make_cert([{key,Alg}]), + {Cert,CertKey} = erl_make_certs:make_cert([{key,Alg},{issuer,CaInfo}]), + CaCertFile = filename:join(PrivDir, Prefix++"cacerts.pem"), + CertFile = filename:join(PrivDir, Prefix++"cert.pem"), + KeyFile = filename:join(PrivDir, Prefix++"key.pem"), + der_to_pem(CaCertFile, [{'Certificate', CaCert, not_encrypted}]), + der_to_pem(CertFile, [{'Certificate', Cert, not_encrypted}]), + der_to_pem(KeyFile, [CertKey]), + ok. + +der_to_pem(File, Entries) -> + PemBin = public_key:pem_encode(Entries), + file:write_file(File, PemBin). + + + +url(AbsPath, Config) -> + Protocol = ?config(protocol, Config), + {ServerName,ServerPort} = ?config(Protocol, Config), + atom_to_list(Protocol) ++ "://" ++ + ServerName ++ ":" ++ integer_to_list(ServerPort) ++ + AbsPath. + +%%-------------------------------------------------------------------- + +init_local_proxy(Config) -> + case os:type() of + {unix,_} -> + case rcmd_local_proxy(["start"], Config) of + {0,[":STARTED:"++String]} -> + init_local_proxy_string(String, Config); + {_,[":SKIP:"++_|_]}=Reason -> + {skip,Reason}; + Error -> + rcmd_local_proxy(["stop"], Config), + ct:fail({local_proxy_start_failed,Error}) + end; + _ -> + {skip,"Platform can not run local proxy start script"} + end. + +init_local_proxy_string(String, Config) -> + {Proxy,Server} = split($|, String), + {ProxyName,ProxyPort} = split($:, Proxy), + {ServerName,ServerPorts} = split($:, Server), + {ServerHttpPort,ServerHttpsPort} = split($:, ServerPorts), + [{proxy,{local,{{ProxyName,list_to_integer(ProxyPort)},[]}}}, + {http,{ServerName,list_to_integer(ServerHttpPort)}}, + {https,{ServerName,list_to_integer(ServerHttpsPort)}} + |Config]. + +rcmd_local_proxy(Args, Config) -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + Script = filename:join(DataDir, ?LOCAL_PROXY_SCRIPT), + rcmd(Script, Args, [{cd,PrivDir}]). + +rcmd(Cmd, Args, Opts) -> + Port = + erlang:open_port( + {spawn_executable,Cmd}, + [{args,Args},{line,80},exit_status,eof,hide|Opts]), + rcmd_loop(Port, [], [], undefined, false). + +rcmd_loop(Port, Lines, Buf, Exit, EOF) -> + receive + {Port,{data,{Flag,Line}}} -> + case Flag of + noeol -> + rcmd_loop(Port, Lines, r(Line, Buf), Exit, EOF); + eol -> + rcmd_loop(Port, [r(Buf, Line)|Lines], [], Exit, EOF) + end; + {Port,{exit_status,Status}} when Exit =:= undefined -> + case EOF of + true -> + rcmd_close(Port, Lines, Buf, Status); + false -> + rcmd_loop(Port, Lines, Buf, Status, EOF) + end; + {Port,eof} when EOF =:= false -> + case Exit of + undefined -> + rcmd_loop(Port, Lines, Buf, Exit, true); + Status -> + rcmd_close(Port, Lines, Buf, Status) + end; + {Port,_}=Unexpected -> + ct:fail({unexpected_from_port,Unexpected}) + end. + +rcmd_close(Port, Lines, Buf, Status) -> + catch port_close(Port), + case Buf of + [] -> + {Status,Lines}; + _ -> + {Status,[r(Buf)|Lines]} + end. + +%%-------------------------------------------------------------------- + +%% Split on first match of X in Ys, do not include X in neither part +split(X, Ys) -> + split(X, Ys, []). +%% +split(X, [X|Ys], Rs) -> + {r(Rs),Ys}; +split(X, [Y|Ys], Rs) -> + split(X, Ys, [Y|Rs]). + +r(L) -> lists:reverse(L). +r(L, R) -> lists:reverse(L, R). diff --git a/lib/inets/test/httpc_proxy_SUITE_data/apache2/apache2.conf b/lib/inets/test/httpc_proxy_SUITE_data/apache2/apache2.conf new file mode 100644 index 0000000000..37af88c510 --- /dev/null +++ b/lib/inets/test/httpc_proxy_SUITE_data/apache2/apache2.conf @@ -0,0 +1,87 @@ +## Simple Apache 2 configuration file for daily test very local http server +## +## %CopyrightBegin% +## +## Copyright Ericsson AB 2012. 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. +## +## %CopyrightEnd% +## +## Author: Raimo Niskanen, Erlang/OTP +# +LockFile ${APACHE_LOCK_DIR}/accept.lock +PidFile ${APACHE_PID_FILE} + +Timeout 300 + +User ${APACHE_RUN_USER} +Group ${APACHE_RUN_GROUP} + +DefaultType text/plain +HostnameLookups Off +ErrorLog ${APACHE_LOG_DIR}/error.log +LogLevel warn + +Include ${APACHE_MODS_DIR}/*.load +Include ${APACHE_MODS_DIR}/*.conf + +Listen ${APACHE_HTTP_PORT} http + + + Listen ${APACHE_HTTPS_PORT} https + SSLMutex file:${APACHE_LOCK_DIR}/ssl_mutex + + +# +# Listen 8443 +# + +#LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined +LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined +#LogFormat "%h %l %u %t \"%r\" %>s %O" common +#LogFormat "%{Referer}i -> %U" referer +#LogFormat "%{User-agent}i" agent + +CustomLog ${APACHE_LOG_DIR}/access.log combined + + + AllowOverride None + Order Deny,Allow + Deny from all + + +ServerTokens Minimal +ServerSignature Off +KeepAlive On +KeepAliveTimeout 5 + +ServerName ${APACHE_SERVER_NAME} +ServerAdmin webmaster@${APACHE_SERVER_NAME} +DocumentRoot ${APACHE_DOCROOT} + + Options Indexes FollowSymLinks MultiViews + AllowOverride None + Order allow,deny + Allow from all + + + + + + + + SSLCertificateFile ${APACHE_CERTS_DIR}/server-cert.pem + SSLCertificateKeyFile ${APACHE_CERTS_DIR}/server-key.pem + SSLEngine on + + diff --git a/lib/inets/test/httpc_proxy_SUITE_data/apache2/htdocs/index.html b/lib/inets/test/httpc_proxy_SUITE_data/apache2/htdocs/index.html new file mode 100644 index 0000000000..1c70d95348 --- /dev/null +++ b/lib/inets/test/httpc_proxy_SUITE_data/apache2/htdocs/index.html @@ -0,0 +1,4 @@ +

It works!

+

This is the default web page for this server.

+

The web server software is running but no content has been added, yet.

+ diff --git a/lib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh b/lib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh new file mode 100755 index 0000000000..d5f18c937f --- /dev/null +++ b/lib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh @@ -0,0 +1,198 @@ +#! /bin/sh +## +## Command file to handle external webserver and proxy +## apache2 and tinyproxy. +## +## %CopyrightBegin% +## +## Copyright Ericsson AB 2012. 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. +## +## %CopyrightEnd% +## +## Author: Raimo Niskanen, Erlang/OTP +# + +PATH=/usr/local/bin:/usr/local/sbin:/bin:/usr/bin:/sbin:/usr/sbin +SHELL=/bin/sh +unset CDPATH ENV BASH_ENV +IFS=' + ' + +APACHE_MODS_AVAILABLE_DIR="/etc/apache2/mods-available" +MODS="authz_host.load mime.conf mime.load ssl.conf ssl.load" + +APACHE_HTTP_PORT=8080 +APACHE_HTTPS_PORT=8443 +APACHE_SERVER_NAME=localhost +export APACHE_HTTP_PORT APACHE_HTTPS_PORT APACHE_SERVER_NAME + +PROXY_SERVER_NAME=localhost +PROXY_PORT=8000 +export PROXY_SERVER_NAME PROXY_PORT + +# All stdout goes to the calling erlang port, therefore +# these helpers push all side info to stderr. +status () { echo "$@"; } +info () { echo "$@" 1>&2; } +die () { REASON="$?"; status "$@"; exit "$REASON"; } +cmd () { "$@" 1>&2; } +silent () { "$@" 1>/dev/null 2>&1; } + +wait_for_pidfile () { + PIDFILE="${1:?Missing argument: PidFile}" + for t in 1 1 1 2 2 3 3 3 4; do + PID="`head -1 "$1" 2>/dev/null`" && [ :"$PID" != : ] && break + sleep $t + done + [ :"$PID" = : ] && die ":ERROR:No or empty PidFile: $1" + info "Started $PIDFILE[$PID]." +} + +kill_and_wait () { + PID_FILE="${1:?Missing argument: PidFile}" + if [ -f "$PID_FILE" ]; then + PID="`head -1 "$PID_FILE" 2>/dev/null`" + [ :"$PID" = : ] && \ + info "Empty Pid file: $1" + info "Stopping $1 [$PID]..." + shift + case :"${1:?Missing argument: kill command}" in + :kill) + [ :"$PID" = : ] || cmd kill "$PID";; + :*) + cmd "$@";; + esac + wait "$PID" + for t in 1 1 1 2; do + sleep $t + [ -e "$PID_FILE" ] || break + done + silent rm "$PID_FILE" + else + info "No pid file: $1" + fi +} + + +PRIV_DIR="`pwd`" +DATA_DIR="`dirname "$0"`" +DATA_DIR="`cd "$DATA_DIR" && pwd`" + +silent type apache2ctl || \ + die ":SKIP: Can not find apache2ctl." +silent type tinyproxy || \ + die ":SKIP: Can not find tinyproxy." + +[ -d "$APACHE_MODS_AVAILABLE_DIR" ] || \ + die ":SKIP:Can not locate modules dir $APACHE_MODS_AVAILABLE_DIR." + +silent mkdir apache2 tinyproxy +cd apache2 || \ + die ":ERROR:Can not cd to apache2" +CWD="`pwd`" +(cd ../tinyproxy) || \ + die ":ERROR:Can not cd to ../tinyproxy" + +unset APACHE_HTTPD APACHE_LYNX APACHE_STATUSURL + +## apache2ctl envvars variables +APACHE_CONFDIR="$DATA_DIR/apache2" +[ -f "$APACHE_CONFDIR"/apache2.conf ] || \ + die ":SKIP:No config file: $APACHE_CONFDIR/apache2.conf." +APACHE_RUN_USER=`id | sed 's/^uid=[0-9]\{1,\}(\([^)]*\)).*/\1/'` +APACHE_RUN_GROUP=`id | sed 's/.*[ ]gid=[0-9]\{1,\}(\([^)]*\)).*/\1/'` +APACHE_RUN_DIR="$CWD/run" +APACHE_PID_FILE="$APACHE_RUN_DIR/pid" +APACHE_LOCK_DIR="$CWD/lock" +APACHE_LOG_DIR="$CWD/log" +export APACHE_CONFDIR APACHE_RUN_USER APACHE_RUN_GROUP +export APACHE_RUN_DIR APACHE_PID_FILE +export APACHE_LOCK_DIR APACHE_LOG_DIR +silent cmd mkdir "$APACHE_CONFDIR" +silent cmd mkdir "$APACHE_RUN_DIR" "$APACHE_LOCK_DIR" "$APACHE_LOG_DIR" + +## Our apache2.conf additional variables +APACHE_MODS_DIR="$CWD/mods" +APACHE_DOCROOT="$APACHE_CONFDIR/htdocs" +APACHE_CERTS_DIR="$PRIV_DIR" +export APACHE_MODS_DIR APACHE_DOCROOT APACHE_CERTS_DIR +[ -d "$APACHE_MODS_DIR" ] || { + cmd mkdir "$APACHE_MODS_DIR" + for MOD in $MODS; do + cmd ln -s "$APACHE_MODS_AVAILABLE_DIR/$MOD" "$APACHE_MODS_DIR" || { + die ":ERROR:ln of apache 2 module $MOD failed" + } + done +} + +case :"${1:?}" in + + :start) + info "Starting apache2..." + cmd apache2ctl start + [ $? = 0 ] || \ + die ":ERROR: apache2 did not start." + wait_for_pidfile "$APACHE_PID_FILE" + + info "Starting tinyproxy..." + cmd cd ../tinyproxy || \ + die ":ERROR:Can not cd to `pwd`/../tinyproxy" + cat >tinyproxy.conf </dev/null 2>&1 Date: Fri, 28 Sep 2012 11:13:14 +0200 Subject: inets: Remove obsolete testcases that are now in the proxy suite --- lib/inets/test/httpc_SUITE.erl | 466 +---------------------------------------- 1 file changed, 2 insertions(+), 464 deletions(-) diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 1cdd96f0b0..cb81d2cc5e 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -34,9 +34,6 @@ -compile(export_all). %% Test server specific exports --define(PROXY_URL, "http://www.erlang.org"). --define(PROXY, "www-proxy.ericsson.se"). --define(PROXY_PORT, 8080). -define(IP_PORT, 8998). -define(SSL_PORT, 8999). -define(NOT_IN_USE_PORT, 8997). @@ -91,7 +88,6 @@ all() -> options, headers_as_is, selecting_session, - {group, proxy}, {group, ssl}, {group, stream}, {group, ipv6}, @@ -101,18 +97,6 @@ all() -> groups() -> [ - {proxy, [], [proxy_options, - proxy_head, - proxy_get, - proxy_trace, - proxy_post, - proxy_put, - proxy_delete, - proxy_auth, - proxy_headers, - proxy_emulate_lower_versions, - proxy_page_does_not_exist, - proxy_https_not_supported]}, {ssl, [], [ssl_head, essl_head, ssl_get, @@ -120,13 +104,11 @@ groups() -> ssl_trace, essl_trace]}, {stream, [], [http_stream, - http_stream_once, - proxy_stream]}, + http_stream_once]}, {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}, @@ -287,66 +269,6 @@ init_per_testcase(Case, Timeout, Config) -> init_per_testcase_ssl(essl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]); - "proxy_" ++ Rest -> - io:format("init_per_testcase -> Rest: ~p~n", [Rest]), - case Rest of - "https_not_supported" -> - tsp("init_per_testcase -> [proxy case] start inets"), - inets:start(), - tsp("init_per_testcase -> " - "[proxy case] start crypto, public_key and ssl"), - try ?ENSURE_STARTED([crypto, public_key, ssl]) of - ok -> - [{watchdog, Dog} | TmpConfig] - catch - throw:{error, {failed_starting, App, _}} -> - SkipString = - "Could not start " ++ atom_to_list(App), - skip(SkipString); - _:X -> - SkipString = - lists:flatten( - io_lib:format("Failed starting apps: ~p", [X])), - skip(SkipString) - 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 -> - 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("proxy not responding") | TmpConfig] - end - end; - "ipv6_" ++ _Rest -> %% Ensure needed apps (crypto, public_key and ssl) are started try ?ENSURE_STARTED([crypto, public_key, ssl]) of @@ -415,13 +337,6 @@ init_per_testcase(Case, Timeout, Config) -> %% so this value will be overwritten (see "ipv6_" below). %% - %% This will fail for the ipv6_ - cases (but that is ok) - ProxyExceptions = ["localhost", ?IPV6_LOCAL_HOST], - tsp("init_per_testcase -> Options before proxy set: ~n~p", - [httpc:get_options(all)]), - ok = httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT}, ProxyExceptions}}]), - tsp("init_per_testcase -> Options after proxy set: ~n~p", - [httpc:get_options(all)]), inets:enable_trace(max, io, httpc), %% inets:enable_trace(max, io, all), %% snmp:set_trace([gen_tcp]), @@ -915,7 +830,7 @@ pipeline_await_async_reply(ReqIds, _, Acc) -> %%------------------------------------------------------------------------- http_trace(doc) -> - ["Perform a TRACE request that goes through a proxy."]; + ["Perform a TRACE request."]; http_trace(suite) -> []; http_trace(Config) when is_list(Config) -> @@ -1553,260 +1468,6 @@ http_cookie(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet6fb4}]), ok. -%%------------------------------------------------------------------------- -proxy_options(doc) -> - ["Perform a OPTIONS request that goes through a proxy."]; -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 - {ok, {{_,200,_}, Headers, _}} -> - case lists:keysearch("allow", 1, Headers) of - {value, {"allow", _}} -> - ok; - _ -> - tsf(http_options_request_failed) - end; - Unexpected -> - tsf({unexpected_result, Unexpected}) - end; - Reason -> - skip(Reason) - end. - - -%%------------------------------------------------------------------------- -proxy_head(doc) -> - ["Perform a HEAD request that goes through a proxy."]; -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 - {ok, {{_,200, _}, [_ | _], []}} -> - ok; - Unexpected -> - tsf({unexpected_result, Unexpected}) - end; - Reason -> - skip(Reason) - end. - - -%%------------------------------------------------------------------------- -proxy_get(doc) -> - ["Perform a GET request that goes through a proxy."]; -proxy_get(suite) -> - []; -proxy_get(Config) when is_list(Config) -> - case ?config(skip, Config) of - undefined -> - case httpc:request(get, {?PROXY_URL, []}, [], []) of - {ok, {{_,200,_}, [_ | _], Body = [_ | _]}} -> - inets_test_lib:check_body(Body); - Unexpected -> - tsf({unexpected_result, Unexpected}) - end; - Reason -> - skip(Reason) - end. - -%%------------------------------------------------------------------------- -proxy_emulate_lower_versions(doc) -> - ["Perform requests as 0.9 and 1.0 clients."]; -proxy_emulate_lower_versions(suite) -> - []; -proxy_emulate_lower_versions(Config) when is_list(Config) -> - case ?config(skip, Config) of - undefined -> - Result09 = pelv_get("HTTP/0.9"), - case Result09 of - {ok, [_| _] = Body0} -> - inets_test_lib:check_body(Body0), - ok; - _ -> - tsf({unexpected_result, "HTTP/0.9", Result09}) - end, - - %% We do not check the version here as many servers - %% do not behave according to the rfc and send - %% 1.1 in its response. - Result10 = pelv_get("HTTP/1.0"), - case Result10 of - {ok,{{_, 200, _}, [_ | _], Body1 = [_ | _]}} -> - inets_test_lib:check_body(Body1), - ok; - _ -> - tsf({unexpected_result, "HTTP/1.0", Result10}) - end, - - Result11 = pelv_get("HTTP/1.1"), - case Result11 of - {ok, {{"HTTP/1.1", 200, _}, [_ | _], Body2 = [_ | _]}} -> - inets_test_lib:check_body(Body2); - _ -> - tsf({unexpected_result, "HTTP/1.1", Result11}) - end; - - Reason -> - skip(Reason) - end. - -pelv_get(Version) -> - httpc:request(get, {?PROXY_URL, []}, [{version, Version}], []). - - -%%------------------------------------------------------------------------- -proxy_trace(doc) -> - ["Perform a TRACE request that goes through a proxy."]; -proxy_trace(suite) -> - []; -proxy_trace(Config) when is_list(Config) -> - %%{ok, {{_,200,_}, [_ | _], "TRACE " ++ _}} = - %% httpc:request(trace, {?PROXY_URL, []}, [], []), - skip("HTTP TRACE is no longer allowed on the ?PROXY_URL server due " - "to security reasons"). - - -%%------------------------------------------------------------------------- -proxy_post(doc) -> - ["Perform a POST request that goes through a proxy. Note the server" - " will reject the request this is a test of the sending of the" - " request."]; -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, [], - "text/plain", "foobar"}, [],[]) of - {ok, {{_,405,_}, [_ | _], [_ | _]}} -> - ok; - Unexpected -> - tsf({unexpected_result, Unexpected}) - end; - Reason -> - skip(Reason) - end. - - -%%------------------------------------------------------------------------- -proxy_put(doc) -> - ["Perform a PUT request that goes through a proxy. Note the server" - " will reject the request this is a test of the sending of the" - " request."]; -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", [], - "html", "

foo

" - "

bar

"}, [], []) of - {ok, {{_,405,_}, [_ | _], [_ | _]}} -> - ok; - Unexpected -> - tsf({unexpected_result, Unexpected}) - end; - Reason -> - skip(Reason) - end. - - -%%------------------------------------------------------------------------- -proxy_delete(doc) -> - ["Perform a DELETE request that goes through a proxy. Note the server" - " will reject the request this is a test of the sending of the" - " request. But as the file does not exist the return code will" - " be 404 not found."]; -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", - case httpc:request(delete, {URL, []}, [], []) of - {ok, {{_,404,_}, [_ | _], [_ | _]}} -> - ok; - Unexpected -> - tsf({unexpected_result, Unexpected}) - end; - Reason -> - skip(Reason) - end. - - -%%------------------------------------------------------------------------- -proxy_headers(doc) -> - ["Use as many request headers as possible"]; -proxy_headers(suite) -> - []; -proxy_headers(Config) when is_list(Config) -> - case ?config(skip, Config) of - undefined -> - {ok, {{_,200,_}, [_ | _], [_ | _]}} - = httpc:request(get, {?PROXY_URL, - [ - {"Accept", - "text/*, text/html," - " text/html;level=1," - " */*"}, - {"Accept-Charset", - "iso-8859-5, unicode-1-1;" - "q=0.8"}, - {"Accept-Encoding", "*"}, - {"Accept-Language", - "sv, en-gb;q=0.8," - " en;q=0.7"}, - {"User-Agent", "inets"}, - {"Max-Forwards","5"}, - {"Referer", - "http://otp.ericsson.se:8000" - "/product/internal"} - ]}, [], []), - ok; - Reason -> - skip(Reason) - end. - - -%%------------------------------------------------------------------------- -proxy_auth(doc) -> - ["Test the code for sending of proxy authorization."]; -proxy_auth(suite) -> - []; -proxy_auth(Config) when is_list(Config) -> - %% Our proxy seems to ignore the header, however our proxy - %% does not requirer an auth header, but we want to know - %% atleast the code for sending the header does not crash! - case ?config(skip, Config) of - undefined -> - case httpc:request(get, {?PROXY_URL, []}, - [{proxy_auth, {"foo", "bar"}}], []) of - {ok, {{_,200, _}, [_ | _], [_|_]}} -> - ok; - Unexpected -> - tsf({unexpected_result, Unexpected}) - end; - Reason -> - skip(Reason) - end. - - %%------------------------------------------------------------------------- http_server_does_not_exist(doc) -> ["Test that we get an error message back when the server " @@ -1834,39 +1495,6 @@ page_does_not_exist(Config) when is_list(Config) -> ok. -%%------------------------------------------------------------------------- -proxy_page_does_not_exist(doc) -> - ["Test that we get a 404 when the page is not found."]; -proxy_page_does_not_exist(suite) -> - []; -proxy_page_does_not_exist(Config) when is_list(Config) -> - case ?config(skip, Config) of - undefined -> - URL = ?PROXY_URL ++ "/doesnotexist.html", - {ok, {{_,404,_}, [_ | _], [_ | _]}} = - httpc:request(get, {URL, []}, [], []), - ok; - Reason -> - skip(Reason) - end. - - -%%------------------------------------------------------------------------- - -proxy_https_not_supported(doc) -> - []; -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, https_through_proxy_is_not_currently_supported} -> - ok; - _ -> - tsf({unexpected_reason, Result}) - end. - - %%------------------------------------------------------------------------- http_stream(doc) -> @@ -1967,36 +1595,6 @@ once(URL) -> ok. -%%------------------------------------------------------------------------- -proxy_stream(doc) -> - ["Test the option stream for asynchrony requests"]; -proxy_stream(suite) -> - []; -proxy_stream(Config) when is_list(Config) -> - case ?config(skip, Config) of - undefined -> - {ok, {{_,200,_}, [_ | _], Body}} = - httpc:request(get, {?PROXY_URL, []}, [], []), - - {ok, RequestId} = - httpc:request(get, {?PROXY_URL, []}, [], - [{sync, false}, {stream, self}]), - - receive - {http, {RequestId, stream_start, _Headers}} -> - ok; - {http, Msg} -> - tsf(Msg) - end, - - StreamedBody = receive_streamed_body(RequestId, <<>>), - - Body == binary_to_list(StreamedBody); - Reason -> - skip(Reason) - end. - - %%------------------------------------------------------------------------- parse_url(doc) -> ["Test that an url is parsed correctly"]; @@ -2587,21 +2185,6 @@ transfer_encoding_otp_6807(Config) when is_list(Config) -> ok. -%%------------------------------------------------------------------------- - -proxy_not_modified_otp_6821(doc) -> - ["If unmodified no body should be returned"]; -proxy_not_modified_otp_6821(suite) -> - []; -proxy_not_modified_otp_6821(Config) when is_list(Config) -> - case ?config(skip, Config) of - undefined -> - provocate_not_modified_bug(?PROXY_URL); - Reason -> - skip(Reason) - end. - - %%------------------------------------------------------------------------- empty_response_header_otp_6830(doc) -> @@ -3410,15 +2993,6 @@ create_config(FileName, ComType, Port, PrivDir, ServerRoot, DocRoot, cline(List) -> lists:flatten([List, "\r\n"]). -is_proxy_available(Proxy, Port) -> - case gen_tcp:connect(Proxy, Port, []) of - {ok, Socket} -> - gen_tcp:close(Socket), - true; - _ -> - false - end. - receive_streamed_body(RequestId, Body) -> receive {http, {RequestId, stream, BinBodyPart}} -> @@ -3912,42 +3486,6 @@ content_length(["content-length:" ++ Value | _]) -> content_length([_Head | Tail]) -> content_length(Tail). -provocate_not_modified_bug(Url) -> - Timeout = 15000, %% 15s should be plenty - - {ok, {{_, 200, _}, ReplyHeaders, _Body}} = - httpc:request(get, {Url, []}, [{timeout, Timeout}], []), - Etag = pick_header(ReplyHeaders, "ETag"), - Last = pick_header(ReplyHeaders, "last-modified"), - - case httpc:request(get, {Url, [{"If-None-Match", Etag}, - {"If-Modified-Since", Last}]}, - [{timeout, 15000}], - []) of - {ok, {{_, 304, _}, _, _}} -> %% The expected reply - page_unchanged; - {ok, {{_, 200, _}, _, _}} -> - %% If the page has changed since the - %% last request we retry to - %% trigger the bug - provocate_not_modified_bug(Url); - {error, timeout} -> - %% Not what we expected. Tcpdump can be used to - %% verify that we receive the complete http-reply - %% but still time out. - incorrect_result - end. - -pick_header(Headers, Name) -> - case lists:keysearch(string:to_lower(Name), 1, - [{string:to_lower(X), Y} || {X, Y} <- Headers]) of - false -> - []; - {value, {_Key, Val}} -> - Val - end. - - %% ------------------------------------------------------------------------- simple_request_and_verify(Config, -- cgit v1.2.3 From fd8996767652dc4e162eddea457155a2411157de Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Wed, 12 Sep 2012 17:36:19 +0200 Subject: inets httpc: TLS via proxy Introduces new option htts_proxy so that it is possible to have different proxies for http and https. The new option defaults to the old proxy option. Implements HTTP-1.1 extension method CONNECT to establish SSL/TLS tunnel We choose not to implement "TLS upgrade" as defined by RFC 2817 as this method of upgrade is vulnerable to man in the middle attacks, can be easily broken by proxies and does not seem to be widely adopted. --- lib/inets/doc/src/httpc.xml | 27 +- lib/inets/src/http_client/httpc.erl | 12 + lib/inets/src/http_client/httpc_handler.erl | 987 +++++++++++++++------------ lib/inets/src/http_client/httpc_internal.hrl | 2 + lib/inets/src/http_client/httpc_manager.erl | 4 + 5 files changed, 593 insertions(+), 439 deletions(-) diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index 14ce3cbe7f..981215000a 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -449,7 +449,8 @@ apply(Module, Function, [ReplyInfo | Args]) Options = [Option] Option = {proxy, {Proxy, NoProxy}} | - {max_sessions, MaxSessions} | + {https_proxy, {Proxy, NoProxy}} | + {max_sessions, MaxSessions} | {max_keep_alive_length, MaxKeepAlive} | {keep_alive_timeout, KeepAliveTimeout} | {max_pipeline_length, MaxPipeline} | @@ -460,25 +461,23 @@ apply(Module, Function, [ReplyInfo | Args]) {port, Port} | {socket_opts, socket_opts()} | {verbose, VerboseMode} + Proxy = {Hostname, Port} Hostname = string() ex: "localhost" or "foo.bar.se" Port = integer() ex: 8080 - socket_opts() = [socket_opt()] - The options are appended to the socket options used by the - client. - These are the default values when a new request handler - is started (for the initial connect). They are passed directly - to the underlying transport (gen_tcp or ssl) without - verification! NoProxy = [NoProxyDesc] NoProxyDesc = DomainDesc | HostName | IPDesc DomainDesc = "*.Domain" ex: "*.ericsson.se" IpDesc = string() ex: "134.138" or "[FEDC:BA98" (all IP-addresses starting with 134.138 or FEDC:BA98), "66.35.250.150" or "[2010:836B:4179::836B:4179]" (a complete IP-address). - MaxSessions = integer() + + proxy defaults to {undefined, []} e.i. no proxy is configured and https_proxy defaults to + the value of proxy. + + MaxSessions = integer() Default is 2. Maximum number of persistent connections to a host. MaxKeepAlive = integer() @@ -520,6 +519,13 @@ apply(Module, Function, [ReplyInfo | Args]) Port = integer() Specify which local port number to use. See gen_tcp:connect/3,4 for more info. + socket_opts() = [socket_opt()] + The options are appended to the socket options used by the + client. + These are the default values when a new request handler + is started (for the initial connect). They are passed directly + to the underlying transport (gen_tcp or ssl) without + verification! VerboseMode = false | verbose | debug | trace Default is false. This option is used to switch on (or off) @@ -554,7 +560,8 @@ apply(Module, Function, [ReplyInfo | Args]) Gets the currently used options. OptionItems = all | [option_item()] - option_item() = proxy | + option_item() = proxy | + https_proxy max_sessions | keep_alive_timeout | max_keep_alive_length | diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index b6e7708353..eb7102d046 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -917,6 +917,10 @@ validate_options([{proxy, Proxy} = Opt| Tail], Acc) -> validate_proxy(Proxy), validate_options(Tail, [Opt | Acc]); +validate_options([{https_proxy, Proxy} = Opt| Tail], Acc) -> + validate_https_proxy(Proxy), + validate_options(Tail, [Opt | Acc]); + validate_options([{max_sessions, Value} = Opt| Tail], Acc) -> validate_max_sessions(Value), validate_options(Tail, [Opt | Acc]); @@ -979,6 +983,14 @@ validate_proxy({{ProxyHost, ProxyPort}, NoProxy} = Proxy) validate_proxy(BadProxy) -> bad_option(proxy, BadProxy). +validate_https_proxy({{ProxyHost, ProxyPort}, NoProxy} = Proxy) + when is_list(ProxyHost) andalso + is_integer(ProxyPort) andalso + is_list(NoProxy) -> + Proxy; +validate_https_proxy(BadProxy) -> + bad_option(https_proxy, BadProxy). + validate_max_sessions(Value) when is_integer(Value) andalso (Value >= 0) -> Value; validate_max_sessions(BadValue) -> diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index 923213d34d..de77321bf8 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -29,44 +29,44 @@ %%-------------------------------------------------------------------- %% Internal Application API -export([ - start_link/4, - %% connect_and_send/2, - send/2, - cancel/3, - stream/3, - stream_next/1, - info/1 - ]). + start_link/4, + %% connect_and_send/2, + send/2, + cancel/3, + stream/3, + stream_next/1, + info/1 + ]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). + terminate/2, code_change/3]). -record(timers, - { - request_timers = [], % [ref()] - queue_timer % ref() - }). + { + request_timers = [], % [ref()] + queue_timer % ref() + }). -record(state, - { - request, % #request{} - session, % #session{} - status_line, % {Version, StatusCode, ReasonPharse} - headers, % #http_response_h{} - body, % binary() - mfa, % {Module, Function, Args} - pipeline = queue:new(), % queue() - keep_alive = queue:new(), % queue() - status, % undefined | new | pipeline | keep_alive | close | ssl_tunnel - canceled = [], % [RequestId] - max_header_size = nolimit, % nolimit | integer() - max_body_size = nolimit, % nolimit | integer() - options, % #options{} - timers = #timers{}, % #timers{} - profile_name, % atom() - id of httpc_manager process. - once % send | undefined - }). + { + request, % #request{} + session, % #session{} + status_line, % {Version, StatusCode, ReasonPharse} + headers, % #http_response_h{} + body, % binary() + mfa, % {Module, Function, Args} + pipeline = queue:new(), % queue() + keep_alive = queue:new(), % queue() + status, % undefined | new | pipeline | keep_alive | close | {ssl_tunnel, Request} + canceled = [], % [RequestId] + max_header_size = nolimit, % nolimit | integer() + max_body_size = nolimit, % nolimit | integer() + options, % #options{} + timers = #timers{}, % #timers{} + profile_name, % atom() - id of httpc_manager process. + once % send | undefined + }). %%==================================================================== @@ -75,8 +75,8 @@ %%-------------------------------------------------------------------- %% Function: start_link(Request, Options, ProfileName) -> {ok, Pid} %% -%% Request = #request{} -%% Options = #options{} +%% Request = #request{} +%% Options = #options{} %% ProfileName = atom() - id of httpc manager process %% %% Description: Starts a http-request handler process. Intended to be @@ -96,11 +96,11 @@ start_link(Parent, Request, Options, ProfileName) -> {ok, proc_lib:start_link(?MODULE, init, [[Parent, Request, Options, - ProfileName]])}. + ProfileName]])}. %%-------------------------------------------------------------------- %% Function: send(Request, Pid) -> ok -%% Request = #request{} +%% Request = #request{} %% Pid = pid() - the pid of the http-request handler process. %% %% Description: Uses this handlers session to send a request. Intended @@ -112,7 +112,7 @@ send(Request, Pid) -> %%-------------------------------------------------------------------- %% Function: cancel(RequestId, Pid) -> ok -%% RequestId = ref() +%% RequestId = ref() %% Pid = pid() - the pid of the http-request handler process. %% %% Description: Cancels a request. Intended to be called by the httpc @@ -147,7 +147,7 @@ info(Pid) -> %%-------------------------------------------------------------------- %% Function: stream(BodyPart, Request, Code) -> _ -%% BodyPart = binary() +%% BodyPart = binary() %% Request = #request{} %% Code = integer() %% @@ -167,7 +167,7 @@ stream(BodyPart, #request{stream = Self} = Request, Code) ((Self =:= self) orelse (Self =:= {self, once})) -> ?hcrt("stream - self", [{stream, Self}, {code, Code}]), httpc_response:send(Request#request.from, - {Request#request.id, stream, BodyPart}), + {Request#request.id, stream, BodyPart}), {<<>>, Request}; %% Stream to file @@ -177,11 +177,11 @@ stream(BodyPart, #request{stream = Filename} = Request, Code) when ((Code =:= 200) orelse (Code =:= 206)) andalso is_list(Filename) -> ?hcrt("stream - filename", [{stream, Filename}, {code, Code}]), case file:open(Filename, [write, raw, append, delayed_write]) of - {ok, Fd} -> - ?hcrt("stream - file open ok", [{fd, Fd}]), - stream(BodyPart, Request#request{stream = Fd}, 200); - {error, Reason} -> - exit({stream_to_file_failed, Reason}) + {ok, Fd} -> + ?hcrt("stream - file open ok", [{fd, Fd}]), + stream(BodyPart, Request#request{stream = Fd}, 200); + {error, Reason} -> + exit({stream_to_file_failed, Reason}) end; %% Stream to file @@ -189,10 +189,10 @@ stream(BodyPart, #request{stream = Fd} = Request, Code) when ((Code =:= 200) orelse (Code =:= 206)) -> ?hcrt("stream to file", [{stream, Fd}, {code, Code}]), case file:write(Fd, BodyPart) of - ok -> - {<<>>, Request}; - {error, Reason} -> - exit({stream_to_file_failed, Reason}) + ok -> + {<<>>, Request}; + {error, Reason} -> + exit({stream_to_file_failed, Reason}) end; stream(BodyPart, Request,_) -> % only 200 and 206 responses can be streamed @@ -208,7 +208,7 @@ stream(BodyPart, Request,_) -> % only 200 and 206 responses can be streamed %% Function: init([Options, ProfileName]) -> {ok, State} | %% {ok, State, Timeout} | ignore | {stop, Reason} %% -%% Options = #options{} +%% Options = #options{} %% ProfileName = atom() - id of httpc manager process %% %% Description: Initiates the httpc_handler process @@ -224,20 +224,19 @@ init([Parent, Request, Options, ProfileName]) -> %% Do not let initial tcp-connection block the manager-process proc_lib:init_ack(Parent, self()), handle_verbose(Options#options.verbose), - Address = handle_proxy(Request#request.address, Options#options.proxy), + ProxyOptions = handle_proxy_options(Request#request.scheme, Options), + Address = handle_proxy(Request#request.address, ProxyOptions), {ok, State} = - case {Address /= Request#request.address, Request#request.scheme} of - {true, https} -> - Error = https_through_proxy_is_not_currently_supported, - self() ! {init_error, - Error, httpc_response:error(Request, Error)}, - {ok, #state{request = Request, options = Options, - status = ssl_tunnel}}; - {_, _} -> - connect_and_send_first_request(Address, Request, - #state{options = Options, - profile_name = ProfileName}) - end, + case {Address /= Request#request.address, Request#request.scheme} of + {true, https} -> + connect_and_send_upgrade_request(Address, Request, + #state{options = Options, + profile_name = ProfileName}); + {_, _} -> + connect_and_send_first_request(Address, Request, + #state{options = Options, + profile_name = ProfileName}) + end, gen_server:enter_loop(?MODULE, [], State). %%-------------------------------------------------------------------- @@ -250,139 +249,139 @@ init([Parent, Request, Options, ProfileName]) -> %% Description: Handling call messages %%-------------------------------------------------------------------- handle_call(#request{address = Addr} = Request, _, - #state{status = Status, - session = #session{type = pipeline} = Session, - timers = Timers, - options = #options{proxy = Proxy} = _Options, - profile_name = ProfileName} = State) + #state{status = Status, + session = #session{type = pipeline} = Session, + timers = Timers, + options = #options{proxy = Proxy} = _Options, + profile_name = ProfileName} = State) when Status =/= undefined -> ?hcrv("new request on a pipeline session", - [{request, Request}, - {profile, ProfileName}, - {status, Status}, - {timers, Timers}]), + [{request, Request}, + {profile, ProfileName}, + {status, Status}, + {timers, Timers}]), Address = handle_proxy(Addr, Proxy), case httpc_request:send(Address, Session, Request) of ok -> - ?hcrd("request sent", []), + ?hcrd("request sent", []), - %% Activate the request time out for the new request - NewState = - activate_request_timeout(State#state{request = Request}), + %% Activate the request time out for the new request + NewState = + activate_request_timeout(State#state{request = Request}), - ClientClose = - httpc_request:is_client_closing(Request#request.headers), + ClientClose = + httpc_request:is_client_closing(Request#request.headers), case State#state.request of #request{} -> %% Old request not yet finished - ?hcrd("old request still not finished", []), - %% Make sure to use the new value of timers in state - NewTimers = NewState#state.timers, + ?hcrd("old request still not finished", []), + %% Make sure to use the new value of timers in state + NewTimers = NewState#state.timers, NewPipeline = queue:in(Request, State#state.pipeline), - NewSession = - Session#session{queue_length = - %% Queue + current - queue:len(NewPipeline) + 1, - client_close = ClientClose}, - insert_session(NewSession, ProfileName), - ?hcrd("session updated", []), + NewSession = + Session#session{queue_length = + %% Queue + current + queue:len(NewPipeline) + 1, + client_close = ClientClose}, + insert_session(NewSession, ProfileName), + ?hcrd("session updated", []), {reply, ok, State#state{pipeline = NewPipeline, - session = NewSession, - timers = NewTimers}}; - undefined -> - %% Note: tcp-message receiving has already been - %% activated by handle_pipeline/2. - ?hcrd("no current request", []), - cancel_timer(Timers#timers.queue_timer, - timeout_queue), - NewSession = - Session#session{queue_length = 1, - client_close = ClientClose}, - httpc_manager:insert_session(NewSession, ProfileName), - Relaxed = - (Request#request.settings)#http_options.relaxed, - MFA = {httpc_response, parse, - [State#state.max_header_size, Relaxed]}, - NewTimers = Timers#timers{queue_timer = undefined}, - ?hcrd("session created", []), - {reply, ok, NewState#state{request = Request, - session = NewSession, - mfa = MFA, - timers = NewTimers}} - end; - {error, Reason} -> - ?hcri("failed sending request", [{reason, Reason}]), - {reply, {pipeline_failed, Reason}, State} + session = NewSession, + timers = NewTimers}}; + undefined -> + %% Note: tcp-message receiving has already been + %% activated by handle_pipeline/2. + ?hcrd("no current request", []), + cancel_timer(Timers#timers.queue_timer, + timeout_queue), + NewSession = + Session#session{queue_length = 1, + client_close = ClientClose}, + httpc_manager:insert_session(NewSession, ProfileName), + Relaxed = + (Request#request.settings)#http_options.relaxed, + MFA = {httpc_response, parse, + [State#state.max_header_size, Relaxed]}, + NewTimers = Timers#timers{queue_timer = undefined}, + ?hcrd("session created", []), + {reply, ok, NewState#state{request = Request, + session = NewSession, + mfa = MFA, + timers = NewTimers}} + end; + {error, Reason} -> + ?hcri("failed sending request", [{reason, Reason}]), + {reply, {pipeline_failed, Reason}, State} end; handle_call(#request{address = Addr} = Request, _, - #state{status = Status, - session = #session{type = keep_alive} = Session, - timers = Timers, - options = #options{proxy = Proxy} = _Options, - profile_name = ProfileName} = State) + #state{status = Status, + session = #session{type = keep_alive} = Session, + timers = Timers, + options = #options{proxy = Proxy} = _Options, + profile_name = ProfileName} = State) when Status =/= undefined -> ?hcrv("new request on a keep-alive session", - [{request, Request}, - {profile, ProfileName}, - {status, Status}]), + [{request, Request}, + {profile, ProfileName}, + {status, Status}]), Address = handle_proxy(Addr, Proxy), case httpc_request:send(Address, Session, Request) of - ok -> + ok -> - ?hcrd("request sent", []), + ?hcrd("request sent", []), - %% Activate the request time out for the new request - NewState = - activate_request_timeout(State#state{request = Request}), + %% Activate the request time out for the new request + NewState = + activate_request_timeout(State#state{request = Request}), - ClientClose = - httpc_request:is_client_closing(Request#request.headers), + ClientClose = + httpc_request:is_client_closing(Request#request.headers), - case State#state.request of - #request{} -> %% Old request not yet finished - %% Make sure to use the new value of timers in state - ?hcrd("old request still not finished", []), - NewTimers = NewState#state.timers, + case State#state.request of + #request{} -> %% Old request not yet finished + %% Make sure to use the new value of timers in state + ?hcrd("old request still not finished", []), + NewTimers = NewState#state.timers, NewKeepAlive = queue:in(Request, State#state.keep_alive), - NewSession = - Session#session{queue_length = - %% Queue + current - queue:len(NewKeepAlive) + 1, - client_close = ClientClose}, - insert_session(NewSession, ProfileName), - ?hcrd("session updated", []), + NewSession = + Session#session{queue_length = + %% Queue + current + queue:len(NewKeepAlive) + 1, + client_close = ClientClose}, + insert_session(NewSession, ProfileName), + ?hcrd("session updated", []), {reply, ok, State#state{keep_alive = NewKeepAlive, - session = NewSession, - timers = NewTimers}}; - undefined -> - %% Note: tcp-message reciving has already been - %% activated by handle_pipeline/2. - ?hcrd("no current request", []), - cancel_timer(Timers#timers.queue_timer, - timeout_queue), - NewSession = - Session#session{queue_length = 1, - client_close = ClientClose}, - insert_session(NewSession, ProfileName), - Relaxed = - (Request#request.settings)#http_options.relaxed, - MFA = {httpc_response, parse, - [State#state.max_header_size, Relaxed]}, - {reply, ok, NewState#state{request = Request, - session = NewSession, - mfa = MFA}} - end; + session = NewSession, + timers = NewTimers}}; + undefined -> + %% Note: tcp-message reciving has already been + %% activated by handle_pipeline/2. + ?hcrd("no current request", []), + cancel_timer(Timers#timers.queue_timer, + timeout_queue), + NewSession = + Session#session{queue_length = 1, + client_close = ClientClose}, + insert_session(NewSession, ProfileName), + Relaxed = + (Request#request.settings)#http_options.relaxed, + MFA = {httpc_response, parse, + [State#state.max_header_size, Relaxed]}, + {reply, ok, NewState#state{request = Request, + session = NewSession, + mfa = MFA}} + end; - {error, Reason} -> - ?hcri("failed sending request", [{reason, Reason}]), - {reply, {request_failed, Reason}, State} + {error, Reason} -> + ?hcri("failed sending request", [{reason, Reason}]), + {reply, {request_failed, Reason}, State} end; @@ -411,25 +410,25 @@ handle_call(info, _, State) -> %% request as if it was never issued as in this case the request will %% not have been sent. handle_cast({cancel, RequestId, From}, - #state{request = #request{id = RequestId} = Request, - profile_name = ProfileName, - canceled = Canceled} = State) -> + #state{request = #request{id = RequestId} = Request, + profile_name = ProfileName, + canceled = Canceled} = State) -> ?hcrv("cancel current request", [{request_id, RequestId}, - {profile, ProfileName}, - {canceled, Canceled}]), + {profile, ProfileName}, + {canceled, Canceled}]), httpc_manager:request_canceled(RequestId, ProfileName, From), ?hcrv("canceled", []), {stop, normal, State#state{canceled = [RequestId | Canceled], - request = Request#request{from = answer_sent}}}; + request = Request#request{from = answer_sent}}}; handle_cast({cancel, RequestId, From}, - #state{profile_name = ProfileName, - request = #request{id = CurrId}, - canceled = Canceled} = State) -> + #state{profile_name = ProfileName, + request = #request{id = CurrId}, + canceled = Canceled} = State) -> ?hcrv("cancel", [{request_id, RequestId}, - {curr_req_id, CurrId}, - {profile, ProfileName}, - {canceled, Canceled}]), + {curr_req_id, CurrId}, + {profile, ProfileName}, + {canceled, Canceled}]), httpc_manager:request_canceled(RequestId, ProfileName, From), ?hcrv("canceled", []), {noreply, State#state{canceled = [RequestId | Canceled]}}; @@ -446,94 +445,94 @@ handle_cast(stream_next, #state{session = Session} = State) -> %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- handle_info({Proto, _Socket, Data}, - #state{mfa = {Module, Function, Args}, - request = #request{method = Method, - stream = Stream} = Request, - session = Session, - status_line = StatusLine} = State) + #state{mfa = {Module, Function, Args}, + request = #request{method = Method, + stream = Stream} = Request, + session = Session, + status_line = StatusLine} = State) when (Proto =:= tcp) orelse (Proto =:= ssl) orelse (Proto =:= httpc_handler) -> ?hcri("received data", [{proto, Proto}, - {module, Module}, - {function, Function}, - {method, Method}, - {stream, Stream}, - {session, Session}, - {status_line, StatusLine}]), + {module, Module}, + {function, Function}, + {method, Method}, + {stream, Stream}, + {session, Session}, + {status_line, StatusLine}]), FinalResult = - try Module:Function([Data | Args]) of - {ok, Result} -> - ?hcrd("data processed - ok", []), - handle_http_msg(Result, State); - {_, whole_body, _} when Method =:= head -> - ?hcrd("data processed - whole body", []), - handle_response(State#state{body = <<>>}); - {Module, whole_body, [Body, Length]} -> - ?hcrd("data processed - whole body", [{length, Length}]), - {_, Code, _} = StatusLine, - {NewBody, NewRequest} = stream(Body, Request, Code), - %% When we stream we will not keep the already - %% streamed data, that would be a waste of memory. - NewLength = - case Stream of - none -> - Length; - _ -> - Length - size(Body) - end, - - NewState = next_body_chunk(State), - NewMFA = {Module, whole_body, [NewBody, NewLength]}, - {noreply, NewState#state{mfa = NewMFA, - request = NewRequest}}; - NewMFA -> - ?hcrd("data processed - new mfa", []), - activate_once(Session), - {noreply, State#state{mfa = NewMFA}} - catch - exit:_Exit -> - ?hcrd("data processing exit", [{exit, _Exit}]), - ClientReason = {could_not_parse_as_http, Data}, - ClientErrMsg = httpc_response:error(Request, ClientReason), - NewState = answer_request(Request, ClientErrMsg, State), - {stop, normal, NewState}; - error:_Error -> - ?hcrd("data processing error", [{error, _Error}]), - ClientReason = {could_not_parse_as_http, Data}, - ClientErrMsg = httpc_response:error(Request, ClientReason), - NewState = answer_request(Request, ClientErrMsg, State), - {stop, normal, NewState} - - end, + try Module:Function([Data | Args]) of + {ok, Result} -> + ?hcrd("data processed - ok", []), + handle_http_msg(Result, State); + {_, whole_body, _} when Method =:= head -> + ?hcrd("data processed - whole body", []), + handle_response(State#state{body = <<>>}); + {Module, whole_body, [Body, Length]} -> + ?hcrd("data processed - whole body", [{length, Length}]), + {_, Code, _} = StatusLine, + {NewBody, NewRequest} = stream(Body, Request, Code), + %% When we stream we will not keep the already + %% streamed data, that would be a waste of memory. + NewLength = + case Stream of + none -> + Length; + _ -> + Length - size(Body) + end, + + NewState = next_body_chunk(State), + NewMFA = {Module, whole_body, [NewBody, NewLength]}, + {noreply, NewState#state{mfa = NewMFA, + request = NewRequest}}; + NewMFA -> + ?hcrd("data processed - new mfa", []), + activate_once(Session), + {noreply, State#state{mfa = NewMFA}} + catch + exit:_Exit -> + ?hcrd("data processing exit", [{exit, _Exit}]), + ClientReason = {could_not_parse_as_http, Data}, + ClientErrMsg = httpc_response:error(Request, ClientReason), + NewState = answer_request(Request, ClientErrMsg, State), + {stop, normal, NewState}; + error:_Error -> + ?hcrd("data processing error", [{error, _Error}]), + ClientReason = {could_not_parse_as_http, Data}, + ClientErrMsg = httpc_response:error(Request, ClientReason), + NewState = answer_request(Request, ClientErrMsg, State), + {stop, normal, NewState} + + end, ?hcri("data processed", [{final_result, FinalResult}]), FinalResult; handle_info({Proto, Socket, Data}, - #state{mfa = MFA, - request = Request, - session = Session, - status = Status, - status_line = StatusLine, - profile_name = Profile} = State) + #state{mfa = MFA, + request = Request, + session = Session, + status = Status, + status_line = StatusLine, + profile_name = Profile} = State) when (Proto =:= tcp) orelse (Proto =:= ssl) orelse (Proto =:= httpc_handler) -> error_logger:warning_msg("Received unexpected ~p data on ~p" - "~n Data: ~p" - "~n MFA: ~p" - "~n Request: ~p" - "~n Session: ~p" - "~n Status: ~p" - "~n StatusLine: ~p" - "~n Profile: ~p" - "~n", - [Proto, Socket, Data, MFA, - Request, Session, Status, StatusLine, Profile]), + "~n Data: ~p" + "~n MFA: ~p" + "~n Request: ~p" + "~n Session: ~p" + "~n Status: ~p" + "~n StatusLine: ~p" + "~n Profile: ~p" + "~n", + [Proto, Socket, Data, MFA, + Request, Session, Status, StatusLine, Profile]), {noreply, State}; @@ -572,45 +571,45 @@ handle_info({ssl_error, _, _} = Reason, State) -> %% Internally, to a request handling process, a request timeout is %% seen as a canceled request. handle_info({timeout, RequestId}, - #state{request = #request{id = RequestId} = Request, - canceled = Canceled, - profile_name = ProfileName} = State) -> + #state{request = #request{id = RequestId} = Request, + canceled = Canceled, + profile_name = ProfileName} = State) -> ?hcri("timeout of current request", [{id, RequestId}]), httpc_response:send(Request#request.from, - httpc_response:error(Request, timeout)), + httpc_response:error(Request, timeout)), httpc_manager:request_done(RequestId, ProfileName), ?hcrv("response (timeout) sent - now terminate", []), {stop, normal, State#state{request = Request#request{from = answer_sent}, - canceled = [RequestId | Canceled]}}; + canceled = [RequestId | Canceled]}}; handle_info({timeout, RequestId}, - #state{canceled = Canceled, - profile_name = ProfileName} = State) -> + #state{canceled = Canceled, + profile_name = ProfileName} = State) -> ?hcri("timeout", [{id, RequestId}]), Filter = - fun(#request{id = Id, from = From} = Request) when Id =:= RequestId -> - ?hcrv("found request", [{id, Id}, {from, From}]), - %% Notify the owner - httpc_response:send(From, - httpc_response:error(Request, timeout)), - httpc_manager:request_done(RequestId, ProfileName), - ?hcrv("response (timeout) sent", []), - [Request#request{from = answer_sent}]; - (_) -> - true - end, + fun(#request{id = Id, from = From} = Request) when Id =:= RequestId -> + ?hcrv("found request", [{id, Id}, {from, From}]), + %% Notify the owner + httpc_response:send(From, + httpc_response:error(Request, timeout)), + httpc_manager:request_done(RequestId, ProfileName), + ?hcrv("response (timeout) sent", []), + [Request#request{from = answer_sent}]; + (_) -> + true + end, case State#state.status of - pipeline -> - ?hcrd("pipeline", []), - Pipeline = queue:filter(Filter, State#state.pipeline), - {noreply, State#state{canceled = [RequestId | Canceled], - pipeline = Pipeline}}; - keep_alive -> - ?hcrd("keep_alive", []), - KeepAlive = queue:filter(Filter, State#state.keep_alive), - {noreply, State#state{canceled = [RequestId | Canceled], - keep_alive = KeepAlive}} + pipeline -> + ?hcrd("pipeline", []), + Pipeline = queue:filter(Filter, State#state.pipeline), + {noreply, State#state{canceled = [RequestId | Canceled], + pipeline = Pipeline}}; + keep_alive -> + ?hcrd("keep_alive", []), + KeepAlive = queue:filter(Filter, State#state.keep_alive), + {noreply, State#state{canceled = [RequestId | Canceled], + keep_alive = KeepAlive}} end; handle_info(timeout_queue, State = #state{request = undefined}) -> @@ -619,11 +618,11 @@ handle_info(timeout_queue, State = #state{request = undefined}) -> %% Timing was such as the pipeline_timout was not canceled! handle_info(timeout_queue, #state{timers = Timers} = State) -> {noreply, State#state{timers = - Timers#timers{queue_timer = undefined}}}; + Timers#timers{queue_timer = undefined}}}; %% Setting up the connection to the server somehow failed. handle_info({init_error, Tag, ClientErrMsg}, - State = #state{request = Request}) -> + State = #state{request = Request}) -> ?hcrv("init error", [{tag, Tag}, {client_error, ClientErrMsg}]), NewState = answer_request(Request, ClientErrMsg, State), {stop, normal, NewState}; @@ -647,21 +646,21 @@ handle_info({'EXIT', _, _}, State) -> %% Init error there is no socket to be closed. terminate(normal, - #state{request = Request, - session = {send_failed, AReason} = Reason} = State) -> + #state{request = Request, + session = {send_failed, AReason} = Reason} = State) -> ?hcrd("terminate", [{send_reason, AReason}, {request, Request}]), maybe_send_answer(Request, - httpc_response:error(Request, Reason), - State), + httpc_response:error(Request, Reason), + State), ok; terminate(normal, - #state{request = Request, - session = {connect_failed, AReason} = Reason} = State) -> + #state{request = Request, + session = {connect_failed, AReason} = Reason} = State) -> ?hcrd("terminate", [{connect_reason, AReason}, {request, Request}]), maybe_send_answer(Request, - httpc_response:error(Request, Reason), - State), + httpc_response:error(Request, Reason), + State), ok; terminate(normal, #state{session = undefined}) -> @@ -670,21 +669,21 @@ terminate(normal, #state{session = undefined}) -> %% Init error sending, no session information has been setup but %% there is a socket that needs closing. terminate(normal, - #state{session = #session{id = undefined} = Session}) -> + #state{session = #session{id = undefined} = Session}) -> close_socket(Session); %% Socket closed remotely terminate(normal, - #state{session = #session{socket = {remote_close, Socket}, - socket_type = SocketType, - id = Id}, - profile_name = ProfileName, - request = Request, - timers = Timers, - pipeline = Pipeline, - keep_alive = KeepAlive} = State) -> + #state{session = #session{socket = {remote_close, Socket}, + socket_type = SocketType, + id = Id}, + profile_name = ProfileName, + request = Request, + timers = Timers, + pipeline = Pipeline, + keep_alive = KeepAlive} = State) -> ?hcrt("terminate(normal) - remote close", - [{id, Id}, {profile, ProfileName}]), + [{id, Id}, {profile, ProfileName}]), %% Clobber session (catch httpc_manager:delete_session(Id, ProfileName)), @@ -702,15 +701,15 @@ terminate(normal, http_transport:close(SocketType, Socket); terminate(Reason, #state{session = #session{id = Id, - socket = Socket, - socket_type = SocketType}, - request = undefined, - profile_name = ProfileName, - timers = Timers, - pipeline = Pipeline, - keep_alive = KeepAlive} = State) -> + socket = Socket, + socket_type = SocketType}, + request = undefined, + profile_name = ProfileName, + timers = Timers, + pipeline = Pipeline, + keep_alive = KeepAlive} = State) -> ?hcrt("terminate", - [{id, Id}, {profile, ProfileName}, {reason, Reason}]), + [{id, Id}, {profile, ProfileName}, {reason, Reason}]), %% Clobber session (catch httpc_manager:delete_session(Id, ProfileName)), @@ -728,16 +727,16 @@ terminate(Reason, #state{request = undefined}) -> terminate(Reason, #state{request = Request} = State) -> ?hcrd("terminate", [{reason, Reason}, {request, Request}]), NewState = maybe_send_answer(Request, - httpc_response:error(Request, Reason), - State), + httpc_response:error(Request, Reason), + State), terminate(Reason, NewState#state{request = undefined}). maybe_retry_queue(Q, State) -> case queue:is_empty(Q) of - false -> - retry_pipeline(queue:to_list(Q), State); - true -> - ok + false -> + retry_pipeline(queue:to_list(Q), State); + true -> + ok end. maybe_send_answer(#request{from = answer_sent}, _Reason, State) -> @@ -761,44 +760,44 @@ deliver_answer(Request) -> %%-------------------------------------------------------------------- code_change(_, - #state{session = OldSession, - profile_name = ProfileName} = State, - upgrade_from_pre_5_8_1) -> + #state{session = OldSession, + profile_name = ProfileName} = State, + upgrade_from_pre_5_8_1) -> case OldSession of - {session, - Id, ClientClose, Scheme, Socket, SocketType, QueueLen, Type} -> - NewSession = #session{id = Id, - client_close = ClientClose, - scheme = Scheme, - socket = Socket, - socket_type = SocketType, - queue_length = QueueLen, - type = Type}, - insert_session(NewSession, ProfileName), - {ok, State#state{session = NewSession}}; - _ -> - {ok, State} + {session, + Id, ClientClose, Scheme, Socket, SocketType, QueueLen, Type} -> + NewSession = #session{id = Id, + client_close = ClientClose, + scheme = Scheme, + socket = Socket, + socket_type = SocketType, + queue_length = QueueLen, + type = Type}, + insert_session(NewSession, ProfileName), + {ok, State#state{session = NewSession}}; + _ -> + {ok, State} end; code_change(_, - #state{session = OldSession, - profile_name = ProfileName} = State, - downgrade_to_pre_5_8_1) -> + #state{session = OldSession, + profile_name = ProfileName} = State, + downgrade_to_pre_5_8_1) -> case OldSession of - #session{id = Id, - client_close = ClientClose, - scheme = Scheme, - socket = Socket, - socket_type = SocketType, - queue_length = QueueLen, - type = Type} -> - NewSession = {session, - Id, ClientClose, Scheme, Socket, SocketType, - QueueLen, Type}, - insert_session(NewSession, ProfileName), - {ok, State#state{session = NewSession}}; - _ -> - {ok, State} + #session{id = Id, + client_close = ClientClose, + scheme = Scheme, + socket = Socket, + socket_type = SocketType, + queue_length = QueueLen, + type = Type} -> + NewSession = {session, + Id, ClientClose, Scheme, Socket, SocketType, + QueueLen, Type}, + insert_session(NewSession, ProfileName), + {ok, State#state{session = NewSession}}; + _ -> + {ok, State} end; code_change(_, State, _) -> @@ -806,22 +805,22 @@ code_change(_, State, _) -> %% new_http_options({http_options, TimeOut, AutoRedirect, SslOpts, -%% Auth, Relaxed}) -> +%% Auth, Relaxed}) -> %% {http_options, "HTTP/1.1", TimeOut, AutoRedirect, SslOpts, %% Auth, Relaxed}. %% old_http_options({http_options, _, TimeOut, AutoRedirect, -%% SslOpts, Auth, Relaxed}) -> +%% SslOpts, Auth, Relaxed}) -> %% {http_options, TimeOut, AutoRedirect, SslOpts, Auth, Relaxed}. %% new_queue(Queue, Fun) -> %% List = queue:to_list(Queue), %% NewList = -%% lists:map(fun(Request) -> -%% Settings = -%% Fun(Request#request.settings), -%% Request#request{settings = Settings} -%% end, List), +%% lists:map(fun(Request) -> +%% Settings = +%% Fun(Request#request.settings), +%% Request#request{settings = Settings} +%% end, List), %% queue:from_list(NewList). @@ -830,97 +829,121 @@ code_change(_, State, _) -> %%%-------------------------------------------------------------------- connect(SocketType, ToAddress, - #options{ipfamily = IpFamily, - ip = FromAddress, - port = FromPort, - socket_opts = Opts0}, Timeout) -> + #options{ipfamily = IpFamily, + ip = FromAddress, + port = FromPort, + socket_opts = Opts0}, Timeout) -> Opts1 = - case FromPort of - default -> - Opts0; - _ -> - [{port, FromPort} | Opts0] - end, + case FromPort of + default -> + Opts0; + _ -> + [{port, FromPort} | Opts0] + end, Opts2 = - case FromAddress of - default -> - Opts1; - _ -> - [{ip, FromAddress} | Opts1] - end, + case FromAddress of + default -> + Opts1; + _ -> + [{ip, FromAddress} | Opts1] + end, case IpFamily of - inet6fb4 -> - Opts3 = [inet6 | Opts2], - case http_transport:connect(SocketType, - ToAddress, Opts3, Timeout) of - {error, Reason6} -> - Opts4 = [inet | Opts2], - case http_transport:connect(SocketType, - ToAddress, Opts4, Timeout) of - {error, Reason4} -> - {error, {failed_connect, - [{to_address, ToAddress}, - {inet6, Opts3, Reason6}, - {inet, Opts4, Reason4}]}}; - OK -> - OK - end; - OK -> - OK - end; - _ -> - Opts3 = [IpFamily | Opts2], - case http_transport:connect(SocketType, ToAddress, Opts3, Timeout) of - {error, Reason} -> - {error, {failed_connect, [{to_address, ToAddress}, - {IpFamily, Opts3, Reason}]}}; - Else -> - Else - end + inet6fb4 -> + Opts3 = [inet6 | Opts2], + case http_transport:connect(SocketType, + ToAddress, Opts3, Timeout) of + {error, Reason6} -> + Opts4 = [inet | Opts2], + case http_transport:connect(SocketType, + ToAddress, Opts4, Timeout) of + {error, Reason4} -> + {error, {failed_connect, + [{to_address, ToAddress}, + {inet6, Opts3, Reason6}, + {inet, Opts4, Reason4}]}}; + OK -> + OK + end; + OK -> + OK + end; + _ -> + Opts3 = [IpFamily | Opts2], + case http_transport:connect(SocketType, ToAddress, Opts3, Timeout) of + {error, Reason} -> + {error, {failed_connect, [{to_address, ToAddress}, + {IpFamily, Opts3, Reason}]}}; + Else -> + Else + end end. connect_and_send_first_request(Address, Request, #state{options = Options} = State) -> SocketType = socket_type(Request), ConnTimeout = (Request#request.settings)#http_options.connect_timeout, ?hcri("connect", - [{address, Address}, {request, Request}, {options, Options}]), + [{address, Address}, {request, Request}, {options, Options}]), case connect(SocketType, Address, Options, ConnTimeout) of - {ok, Socket} -> - ClientClose = - httpc_request:is_client_closing( - Request#request.headers), + {ok, Socket} -> + ClientClose = + httpc_request:is_client_closing( + Request#request.headers), + SessionType = httpc_manager:session_type(Options), + SocketType = socket_type(Request), + Session = #session{id = {Request#request.address, self()}, + scheme = Request#request.scheme, + socket = Socket, + socket_type = SocketType, + client_close = ClientClose, + type = SessionType}, + ?hcri("connected - now send first request", [{socket, Socket}]), + + case httpc_request:send(Address, Session, Request) of + ok -> + ?hcri("first request sent", []), + TmpState = State#state{request = Request, + session = Session, + mfa = init_mfa(Request, State), + status_line = + init_status_line(Request), + headers = undefined, + body = undefined, + status = new}, + http_transport:setopts(SocketType, + Socket, [{active, once}]), + NewState = activate_request_timeout(TmpState), + {ok, NewState}; + {error, Reason} -> + self() ! {init_error, error_sending, + httpc_response:error(Request, Reason)}, + {ok, State#state{request = Request, + session = + #session{socket = Socket}}} + end; + {error, Reason} -> + self() ! {init_error, error_connecting, + httpc_response:error(Request, Reason)}, + {ok, State#state{request = Request}} + end. + +connect_and_send_upgrade_request(Address, Request, #state{options = Options} = State) -> + ConnTimeout = (Request#request.settings)#http_options.connect_timeout, + SocketType = ip_comm, + case connect(SocketType, Address, Options, ConnTimeout) of + {ok, Socket} -> SessionType = httpc_manager:session_type(Options), - SocketType = socket_type(Request), - Session = #session{id = {Request#request.address, self()}, - scheme = Request#request.scheme, - socket = Socket, + Session = #session{socket = Socket, socket_type = SocketType, - client_close = ClientClose, + id = {Request#request.address, self()}, + scheme = http, + client_close = false, type = SessionType}, - ?hcri("connected - now send first request", [{socket, Socket}]), - - case httpc_request:send(Address, Session, Request) of - ok -> - ?hcri("first request sent", []), - TmpState = State#state{request = Request, - session = Session, - mfa = init_mfa(Request, State), - status_line = - init_status_line(Request), - headers = undefined, - body = undefined, - status = new}, - http_transport:setopts(SocketType, - Socket, [{active, once}]), - NewState = activate_request_timeout(TmpState), - {ok, NewState}; - {error, Reason} -> - self() ! {init_error, error_sending, - httpc_response:error(Request, Reason)}, - {ok, State#state{request = Request, - session = - #session{socket = Socket}}} - end; + ErrorHandler = + fun(ERequest, EState, EReason) -> + self() ! {init_error, error_sending, + httpc_response:error(ERequest, EReason)}, + {ok, EState#state{request = ERequest}} end, + tls_tunnel(Address, Request, State#state{session = Session}, ErrorHandler); {error, Reason} -> self() ! {init_error, error_connecting, httpc_response:error(Request, Reason)}, @@ -1024,15 +1047,25 @@ handle_http_msg(Body, #state{status_line = {_,Code, _}} = State) -> {NewBody, NewRequest} = stream(Body, State#state.request, Code), handle_response(State#state{body = NewBody, request = NewRequest}). -handle_http_body(<<>>, State = #state{status_line = {_,304, _}}) -> +handle_http_body(_, #state{status = {ssl_tunnel, _}, + status_line = {_,200, _}} = State) -> + tls_upgrade(State); + +handle_http_body(_, #state{status = {ssl_tunnel, Request}, + status_line = StatusLine} = State) -> + ClientErrMsg = httpc_response:error(Request,{could_no_establish_ssh_tunnel, StatusLine}), + NewState = answer_request(Request, ClientErrMsg, State), + {stop, normal, NewState}; + +handle_http_body(<<>>, #state{status_line = {_,304, _}} = State) -> ?hcrt("handle_http_body - 304", []), handle_response(State#state{body = <<>>}); -handle_http_body(<<>>, State = #state{status_line = {_,204, _}}) -> +handle_http_body(<<>>, #state{status_line = {_,204, _}} = State) -> ?hcrt("handle_http_body - 204", []), handle_response(State#state{body = <<>>}); -handle_http_body(<<>>, State = #state{request = #request{method = head}}) -> +handle_http_body(<<>>, #state{request = #request{method = head}} = State) -> ?hcrt("handle_http_body - head", []), handle_response(State#state{body = <<>>}); @@ -1119,7 +1152,7 @@ handle_response(#state{request = Request, {session, Session}, {status_line, StatusLine}]), - handle_cookies(Headers, Request, Options, ProfileName), + handle_cookies(Headers, Request, Options, httpc_manager), %% FOO profile_name case httpc_response:result({StatusLine, Headers, Body}, Request) of %% 100-continue continue -> @@ -1503,6 +1536,12 @@ retry_pipeline([Request | PipeLine], end, retry_pipeline(PipeLine, NewState). +handle_proxy_options(https, #options{https_proxy = {HttpsProxy, _} = HttpsProxyOpt}) when + HttpsProxy =/= undefined -> + HttpsProxyOpt; +handle_proxy_options(_, #options{proxy = Proxy}) -> + Proxy. + %%% Check to see if the given {Host,Port} tuple is in the NoProxyList %%% Returns an eventually updated {Host,Port} tuple, with the proxy address handle_proxy(HostPort = {Host, _Port}, {Proxy, NoProxy}) -> @@ -1696,6 +1735,96 @@ send_raw(SocketType, Socket, ProcessBody, Acc) -> end end. +tls_tunnel(Address, Request, #state{session = #session{socket = Socket, + socket_type = SocketType} = Session} = State, + ErrorHandler) -> + UpgradeRequest = tls_tunnel_request(Request), + case httpc_request:send(Address, Session, UpgradeRequest) of + ok -> + TmpState = State#state{request = UpgradeRequest, + %% session = Session, + mfa = init_mfa(UpgradeRequest, State), + status_line = + init_status_line(UpgradeRequest), + headers = undefined, + body = undefined}, + http_transport:setopts(SocketType, + Socket, [{active, once}]), + NewState = activate_request_timeout(TmpState), + {ok, NewState#state{status = {ssl_tunnel, Request}}}; + {error, Reason} -> + ErrorHandler(Request, State, Reason) + end. + +tls_tunnel_request(#request{headers = Headers, + settings = Options, + address = {Host, Port}= Adress, + ipv6_host_with_brackets = IPV6}) -> + + URI = Host ++":" ++ integer_to_list(Port), + + #request{ + id = make_ref(), + from = self(), + scheme = http, %% Use tcp-first and then upgrade! + address = Adress, + path = URI, + pquery = "", + method = connect, + headers = #http_request_h{host = host_header(Headers, URI), + te = "", + pragma = "no-cache", + other = [{"Proxy-Connection", " Keep-Alive"}]}, + settings = Options, + abs_uri = URI, + stream = false, + userinfo = "", + headers_as_is = [], + started = http_util:timestamp(), + ipv6_host_with_brackets = IPV6 + }. + +host_header(#http_request_h{host = Host}, _) -> + Host; + +%% Handles header_as_is +host_header(_, URI) -> + {ok, {_, _, Host, _, _, _}} = http_uri:parse(URI), + Host. + +tls_upgrade(#state{status = + {ssl_tunnel, + #request{settings = + #http_options{ssl = {_, TLSOptions} = SocketType}} = Request}, + session = #session{socket = TCPSocket} = Session0, + options = Options} = State) -> + + case ssl:connect(TCPSocket, TLSOptions) of + {ok, TLSSocket} -> + Address = Request#request.address, + ClientClose = httpc_request:is_client_closing(Request#request.headers), + SessionType = httpc_manager:session_type(Options), + Session = Session0#session{ + scheme = https, + socket = TLSSocket, + socket_type = SocketType, + type = SessionType, + client_close = ClientClose}, + httpc_request:send(Address, Session, Request), + http_transport:setopts(SocketType, TLSSocket, [{active, once}]), + NewState = State#state{session = Session, + request = Request, + mfa = init_mfa(Request, State), + status_line = + init_status_line(Request), + headers = undefined, + body = undefined, + status = new + }, + {noreply, activate_request_timeout(NewState)}; + {error, _Reason} -> + {stop, normal, State#state{request = Request}} + end. %% --------------------------------------------------------------------- %% Session wrappers diff --git a/lib/inets/src/http_client/httpc_internal.hrl b/lib/inets/src/http_client/httpc_internal.hrl index 8af752546c..30e2742e9d 100644 --- a/lib/inets/src/http_client/httpc_internal.hrl +++ b/lib/inets/src/http_client/httpc_internal.hrl @@ -37,6 +37,7 @@ -define(HTTP_MAX_REDIRECTS, 4). -define(HTTP_KEEP_ALIVE_TIMEOUT, 120000). -define(HTTP_KEEP_ALIVE_LENGTH, 5). +-define(TLS_UPGRADE_TOKEN, "TLS/1.0"). %%% HTTP Client per request settings -record(http_options, @@ -72,6 +73,7 @@ -record(options, { proxy = {undefined, []}, % {{ProxyHost, ProxyPort}, [NoProxy]}, + https_proxy = {undefined, []}, % {{ProxyHost, ProxyPort}, [NoProxy]} %% 0 means persistent connections are used without pipelining pipeline_timeout = ?HTTP_PIPELINE_TIMEOUT, max_pipeline_length = ?HTTP_PIPELINE_LENGTH, diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl index 3612b331e7..a267be5d05 100644 --- a/lib/inets/src/http_client/httpc_manager.erl +++ b/lib/inets/src/http_client/httpc_manager.erl @@ -577,6 +577,7 @@ handle_cast({set_options, Options}, State = #state{options = OldOptions}) -> ?hcrv("set options", [{options, Options}, {old_options, OldOptions}]), NewOptions = #options{proxy = get_proxy(Options, OldOptions), + https_proxy = get_https_proxy(Options, OldOptions), pipeline_timeout = get_pipeline_timeout(Options, OldOptions), max_pipeline_length = get_max_pipeline_length(Options, OldOptions), max_keep_alive_length = get_max_keep_alive_length(Options, OldOptions), @@ -1027,6 +1028,9 @@ get_option(socket_opts, #options{socket_opts = SocketOpts}) -> get_proxy(Opts, #options{proxy = Default}) -> proplists:get_value(proxy, Opts, Default). +get_https_proxy(Opts, #options{https_proxy = Default}) -> + proplists:get_value(https_proxy, Opts, Default). + get_pipeline_timeout(Opts, #options{pipeline_timeout = Default}) -> proplists:get_value(pipeline_timeout, Opts, Default). -- cgit v1.2.3 From 926e7f4572363989e3132355197dde6be47e1c2e Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Mon, 24 Sep 2012 14:28:02 +0200 Subject: inets: Dialyzer fixes --- lib/inets/src/http_client/httpc_handler.erl | 8 ++++++-- lib/inets/src/http_client/httpc_manager.erl | 19 ++++--------------- lib/inets/src/http_server/httpd_conf.erl | 4 +--- lib/inets/src/inets_app/inets.erl | 9 ++------- 4 files changed, 13 insertions(+), 27 deletions(-) diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index de77321bf8..d84030a831 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -142,8 +142,12 @@ stream_next(Pid) -> %% Used for debugging and testing %%-------------------------------------------------------------------- info(Pid) -> - call(info, Pid). - + try + call(info, Pid) + catch + _:_ -> + [] + end. %%-------------------------------------------------------------------- %% Function: stream(BodyPart, Request, Code) -> _ diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl index a267be5d05..0d5157894b 100644 --- a/lib/inets/src/http_client/httpc_manager.erl +++ b/lib/inets/src/http_client/httpc_manager.erl @@ -742,7 +742,7 @@ get_manager_info(#state{handler_db = HDB, SessionInfo = which_sessions2(SDB), OptionsInfo = [{Item, get_option(Item, Options)} || - Item <- record_info(fields, options)], + Item <- record_info(fields, options)], CookieInfo = httpc_cookie:which_cookies(CDB), [{handlers, HandlerInfo}, {sessions, SessionInfo}, @@ -770,20 +770,7 @@ get_handler_info(Tab) -> Pattern = {'$2', '$1', '_'}, Handlers1 = [{Pid, Id} || [Pid, Id] <- ets:match(Tab, Pattern)], Handlers2 = sort_handlers(Handlers1), - Handlers3 = [{Pid, Reqs, - try - begin - httpc_handler:info(Pid) - end - catch - _:_ -> - %% Why would this crash? - %% Only if the process has died, but we don't - %% know about it? - [] - end} || {Pid, Reqs} <- Handlers2], - Handlers3. - + [{Pid, Reqs, httpc_handler:info(Pid)} || {Pid, Reqs} <- Handlers2]. handle_request(#request{settings = #http_options{version = "HTTP/0.9"}} = Request, @@ -1002,6 +989,8 @@ cast(ProfileName, Msg) -> get_option(proxy, #options{proxy = Proxy}) -> Proxy; +get_option(https_proxy, #options{https_proxy = Proxy}) -> + Proxy; get_option(pipeline_timeout, #options{pipeline_timeout = Timeout}) -> Timeout; get_option(max_pipeline_length, #options{max_pipeline_length = Length}) -> diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl index 190967f656..05843d960f 100644 --- a/lib/inets/src/http_server/httpd_conf.erl +++ b/lib/inets/src/http_server/httpd_conf.erl @@ -847,9 +847,7 @@ os_info(Info) -> {OsFamily, _OsName} when Info =:= partial -> lists:flatten(io_lib:format("(~w)", [OsFamily])); {OsFamily, OsName} -> - lists:flatten(io_lib:format("(~w/~w)", [OsFamily, OsName])); - OsFamily -> - lists:flatten(io_lib:format("(~w)", [OsFamily])) + lists:flatten(io_lib:format("(~w/~w)", [OsFamily, OsName])) end. otp_release() -> diff --git a/lib/inets/src/inets_app/inets.erl b/lib/inets/src/inets_app/inets.erl index f33e0abe27..ed8082534f 100644 --- a/lib/inets/src/inets_app/inets.erl +++ b/lib/inets/src/inets_app/inets.erl @@ -274,13 +274,8 @@ sys_info() -> os_info() -> V = os:version(), - case os:type() of - {OsFam, OsName} -> - [{fam, OsFam}, {name, OsName}, {ver, V}]; - OsFam -> - [{fam, OsFam}, {ver, V}] - end. - + {OsFam, OsName} = os:type(), + [{fam, OsFam}, {name, OsName}, {ver, V}]. print_mods_info(Versions) -> case key1search(mod_info, Versions) of -- cgit v1.2.3 From 1f92bda6d4a9199f6e2962de139e1fd8215051de Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Mon, 24 Sep 2012 16:44:22 +0200 Subject: inets httpc: Document that RFC 2817 is not supported --- lib/inets/doc/src/httpc.xml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index 981215000a..b1f4e6dfe4 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -43,8 +43,12 @@ cookies and other options that can be applied to more than one request.

-

If the scheme - https is used the ssl application needs to be started.

+

If the scheme https is used the ssl application needs to be + started. When https links needs to go through a proxy the + CONNECT method extension to HTTP-1.1 is used to establish a + tunnel and then the connection is upgraded to TLS, + however "TLS upgrade" according to RFC 2817 is not + supported.

Also note that pipelining will only be used if the pipeline timeout is set, otherwise persistent connections without -- cgit v1.2.3 From e24be5cb3d1fb2b3e8eee3752f93ac4905597067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 3 Oct 2012 18:22:13 +0200 Subject: inets: Remove dbg tracing from testcases --- lib/inets/test/ftp_suite_lib.erl | 1 - lib/inets/test/httpc_SUITE.erl | 2 -- lib/inets/test/httpc_cookie_SUITE.erl | 3 --- lib/inets/test/httpd_SUITE.erl | 14 -------------- lib/inets/test/inets_SUITE.erl | 7 ------- lib/inets/test/inets_sup_SUITE.erl | 6 ------ 6 files changed, 33 deletions(-) diff --git a/lib/inets/test/ftp_suite_lib.erl b/lib/inets/test/ftp_suite_lib.erl index ffb58c91b6..211c9b5bee 100644 --- a/lib/inets/test/ftp_suite_lib.erl +++ b/lib/inets/test/ftp_suite_lib.erl @@ -206,7 +206,6 @@ init_per_testcase(Case, Config) init_per_testcase(Case, Config) -> put(ftp_testcase, Case), - inets:enable_trace(max, io, ftpc), do_init_per_testcase(Case, Config). do_init_per_testcase(Case, Config) diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index cb81d2cc5e..644b01120c 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -337,7 +337,6 @@ init_per_testcase(Case, Timeout, Config) -> %% so this value will be overwritten (see "ipv6_" below). %% - inets:enable_trace(max, io, httpc), %% inets:enable_trace(max, io, all), %% snmp:set_trace([gen_tcp]), tsp("init_per_testcase(~w) -> done when" @@ -381,7 +380,6 @@ end_per_testcase(http_save_to_file = Case, Config) -> end_per_testcase(Case, Config) -> io:format(user, "~n~n*** END ~w:~w ***~n~n", [?MODULE, Case]), - dbg:stop(), % ? case atom_to_list(Case) of "ipv6_" ++ _Rest -> tsp("end_per_testcase(~w) -> stop ssl", [Case]), diff --git a/lib/inets/test/httpc_cookie_SUITE.erl b/lib/inets/test/httpc_cookie_SUITE.erl index 93dbc270c5..3862bf7a20 100644 --- a/lib/inets/test/httpc_cookie_SUITE.erl +++ b/lib/inets/test/httpc_cookie_SUITE.erl @@ -276,8 +276,6 @@ secure_cookie(Config) when is_list(Config) -> tsp("secure_cookie -> entry with" "~n Config: ~p", [Config]), - inets:enable_trace(max, io, httpc), - %% httpc:reset_cookies(), tsp("secure_cookie -> Cookies 1: ~p", [httpc:which_cookies()]), @@ -309,7 +307,6 @@ secure_cookie(Config) when is_list(Config) -> tsp("secure_cookie -> Cookies 4: ~p", [httpc:which_cookies()]), - inets:disable_trace(), tsp("secure_cookie -> done"), ok. diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index 41e4188e5f..93a878e019 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -531,22 +531,8 @@ init_per_testcase3(Case, Config) -> application:stop(ssl), cleanup_mnesia(), - %% Set trace level - case lists:reverse(atom_to_list(Case)) of - "tset_emit" ++ _Rest -> % test-cases ending with time_test - tsp("init_per_testcase3(~w) -> disabling trace", [Case]), - inets:disable_trace(); - _ -> - tsp("init_per_testcase3(~w) -> enabling trace", [Case]), - %% TraceLevel = 70, - TraceLevel = max, - TraceDest = io, - inets:enable_trace(TraceLevel, TraceDest, httpd) - end, - %% Start initialization tsp("init_per_testcase3(~w) -> start init", [Case]), - Dog = test_server:timetrap(inets_test_lib:minutes(10)), NewConfig = lists:keydelete(watchdog, 1, Config), diff --git a/lib/inets/test/inets_SUITE.erl b/lib/inets/test/inets_SUITE.erl index 6fa0f44d77..069c68fa1e 100644 --- a/lib/inets/test/inets_SUITE.erl +++ b/lib/inets/test/inets_SUITE.erl @@ -363,8 +363,6 @@ start_ftpc(suite) -> []; start_ftpc(Config) when is_list(Config) -> process_flag(trap_exit, true), - inets:disable_trace(), - inets:enable_trace(max, io, ftpc), ok = inets:start(), try begin @@ -393,16 +391,13 @@ start_ftpc(Config) when is_list(Config) -> tsf(stand_alone_not_shutdown) end, ok = inets:stop(), - inets:disable_trace(), ok; _ -> - inets:disable_trace(), {skip, "Unable to reach selected FTP server " ++ FtpdHost} end end catch throw:{error, not_found} -> - inets:disable_trace(), {skip, "No available FTP servers"} end. @@ -462,8 +457,6 @@ httpd_reload(Config) when is_list(Config) -> {document_root, PrivDir}, {bind_address, "localhost"}], - inets:enable_trace(max, io), - i("httpd_reload -> start inets"), ok = inets:start(), diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl index 1d262a2739..65f0f0e09a 100644 --- a/lib/inets/test/inets_sup_SUITE.erl +++ b/lib/inets/test/inets_sup_SUITE.erl @@ -226,8 +226,6 @@ ftpc_worker(doc) -> ftpc_worker(suite) -> []; ftpc_worker(Config) when is_list(Config) -> - inets:disable_trace(), - inets:enable_trace(max, io, ftpc), [] = supervisor:which_children(ftp_sup), try begin @@ -239,20 +237,16 @@ ftpc_worker(Config) when is_list(Config) -> inets:stop(ftpc, Pid), test_server:sleep(5000), [] = supervisor:which_children(ftp_sup), - inets:disable_trace(), ok; Children -> - inets:disable_trace(), exit({unexpected_children, Children}) end; _ -> - inets:disable_trace(), {skip, "Unable to reach test FTP server"} end end catch throw:{error, not_found} -> - inets:disable_trace(), {skip, "No available FTP servers"} end. -- cgit v1.2.3 From 2e24f6fe79cf532d1276cb53b4c2b36528e0ac8b Mon Sep 17 00:00:00 2001 From: Hans Svensson Date: Thu, 17 Jan 2013 22:31:49 +0100 Subject: inets - httpc_SUITE: add redirect tests for response 303 httpc contains code for redirecting http response 301, 302, 303, and 307, but no tests existed for response '303 See Other'. Added tests (get, head, and post) for 303 and renumbered tests for 307. The subtle thing is that for 303 also post requests *should* be redirected. Also added the necessary 303 page to the dummy_server. --- lib/inets/test/httpc_SUITE.erl | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 644b01120c..fbd1b3d38a 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -1326,24 +1326,42 @@ http_redirect(Config) when is_list(Config) -> = httpc:request(post, {URL302, [],"text/plain", "foobar"}, [], []), - URL307 = ?URL_START ++ integer_to_list(Port) ++ "/307.html", + URL303 = ?URL_START ++ integer_to_list(Port) ++ "/303.html", tsp("http_redirect -> issue request 9: " + "~n ~p", [URL303]), + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, {URL303, []}, [], []), + + tsp("http_redirect -> issue request 10: " + "~n ~p", [URL303]), + {ok, {{_,200,_}, [_ | _], []}} + = httpc:request(head, {URL303, []}, [], []), + + tsp("http_redirect -> issue request 11: " + "~n ~p", [URL303]), + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(post, {URL303, [],"text/plain", "foobar"}, + [], []), + + URL307 = ?URL_START ++ integer_to_list(Port) ++ "/307.html", + + tsp("http_redirect -> issue request 12: " "~n ~p", [URL307]), {ok, {{_,200,_}, [_ | _], [_|_]}} = httpc:request(get, {URL307, []}, [], []), - tsp("http_redirect -> issue request 10: " + tsp("http_redirect -> issue request 13: " "~n ~p", [URL307]), {ok, {{_,200,_}, [_ | _], []}} = httpc:request(head, {URL307, []}, [], []), - tsp("http_redirect -> issue request 11: " + tsp("http_redirect -> issue request 14: " "~n ~p", [URL307]), {ok, {{_,307,_}, [_ | _], [_|_]}} = httpc:request(post, {URL307, [],"text/plain", "foobar"}, [], []), - + tsp("http_redirect -> stop dummy server"), DummyServerPid ! stop, tsp("http_redirect -> reset ipfamily option (to inet6fb4)"), @@ -3298,6 +3316,14 @@ handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket, Close, Send) -> "Content-Length:80\r\n\r\n" ++ "New place"; + "/303.html" -> + NewUri = ?URL_START ++ + integer_to_list(?IP_PORT) ++ "/dummy.html", + "HTTP/1.1 303 See Other \r\n" ++ + "Location:" ++ NewUri ++ "\r\n" ++ + "Content-Length:80\r\n\r\n" ++ + "New place"; "/307.html" -> NewUri = ?URL_START ++ integer_to_list(?IP_PORT) ++ "/dummy.html", -- cgit v1.2.3 From 85a398ab59ed83db7825d8c03f573c23540acda6 Mon Sep 17 00:00:00 2001 From: Hans Svensson Date: Thu, 17 Jan 2013 22:53:39 +0100 Subject: inets: fix autoredirect for POST requests responding 303 Changed httpc_response, for redirect '303 See Other' also POST requests should be redirected (using GET). See RFC2616 sect. 10.3.4 for clarification. --- lib/inets/src/http_client/httpc_response.erl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/inets/src/http_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl index 04976447cc..37f5f2ce6d 100644 --- a/lib/inets/src/http_client/httpc_response.erl +++ b/lib/inets/src/http_client/httpc_response.erl @@ -124,6 +124,11 @@ result(Response = {{_, Code, _}, _, _}, (Code =:= 303) orelse (Code =:= 307) -> redirect(Response, Request); +result(Response = {{_, 303, _}, _, _}, + Request = #request{settings = + #http_options{autoredirect = true}, + method = post}) -> + redirect(Response, Request#request{method = get}); result(Response = {{_,503,_}, _, _}, Request) -> -- cgit v1.2.3 From 51641778f6db9d3d6d325f2f4ad20258a0fbf21e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 25 Jan 2013 12:41:37 +0100 Subject: [inets] Appup test suite now handles version REs --- lib/inets/test/inets_appup_test.erl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/inets/test/inets_appup_test.erl b/lib/inets/test/inets_appup_test.erl index 7ed237243e..648e373312 100644 --- a/lib/inets/test/inets_appup_test.erl +++ b/lib/inets/test/inets_appup_test.erl @@ -257,6 +257,21 @@ check_instruction(_, Instr, _AllInstr, _Modules) -> check_version(V) when is_list(V) -> ok; +check_version(REBin) when is_binary(REBin) -> + try + begin + RE = binary_to_list(REBin), + case re:compile(RE) of + {ok, _} -> + ok; + {error, _} -> + error({bad_version, REBin}) + end + end + catch + _T:_E -> + error({bad_version, REBin}) + end; check_version(V) -> error({bad_version, V}). -- cgit v1.2.3 From 234e1eb46e1c883bae2e56828e0d6be5c64c3167 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Wed, 30 Jan 2013 13:20:13 +0100 Subject: inets httpc: Corrected separation of pipelining and persisten connections OTP-10845 --- lib/inets/src/http_client/httpc_handler.erl | 289 ++++++++++++---------------- 1 file changed, 123 insertions(+), 166 deletions(-) diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index d84030a831..dc7b90a432 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -257,10 +257,10 @@ handle_call(#request{address = Addr} = Request, _, session = #session{type = pipeline} = Session, timers = Timers, options = #options{proxy = Proxy} = _Options, - profile_name = ProfileName} = State) + profile_name = ProfileName} = State0) when Status =/= undefined -> - ?hcrv("new request on a pipeline session", + ?hcrv("new request on a pipeline session", [{request, Request}, {profile, ProfileName}, {status, Status}, @@ -274,18 +274,18 @@ handle_call(#request{address = Addr} = Request, _, ?hcrd("request sent", []), %% Activate the request time out for the new request - NewState = - activate_request_timeout(State#state{request = Request}), + State1 = + activate_request_timeout(State0#state{request = Request}), ClientClose = httpc_request:is_client_closing(Request#request.headers), - case State#state.request of + case State0#state.request of #request{} -> %% Old request not yet finished ?hcrd("old request still not finished", []), %% Make sure to use the new value of timers in state - NewTimers = NewState#state.timers, - NewPipeline = queue:in(Request, State#state.pipeline), + NewTimers = State1#state.timers, + NewPipeline = queue:in(Request, State1#state.pipeline), NewSession = Session#session{queue_length = %% Queue + current @@ -293,9 +293,9 @@ handle_call(#request{address = Addr} = Request, _, client_close = ClientClose}, insert_session(NewSession, ProfileName), ?hcrd("session updated", []), - {reply, ok, State#state{pipeline = NewPipeline, - session = NewSession, - timers = NewTimers}}; + {reply, ok, State1#state{pipeline = NewPipeline, + session = NewSession, + timers = NewTimers}}; undefined -> %% Note: tcp-message receiving has already been %% activated by handle_pipeline/2. @@ -306,94 +306,78 @@ handle_call(#request{address = Addr} = Request, _, Session#session{queue_length = 1, client_close = ClientClose}, httpc_manager:insert_session(NewSession, ProfileName), - Relaxed = - (Request#request.settings)#http_options.relaxed, - MFA = {httpc_response, parse, - [State#state.max_header_size, Relaxed]}, NewTimers = Timers#timers{queue_timer = undefined}, ?hcrd("session created", []), - {reply, ok, NewState#state{request = Request, - session = NewSession, - mfa = MFA, - timers = NewTimers}} + State = init_wait_for_response_state(Request, State1#state{session = NewSession, + timers = NewTimers}), + {reply, ok, State} end; {error, Reason} -> ?hcri("failed sending request", [{reason, Reason}]), - {reply, {pipeline_failed, Reason}, State} + {reply, {pipeline_failed, Reason}, State0} end; -handle_call(#request{address = Addr} = Request, _, +handle_call(#request{address = Addr} = Request, _, #state{status = Status, session = #session{type = keep_alive} = Session, timers = Timers, options = #options{proxy = Proxy} = _Options, - profile_name = ProfileName} = State) + profile_name = ProfileName} = State0) when Status =/= undefined -> - - ?hcrv("new request on a keep-alive session", + + ?hcrv("new request on a keep-alive session", [{request, Request}, {profile, ProfileName}, {status, Status}]), - Address = handle_proxy(Addr, Proxy), - case httpc_request:send(Address, Session, Request) of - ok -> - - ?hcrd("request sent", []), - - %% Activate the request time out for the new request - NewState = - activate_request_timeout(State#state{request = Request}), - - ClientClose = - httpc_request:is_client_closing(Request#request.headers), - - case State#state.request of - #request{} -> %% Old request not yet finished - %% Make sure to use the new value of timers in state - ?hcrd("old request still not finished", []), - NewTimers = NewState#state.timers, - NewKeepAlive = queue:in(Request, State#state.keep_alive), - NewSession = - Session#session{queue_length = - %% Queue + current - queue:len(NewKeepAlive) + 1, - client_close = ClientClose}, - insert_session(NewSession, ProfileName), - ?hcrd("session updated", []), - {reply, ok, State#state{keep_alive = NewKeepAlive, - session = NewSession, - timers = NewTimers}}; - undefined -> - %% Note: tcp-message reciving has already been - %% activated by handle_pipeline/2. - ?hcrd("no current request", []), - cancel_timer(Timers#timers.queue_timer, - timeout_queue), - NewSession = - Session#session{queue_length = 1, - client_close = ClientClose}, - insert_session(NewSession, ProfileName), - Relaxed = - (Request#request.settings)#http_options.relaxed, - MFA = {httpc_response, parse, - [State#state.max_header_size, Relaxed]}, - {reply, ok, NewState#state{request = Request, - session = NewSession, - mfa = MFA}} - end; - - {error, Reason} -> - ?hcri("failed sending request", [{reason, Reason}]), - {reply, {request_failed, Reason}, State} + ClientClose = httpc_request:is_client_closing(Request#request.headers), + + case State0#state.request of + #request{} -> %% Old request not yet finished + %% Make sure to use the new value of timers in state + ?hcrd("old request still not finished", []), + NewKeepAlive = queue:in(Request, State0#state.keep_alive), + NewSession = + Session#session{queue_length = + %% Queue + current + queue:len(NewKeepAlive) + 1, + client_close = ClientClose}, + insert_session(NewSession, ProfileName), + ?hcrd("session updated", []), + {reply, ok, State0#state{keep_alive = NewKeepAlive, + session = NewSession}}; + undefined -> + %% Note: tcp-message reciving has already been + %% activated by handle_pipeline/2. + ?hcrd("no current request", []), + cancel_timer(Timers#timers.queue_timer, + timeout_queue), + Address = handle_proxy(Addr, Proxy), + case httpc_request:send(Address, Session, Request) of + ok -> + ?hcrd("request sent", []), + + %% Activate the request time out for the new request + State1 = + activate_request_timeout(State0#state{request = Request}), + NewTimers = State1#state.timers, + NewSession = + Session#session{queue_length = 1, + client_close = ClientClose}, + insert_session(NewSession, ProfileName), + State = init_wait_for_response_state(Request, State1#state{session = NewSession, + timers = NewTimers}), + {reply, ok, State}; + {error, Reason} -> + ?hcri("failed sending request", [{reason, Reason}]), + {reply, {request_failed, Reason}, State0} + end end; - handle_call(info, _, State) -> - Info = handler_info(State), + Info = handler_info(State), {reply, Info, State}. - %%-------------------------------------------------------------------- %% Function: handle_cast(Msg, State) -> {noreply, State} | %% {noreply, State, Timeout} | @@ -1239,8 +1223,7 @@ handle_queue(#state{status = pipeline} = State, Data) -> handle_pipeline(#state{status = pipeline, session = Session, profile_name = ProfileName, - options = #options{pipeline_timeout = TimeOut}} = - State, + options = #options{pipeline_timeout = TimeOut}} = State, Data) -> ?hcrd("handle pipeline", [{profile, ProfileName}, @@ -1250,25 +1233,7 @@ handle_pipeline(#state{status = pipeline, case queue:out(State#state.pipeline) of {empty, _} -> ?hcrd("pipeline queue empty", []), - - %% The server may choose too teminate an idle pipeline - %% in this case we want to receive the close message - %% at once and not when trying to pipeline the next - %% request. - activate_once(Session), - - %% If a pipeline that has been idle for some time is not - %% closed by the server, the client may want to close it. - NewState = activate_queue_timeout(TimeOut, State), - update_session(ProfileName, Session, #session.queue_length, 0), - %% Note mfa will be initilized when a new request - %% arrives. - {noreply, - NewState#state{request = undefined, - mfa = undefined, - status_line = undefined, - headers = undefined, - body = undefined}}; + handle_empty_queue(Session, ProfileName, TimeOut, State); {{value, NextRequest}, Pipeline} -> ?hcrd("pipeline queue non-empty", []), case lists:member(NextRequest#request.id, @@ -1286,38 +1251,17 @@ handle_pipeline(#state{status = pipeline, Session#session{queue_length = %% Queue + current queue:len(Pipeline) + 1}, - insert_session(NewSession, ProfileName), - Relaxed = - (NextRequest#request.settings)#http_options.relaxed, - MFA = {httpc_response, - parse, - [State#state.max_header_size, Relaxed]}, - NewState = - State#state{pipeline = Pipeline, - request = NextRequest, - mfa = MFA, - status_line = undefined, - headers = undefined, - body = undefined}, - case Data of - <<>> -> - activate_once(Session), - {noreply, NewState}; - _ -> - %% If we already received some bytes of - %% the next response - handle_info({httpc_handler, dummy, Data}, - NewState) - end + receive_response(NextRequest, + NewSession, Data, + State#state{pipeline = Pipeline}) end end. -handle_keep_alive_queue( - #state{status = keep_alive, - session = Session, - profile_name = ProfileName, - options = #options{keep_alive_timeout = TimeOut}} = State, - Data) -> +handle_keep_alive_queue(#state{status = keep_alive, + session = Session, + profile_name = ProfileName, + options = #options{keep_alive_timeout = TimeOut}} = State, + Data) -> ?hcrd("handle keep_alive", [{profile, ProfileName}, {session, Session}, @@ -1326,25 +1270,7 @@ handle_keep_alive_queue( case queue:out(State#state.keep_alive) of {empty, _} -> ?hcrd("keep_alive queue empty", []), - %% The server may choose too terminate an idle keep_alive session - %% in this case we want to receive the close message - %% at once and not when trying to send the next - %% request. - activate_once(Session), - %% If a keep_alive session has been idle for some time is not - %% closed by the server, the client may want to close it. - NewState = activate_queue_timeout(TimeOut, State), - update_session(ProfileName, Session, #session.queue_length, 0), - %% Note mfa will be initilized when a new request - %% arrives. - {noreply, - NewState#state{request = undefined, - mfa = undefined, - status_line = undefined, - headers = undefined, - body = undefined - } - }; + handle_empty_queue(Session, ProfileName, TimeOut, State); {{value, NextRequest}, KeepAlive} -> ?hcrd("keep_alive queue non-empty", []), case lists:member(NextRequest#request.id, @@ -1355,30 +1281,61 @@ handle_keep_alive_queue( State#state{keep_alive = KeepAlive}, Data); false -> ?hcrv("next request", [{request, NextRequest}]), - Relaxed = - (NextRequest#request.settings)#http_options.relaxed, - MFA = {httpc_response, parse, - [State#state.max_header_size, Relaxed]}, - NewState = - State#state{request = NextRequest, - keep_alive = KeepAlive, - mfa = MFA, - status_line = undefined, - headers = undefined, - body = undefined}, - case Data of - <<>> -> - activate_once(Session), - {noreply, NewState}; - _ -> - %% If we already received some bytes of - %% the next response - handle_info({httpc_handler, dummy, Data}, - NewState) + #request{address = Address} = NextRequest, + case httpc_request:send(Address, Session, NextRequest) of + ok -> + receive_response(NextRequest, + Session, <<>>, + State#state{keep_alive = KeepAlive}); + {error, Reason} -> + {reply, {keep_alive_failed, Reason}, State} end end end. +handle_empty_queue(Session, ProfileName, TimeOut, State) -> + %% The server may choose too terminate an idle pipline| keep_alive session + %% in this case we want to receive the close message + %% at once and not when trying to send the next + %% request. + activate_once(Session), + %% If a pipline | keep_alive session has been idle for some time is not + %% closed by the server, the client may want to close it. + NewState = activate_queue_timeout(TimeOut, State), + update_session(ProfileName, Session, #session.queue_length, 0), + %% Note mfa will be initilized when a new request + %% arrives. + {noreply, + NewState#state{request = undefined, + mfa = undefined, + status_line = undefined, + headers = undefined, + body = undefined + } + }. + +receive_response(Request, Session, Data, State) -> + NewState = init_wait_for_response_state(Request, State), + gather_data(Data, Session, NewState). + +init_wait_for_response_state(Request, State) -> + Relaxed = + (Request#request.settings)#http_options.relaxed, + MFA = {httpc_response, parse, + [State#state.max_header_size, Relaxed]}, + State#state{request = Request, + mfa = MFA, + status_line = undefined, + headers = undefined, + body = undefined}. + +gather_data(<<>>, Session, State) -> + activate_once(Session), + {noreply, State}; +gather_data(Data, _, State) -> + %% If we already received some bytes of + %% the next response + handle_info({httpc_handler, dummy, Data}, State). case_insensitive_header(Str) when is_list(Str) -> http_util:to_lower(Str); -- cgit v1.2.3 From b9d6b674e70b99ce97c6ab9de3c04d3e20c320be Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 12 Feb 2013 10:12:02 +0100 Subject: inets httpd: The option modules now defaults to its documented default value. OTP-10844 --- lib/inets/src/http_server/httpd_response.erl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl index 2dedb088e4..6b6532266b 100644 --- a/lib/inets/src/http_server/httpd_response.erl +++ b/lib/inets/src/http_server/httpd_response.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2013. 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 @@ -35,8 +35,7 @@ generate_and_send_response(#mod{init_data = #init_data{peername = {_,"unknown"}}}) -> ok; generate_and_send_response(#mod{config_db = ConfigDB} = ModData) -> - Modules = httpd_util:lookup(ConfigDB,modules, - [mod_get, mod_head, mod_log]), + Modules = httpd_util:lookup(ConfigDB,modules, ?DEFAULT_MODS), case traverse_modules(ModData, Modules) of done -> ok; @@ -71,7 +70,6 @@ traverse_modules(ModData,[Module|Rest]) -> ?hdrd("traverse modules", [{callback_module, Module}]), case (catch apply(Module, do, [ModData])) of {'EXIT', Reason} -> - ?hdrd("traverse modules - exit", [{reason, Reason}]), String = lists:flatten( io_lib:format("traverse exit from apply: ~p:do => ~n~p", -- cgit v1.2.3 From 0047d213e4e345fc04bb492ed877a35d67d86969 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Thu, 31 Jan 2013 15:34:51 +0100 Subject: inets: Improve ssl handling httpc: CTfy test suite httpd: Simplify ssl configuration OTP-10846 --- lib/inets/doc/src/httpd.xml | 72 +- lib/inets/src/http_client/httpc_handler.erl | 12 +- lib/inets/src/http_lib/http_transport.erl | 6 +- lib/inets/src/http_server/httpd_conf.erl | 16 +- lib/inets/src/http_server/httpd_sup.erl | 109 +- lib/inets/test/Makefile | 3 +- lib/inets/test/httpc_SUITE.erl | 4501 +++++++++------------------ lib/inets/test/httpd_SUITE.erl | 350 +-- lib/inets/test/httpd_mod.erl | 23 +- lib/inets/test/httpd_test_lib.erl | 19 +- lib/inets/test/inets_test_lib.erl | 107 +- lib/inets/test/old_httpc_SUITE.erl | 3602 +++++++++++++++++++++ lib/inets/test/uri_SUITE.erl | 159 + 13 files changed, 5343 insertions(+), 3636 deletions(-) create mode 100644 lib/inets/test/old_httpc_SUITE.erl create mode 100644 lib/inets/test/uri_SUITE.erl diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index 3fced5dfcd..58293f79fc 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -4,7 +4,7 @@

- 19972012 + 19972013 Ericsson AB. All Rights Reserved. @@ -163,11 +163,9 @@ - {socket_type, ip_comm | ssl | essl} + {socket_type, ip_comm | {essl, Config::proplist()}} -

When using ssl, there are currently only one alternative. - essl specifically uses the Erlang based SSL. - ssl defaults to essl.

+

For ssl configuration options see ssl:listen/2

Defaults to ip_comm.

@@ -395,71 +393,7 @@ bytes - - -

ssl properties

- - - {ssl_ca_certificate_file, path()} - -

Used as cacertfile option in ssl:listen/2 see - ssl(3).

-
- - - {ssl_certificate_file, path()} - -

Used as certfile option in ssl:listen/2 see - ssl(3).

-
- - - {ssl_ciphers, list()} - -

Used as ciphers option in ssl:listen/2 see - ssl(3).

-
- - - {ssl_verify_client, integer()} - -

Used as verify option in ssl:listen/2 see - ssl(3).

-
- - {ssl_verify_depth, integer()} - -

Used as depth option in ssl:listen/2 see - ssl(3).

-
- - - {ssl_password_callback_function, atom()} - -

Used together with ssl_password_callback_module - to retrieve a value to use as password option to ssl:listen/2 - see ssl(3).

-
- - - {ssl_password_callback_arguments, list()} - -

Used together with ssl_password_callback_function to supply a - list of arguments to the callback function. If not specified - the callback function will be assumed to have arity 0.

-
- - - {ssl_password_callback_module, atom()} - -

Used together with ssl_password_callback_function - to retrieve a value to use as password option to ssl:listen/2 - see ssl(3).

-
- -
-

URL aliasing properties - requires mod_alias

diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index dc7b90a432..54db6a5c81 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2012. All Rights Reserved. +%% Copyright Ericsson AB 2002-2013. 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 @@ -281,7 +281,7 @@ handle_call(#request{address = Addr} = Request, _, httpc_request:is_client_closing(Request#request.headers), case State0#state.request of - #request{} -> %% Old request not yet finished + #request{} = OldRequest -> %% Old request not yet finished ?hcrd("old request still not finished", []), %% Make sure to use the new value of timers in state NewTimers = State1#state.timers, @@ -293,9 +293,11 @@ handle_call(#request{address = Addr} = Request, _, client_close = ClientClose}, insert_session(NewSession, ProfileName), ?hcrd("session updated", []), - {reply, ok, State1#state{pipeline = NewPipeline, - session = NewSession, - timers = NewTimers}}; + {reply, ok, State1#state{ + request = OldRequest, + pipeline = NewPipeline, + session = NewSession, + timers = NewTimers}}; undefined -> %% Note: tcp-message receiving has already been %% activated by handle_pipeline/2. diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index 8b103628d7..1692dcad78 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. 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 @@ -184,7 +184,9 @@ listen({essl, SSLConfig}, Addr, Port) -> listen(ip_comm, Addr, Port, Fd) -> - listen_ip_comm(Addr, Port, Fd). + listen_ip_comm(Addr, Port, Fd); +listen({_, SSLConfig}, Addr, Port, _Fd) -> + listen_ssl(Addr, Port, SSLConfig). listen_ip_comm(Addr, Port, Fd) -> case (catch do_listen_ip_comm(Addr, Port, Fd)) of diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl index 05843d960f..b3ca13e2fe 100644 --- a/lib/inets/src/http_server/httpd_conf.erl +++ b/lib/inets/src/http_server/httpd_conf.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2012. All Rights Reserved. +%% Copyright Ericsson AB 1997-2013. 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 @@ -589,11 +589,17 @@ validate_config_params([{server_tokens, {private, Value}} | Rest]) validate_config_params([{server_tokens, Value} | _]) -> throw({server_tokens, Value}); +validate_config_params([{socket_type, ip_comm} | Rest]) -> + validate_config_params(Rest); + validate_config_params([{socket_type, Value} | Rest]) - when (Value =:= ip_comm) orelse - (Value =:= ssl) orelse - (Value =:= essl) -> + when Value == ssl; Value == essl -> validate_config_params(Rest); + +validate_config_params([{socket_type, {Value, _}} | Rest]) + when Value == essl orelse Value == ssl -> + validate_config_params(Rest); + validate_config_params([{socket_type, Value} | _]) -> throw({socket_type, Value}); @@ -923,6 +929,8 @@ lookup_socket_type(ConfigDB) -> case httpd_util:lookup(ConfigDB, socket_type, ip_comm) of ip_comm -> ip_comm; + {Tag, Conf} -> + {Tag, Conf}; SSL when (SSL =:= ssl) orelse (SSL =:= essl) -> SSLTag = if diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl index 8f3e8f9500..4be298939f 100644 --- a/lib/inets/src/http_server/httpd_sup.erl +++ b/lib/inets/src/http_server/httpd_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2012. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. 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 @@ -196,7 +196,8 @@ httpd_child_spec(ConfigFile, AcceptTimeoutDef, DebugDef) -> end. httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port) -> - case (Port =:= 0) orelse proplists:is_defined(fd, Config) of + Fd = proplists:get_value(fd, Config, undefined), + case Port == 0 orelse Fd =/= undefined of true -> httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port); false -> @@ -242,19 +243,24 @@ error_msg(F, A) -> error_logger:error_msg(F ++ "~n", A). listen(Address, Port, Config) -> - SocketType = proplists:get_value(socket_type, Config, ip_comm), - case http_transport:start(SocketType) of - ok -> - Fd = proplists:get_value(fd, Config), - case http_transport:listen(SocketType, Address, Port, Fd) of - {ok, ListenSocket} -> - NewConfig = proplists:delete(port, Config), - {ok, NewPort} = inet:port(ListenSocket), - {NewPort, [{port, NewPort} | NewConfig], ListenSocket}; + try socket_type(Config) of + SocketType -> + case http_transport:start(SocketType) of + ok -> + Fd = proplists:get_value(fd, Config), + case http_transport:listen(SocketType, Address, Port, Fd) of + {ok, ListenSocket} -> + NewConfig = proplists:delete(port, Config), + {NewPort, _} = http_transport:sockname(SocketType, ListenSocket), + {NewPort, [{port, NewPort} | NewConfig], ListenSocket}; + {error, Reason} -> + {error, {listen, Reason}} + end; {error, Reason} -> - {error, {listen, Reason}} - end; - {error, Reason} -> + {error, {socket_start_failed, Reason}} + end + catch + _:Reason -> {error, {socket_start_failed, Reason}} end. @@ -280,7 +286,82 @@ listen_loop() -> ok end. +socket_type(Config) -> + SocketType = proplists:get_value(socket_type, Config, ip_comm), + socket_type(SocketType, Config). + +socket_type(ip_comm = SocketType, _) -> + SocketType; +socket_type({essl, _} = SocketType, _) -> + SocketType; +socket_type(_, Config) -> + {essl, ssl_config(Config)}. + +%%% Backwards compatibility +ssl_config(Config) -> + ssl_certificate_key_file(Config) ++ + ssl_verify_client(Config) ++ + ssl_ciphers(Config) ++ + ssl_password(Config) ++ + ssl_verify_depth(Config) ++ + ssl_ca_certificate_file(Config). + +ssl_certificate_key_file(Config) -> + case proplists:get_value(ssl_certificate_key_file, Config) of + undefined -> + []; + SSLCertificateKeyFile -> + [{keyfile,SSLCertificateKeyFile}] + end. +ssl_verify_client(Config) -> + case proplists:get_value(ssl_verify_client, Config) of + undefined -> + []; + SSLVerifyClient -> + [{verify,SSLVerifyClient}] + end. +ssl_ciphers(Config) -> + case proplists:get_value(ssl_ciphers, Config) of + undefined -> + []; + Ciphers -> + [{ciphers, Ciphers}] + end. +ssl_password(Config) -> + case proplists:get_value(ssl_password_callback_module, Config) of + undefined -> + []; + Module -> + case proplists:get_value(ssl_password_callback_function, Config) of + undefined -> + []; + Function -> + Args = case proplists:get_value(ssl_password_callback_arguments, Config) of + undefined -> + []; + Arguments -> + [Arguments] + end, + Password = apply(Module, Function, Args), + [{password, Password}] + end + end. +ssl_verify_depth(Config) -> + case proplists:get_value(ssl_verify_client_depth, Config) of + undefined -> + []; + Depth -> + [{depth, Depth}] + end. + +ssl_ca_certificate_file(Config) -> + case proplists:get_value(ssl_ca_certificate_file, Config) of + undefined -> + []; + File -> + [{cacertfile, File}] + end. diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile index 0ca99e8692..0e7954fba8 100644 --- a/lib/inets/test/Makefile +++ b/lib/inets/test/Makefile @@ -184,7 +184,8 @@ MODULES = \ inets_app_test \ inets_appup_test \ tftp_test_lib \ - tftp_SUITE + tftp_SUITE \ + uri_SUITE EBIN = . diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index fbd1b3d38a..43eaa48961 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -19,2656 +19,919 @@ %% %% -%% ts:run(inets, httpc_SUITE, [batch]). -%% +%% ct:run("../inets_test", httpc_SUITE). +%% -module(httpc_SUITE). --include_lib("common_test/include/ct.hrl"). --include("test_server_line.hrl"). - -include_lib("kernel/include/file.hrl"). +-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). -%% Test server specific exports --define(IP_PORT, 8998). --define(SSL_PORT, 8999). +-define(URL_START, "http://"). +-define(TLS_URL_START, "https://"). -define(NOT_IN_USE_PORT, 8997). --define(LOCAL_HOST, {127,0,0,1}). --define(IPV6_LOCAL_HOST, "0:0:0:0:0:0:0:1"). --define(URL_START, "http://localhost:"). --define(SSL_URL_START, "https://localhost:"). --define(CR, $\r). -define(LF, $\n). -define(HTTP_MAX_HEADER_SIZE, 10240). - - +-record(sslsocket, {fd = nil, pid = nil}). %%-------------------------------------------------------------------- -%% all(Arg) -> [Doc] | [Case] | {skip, Comment} -%% Arg - doc | suite -%% Doc - string() -%% Case - atom() -%% Name of a test case function. -%% Comment - string() -%% Description: Returns documentation/test cases in this test suite -%% or a skip tuple if the platform is not supported. +%% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- +suite() -> + [{ct_hooks,[ts_install_cth]}]. -suite() -> [{ct_hooks,[ts_install_cth]}]. - -all() -> +all() -> [ - 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, - 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, - parse_url, - options, - headers_as_is, - selecting_session, - {group, ssl}, - {group, stream}, - {group, ipv6}, - {group, tickets}, - initial_server_connect + {group, http}, + {group, sim_http}, + {group, https}, + {group, sim_https}, + {group, misc} ]. -groups() -> +groups() -> [ - {ssl, [], [ssl_head, - essl_head, - ssl_get, - essl_get, - ssl_trace, - essl_trace]}, - {stream, [], [http_stream, - http_stream_once]}, - {tickets, [], [hexed_query_otp_6191, - empty_body_otp_6243, - empty_response_header_otp_6830, - transfer_encoding_otp_6807, - 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]}, - {ipv6, [], [ipv6_ipcomm, ipv6_essl]} + {http, [], real_requests()}, + {sim_http, [], only_simulated()}, + {https, [], real_requests()}, + {sim_https, [], only_simulated()}, + {misc, [], misc()} ]. +real_requests()-> + [ + head, + get, + post, + post_stream, + async, + pipeline, + persistent_connection, + save_to_file, + save_to_file_async, + headers_as_is, + page_does_not_exist, + emulate_lower_versions, + headers, + headers_as_is, + empty_body, + stream, + stream_to_pid, + stream_through_fun, + stream_through_mfa, + streaming_error, + inet_opts + ]. -init_per_group(ipv6 = _GroupName, Config) -> - case inets_test_lib:has_ipv6_support() of - {ok, _} -> - Config; - _ -> - {skip, "Host does not support IPv6"} - end; -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. +only_simulated() -> + [ + cookie, + trace, + stream_once, + no_content_204, + tolerate_missing_CR, + userinfo, + bad_response, + internal_server_error, + invalid_http, + headers_dummy, + empty_response_header, + remote_socket_close, + remote_socket_close_async, + transfer_encoding, + redirect_loop, + redirect_moved_permanently, + redirect_multiple_choises, + redirect_found, + redirect_see_other, + redirect_temporary_redirect, + port_in_host_header, + relaxed + ]. +misc() -> + [ + server_does_not_exist, + timeout_memory_leak, + wait_for_whole_response + ]. %%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initiation before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - - ?PRINT_SYSTEM_INFO([]), +init_per_suite(Config) -> PrivDir = ?config(priv_dir, Config), DataDir = ?config(data_dir, Config), + inets_test_lib:start_apps([inets]), ServerRoot = filename:join(PrivDir, "server_root"), DocRoot = filename:join(ServerRoot, "htdocs"), - IpConfFile = integer_to_list(?IP_PORT) ++ ".conf", - SslConfFile = integer_to_list(?SSL_PORT) ++ ".conf", - setup_server_dirs(ServerRoot, DocRoot, DataDir), - create_config(IpConfFile, ip_comm, ?IP_PORT, PrivDir, ServerRoot, - DocRoot, DataDir), - create_config(SslConfFile, ssl, ?SSL_PORT, PrivDir, ServerRoot, - DocRoot, DataDir), - - Cgi = case test_server:os_type() of - {win32, _} -> - filename:join([ServerRoot, "cgi-bin", "cgi_echo.exe"]); - _ -> - filename:join([ServerRoot, "cgi-bin", "cgi_echo"]) - end, - - {ok, FileInfo} = file:read_file_info(Cgi), - ok = file:write_file_info(Cgi, FileInfo#file_info{mode = 8#00755}), - - [{has_ipv6_support, inets_test_lib:has_ipv6_support()}, - {server_root, ServerRoot}, - {doc_root, DocRoot}, - {local_port, ?IP_PORT}, - {local_ssl_port, ?SSL_PORT} | Config]. + [{server_root, ServerRoot}, {doc_root, DocRoot} | Config]. - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- end_per_suite(Config) -> - PrivDir = ?config(priv_dir, Config), + inets_test_lib:stop_apps([inets]), + PrivDir = ?config(priv_dir, Config), inets_test_lib:del_dirs(PrivDir), - application:stop(inets), - application:stop(ssl), ok. - %%-------------------------------------------------------------------- -%% Function: init_per_testcase(Case, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. +init_per_group(misc = Group, Config) -> + start_apps(Group), + Inet = inet_version(), + ok = httpc:set_options([{ipfamily, Inet}]), + Config; + +init_per_group(Group, Config0) -> + start_apps(Group), + Config = proplists:delete(port, Config0), + Port = server_start(Group, server_config(Group, Config)), + [{port, Port} | Config]. + +end_per_group(_, _Config) -> + ok. + %%-------------------------------------------------------------------- +init_per_testcase(pipeline, Config) -> + inets:start(httpc, [{profile, pipeline}]), + httpc:set_options([{pipeline_timeout, 50000}, + {max_pipeline_length, 3}], pipeline), -init_per_testcase(otp_8154_1 = Case, Config) -> - init_per_testcase(Case, 5, Config); - -init_per_testcase(initial_server_connect = Case, Config) -> - %% Try to check if crypto actually exist or not, - %% this test case does not work unless it does - try - begin - ?ENSURE_STARTED([crypto, public_key, ssl]), - inets:start(), - Config - end - catch - throw:{error, {failed_starting, App, ActualError}} -> - tsp("init_per_testcase(~w) -> failed starting ~w: " - "~n ~p", [Case, App, ActualError]), - SkipString = - "Could not start " ++ atom_to_list(App), - skip(SkipString); - _:X -> - SkipString = - lists:flatten( - io_lib:format("Failed starting apps: ~p", [X])), - skip(SkipString) - end; + Config; +init_per_testcase(persistent_connection, Config) -> + inets:start(httpc, [{profile, persistent}]), + httpc:set_options([{keep_alive_timeout, 50000}, + {max_keep_alive_length, 3}], persistent_connection), -init_per_testcase(Case, Config) -> - init_per_testcase(Case, 2, Config). + Config; -init_per_testcase(Case, Timeout, Config) -> - io:format(user, - "~n~n*** INIT ~w:~w[~w] ***" - "~n~n", [?MODULE, Case, Timeout]), +init_per_testcase(_Case, Config) -> + Config. - PrivDir = ?config(priv_dir, Config), - application:stop(inets), - Dog = test_server:timetrap(inets_test_lib:minutes(Timeout)), - TmpConfig = lists:keydelete(watchdog, 1, Config), - IpConfFile = integer_to_list(?IP_PORT) ++ ".conf", - SslConfFile = integer_to_list(?SSL_PORT) ++ ".conf", - - %% inets:enable_trace(max, io, httpd), - %% inets:enable_trace(max, io, httpc), - %% inets:enable_trace(max, io, all), - - NewConfig = - case atom_to_list(Case) of - [$s, $s, $l | _] -> - ?ENSURE_STARTED([crypto, public_key, ssl]), - init_per_testcase_ssl(ssl, PrivDir, SslConfFile, - [{watchdog, Dog} | TmpConfig]); - - [$e, $s, $s, $l | _] -> - ?ENSURE_STARTED([crypto, public_key, ssl]), - init_per_testcase_ssl(essl, PrivDir, SslConfFile, - [{watchdog, Dog} | TmpConfig]); - - "ipv6_" ++ _Rest -> - %% Ensure needed apps (crypto, public_key and ssl) are started - try ?ENSURE_STARTED([crypto, public_key, ssl]) of - ok -> - Profile = ipv6, - %% A stand-alone profile is represented by a pid() - {ok, ProfilePid} = - inets:start(httpc, - [{profile, Profile}, - {data_dir, PrivDir}], stand_alone), - ok = httpc:set_options([{ipfamily, inet6}], - ProfilePid), - tsp("httpc profile pid: ~p", [ProfilePid]), - [{watchdog, Dog}, {profile, ProfilePid}| TmpConfig] - catch - throw:{error, {failed_starting, App, ActualError}} -> - tsp("init_per_testcase(~w) -> failed starting ~w: " - "~n ~p", [Case, App, ActualError]), - SkipString = - "Could not start " ++ atom_to_list(App), - skip(SkipString); - _:X -> - SkipString = - lists:flatten( - io_lib:format("Failed starting apps: ~p", [X])), - skip(SkipString) - end; - - _ -> - %% Try inet6fb4 on windows... - %% No need? Since it is set above? - - %% tsp("init_per_testcase -> allways try IPv6 on windows"), - %% ?RUN_ON_WINDOWS( - %% fun() -> - %% tsp("init_per_testcase:set_options_fun -> " - %% "set-option ipfamily to inet6fb4"), - %% Res = httpc:set_options([{ipfamily, inet6fb4}]), - %% tsp("init_per_testcase:set_options_fun -> " - %% "~n Res: ~p", [Res]), - %% Res - %% end), - - TmpConfig2 = lists:keydelete(local_server, 1, TmpConfig), - %% Will start inets - tsp("init_per_testcase -> try start server"), - Server = start_http_server(PrivDir, IpConfFile), - [{watchdog, Dog}, {local_server, Server} | TmpConfig2] - end, - - %% - %% Set default ipfamily to the same as the main server has by default - %% This makes the client try w/ ipv6 before falling back to ipv4, - %% as that is what the server is configured to do. - %% Note that this is required for the tests to run on *BSD w/ ipv6 enabled - %% as well as on Windows. The Linux behaviour of allowing ipv4 connects - %% to ipv6 sockets is not required or even encouraged. - - tsp("init_per_testcase -> Options before ipfamily set: ~n~p", - [httpc:get_options(all)]), - ok = httpc:set_options([{ipfamily, inet6fb4}]), - tsp("init_per_testcase -> Options after ipfamily set: ~n~p", - [httpc:get_options(all)]), - - %% Note that the IPv6 test case(s) *must* use inet6, - %% so this value will be overwritten (see "ipv6_" below). - %% - - %% inets:enable_trace(max, io, all), - %% snmp:set_trace([gen_tcp]), - tsp("init_per_testcase(~w) -> done when" - "~n NewConfig: ~p" - "~n~n", [Case, NewConfig]), - NewConfig. - - -init_per_testcase_ssl(Tag, PrivDir, SslConfFile, Config) -> - tsp("init_per_testcase_ssl(~w) -> stop ssl", [Tag]), - application:stop(ssl), - Config2 = lists:keydelete(local_ssl_server, 1, Config), - %% Will start inets - tsp("init_per_testcase_ssl(~w) -> try start http server (including inets)", - [Tag]), - Server = inets_test_lib:start_http_server( - filename:join(PrivDir, SslConfFile), Tag), - tsp("init_per_testcase(~w) -> Server: ~p", [Tag, Server]), - [{local_ssl_server, Server} | Config2]. - -start_http_server(ConfDir, ConfFile) -> - inets_test_lib:start_http_server( filename:join(ConfDir, ConfFile) ). +end_per_testcase(pipeline, _Config) -> + inets:stop(httpc, pipeline); +end_per_testcase(persistent_connection, _Config) -> + inets:stop(httpc, persistent); +end_per_testcase(_Case, _Config) -> + ok. %%-------------------------------------------------------------------- -%% Function: end_per_testcase(Case, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case +%% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- -end_per_testcase(http_save_to_file = Case, Config) -> - io:format(user, "~n~n*** END ~w:~w ***~n~n", - [?MODULE, Case]), - PrivDir = ?config(priv_dir, Config), - FullPath = filename:join(PrivDir, "dummy.html"), - file:delete(FullPath), - finish(Config); - -end_per_testcase(Case, Config) -> - io:format(user, "~n~n*** END ~w:~w ***~n~n", - [?MODULE, Case]), - case atom_to_list(Case) of - "ipv6_" ++ _Rest -> - tsp("end_per_testcase(~w) -> stop ssl", [Case]), - application:stop(ssl), - tsp("end_per_testcase(~w) -> stop public_key", [Case]), - application:stop(public_key), - tsp("end_per_testcase(~w) -> stop crypto", [Case]), - application:stop(crypto), - ProfilePid = ?config(profile, Config), - tsp("end_per_testcase(~w) -> stop httpc profile (~p)", - [Case, ProfilePid]), - unlink(ProfilePid), - inets:stop(stand_alone, ProfilePid), - tsp("end_per_testcase(~w) -> httpc profile (~p) stopped", - [Case, ProfilePid]), - ok; - _ -> - ok - end, - finish(Config). - -finish(Config) -> - Dog = ?config(watchdog, Config), - case Dog of - undefined -> - ok; - _ -> - tsp("finish -> stop watchdog (~p)", [Dog]), - test_server:timetrap_cancel(Dog) - end. -%%------------------------------------------------------------------------- -%% Test cases starts here. -%%------------------------------------------------------------------------- +head() -> + [{doc, "Test http head request against local server."}]. +head(Config) when is_list(Config) -> + Request = {url(group_name(Config), "/dummy.html", Config), []}, + {ok, {{_,200,_}, [_ | _], []}} = httpc:request(head, Request, [], []). +%%-------------------------------------------------------------------- +get() -> + [{doc, "Test http get request against local server"}]. +get(Config) when is_list(Config) -> + Request = {url(group_name(Config), "/dummy.html", Config), []}, + {ok, {{_,200,_}, [_ | _], Body = [_ | _]}} = httpc:request(get, Request, [], []), + inets_test_lib:check_body(Body), -%%------------------------------------------------------------------------- + {ok, {{_,200,_}, [_ | _], BinBody}} = httpc:request(get, Request, [], [{body_format, binary}]), + true = is_binary(BinBody). +%%-------------------------------------------------------------------- +post() -> + [{"Test http post request against local server. We do in this case " + "only care about the client side of the the post. The server " + "script will not actually use the post data."}]. +post(Config) when is_list(Config) -> + CGI = case test_server:os_type() of + {win32, _} -> + "/cgi-bin/cgi_echo.exe"; + _ -> + "/cgi-bin/cgi_echo" + end, -http_options(doc) -> - ["Test http options request against local server."]; -http_options(suite) -> - []; -http_options(Config) when is_list(Config) -> - skip("Not supported by httpd"). + URL = url(group_name(Config), CGI, Config), -http_head(doc) -> - ["Test http head request against local server."]; -http_head(suite) -> - []; -http_head(Config) when is_list(Config) -> - tsp("http_head -> entry with" - "~n Config: ~p", [Config]), - Method = head, - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - Request = {URL, []}, - HttpOpts = [], - Opts = [], - VerifyResult = - fun({ok, {{_,200,_}, [_ | _], []}}) -> - ok; - ({ok, UnexpectedReply}) -> - tsp("http_head:verify_fun -> Unexpected Reply: " - "~n ~p", [UnexpectedReply]), - tsf({unexpected_reply, UnexpectedReply}); - ({error, Reason} = Error) -> - tsp("http_head:verify_fun -> Error reply: " - "~n Reason: ~p", [Reason]), - tsf({bad_reply, Error}) - end, - simple_request_and_verify(Config, - Method, Request, HttpOpts, Opts, VerifyResult). + %% Cgi-script expects the body length to be 100 + Body = lists:duplicate(100, "1"), + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + httpc:request(post, {URL, [{"expect","100-continue"}], + "text/plain", Body}, [], []), -%%------------------------------------------------------------------------- + {ok, {{_,504,_}, [_ | _], []}} = + httpc:request(post, {URL, [{"expect","100-continue"}], + "text/plain", "foobar"}, [], []). -http_get(doc) -> - ["Test http get request against local server"]; -http_get(suite) -> - []; -http_get(Config) when is_list(Config) -> - tsp("http_get -> entry with" - "~n Config: ~p", [Config]), - case ?config(local_server, Config) of - ok -> - tsp("local-server running"), - Method = get, - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - Request = {URL, []}, - Timeout = timer:seconds(1), - ConnTimeout = Timeout + timer:seconds(1), - HttpOptions1 = [{timeout, Timeout}, - {connect_timeout, ConnTimeout}], - Options1 = [], - Body = - case httpc:request(Method, Request, HttpOptions1, Options1) of - {ok, {{_,200,_}, [_ | _], ReplyBody = [_ | _]}} -> - ReplyBody; - {ok, UnexpectedReply1} -> - tsf({unexpected_reply, UnexpectedReply1}); - {error, _} = Error1 -> - tsf({bad_reply, Error1}) - end, - - %% eqvivivalent to httpc:request(get, {URL, []}, [], []), - inets_test_lib:check_body(Body), - - HttpOptions2 = [], - Options2 = [{body_format, binary}], - case httpc:request(Method, Request, HttpOptions2, Options2) of - {ok, {{_,200,_}, [_ | _], Bin}} when is_binary(Bin) -> - ok; - {ok, {{_,200,_}, [_ | _], BadBin}} -> - tsf({body_format_not_binary, BadBin}); - {ok, UnexpectedReply2} -> - tsf({unexpected_reply, UnexpectedReply2}); - {error, _} = Error2 -> - tsf({bad_reply, Error2}) - end; - _ -> - skip("Failed to start local http-server") - end. +%%-------------------------------------------------------------------- +post_stream() -> + [{"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."}]. +post_stream(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), -http_post(doc) -> - ["Test http post request against local server. We do in this case " - "only care about the client side of the the post. The server " - "script will not actually use the post data."]; -http_post(suite) -> - []; -http_post(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 - Body = lists:duplicate(100, "1"), - - {ok, {{_,200,_}, [_ | _], [_ | _]}} = - httpc:request(post, {URL, [{"expect","100-continue"}], - "text/plain", Body}, [], []), - - {ok, {{_,504,_}, [_ | _], []}} = - httpc:request(post, {URL, [{"expect","100-continue"}], - "text/plain", "foobar"}, [], []); - _ -> - skip("Failed to start local http-server") - end. + %% Cgi-script expects the body length to be 100 + BodyFun = fun(0) -> + eof; + (LenLeft) -> + {ok, lists:duplicate(10, "1"), LenLeft - 10} + 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. + {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}}, [], []). -%%------------------------------------------------------------------------- -http_emulate_lower_versions(doc) -> - ["Perform request as 0.9 and 1.0 clients."]; -http_emulate_lower_versions(suite) -> - []; -http_emulate_lower_versions(Config) when is_list(Config) -> - case ?config(local_server, Config) of - ok -> - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - {ok, Body0} = - httpc:request(get, {URL, []}, [{version, "HTTP/0.9"}], []), - inets_test_lib:check_body(Body0), - {ok, {{"HTTP/1.0", 200, _}, [_ | _], Body1 = [_ | _]}} = - httpc:request(get, {URL, []}, [{version, "HTTP/1.0"}], []), - inets_test_lib:check_body(Body1), - {ok, {{"HTTP/1.1", 200, _}, [_ | _], Body2 = [_ | _]}} = - httpc:request(get, {URL, []}, [{version, "HTTP/1.1"}], []), - inets_test_lib:check_body(Body2); - _-> - skip("Failed to start local http-server") +%%-------------------------------------------------------------------- +trace() -> + [{doc, "Perform a TRACE request."}]. +trace(Config) when is_list(Config) -> + Request = {url(group_name(Config), "/trace.html", Config), []}, + case httpc:request(trace, Request, [], []) of + {ok, {{_,200,_}, [_ | _], "TRACE /trace.html" ++ _}} -> + ok; + Other -> + ct:fail({unexpected, Other}) end. +%%-------------------------------------------------------------------- -%%------------------------------------------------------------------------- +pipeline(Config) when is_list(Config) -> + Request = {url(group_name(Config), "/dummy.html", Config), []}, + {ok, _} = httpc:request(get, Request, [], [], pipeline), -http_relaxed(doc) -> - ["Test relaxed mode"]; -http_relaxed(suite) -> - []; -http_relaxed(Config) when is_list(Config) -> - ok = httpc:set_options([{ipv6, disabled}]), % also test the old option - %% ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(ipv4), - - URL = ?URL_START ++ integer_to_list(Port) ++ - "/missing_reason_phrase.html", - - {error, Reason} = - httpc:request(get, {URL, []}, [{relaxed, false}], []), + %% Make sure pipeline session is registerd + test_server:sleep(4000), + keep_alive_requests(Request, pipeline). - test_server:format("Not relaxed: ~p~n", [Reason]), - - {ok, {{_, 200, _}, [_ | _], [_ | _]}} = - httpc:request(get, {URL, []}, [{relaxed, true}], []), +%%-------------------------------------------------------------------- - DummyServerPid ! stop, - ok = httpc:set_options([{ipv6, enabled}]), - %% ok = httpc:set_options([{ipfamily, inet6fb4}]), - ok. +persistent_connection(Config) when is_list(Config) -> + Request = {url(group_name(Config), "/dummy.html", Config), []}, + {ok, _} = httpc:request(get, Request, [], [], persistent), + %% Make sure pipeline session is registerd + test_server:sleep(4000), + keep_alive_requests(Request, persistent). %%------------------------------------------------------------------------- -http_dummy_pipe(doc) -> - ["Test pipelining code."]; -http_dummy_pipe(suite) -> - []; -http_dummy_pipe(Config) when is_list(Config) -> - ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(ipv4), - - URL = ?URL_START ++ integer_to_list(Port) ++ "/foobar.html", - - test_pipeline(URL), - - DummyServerPid ! stop, - ok = httpc:set_options([{ipfamily, inet6fb4}]), - ok. - -http_inets_pipe(doc) -> - ["Test pipelining code."]; -http_inets_pipe(suite) -> - []; -http_inets_pipe(Config) when is_list(Config) -> - - case ?config(local_server, Config) of - ok -> - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - test_pipeline(URL); - _ -> - skip("Failed to start local http-server") - end. +async() -> + [{doc, "Test an asynchrony http request."}]. +async(Config) when is_list(Config) -> + Request = {url(group_name(Config), "/dummy.html", Config), []}, + {ok, RequestId} = + httpc:request(get, Request, [], [{sync, false}]), + Body = + receive + {http, {RequestId, {{_, 200, _}, _, BinBody}}} -> + BinBody; + {http, Msg} -> + ct:fail(Msg) + end, + inets_test_lib:check_body(binary_to_list(Body)), -test_pipeline(URL) -> - p("test_pipeline -> entry with" - "~n URL: ~p", [URL]), - - httpc:set_options([{pipeline_timeout, 50000}]), - - p("test_pipeline -> issue (async) request 1" - "~n when profile info: ~p", [httpc:info()]), - {ok, RequestIdA1} = - httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestIdA1: ~p", [RequestIdA1]), - p("test_pipeline -> RequestIdA1: ~p" - "~n when profile info: ~p", [RequestIdA1, httpc:info()]), - - %% Make sure pipeline is initiated - p("test_pipeline -> sleep some", []), - test_server:sleep(4000), - - p("test_pipeline -> issue (async) request A2, A3 and A4" - "~n when profile info: ~p", [httpc:info()]), - {ok, RequestIdA2} = - httpc:request(get, {URL, []}, [], [{sync, false}]), - {ok, RequestIdA3} = - httpc:request(get, {URL, []}, [], [{sync, false}]), - {ok, RequestIdA4} = - httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestIdAs => A2: ~p, A3: ~p and A4: ~p", - [RequestIdA2, RequestIdA3, RequestIdA4]), - p("test_pipeline -> RequestIds => A2: ~p, A3: ~p and A4: ~p" - "~n when profile info: ~p", - [RequestIdA2, RequestIdA3, RequestIdA4, httpc:info()]), - - p("test_pipeline -> issue (sync) request 3"), - {ok, {{_,200,_}, [_ | _], [_ | _]}} = - httpc:request(get, {URL, []}, [], []), - - p("test_pipeline -> expect reply for (async) request A1, A2, A3 and A4" - "~n when profile info: ~p", [httpc:info()]), - pipeline_await_async_reply([{RequestIdA1, a1, 200}, - {RequestIdA2, a2, 200}, - {RequestIdA3, a3, 200}, - {RequestIdA4, a4, 200}], ?MINS(1)), - - p("test_pipeline -> sleep some" - "~n when profile info: ~p", [httpc:info()]), - test_server:sleep(4000), - - p("test_pipeline -> issue (async) request B1, B2, B3 and B4" - "~n when profile info: ~p", [httpc:info()]), - {ok, RequestIdB1} = - httpc:request(get, {URL, []}, [], [{sync, false}]), - {ok, RequestIdB2} = - httpc:request(get, {URL, []}, [], [{sync, false}]), - {ok, RequestIdB3} = - httpc:request(get, {URL, []}, [], [{sync, false}]), - {ok, RequestIdB4} = - httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestIdBs => B1: ~p, B2: ~p, B3: ~p and B4: ~p", - [RequestIdB1, RequestIdB2, RequestIdB3, RequestIdB4]), - p("test_pipeline -> RequestIdBs => B1: ~p, B2: ~p, B3: ~p and B4: ~p" - "~n when profile info: ~p", - [RequestIdB1, RequestIdB2, RequestIdB3, RequestIdB4, httpc:info()]), - - p("test_pipeline -> cancel (async) request B2" - "~n when profile info: ~p", [httpc:info()]), - ok = httpc:cancel_request(RequestIdB2), - - p("test_pipeline -> " - "expect *no* reply for cancelled (async) request B2 (for 3 secs)" - "~n when profile info: ~p", [httpc:info()]), + {ok, NewRequestId} = + httpc:request(get, Request, [], [{sync, false}]), + ok = httpc:cancel_request(NewRequestId), receive - {http, {RequestIdB2, _}} -> - tsf(http_cancel_request_failed) + {http, {NewRequestId, _}} -> + ct:fail(http_cancel_request_failed) after 3000 -> ok - end, - - p("test_pipeline -> expect reply for (async) request B1, B3 and B4" - "~n when profile info: ~p", [httpc:info()]), - Bodies = pipeline_await_async_reply([{RequestIdB1, b1, 200}, - {RequestIdB3, b3, 200}, - {RequestIdB4, b4, 200}], ?MINS(1)), - [{b1, Body}|_] = Bodies, - - p("test_pipeline -> check reply for (async) request B1" - "~n when profile info: ~p", [httpc:info()]), - inets_test_lib:check_body(binary_to_list(Body)), - - p("test_pipeline -> ensure no unexpected incomming" - "~n when profile info: ~p", [httpc:info()]), - receive - {http, Any} -> - tsf({unexpected_message, Any}) - after 500 -> - ok - end, - - p("test_pipeline -> done" - "~n when profile info: ~p", [httpc:info()]), - ok. - -pipeline_await_async_reply(ReqIds, Timeout) -> - pipeline_await_async_reply(ReqIds, Timeout, []). + end. +%%------------------------------------------------------------------------- +save_to_file() -> + [{doc, "Test to save the http body to a file"}]. +save_to_file(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FilePath = filename:join(PrivDir, "dummy.html"), + URL = url(group_name(Config), "/dummy.html", Config), + Request = {URL, []}, + {ok, saved_to_file} + = httpc:request(get, Request, [], [{stream, FilePath}]), + {ok, Bin} = file:read_file(FilePath), + {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL), + Bin == Body. -pipeline_await_async_reply([], _, Acc) -> - lists:keysort(1, Acc); -pipeline_await_async_reply(ReqIds, Timeout, Acc) when Timeout > 0 -> - T1 = inets_test_lib:timestamp(), - p("pipeline_await_async_reply -> await replies" - "~n ReqIds: ~p" - "~n Timeout: ~p", [ReqIds, Timeout]), +%%------------------------------------------------------------------------- +save_to_file_async() -> + [{doc,"Test to save the http body to a file"}]. +save_to_file_async(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + FilePath = filename:join(PrivDir, "dummy.html"), + URL = url(group_name(Config), "/dummy.html", Config), + Request = {URL, []}, + {ok, RequestId} = httpc:request(get, Request, [], + [{stream, FilePath}, + {sync, false}]), receive - {http, {RequestId, {{_, Status, _}, _, Body}}} -> - p("pipeline_await_async_reply -> received reply for" - "~n RequestId: ~p" - "~n Status: ~p", [RequestId, Status]), - case lists:keysearch(RequestId, 1, ReqIds) of - {value, {RequestId, N, Status}} -> - p("pipeline_await_async_reply -> " - "found expected request ~w", [N]), - ReqIds2 = lists:keydelete(RequestId, 1, ReqIds), - NewTimeout = Timeout - (inets_test_lib:timestamp()-T1), - pipeline_await_async_reply(ReqIds2, NewTimeout, - [{N, Body} | Acc]); - {value, {RequestId, N, WrongStatus}} -> - p("pipeline_await_async_reply -> " - "found request ~w with wrong status", [N]), - tsf({reply_with_unexpected_status, - {RequestId, N, WrongStatus}}); - false -> - tsf({unexpected_reply, {RequestId, Status}}) - end; + {http, {RequestId, saved_to_file}} -> + ok; {http, Msg} -> - tsf({unexpected_reply, Msg}) - after Timeout -> - receive - Any -> - tsp("pipeline_await_async_reply -> " - "received unknown data after timeout: " - "~n ~p", [Any]), - tsf({timeout, {unknown, Any}}) - end - end; -pipeline_await_async_reply(ReqIds, _, Acc) -> - tsp("pipeline_await_async_reply -> " - "timeout: " - "~n ~p" - "~nwhen" - "~n ~p", [ReqIds, Acc]), - tsf({timeout, ReqIds, Acc}). - + ct:fail(Msg) + end, - + {ok, Bin} = file:read_file(FilePath), + {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL), + Bin == Body. %%------------------------------------------------------------------------- -http_trace(doc) -> - ["Perform a TRACE request."]; -http_trace(suite) -> - []; -http_trace(Config) when is_list(Config) -> - case ?config(local_server, Config) of - ok -> - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - case httpc:request(trace, {URL, []}, [], []) of - {ok, {{_,200,_}, [_ | _], "TRACE /dummy.html" ++ _}} -> - ok; - {ok, {{_,200,_}, [_ | _], WrongBody}} -> - tsf({wrong_body, WrongBody}); - {ok, WrongReply} -> - tsf({wrong_reply, WrongReply}); - Error -> - tsf({failed, Error}) - end; - _ -> - skip("Failed to start local http-server") - end. +stream() -> + [{doc, "Test the option stream for asynchrony requests"}]. +stream(Config) when is_list(Config) -> + Request = {url(group_name(Config), "/dummy.html", Config), []}, + stream_test(Request, {stream, self}). %%------------------------------------------------------------------------- -http_async(doc) -> - ["Test an asynchrony http request."]; -http_async(suite) -> - []; -http_async(Config) when is_list(Config) -> - case ?config(local_server, Config) of - ok -> - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - {ok, RequestId} = - httpc:request(get, {URL, []}, [], [{sync, false}]), - - Body = - receive - {http, {RequestId, {{_, 200, _}, _, BinBody}}} -> - BinBody; - {http, Msg} -> - tsf(Msg) - end, - - inets_test_lib:check_body(binary_to_list(Body)), - - {ok, NewRequestId} = - httpc:request(get, {URL, []}, [], [{sync, false}]), - ok = httpc:cancel_request(NewRequestId), - receive - {http, {NewRequestId, _NewResult}} -> - tsf(http_cancel_request_failed) - after 3000 -> - ok - end; - _ -> - skip("Failed to start local http-server") - end. +stream_once() -> + [{doc, "Test the option stream for asynchrony requests"}]. +stream_once(Config) when is_list(Config) -> + Request0 = {url(group_name(Config), "/dummy.html", Config), []}, + stream_test(Request0, {stream, {self, once}}), -%%------------------------------------------------------------------------- -http_save_to_file(doc) -> - ["Test to save the http body to a file"]; -http_save_to_file(suite) -> - []; -http_save_to_file(Config) when is_list(Config) -> - case ?config(local_server, Config) of - ok -> - PrivDir = ?config(priv_dir, Config), - FilePath = filename:join(PrivDir, "dummy.html"), - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - {ok, saved_to_file} - = httpc:request(get, {URL, []}, [], [{stream, FilePath}]), - {ok, Bin} = file:read_file(FilePath), - {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL), - Bin == Body; - _ -> - skip("Failed to start local http-server") - end. + Request1 = {url(group_name(Config), "/once.html", Config), []}, + stream_test(Request1, {stream, {self, once}}), + Request2 = {url(group_name(Config), "/once_chunked.html", Config), []}, + stream_test(Request2, {stream, {self, once}}). %%------------------------------------------------------------------------- -http_save_to_file_async(doc) -> - ["Test to save the http body to a file"]; -http_save_to_file_async(suite) -> - []; -http_save_to_file_async(Config) when is_list(Config) -> - case ?config(local_server, Config) of - ok -> - PrivDir = ?config(priv_dir, Config), - FilePath = filename:join(PrivDir, "dummy.html"), - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - {ok, RequestId} = httpc:request(get, {URL, []}, [], - [{stream, FilePath}, - {sync, false}]), - receive - {http, {RequestId, saved_to_file}} -> - ok; - {http, Msg} -> - tsf(Msg) - end, +redirect_multiple_choises() -> + [{doc, "The user agent, selection of the most appropriate choice MAY " + "be performed automatically."}]. +redirect_multiple_choises(Config) when is_list(Config) -> + URL300 = url(group_name(Config), "/300.html", Config), - {ok, Bin} = file:read_file(FilePath), - {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL), - Bin == Body; - _ -> - skip("Failed to start local http-server") - end. -%%------------------------------------------------------------------------- -http_headers(doc) -> - ["Use as many request headers as possible not used in proxy_headers"]; -http_headers(suite) -> - []; -http_headers(Config) when is_list(Config) -> - - case ?config(local_server, Config) of - ok -> - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - DocRoot = ?config(doc_root, Config), - {ok, FileInfo} = - file:read_file_info(filename:join([DocRoot,"dummy.html"])), - CreatedSec = - calendar:datetime_to_gregorian_seconds( - FileInfo#file_info.mtime), - - Mod = httpd_util:rfc1123_date( - calendar:gregorian_seconds_to_datetime( - CreatedSec-1)), - - Date = httpd_util:rfc1123_date({date(), time()}), - - {ok, {{_,200,_}, [_ | _], [_ | _]}} = - httpc:request(get, {URL, [{"If-Modified-Since", - Mod}, - {"From","webmaster@erlang.se"}, - {"Date", Date} - ]}, [], []), - - Mod1 = httpd_util:rfc1123_date( - calendar:gregorian_seconds_to_datetime( - CreatedSec+1)), - - {ok, {{_,200,_}, [_ | _], [_ | _]}} = - httpc:request(get, {URL, [{"If-UnModified-Since", - Mod1} - ]}, [], []), - - Tag = httpd_util:create_etag(FileInfo), - - - {ok, {{_,200,_}, [_ | _], [_ | _]}} = - httpc:request(get, {URL, [{"If-Match", - Tag} - ]}, [], []), - - {ok, {{_,200,_}, [_ | _], _}} = - httpc:request(get, {URL, [{"If-None-Match", - "NotEtag,NeihterEtag"}, - {"Connection", "Close"} - ]}, [], []), - ok; - _ -> - skip("Failed to start local http-server") - end. + catch {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, {URL300, []}, [], []), + {ok, {{_,300,_}, [_ | _], _}} = + httpc:request(get, {URL300, []}, [{autoredirect, false}], []). %%------------------------------------------------------------------------- -http_headers_dummy(doc) -> - ["Test the code for handling headers we do not want/can send " - "to a real server. Note it is not logical to send" - "all of these headers together, we only want to test that" - "the code for handling headers will not crash."]; -http_headers_dummy(suite) -> - []; -http_headers_dummy(Config) when is_list(Config) -> - ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(ipv4), - - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy_headers.html", - - Foo = http_chunk:encode("foobar") ++ - binary_to_list(http_chunk:encode_last()), - FooBar = Foo ++ "\r\n\r\nOther:inets_test\r\n\r\n", +redirect_moved_permanently() -> + [{doc, "If the 301 status code is received in response to a request other " + "than GET or HEAD, the user agent MUST NOT automatically redirect the request " + "unless it can be confirmed by the user, since this might change " + "the conditions under which the request was issued."}]. +redirect_moved_permanently(Config) when is_list(Config) -> - UserPasswd = base64:encode_to_string("Alladin:Sesame"), - Auth = "Basic " ++ UserPasswd, + URL301 = url(group_name(Config), "/301.html", Config), - %% The dummy server will ignore the headers, we only want to test - %% that the client header-handling code. This would not - %% be a vaild http-request! - {ok, {{_,200,_}, [_ | _], [_|_]}} = - httpc:request(post, - {URL, - [{"Via", - "1.0 fred, 1.1 nowhere.com (Apache/1.1)"}, - {"Warning","1#pseudonym foobar"}, - {"Vary","*"}, - {"Upgrade","HTTP/2.0"}, - {"Pragma", "1#no-cache"}, - {"Cache-Control", "no-cache"}, - {"Connection", "close"}, - {"Date", "Sat, 29 Oct 1994 19:43:31 GMT"}, - {"Accept", " text/plain; q=0.5, text/html"}, - {"Accept-Language", "en"}, - {"Accept-Encoding","chunked"}, - {"Accept-Charset", "ISO8859-1"}, - {"Authorization", Auth}, - {"Expect", "1#100-continue"}, - {"User-Agent","inets"}, - {"Transfer-Encoding","chunked"}, - {"Range", " bytes=0-499"}, - {"If-Range", "Sat, 29 Oct 1994 19:43:31 GMT"}, - {"If-Match", "*"}, - {"Content-Type", "text/plain"}, - {"Content-Encoding", "chunked"}, - {"Content-Length", "6"}, - {"Content-Language", "en"}, - {"Content-Location", "http://www.foobar.se"}, - {"Content-MD5", - "104528739076276072743283077410617235478"}, - {"Content-Range", "bytes 0-499/1234"}, - {"Allow", "GET"}, - {"Proxy-Authorization", Auth}, - {"Expires", "Sat, 29 Oct 1994 19:43:31 GMT"}, - {"Upgrade", "HTTP/2.0"}, - {"Last-Modified", "Sat, 29 Oct 1994 19:43:31 GMT"}, - {"Trailer","1#User-Agent"} - ], "text/plain", FooBar}, - [], []), - DummyServerPid ! stop, - ok = httpc:set_options([{ipfamily, inet6fb4}]), - ok. - + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, {URL301, []}, [], []), + {ok, {{_,200,_}, [_ | _], []}} + = httpc:request(head, {URL301, []}, [], []), + + {ok, {{_,301,_}, [_ | _], [_|_]}} + = httpc:request(post, {URL301, [],"text/plain", "foobar"}, + [], []). %%------------------------------------------------------------------------- -http_bad_response(doc) -> - ["Test what happens when the server does not follow the protocol"]; -http_bad_response(suite) -> - []; -http_bad_response(Config) when is_list(Config) -> - ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(ipv4), - - URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_crlf.html", - - URL1 = ?URL_START ++ integer_to_list(Port) ++ "/wrong_statusline.html", - - {error, timeout} = httpc:request(get, {URL, []}, [{timeout, 400}], []), - - {error, Reason} = httpc:request(URL1), - - test_server:format("Wrong Statusline: ~p~n", [Reason]), +redirect_found() -> + [{doc," If the 302 status code is received in response to a request other " + "than GET or HEAD, the user agent MUST NOT automatically redirect the " + "request unless it can be confirmed by the user, since this might change " + "the conditions under which the request was issued."}]. +redirect_found(Config) when is_list(Config) -> - DummyServerPid ! stop, - ok = httpc:set_options([{ipfamily, inet6fb4}]), - ok. + URL302 = url(group_name(Config), "/302.html", Config), + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, {URL302, []}, [], []), + {ok, {{_,200,_}, [_ | _], []}} + = httpc:request(head, {URL302, []}, [], []), + + {ok, {{_,302,_}, [_ | _], [_|_]}} + = httpc:request(post, {URL302, [],"text/plain", "foobar"}, + [], []). %%------------------------------------------------------------------------- -ssl_head(doc) -> - ["Same as http_head/1 but over ssl sockets."]; -ssl_head(suite) -> - []; -ssl_head(Config) when is_list(Config) -> - ssl_head(ssl, Config). +redirect_see_other() -> + [{doc, "The different URI SHOULD be given by the Location field in the response. " + "Unless the request method was HEAD, the entity of the response SHOULD contain a short " + "hypertext note with a hyperlink to the new URI(s). "}]. +redirect_see_other(Config) when is_list(Config) -> -essl_head(doc) -> - ["Same as http_head/1 but over ssl sockets."]; -essl_head(suite) -> - []; -essl_head(Config) when is_list(Config) -> - ssl_head(essl, Config). - -ssl_head(SslTag, Config) -> - tsp("ssl_head -> entry with" - "~n SslTag: ~p" - "~n Config: ~p", [SslTag, Config]), - case ?config(local_ssl_server, Config) of - ok -> - DataDir = ?config(data_dir, Config), - Port = ?config(local_ssl_port, Config), - URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html", - CertFile = filename:join(DataDir, "ssl_client_cert.pem"), - SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], - SSLConfig = - case SslTag of - ssl -> - SSLOptions; - essl -> - {essl, SSLOptions} - end, - tsp("ssl_head -> make request using: " - "~n URL: ~p" - "~n SslTag: ~p" - "~n SSLOptions: ~p", [URL, SslTag, SSLOptions]), - {ok, {{_,200, _}, [_ | _], []}} = - httpc:request(head, {URL, []}, [{ssl, SSLConfig}], []); - {ok, _} -> - skip("local http-server not started"); - _ -> - skip("SSL not started") - end. + URL303 = url(group_name(Config), "/303.html", Config), - + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, {URL303, []}, [], []), + + {ok, {{_,200,_}, [_ | _], []}} + = httpc:request(head, {URL303, []}, [], []), + + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(post, {URL303, [],"text/plain", "foobar"}, + [], []). %%------------------------------------------------------------------------- -ssl_get(doc) -> - ["Same as http_get/1 but over ssl sockets."]; -ssl_get(suite) -> - []; -ssl_get(Config) when is_list(Config) -> - ssl_get(ssl, Config). +redirect_temporary_redirect() -> + [{doc," If the 307 status code is received in response to a request other " + "than GET or HEAD, the user agent MUST NOT automatically redirect the request " + "unless it can be confirmed by the user, since this might change " + "the conditions under which the request was issued."}]. +redirect_temporary_redirect(Config) when is_list(Config) -> -essl_get(doc) -> - ["Same as http_get/1 but over ssl sockets."]; -essl_get(suite) -> - []; -essl_get(Config) when is_list(Config) -> - ssl_get(essl, Config). + URL307 = url(group_name(Config), "/307.html", Config), -ssl_get(SslTag, Config) when is_list(Config) -> - case ?config(local_ssl_server, Config) of - ok -> - DataDir = ?config(data_dir, Config), - Port = ?config(local_ssl_port, Config), - URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html", - CertFile = filename:join(DataDir, "ssl_client_cert.pem"), - SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], - SSLConfig = - case SslTag of - ssl -> - SSLOptions; - essl -> - {essl, SSLOptions} - end, - tsp("ssl_get -> make request using: " - "~n URL: ~p" - "~n SslTag: ~p" - "~n SSLOptions: ~p", [URL, SslTag, SSLOptions]), - case httpc:request(get, {URL, []}, [{ssl, SSLConfig}], []) of - {ok, {{_,200, _}, [_ | _], Body = [_ | _]}} -> - inets_test_lib:check_body(Body), - ok; - {ok, {StatusLine, Headers, _Body}} -> - tsp("ssl_get -> unexpected result: " - "~n StatusLine: ~p" - "~n Headers: ~p", [StatusLine, Headers]), - tsf({unexpected_response, StatusLine, Headers}); - {error, Reason} -> - tsp("ssl_get -> request failed: " - "~n Reason: ~p", [Reason]), - tsf({request_failed, Reason}) - end; - {ok, _} -> - skip("local http-server not started"); - _ -> - skip("SSL not started") - end. + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, {URL307, []}, [], []), + + {ok, {{_,200,_}, [_ | _], []}} + = httpc:request(head, {URL307, []}, [], []), + {ok, {{_,307,_}, [_ | _], [_|_]}} + = httpc:request(post, {URL307, [],"text/plain", "foobar"}, + [], []). %%------------------------------------------------------------------------- -ssl_trace(doc) -> - ["Same as http_trace/1 but over ssl sockets."]; -ssl_trace(suite) -> - []; -ssl_trace(Config) when is_list(Config) -> - ssl_trace(ssl, Config). +redirect_loop() -> + [{"doc, Test redirect loop detection"}]. +redirect_loop(Config) when is_list(Config) -> -essl_trace(doc) -> - ["Same as http_trace/1 but over ssl sockets."]; -essl_trace(suite) -> - []; -essl_trace(Config) when is_list(Config) -> - ssl_trace(essl, Config). - -ssl_trace(SslTag, Config) when is_list(Config) -> - case ?config(local_ssl_server, Config) of - ok -> - DataDir = ?config(data_dir, Config), - Port = ?config(local_ssl_port, Config), - URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html", - CertFile = filename:join(DataDir, "ssl_client_cert.pem"), - SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], - SSLConfig = - case SslTag of - ssl -> - SSLOptions; - essl -> - {essl, SSLOptions} - end, - tsp("ssl_trace -> make request using: " - "~n URL: ~p" - "~n SslTag: ~p" - "~n SSLOptions: ~p", [URL, SslTag, SSLOptions]), - case httpc:request(trace, {URL, []}, [{ssl, SSLConfig}], []) of - {ok, {{_,200, _}, [_ | _], "TRACE /dummy.html" ++ _}} -> - ok; - {ok, {{_,200,_}, [_ | _], WrongBody}} -> - tsf({wrong_body, WrongBody}); - {ok, WrongReply} -> - tsf({wrong_reply, WrongReply}); - Error -> - tsf({failed, Error}) - end; - {ok, _} -> - skip("local http-server not started"); - _ -> - skip("SSL not started") - end. + URL = url(group_name(Config), "/redirectloop.html", Config), + {ok, {{_,300,_}, [_ | _], _}} + = httpc:request(get, {URL, []}, [], []). %%------------------------------------------------------------------------- -http_redirect(doc) -> - ["Test redirect with dummy server as httpd does not implement" - " server redirect"]; -http_redirect(suite) -> - []; -http_redirect(Config) when is_list(Config) -> - tsp("http_redirect -> entry with" - "~n Config: ~p", [Config]), - case ?config(local_server, Config) of - ok -> - %% tsp("http_redirect -> set ipfamily option to inet"), - %% ok = httpc:set_options([{ipfamily, inet}]), +cookie() -> + [{doc, "Test cookies."}]. +cookie(Config) when is_list(Config) -> + ok = httpc:set_options([{cookies, enabled}]), - tsp("http_redirect -> start dummy server inet"), - {DummyServerPid, Port} = dummy_server(ipv4), - tsp("http_redirect -> server port = ~p", [Port]), - - URL300 = ?URL_START ++ integer_to_list(Port) ++ "/300.html", - - tsp("http_redirect -> issue request 1: " - "~n ~p", [URL300]), - {ok, {{_,200,_}, [_ | _], [_|_]}} - = httpc:request(get, {URL300, []}, [], []), - - tsp("http_redirect -> issue request 2: " - "~n ~p", [URL300]), - {ok, {{_,300,_}, [_ | _], _}} = - httpc:request(get, {URL300, []}, [{autoredirect, false}], []), - - URL301 = ?URL_START ++ integer_to_list(Port) ++ "/301.html", - - tsp("http_redirect -> issue request 3: " - "~n ~p", [URL301]), - {ok, {{_,200,_}, [_ | _], [_|_]}} - = httpc:request(get, {URL301, []}, [], []), - - tsp("http_redirect -> issue request 4: " - "~n ~p", [URL301]), - {ok, {{_,200,_}, [_ | _], []}} - = httpc:request(head, {URL301, []}, [], []), - - tsp("http_redirect -> issue request 5: " - "~n ~p", [URL301]), - {ok, {{_,301,_}, [_ | _], [_|_]}} - = httpc:request(post, {URL301, [],"text/plain", "foobar"}, - [], []), - - URL302 = ?URL_START ++ integer_to_list(Port) ++ "/302.html", - - tsp("http_redirect -> issue request 6: " - "~n ~p", [URL302]), - {ok, {{_,200,_}, [_ | _], [_|_]}} - = httpc:request(get, {URL302, []}, [], []), - case httpc:request(get, {URL302, []}, [], []) of - {ok, Reply7} -> - case Reply7 of - {{_,200,_}, [_ | _], [_|_]} -> - tsp("http_redirect -> " - "expected reply for request 7"), - ok; - {StatusLine, Headers, Body} -> - tsp("http_redirect -> " - "unexpected reply for request 7: " - "~n StatusLine: ~p" - "~n Headers: ~p" - "~n Body: ~p", - [StatusLine, Headers, Body]), - tsf({unexpected_reply, Reply7}) - end; - Error7 -> - tsp("http_redirect -> " - "unexpected result for request 7: " - "~n Error7: ~p", - [Error7]), - tsf({unexpected_result, Error7}) - end, - - tsp("http_redirect -> issue request 7: " - "~n ~p", [URL302]), - {ok, {{_,200,_}, [_ | _], []}} - = httpc:request(head, {URL302, []}, [], []), - - tsp("http_redirect -> issue request 8: " - "~n ~p", [URL302]), - {ok, {{_,302,_}, [_ | _], [_|_]}} - = httpc:request(post, {URL302, [],"text/plain", "foobar"}, - [], []), - - URL303 = ?URL_START ++ integer_to_list(Port) ++ "/303.html", - - tsp("http_redirect -> issue request 9: " - "~n ~p", [URL303]), - {ok, {{_,200,_}, [_ | _], [_|_]}} - = httpc:request(get, {URL303, []}, [], []), - - tsp("http_redirect -> issue request 10: " - "~n ~p", [URL303]), - {ok, {{_,200,_}, [_ | _], []}} - = httpc:request(head, {URL303, []}, [], []), - - tsp("http_redirect -> issue request 11: " - "~n ~p", [URL303]), - {ok, {{_,200,_}, [_ | _], [_|_]}} - = httpc:request(post, {URL303, [],"text/plain", "foobar"}, - [], []), - - URL307 = ?URL_START ++ integer_to_list(Port) ++ "/307.html", - - tsp("http_redirect -> issue request 12: " - "~n ~p", [URL307]), - {ok, {{_,200,_}, [_ | _], [_|_]}} - = httpc:request(get, {URL307, []}, [], []), - - tsp("http_redirect -> issue request 13: " - "~n ~p", [URL307]), - {ok, {{_,200,_}, [_ | _], []}} - = httpc:request(head, {URL307, []}, [], []), - - tsp("http_redirect -> issue request 14: " - "~n ~p", [URL307]), - {ok, {{_,307,_}, [_ | _], [_|_]}} - = httpc:request(post, {URL307, [],"text/plain", "foobar"}, - [], []), - - tsp("http_redirect -> stop dummy server"), - DummyServerPid ! stop, - tsp("http_redirect -> reset ipfamily option (to inet6fb4)"), - ok = httpc:set_options([{ipfamily, inet6fb4}]), - tsp("http_redirect -> done"), - ok; + Request0 = {url(group_name(Config), "/cookie.html", Config), []}, - _ -> - skip("Failed to start local http-server") - end. + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, Request0, [], []), + %% Populate table to be used by the "dummy" server + ets:new(cookie, [named_table, public, set]), + ets:insert(cookie, {cookies, true}), + Request1 = {url(group_name(Config), "/", Config), []}, -%%------------------------------------------------------------------------- -http_redirect_loop(doc) -> - ["Test redirect loop detection"]; -http_redirect_loop(suite) -> - []; -http_redirect_loop(Config) when is_list(Config) -> - ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(ipv4), - - URL = ?URL_START ++ integer_to_list(Port) ++ "/redirectloop.html", - - {ok, {{_,300,_}, [_ | _], _}} - = httpc:request(get, {URL, []}, [], []), - DummyServerPid ! stop, - ok = httpc:set_options([{ipfamily, inet6fb4}]), - ok. + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, Request1, [], []), -%%------------------------------------------------------------------------- -http_internal_server_error(doc) -> - ["Test 50X codes"]; -http_internal_server_error(suite) -> - []; -http_internal_server_error(Config) when is_list(Config) -> - ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(ipv4), - - URL500 = ?URL_START ++ integer_to_list(Port) ++ "/500.html", - - {ok, {{_,500,_}, [_ | _], _}} - = httpc:request(get, {URL500, []}, [], []), + ets:delete(cookie), + ok = httpc:set_options([{cookies, disabled}]). +%%------------------------------------------------------------------------- +headers_as_is(doc) -> + ["Test the option headers_as_is"]; +headers_as_is(Config) when is_list(Config) -> + URL = url(group_name(Config), "/dummy.html", Config), + {ok, {{_,200,_}, [_|_], [_|_]}} = + httpc:request(get, {URL, [{"Host", "localhost"},{"Te", ""}]}, + [], [{headers_as_is, true}]), - URL503 = ?URL_START ++ integer_to_list(Port) ++ "/503.html", + {ok, {{_,400,_}, [_|_], [_|_]}} = + httpc:request(get, {URL, [{"Te", ""}]},[], [{headers_as_is, true}]). +%%------------------------------------------------------------------------- - %% Used to be able to make the service available after retry. - ets:new(unavailable, [named_table, public, set]), - ets:insert(unavailable, {503, unavailable}), - - {ok, {{_,200, _}, [_ | _], [_|_]}} = - httpc:request(get, {URL503, []}, [], []), +userinfo(doc) -> + [{doc, "Test user info e.i. http://user:passwd@host:port/"}]; +userinfo(Config) when is_list(Config) -> - ets:insert(unavailable, {503, long_unavailable}), + {ok,Host} = inet:gethostname(), - {ok, {{_,503, _}, [_ | _], [_|_]}} = - httpc:request(get, {URL503, []}, [], []), + URLAuth = url(group_name(Config), "alladin:sesame@" ++ Host ++ ":","/userinfo.html", Config), - ets:delete(unavailable), - DummyServerPid ! stop, - ok = httpc:set_options([{ipfamily, inet6fb4}]), - ok. + {ok, {{_,200,_}, [_ | _], _}} + = httpc:request(get, {URLAuth, []}, [], []), + URLUnAuth = url(group_name(Config), "alladin:foobar@" ++ Host ++ ":","/userinfo.html", Config), + + {ok, {{_,401, _}, [_ | _], _}} = + httpc:request(get, {URLUnAuth, []}, [], []). %%------------------------------------------------------------------------- -http_userinfo(doc) -> - ["Test user info e.i. http://user:passwd@host:port/"]; -http_userinfo(suite) -> - []; -http_userinfo(Config) when is_list(Config) -> - ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(ipv4), - - URLAuth = "http://alladin:sesame@localhost:" - ++ integer_to_list(Port) ++ "/userinfo.html", - - {ok, {{_,200,_}, [_ | _], _}} - = httpc:request(get, {URLAuth, []}, [], []), +page_does_not_exist(doc) -> + ["Test that we get a 404 when the page is not found."]; +page_does_not_exist(Config) when is_list(Config) -> + URL = url(group_name(Config), "/doesnotexist.html", Config), + {ok, {{_,404,_}, [_ | _], [_ | _]}} + = httpc:request(get, {URL, []}, [], []). +%%------------------------------------------------------------------------- - URLUnAuth = "http://alladin:foobar@localhost:" - ++ integer_to_list(Port) ++ "/userinfo.html", - - {ok, {{_,401, _}, [_ | _], _}} = - httpc:request(get, {URLUnAuth, []}, [], []), - - DummyServerPid ! stop, - ok = httpc:set_options([{ipfamily, inet6fb4}]), - ok. +streaming_error(doc) -> + [{doc, "Only async requests can be stremed - Solves OTP-8056"}]; +streaming_error(Config) when is_list(Config) -> + Method = get, + Request = {url(group_name(Config), "/dummy.html", Config), []}, + {error, streaming_error} = httpc:request(Method, Request, + [], [{sync, true}, {stream, {self, once}}]), + {error, streaming_error} = httpc:request(Method, Request, + [], [{sync, true}, {stream, self}]). +%%------------------------------------------------------------------------- +server_does_not_exist(doc) -> + [{doc, "Test that we get an error message back when the server " + "does note exist."}]; +server_does_not_exist(Config) when is_list(Config) -> + {error, _} = + httpc:request(get, {"http://localhost:" ++ + integer_to_list(?NOT_IN_USE_PORT) + ++ "/", []},[], []). %%------------------------------------------------------------------------- -http_cookie(doc) -> - ["Test cookies."]; -http_cookie(suite) -> - []; -http_cookie(Config) when is_list(Config) -> - ok = httpc:set_options([{cookies, enabled}, {ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(ipv4), - - URLStart = ?URL_START - ++ integer_to_list(Port), - - URLCookie = URLStart ++ "/cookie.html", - - {ok, {{_,200,_}, [_ | _], [_|_]}} - = httpc:request(get, {URLCookie, []}, [], []), - ets:new(cookie, [named_table, public, set]), - ets:insert(cookie, {cookies, true}), +no_content_204(doc) -> + ["Test the case that the HTTP 204 no content header - Solves OTP 6982"]; +no_content_204(Config) when is_list(Config) -> + URL = url(group_name(Config), "/no_content.html", Config), + {ok, {{_,204,_}, [], []}} = httpc:request(URL). - {ok, {{_,200,_}, [_ | _], [_|_]}} - = httpc:request(get, {URLStart ++ "/", []}, [], []), - - ets:delete(cookie), +%%------------------------------------------------------------------------- - ok = httpc:set_options([{cookies, disabled}]), - DummyServerPid ! stop, - ok = httpc:set_options([{ipfamily, inet6fb4}]), - ok. +tolerate_missing_CR() -> + [{doc, "Test the case that the HTTP server uses only LF instead of CRLF" + "as delimitor. Solves OTP-7304"}]. +tolerate_missing_CR(Config) when is_list(Config) -> + URL = url(group_name(Config), "/missing_CR.html", Config), + {ok, {{_,200,_}, _, [_ | _]}} = httpc:request(URL). +%%------------------------------------------------------------------------- + +empty_body() -> + [{doc, "An empty body was not returned directly. There was a delay for several" + "seconds. Solves OTP-6243."}]. +empty_body(Config) when is_list(Config) -> + URL = url(group_name(Config), "/empty.html", Config), + {ok, {{_,200,_}, [_ | _], []}} = + httpc:request(get, {URL, []}, [{timeout, 500}], []). %%------------------------------------------------------------------------- -http_server_does_not_exist(doc) -> - ["Test that we get an error message back when the server " - "does note exist."]; -http_server_does_not_exist(suite) -> - []; -http_server_does_not_exist(Config) when is_list(Config) -> - {error, _} = - httpc:request(get, {"http://localhost:" ++ - integer_to_list(?NOT_IN_USE_PORT) - ++ "/", []},[], []), - ok. +transfer_encoding() -> + [{doc, "Transfer encoding is case insensitive. Solves OTP-6807"}]. +transfer_encoding(Config) when is_list(Config) -> + URL = url(group_name(Config), "/capital_transfer_encoding.html", Config), + {ok, {{_,200,_}, [_|_], [_ | _]}} = httpc:request(URL). %%------------------------------------------------------------------------- -page_does_not_exist(doc) -> - ["Test that we get a 404 when the page is not found."]; -page_does_not_exist(suite) -> - []; -page_does_not_exist(Config) when is_list(Config) -> - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/doesnotexist.html", - {ok, {{_,404,_}, [_ | _], [_ | _]}} - = httpc:request(get, {URL, []}, [], []), - ok. +empty_response_header() -> + [{doc, "Test the case that the HTTP server does not send any headers. Solves OTP-6830"}]. +empty_response_header(Config) when is_list(Config) -> + URL = url(group_name(Config), "/no_headers.html", Config), + {ok, {{_,200,_}, [], [_ | _]}} = httpc:request(URL). %%------------------------------------------------------------------------- -http_stream(doc) -> - ["Test the option stream for asynchrony requests"]; -http_stream(suite) -> - []; -http_stream(Config) when is_list(Config) -> - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - {ok, {{_,200,_}, [_ | _], Body}} = - httpc:request(get, {URL, []}, [], []), - - {ok, RequestId} = - httpc:request(get, {URL, []}, [], [{sync, false}, - {stream, self}]), - - receive - {http, {RequestId, stream_start, _Headers}} -> - ok; - {http, Msg} -> - tsf(Msg) - end, +bad_response(doc) -> + [{doc, "Test what happens when the server does not follow the protocol"}]; - StreamedBody = receive_streamed_body(RequestId, <<>>), - - Body == binary_to_list(StreamedBody). +bad_response(Config) when is_list(Config) -> + + URL0 = url(group_name(Config), "/missing_crlf.html", Config), + URL1 = url(group_name(Config), "/wrong_statusline.html", Config), + {error, timeout} = httpc:request(get, {URL0, []}, [{timeout, 400}], []), + {error, Reason} = httpc:request(URL1), + ct:print("Wrong Statusline: ~p~n", [Reason]). %%------------------------------------------------------------------------- -http_stream_once(doc) -> - ["Test the option stream for asynchrony requests"]; -http_stream_once(suite) -> - []; -http_stream_once(Config) when is_list(Config) -> - p("http_stream_once -> entry with" - "~n Config: ~p", [Config]), - - p("http_stream_once -> set ipfamily to inet", []), - ok = httpc:set_options([{ipfamily, inet}]), - p("http_stream_once -> start dummy server", []), - {DummyServerPid, Port} = dummy_server(ipv4), - - PortStr = integer_to_list(Port), - p("http_stream_once -> once", []), - once(?URL_START ++ PortStr ++ "/once.html"), - p("http_stream_once -> once_chunked", []), - once(?URL_START ++ PortStr ++ "/once_chunked.html"), - p("http_stream_once -> dummy", []), - once(?URL_START ++ PortStr ++ "/dummy.html"), - - p("http_stream_once -> stop dummy server", []), - DummyServerPid ! stop, - p("http_stream_once -> set ipfamily to inet6fb4", []), - ok = httpc:set_options([{ipfamily, inet6fb4}]), - p("http_stream_once -> done", []), - ok. +internal_server_error(doc) -> + ["Test 50X codes"]; +internal_server_error(Config) when is_list(Config) -> -once(URL) -> - p("once -> issue sync request for ~p", [URL]), - {ok, {{_,200,_}, [_ | _], Body}} = - httpc:request(get, {URL, []}, [], []), - - p("once -> issue async (self stream) request for ~p", [URL]), - {ok, RequestId} = - httpc:request(get, {URL, []}, [], [{sync, false}, - {stream, {self, once}}]), - - p("once -> await stream_start reply for (async) request ~p", [RequestId]), - NewPid = - receive - {http, {RequestId, stream_start, _Headers, Pid}} -> - p("once -> received stream_start reply for (async) request ~p: ~p", - [RequestId, Pid]), - Pid; - {http, Msg} -> - tsf(Msg) - end, + URL500 = url(group_name(Config), "/500.html", Config), - tsp("once -> request handler: ~p", [NewPid]), + {ok, {{_,500,_}, [_ | _], _}} + = httpc:request(get, {URL500, []}, [], []), - p("once -> await stream reply for (async) request ~p", [RequestId]), - BodyPart = - receive - {http, {RequestId, stream, BinBodyPart}} -> - p("once -> received stream reply for (async) request ~p: " - "~n~p", [RequestId, binary_to_list(BinBodyPart)]), - BinBodyPart - end, + URL503 = url(group_name(Config), "/503.html", Config), + + %% Used to be able to make the service available after retry. + ets:new(unavailable, [named_table, public, set]), + ets:insert(unavailable, {503, unavailable}), - tsp("once -> first body part '~p' received", [binary_to_list(BodyPart)]), + {ok, {{_,200, _}, [_ | _], [_|_]}} = + httpc:request(get, {URL503, []}, [], []), - StreamedBody = receive_streamed_body(RequestId, BinBodyPart, NewPid), - - Body = binary_to_list(StreamedBody), + ets:insert(unavailable, {503, long_unavailable}), - p("once -> done when Bode: ~p", [Body]), - ok. + {ok, {{_,503, _}, [_ | _], [_|_]}} = + httpc:request(get, {URL503, []}, [], []), + ets:delete(unavailable). %%------------------------------------------------------------------------- -parse_url(doc) -> - ["Test that an url is parsed correctly"]; -parse_url(suite) -> + +invalid_http(doc) -> + ["Test parse error"]; +invalid_http(suite) -> []; -parse_url(Config) when is_list(Config) -> - %% ipv6 - {ok, {http,[],"2010:836B:4179::836B:4179",80,"/foobar.html",[]}} = - http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html"), - {ok, {http,[],"[2010:836B:4179::836B:4179]",80,"/foobar.html",[]}} = - http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html", - [{ipv6_host_with_brackets, true}]), - {ok, {http,[],"2010:836B:4179::836B:4179",80,"/foobar.html",[]}} = - http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html", - [{ipv6_host_with_brackets, false}]), - {ok, {http,[],"2010:836B:4179::836B:4179",80,"/foobar.html",[]}} = - http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html", - [{foo, false}]), - {error, - {malformed_url, _, "http://2010:836B:4179::836B:4179/foobar.html"}} = - http_uri:parse("http://2010:836B:4179::836B:4179/foobar.html"), - - %% ipv4 - {ok, {http,[],"127.0.0.1",80,"/foobar.html",[]}} = - http_uri:parse("http://127.0.0.1/foobar.html"), - - %% host - {ok, {http,[],"localhost",8888,"/foobar.html",[]}} = - http_uri:parse("http://localhost:8888/foobar.html"), - - %% Userinfo - {ok, {http,"nisse:foobar","localhost",8888,"/foobar.html",[]}} = - http_uri:parse("http://nisse:foobar@localhost:8888/foobar.html"), - - %% Scheme error - {error, no_scheme} = http_uri:parse("localhost/foobar.html"), - {error, {malformed_url, _, _}} = - http_uri:parse("localhost:8888/foobar.html"), - - %% Query - {ok, {http,[],"localhost",8888,"/foobar.html","?foo=bar&foobar=42"}} = - http_uri:parse("http://localhost:8888/foobar.html?foo=bar&foobar=42"), - - %% Esc chars - {ok, {http,[],"www.somedomain.com",80,"/%2Eabc",[]}} = - http_uri:parse("http://www.somedomain.com/%2Eabc"), - {ok, {http,[],"www.somedomain.com",80,"/%252Eabc",[]}} = - http_uri:parse("http://www.somedomain.com/%252Eabc"), - {ok, {http,[],"www.somedomain.com",80,"/%25abc",[]}} = - http_uri:parse("http://www.somedomain.com/%25abc"), - {ok, {http,[],"www.somedomain.com",80,"/%25abc", "?foo=bar"}} = - http_uri:parse("http://www.somedomain.com/%25abc?foo=bar"), +invalid_http(Config) when is_list(Config) -> - - ok. + URL = url(group_name(Config), "/invalid_http.html", Config), + + {error, {could_not_parse_as_http, _} = 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) -> + + URL = url(group_name(Config), "/dummy.html", Config), + + {ok, Body0} = + httpc:request(get, {URL, []}, [{version, "HTTP/0.9"}], []), + inets_test_lib:check_body(Body0), + {ok, {{"HTTP/1.0", 200, _}, [_ | _], Body1 = [_ | _]}} = + httpc:request(get, {URL, []}, [{version, "HTTP/1.0"}], []), + inets_test_lib:check_body(Body1), + {ok, {{"HTTP/1.1", 200, _}, [_ | _], Body2 = [_ | _]}} = + httpc:request(get, {URL, []}, [{version, "HTTP/1.1"}], []), + inets_test_lib:check_body(Body2). -ipv6_ipcomm() -> - [{require, ipv6_hosts}]. -ipv6_ipcomm(doc) -> - ["Test ip_comm ipv6."]; -ipv6_ipcomm(suite) -> - []; -ipv6_ipcomm(Config) when is_list(Config) -> - HTTPOptions = [], - SocketType = ip_comm, - Scheme = "http", - Extra = [], - ipv6(SocketType, Scheme, HTTPOptions, Extra, Config). +%%------------------------------------------------------------------------- + +relaxed(doc) -> + ["Test relaxed mode"]; +relaxed(Config) when is_list(Config) -> + URL = url(group_name(Config), "/missing_reason_phrase.html", Config), -%%------------------------------------------------------------------------- + {error, Reason} = + httpc:request(get, {URL, []}, [{relaxed, false}], []), -ipv6_essl() -> - [{require, ipv6_hosts}]. -ipv6_essl(doc) -> - ["Test essl ipv6."]; -ipv6_essl(suite) -> - []; -ipv6_essl(Config) when is_list(Config) -> - DataDir = ?config(data_dir, Config), - CertFile = filename:join(DataDir, "ssl_client_cert.pem"), - SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], - SSLConfig = {essl, SSLOptions}, - tsp("ssl_ipv6 -> make request using: " - "~n SSLOptions: ~p", [SSLOptions]), - HTTPOptions = [{ssl, SSLConfig}], - SocketType = essl, - Scheme = "https", - Extra = SSLOptions, - ipv6(SocketType, Scheme, HTTPOptions, Extra, Config). + ct:print("Not relaxed: ~p~n", [Reason]), + {ok, {{_, 200, _}, [_ | _], [_ | _]}} = + httpc:request(get, {URL, []}, [{relaxed, true}], []). %%------------------------------------------------------------------------- -ipv6(SocketType, Scheme, HTTPOptions, Extra, Config) -> - %% Check if we are a IPv6 host - tsp("ipv6 -> verify ipv6 support"), - case inets_test_lib:has_ipv6_support(Config) of - {ok, Addr} -> - tsp("ipv6 -> ipv6 supported: ~p", [Addr]), - {DummyServerPid, Port} = dummy_server(SocketType, ipv6, Extra), - Profile = ?config(profile, Config), - URL = - Scheme ++ - "://[" ++ http_transport:ipv6_name(Addr) ++ "]:" ++ - integer_to_list(Port) ++ "/foobar.html", - tsp("ipv6 -> issue request with: " - "~n URL: ~p" - "~n HTTPOptions: ~p", [URL, HTTPOptions]), - case httpc:request(get, {URL, []}, HTTPOptions, [], Profile) of - {ok, {{_,200,_}, [_ | _], [_|_]}} -> - tsp("ipv6 -> expected result"), - DummyServerPid ! stop, - ok; - {ok, Unexpected} -> - tsp("ipv6 -> unexpected result: " - "~n ~p", [Unexpected]), - DummyServerPid ! stop, - tsf({unexpected_result, Unexpected}); - {error, Reason} -> - tsp("ipv6 -> error: " - "~n Reason: ~p", [Reason]), - DummyServerPid ! stop, - tsf(Reason) - end, - ok; - _ -> - tsp("ipv6 -> ipv6 not supported"), - skip("Host does not support IPv6") - end. +headers() -> + [{doc,"Use as many request headers as possible not used in proxy_headers"}]. +headers(Config) when is_list(Config) -> + URL = url(group_name(Config), "/dummy.html", Config), + DocRoot = ?config(doc_root, Config), -%%------------------------------------------------------------------------- + {ok, FileInfo} = + file:read_file_info(filename:join([DocRoot,"dummy.html"])), + CreatedSec = + calendar:datetime_to_gregorian_seconds( + FileInfo#file_info.mtime), -headers_as_is(doc) -> - ["Test the option headers_as_is"]; -headers_as_is(suite) -> - []; -headers_as_is(Config) when is_list(Config) -> - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - {ok, {{_,200,_}, [_|_], [_|_]}} = - httpc:request(get, {URL, [{"Host", "localhost"},{"Te", ""}]}, - [], [{headers_as_is, true}]), - - {ok, {{_,400,_}, [_|_], [_|_]}} = - httpc:request(get, {URL, [{"Te", ""}]},[], [{headers_as_is, true}]), - ok. + Mod = httpd_util:rfc1123_date( + calendar:gregorian_seconds_to_datetime( + CreatedSec-1)), + Date = httpd_util:rfc1123_date({date(), time()}), -%%------------------------------------------------------------------------- + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + httpc:request(get, {URL, [{"If-Modified-Since", + Mod}, + {"From","webmaster@erlang.se"}, + {"Date", Date} + ]}, [], []), -selecting_session(doc) -> - ["Test selection of sessions - OTP-9847"]; -selecting_session(suite) -> - []; -selecting_session(Config) when is_list(Config) -> - tsp("selecting_session -> entry with" - "~n Config: ~p", [Config]), - - tsp("selecting_session -> set ipfamily to inet"), - ok = httpc:set_options([{ipfamily, inet}]), - - tsp("selecting_session -> start server"), - {ServerPid, Port} = otp_9847_server(), - - PortStr = integer_to_list(Port), - URL = ?URL_START ++ PortStr ++ "/index.html", - - tsp("selecting_session -> issue the first batch (three) requests"), - lists:foreach(fun(P) -> - tsp("selecting_session:fun1 -> " - "send stop request to ~p", [P]), - P ! stop - end, - reqs(URL, ServerPid, 3, 3, false)), - tsp("selecting_session -> sleep some (1) to make sure nothing lingers"), - ?SLEEP(5000), - tsp("selecting_session -> " - "instruct the server to reply to the first request"), - ServerPid ! {answer, true}, - receive - {answer, true} -> - tsp("selecting_session -> " - "received ack from server to reply to the first request"), - ok - end, - tsp("selecting_session -> issue the second batch (four) requests"), - lists:foreach(fun(P) -> - tsp("selecting_session:fun2 -> " - "send stop request to ~p", [P]), - P ! stop - end, - reqs(URL, ServerPid, 4, 1, true)), - tsp("selecting_session -> sleep some (2) to make sure nothing lingers"), - ?SLEEP(5000), - - tsp("selecting_session -> stop server"), - ServerPid ! stop, - tsp("selecting_session -> set ipfamily (back) to inet6fb4"), - ok = httpc:set_options([{ipfamily, inet6fb4}]), - tsp("selecting_session -> done"), - ok. + Mod1 = httpd_util:rfc1123_date( + calendar:gregorian_seconds_to_datetime( + CreatedSec+1)), -reqs(URL, ServerPid, NumReqs, NumHandlers, InitialSync) -> - tsp("reqs -> entry with" - "~n URL: ~p" - "~n ServerPid: ~w" - "~n NumReqs: ~w" - "~n NumHandlers: ~w" - "~n InitialSync: ~w", - [URL, ServerPid, NumReqs, NumHandlers, InitialSync]), - Handlers = reqs2(URL, NumReqs, [], InitialSync), - tsp("reqs -> " - "~n Handlers: ~w", [Handlers]), - case length(Handlers) of - NumHandlers -> - tsp("reqs -> " - "~n NumHandlers: ~w", [NumHandlers]), - ServerPid ! num_handlers, - receive - {num_handlers, NumHandlers} -> - tsp("reqs -> received num_handlers with" - "~n NumHandlers: ~w", [NumHandlers]), - Handlers; - {num_handlers, WrongNumHandlers} -> - tsp("reqs -> received num_handlers with" - "~n WrongNumHandlers: ~w", [WrongNumHandlers]), - exit({wrong_num_handlers1, WrongNumHandlers, NumHandlers}) - end; - WrongNumHandlers -> - tsp("reqs -> " - "~n WrongNumHandlers: ~w", [WrongNumHandlers]), - exit({wrong_num_handlers2, WrongNumHandlers, NumHandlers}) - end. - - -reqs2(_URL, 0, Acc, _Sync) -> - lists:reverse(Acc); -reqs2(URL, Num, Acc, Sync) -> - tsp("reqs2 -> entry with" - "~n Num: ~w" - "~n Sync: ~w", [Num, Sync]), - case httpc:request(get, {URL, []}, [], [{sync, Sync}]) of - {ok, _Reply} -> - tsp("reqs2 -> successful request: ~p", [_Reply]), - receive - {handler, Handler, _Manager} -> - %% This is when a new handler is created - tsp("reqs2 -> received handler: ~p", [Handler]), - case lists:member(Handler, Acc) of - true -> - tsp("reqs2 -> duplicate handler"), - exit({duplicate_handler, Handler, Num, Acc}); - false -> - tsp("reqs2 -> wait for data ack"), - receive - {data_received, Handler} -> - tsp("reqs2 -> " - "received data ack from ~p", [Handler]), - case Sync of - true -> - reqs2(URL, Num-1, [Handler|Acc], - false); - false -> - reqs2(URL, Num-1, [Handler|Acc], - Sync) - end - end - end; - - {data_received, Handler} -> - tsp("reqs2 -> " - "received data ack from ~p", [Handler]), - reqs2(URL, Num-1, Acc, false) + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + httpc:request(get, {URL, [{"If-UnModified-Since", + Mod1} + ]}, [], []), - end; + Tag = httpd_util:create_etag(FileInfo), - {error, Reason} -> - tsp("reqs2 -> request ~w failed: ~p", [Num, Reason]), - exit({request_failed, Reason, Num, Acc}) - end. -otp_9847_server() -> - TC = self(), - Pid = spawn_link(fun() -> otp_9847_server_init(TC) end), - receive - {port, Port} -> - {Pid, Port} - end. + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + httpc:request(get, {URL, [{"If-Match", + Tag} + ]}, [], []), + + {ok, {{_,200,_}, [_ | _], _}} = + httpc:request(get, {URL, [{"If-None-Match", + "NotEtag,NeihterEtag"}, + {"Connection", "Close"} + ]}, [], []). +%%------------------------------------------------------------------------- +headers_dummy() -> + ["Test the code for handling headers we do not want/can send " + "to a real server. Note it is not logical to send" + "all of these headers together, we only want to test that" + "the code for handling headers will not crash."]. -otp_9847_server_init(TC) -> - tsp("otp_9847_server_init -> entry with" - "~n TC: ~p", [TC]), - {ok, ListenSocket} = - gen_tcp:listen(0, [binary, inet, {packet, 0}, - {reuseaddr,true}, - {active, false}]), - tsp("otp_9847_server_init -> listen socket created: " - "~n ListenSocket: ~p", [ListenSocket]), - {ok, Port} = inet:port(ListenSocket), - tsp("otp_9847_server_init -> Port: ~p", [Port]), - TC ! {port, Port}, - otp_9847_server_main(TC, ListenSocket, false, []). - -otp_9847_server_main(TC, ListenSocket, Answer, Handlers) -> - tsp("otp_9847_server_main -> entry with" - "~n TC: ~p" - "~n ListenSocket: ~p" - "~n Answer: ~p" - "~n Handlers: ~p", [TC, ListenSocket, Answer, Handlers]), - case gen_tcp:accept(ListenSocket, 1000) of - {ok, Sock} -> - tsp("otp_9847_server_main -> accepted" - "~n Sock: ~p", [Sock]), - {Handler, Mon, Port} = otp_9847_handler(TC, Sock, Answer), - tsp("otp_9847_server_main -> handler ~p created for ~w", - [Handler, Port]), - gen_tcp:controlling_process(Sock, Handler), - tsp("otp_9847_server_main -> control transfer"), - Handler ! owner, - tsp("otp_9847_server_main -> " - "handler ~p informed of owner transfer", [Handler]), - TC ! {handler, Handler, self()}, - tsp("otp_9847_server_main -> " - "TC ~p informed of handler ~p", [TC, Handler]), - otp_9847_server_main(TC, ListenSocket, Answer, - [{Handler, Mon, Sock, Port}|Handlers]); +headers_dummy(Config) when is_list(Config) -> - {error, timeout} -> - tsp("otp_9847_server_main -> timeout"), - receive - {answer, true} -> - tsp("otp_9847_server_main -> received answer request"), - TC ! {answer, true}, - otp_9847_server_main(TC, ListenSocket, true, Handlers); - - {'DOWN', _Mon, process, Pid, _Reason} -> - %% Could be one of the handlers - tsp("otp_9847_server_main -> received DOWN for ~p", [Pid]), - otp_9847_server_main(TC, ListenSocket, Answer, - lists:keydelete(Pid, 1, Handlers)); - - num_handlers -> - tsp("otp_9847_server_main -> " - "received request for number of handlers (~w)", - [length(Handlers)]), - TC ! {num_handlers, length(Handlers)}, - otp_9847_server_main(TC, ListenSocket, Answer, Handlers); + URL = url(group_name(Config), "/dummy_headers.html", Config), - stop -> - tsp("otp_9847_server_main -> received stop request"), - %% Stop all handlers (just in case) - Pids = [Handler || {Handler, _, _} <- Handlers], - lists:foreach(fun(Pid) -> Pid ! stop end, Pids), - exit(normal); + Foo = http_chunk:encode("foobar") ++ + binary_to_list(http_chunk:encode_last()), + FooBar = Foo ++ "\r\n\r\nOther:inets_test\r\n\r\n", - Any -> - tsp("otp_9847_server_main -> received" - "~n Any: ~p", [Any]), - exit({crap, Any}) + UserPasswd = base64:encode_to_string("Alladin:Sesame"), + Auth = "Basic " ++ UserPasswd, - after 0 -> - tsp("otp_9847_server_main -> nothing in queue"), - otp_9847_server_main(TC, ListenSocket, Answer, Handlers) - end; + %% The dummy server will ignore the headers, we only want to test + %% that the client header-handling code. This would not + %% be a vaild http-request! + {ok, {{_,200,_}, [_ | _], [_|_]}} = + httpc:request(post, + {URL, + [{"Via", + "1.0 fred, 1.1 nowhere.com (Apache/1.1)"}, + {"Warning","1#pseudonym foobar"}, + {"Vary","*"}, + {"Upgrade","HTTP/2.0"}, + {"Pragma", "1#no-cache"}, + {"Cache-Control", "no-cache"}, + {"Connection", "close"}, + {"Date", "Sat, 29 Oct 1994 19:43:31 GMT"}, + {"Accept", " text/plain; q=0.5, text/html"}, + {"Accept-Language", "en"}, + {"Accept-Encoding","chunked"}, + {"Accept-Charset", "ISO8859-1"}, + {"Authorization", Auth}, + {"Expect", "1#100-continue"}, + {"User-Agent","inets"}, + {"Transfer-Encoding","chunked"}, + {"Range", " bytes=0-499"}, + {"If-Range", "Sat, 29 Oct 1994 19:43:31 GMT"}, + {"If-Match", "*"}, + {"Content-Type", "text/plain"}, + {"Content-Encoding", "chunked"}, + {"Content-Length", "6"}, + {"Content-Language", "en"}, + {"Content-Location", "http://www.foobar.se"}, + {"Content-MD5", + "104528739076276072743283077410617235478"}, + {"Content-Range", "bytes 0-499/1234"}, + {"Allow", "GET"}, + {"Proxy-Authorization", Auth}, + {"Expires", "Sat, 29 Oct 1994 19:43:31 GMT"}, + {"Upgrade", "HTTP/2.0"}, + {"Last-Modified", "Sat, 29 Oct 1994 19:43:31 GMT"}, + {"Trailer","1#User-Agent"} + ], "text/plain", FooBar}, + [], []). - Error -> - exit(Error) - end. +%%------------------------------------------------------------------------- -otp_9847_handler(TC, Sock, Answer) -> - tsp("otp_9847_handler -> entry with" - "~n TC: ~p" - "~n Sock: ~p" - "~n Answer: ~p", [TC, Sock, Answer]), - Self = self(), - {Pid, Mon} = - spawn_opt(fun() -> - otp_9847_handler_init(TC, Self, Sock, Answer) - end, - [monitor]), - receive - {port, Port} -> - tsp("otp_9847_handler -> received port message (from ~p)" - "~n Port: ~p", [Pid, Port]), - {Pid, Mon, Port} - end. - +remote_socket_close(Config) when is_list(Config) -> + URL = url(group_name(Config), "/just_close.html", Config), + {error, socket_closed_remotely} = httpc:request(URL). -otp_9847_handler_init(TC, Server, Sock, Answer) -> - tsp("otp_9847_handler_init -> entry with" - "~n TC: ~p" - "~n Server: ~p" - "~n Sock: ~p" - "~n Answer: ~p", [TC, Server, Sock, Answer]), - {ok, Port} = inet:port(Sock), - Server ! {port, Port}, - receive - owner -> - tsp("otp_9847_handler_init -> " - "received owner message - activate socket"), - inet:setopts(Sock, [{active, true}]), - otp_9847_handler_main(TC, Server, Sock, Answer, [?HTTP_MAX_HEADER_SIZE]) - end. - -otp_9847_handler_main(TC, Server, Sock, Answer, ParseArgs) -> - tsp("otp_9847_handler_main -> entry with" - "~n TC: ~p" - "~n Server: ~p" - "~n Sock: ~p" - "~n Answer: ~p" - "~n ParseArgs: ~p", [TC, Server, Sock, Answer, ParseArgs]), +%%------------------------------------------------------------------------- + +remote_socket_close_async(Config) when is_list(Config) -> + Request = {url(group_name(Config), "/just_close.html", Config), []}, + Options = [{sync, false}], + Profile = httpc:default_profile(), + {ok, RequestId} = + httpc:request(get, Request, [], Options, Profile), receive - stop -> - tsp("otp_9847_handler_main -> received stop request"), - exit(normal); - - {tcp, Sock, _Data} when Answer =:= false -> - tsp("otp_9847_handler_main -> received tcp data - no answer"), - TC ! {data_received, self()}, - inet:setopts(Sock, [{active, true}]), - %% Ignore all data - otp_9847_handler_main(TC, Server, Sock, Answer, ParseArgs); - - {tcp, Sock, Data} when Answer =:= true -> - tsp("otp_9847_handler_main -> received tcp data - answer"), - TC ! {data_received, self()}, - inet:setopts(Sock, [{active, true}]), - NewParseArgs = otp_9847_handler_request(Sock, [Data|ParseArgs]), - otp_9847_handler_main(TC, Server, Sock, Answer, NewParseArgs); - - {tcp_closed, Sock} -> - tsp("otp_9847_handler_main -> received tcp socket closed"), - exit(normal); - - {tcp_error, Sock, Reason} -> - tsp("otp_9847_handler_main -> socket error: ~p", [Reason]), - (catch gen_tcp:close(Sock)), - exit(normal) - - %% after 30000 -> - %% gen_tcp:close(Sock), - %% exit(normal) + {http, {RequestId, {error, socket_closed_remotely}}} -> + ok end. - -otp_9847_handler_request(Sock, Args) -> - Msg = - case httpd_request:parse(Args) of - {ok, {_, "/index.html" = _RelUrl, _, _, _}} -> - B = - "" ++ - "...some body part..." ++ - "", - Len = integer_to_list(length(B)), - "HTTP/1.1 200 ok\r\n" ++ - "Content-Length:" ++ Len ++ "\r\n\r\n" ++ B - end, - gen_tcp:send(Sock, Msg), - [?HTTP_MAX_HEADER_SIZE]. - - %%------------------------------------------------------------------------- -options(doc) -> - ["Test the option parameters."]; -options(suite) -> - []; -options(Config) when is_list(Config) -> - case ?config(local_server, Config) of - ok -> - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - {ok, {{_,200,_}, [_ | _], Bin}} - = httpc:request(get, {URL, []}, [{foo, bar}], - %% Ignore unknown options - [{body_format, binary}, {foo, bar}]), - - true = is_binary(Bin), - {ok, {200, [_|_]}} - = httpc:request(get, {URL, []}, [{timeout, infinity}], - [{full_result, false}]); - _ -> - skip("Failed to start local http-server") - end. +stream_to_pid(Config) when is_list(Config) -> + ReceiverPid = create_receiver(pid), + Receiver = ReceiverPid, + stream(ReceiverPid, Receiver, Config), -%%------------------------------------------------------------------------- + stop_receiver(ReceiverPid). -http_invalid_http(doc) -> - ["Test parse error"]; -http_invalid_http(suite) -> - []; -http_invalid_http(Config) when is_list(Config) -> - ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(ipv4), - - URL = ?URL_START ++ integer_to_list(Port) ++ "/invalid_http.html", - - {error, {could_not_parse_as_http, _} = Reason} = - httpc:request(get, {URL, []}, [], []), - - test_server:format("Parse error: ~p ~n", [Reason]), - DummyServerPid ! stop, - ok = httpc:set_options([{ipfamily, inet6fb4}]), - ok. +stream_through_fun(Config) when is_list(Config) -> + ReceiverPid = create_receiver(function), + Receiver = stream_deliver_fun(ReceiverPid), + stream(ReceiverPid, Receiver, Config), -%%------------------------------------------------------------------------- + stop_receiver(ReceiverPid). --define(GOOGLE, "www.google.com"). +stream_through_mfa(Config) when is_list(Config) -> + ReceiverPid = create_receiver(mfa), + Receiver = {?MODULE, stream_deliver, [mfa, ReceiverPid]}, -hexed_query_otp_6191(doc) -> - []; -hexed_query_otp_6191(suite) -> - []; -hexed_query_otp_6191(Config) when is_list(Config) -> - Google = ?GOOGLE, - GoogleSearch = "http://" ++ Google ++ "/search", - Search1 = "?hl=en&q=a%D1%85%D1%83%D0%B9&btnG=Google+Search", - URI1 = GoogleSearch ++ Search1, - Search2 = "?hl=en&q=%25%25", - URI2 = GoogleSearch ++ Search2, - Search3 = "?hl=en&q=%foo", - URI3 = GoogleSearch ++ Search3, - - Verify1 = - fun({http, [], ?GOOGLE, 80, "/search", _}) -> ok; - (_) -> error - end, - Verify2 = Verify1, - Verify3 = Verify1, - verify_uri(URI1, Verify1), - verify_uri(URI2, Verify2), - verify_uri(URI3, Verify3), - ok. + stream(ReceiverPid, Receiver, Config). -verify_uri(URI, Verify) -> - case http_uri:parse(URI) of - {ok, ParsedURI} -> - case Verify(ParsedURI) of - ok -> - ok; - error -> - Reason = {unexpected_parse_result, URI, ParsedURI}, - ERROR = {error, Reason}, - throw(ERROR) - end; - {error, _} = ERROR -> - throw(ERROR) - end. +%%------------------------------------------------------------------------- +inet_opts(Config) when is_list(Config) -> + MaxSessions = 5, + MaxKeepAlive = 10, + KeepAliveTimeout = timer:minutes(2), + ConnOptions = [{max_sessions, MaxSessions}, + {max_keep_alive_length, MaxKeepAlive}, + {keep_alive_timeout, KeepAliveTimeout}], + httpc:set_options(ConnOptions), + + Request = {url(group_name(Config), "/dummy.html", Config), []}, + Timeout = timer:seconds(1), + ConnTimeout = Timeout + timer:seconds(1), + HttpOptions = [{timeout, Timeout}, {connect_timeout, ConnTimeout}], + Options0 = [{socket_opts, [{tos, 87}, + {recbuf, 16#FFFF}, + {sndbuf, 16#FFFF}]}], + + {ok, {{_,200,_}, [_ | _], ReplyBody0 = [_ | _]}} = httpc:request(get, Request, HttpOptions, Options0), + inets_test_lib:check_body(ReplyBody0), + + Options1 = [{socket_opts, [{tos, 84}, + {recbuf, 32#1FFFF}, + {sndbuf, 32#1FFFF}]}], + {ok, {{_,200,_}, [_ | _], ReplyBody1 = [_ | _]}} = httpc:request(get, Request, [], Options1), + inets_test_lib:check_body(ReplyBody1). %%------------------------------------------------------------------------- +port_in_host_header(Config) when is_list(Config) -> -empty_body_otp_6243(doc) -> - ["An empty body was not returned directly. There was a delay for several" - "seconds."]; -empty_body_otp_6243(suite) -> - []; -empty_body_otp_6243(Config) when is_list(Config) -> - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/empty.html", - {ok, {{_,200,_}, [_ | _], []}} = - httpc:request(get, {URL, []}, [{timeout, 500}], []). - - -%%------------------------------------------------------------------------- - -transfer_encoding_otp_6807(doc) -> - ["Transfer encoding is case insensitive"]; -transfer_encoding_otp_6807(suite) -> - []; -transfer_encoding_otp_6807(Config) when is_list(Config) -> - ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(ipv4), - - URL = ?URL_START ++ integer_to_list(Port) ++ - "/capital_transfer_encoding.html", - {ok, {{_,200,_}, [_|_], [_ | _]}} = httpc:request(URL), - DummyServerPid ! stop, - ok = httpc:set_options([{ipfamily, inet6fb4}]), - ok. - - -%%------------------------------------------------------------------------- - -empty_response_header_otp_6830(doc) -> - ["Test the case that the HTTP server does not send any headers"]; -empty_response_header_otp_6830(suite) -> - []; -empty_response_header_otp_6830(Config) when is_list(Config) -> - ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(ipv4), - - URL = ?URL_START ++ integer_to_list(Port) ++ "/no_headers.html", - {ok, {{_,200,_}, [], [_ | _]}} = httpc:request(URL), - DummyServerPid ! stop, - ok = httpc:set_options([{ipfamily, inet6fb4}]), - ok. - - -%%------------------------------------------------------------------------- - -no_content_204_otp_6982(doc) -> - ["Test the case that the HTTP 204 no content header"]; -no_content_204_otp_6982(suite) -> - []; -no_content_204_otp_6982(Config) when is_list(Config) -> - ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(ipv4), - - URL = ?URL_START ++ integer_to_list(Port) ++ "/no_content.html", - {ok, {{_,204,_}, [], []}} = httpc:request(URL), - DummyServerPid ! stop, - ok = httpc:set_options([{ipfamily, inet6fb4}]), - ok. - - -%%------------------------------------------------------------------------- - -missing_CR_otp_7304(doc) -> - ["Test the case that the HTTP server uses only LF instead of CRLF" - "as delimitor"]; -missing_CR_otp_7304(suite) -> - []; -missing_CR_otp_7304(Config) when is_list(Config) -> - ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(ipv4), - - URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_CR.html", - {ok, {{_,200,_}, _, [_ | _]}} = httpc:request(URL), - DummyServerPid ! stop, - ok = httpc:set_options([{ipfamily, inet6fb4}]), - ok. - - -%%------------------------------------------------------------------------- - - -otp_7883_1(doc) -> - ["OTP-7883-sync"]; -otp_7883_1(suite) -> - []; -otp_7883_1(Config) when is_list(Config) -> - ok = httpc:set_options([{ipfamily, inet}]), - - {DummyServerPid, Port} = dummy_server(ipv4), - - URL = ?URL_START ++ integer_to_list(Port) ++ "/just_close.html", - {error, socket_closed_remotely} = httpc:request(URL), - DummyServerPid ! stop, - - ok = httpc:set_options([{ipfamily, inet6fb4}]), - ok. - -otp_7883_2(doc) -> - ["OTP-7883-async"]; -otp_7883_2(suite) -> - []; -otp_7883_2(Config) when is_list(Config) -> - ok = httpc:set_options([{ipfamily, inet}]), - - {DummyServerPid, Port} = dummy_server(ipv4), - - URL = ?URL_START ++ integer_to_list(Port) ++ "/just_close.html", - Method = get, - Request = {URL, []}, - HttpOptions = [], - Options = [{sync, false}], - Profile = httpc:default_profile(), - {ok, RequestId} = - httpc:request(Method, Request, HttpOptions, Options, Profile), - ok = - receive - {http, {RequestId, {error, socket_closed_remotely}}} -> - ok - end, - DummyServerPid ! stop, - - ok = httpc:set_options([{ipfamily, inet6fb4}]), - ok. - + Request = {url(group_name(Config), "/ensure_host_header_with_port.html", Config), []}, + {ok, {{_, 200, _}, _, Body}} = httpc:request(get, Request, [], []), + inets_test_lib:check_body(Body). %%------------------------------------------------------------------------- - - -otp_8154_1(doc) -> - ["OTP-8154"]; -otp_8154_1(suite) -> - []; -otp_8154_1(Config) when is_list(Config) -> - start_inets(), - ReqSeqNumServer = start_sequence_number_server(), - RespSeqNumServer = start_sequence_number_server(), - {ok, Server, Port} = start_slow_server(RespSeqNumServer), - Clients = run_clients(105, Port, ReqSeqNumServer), - %% ok = wait_for_clients(Clients), - ok = wait4clients(Clients, timer:minutes(3)), - Server ! shutdown, - RespSeqNumServer ! shutdown, - ReqSeqNumServer ! shutdown, - ok. - -start_inets() -> - inets:start(), - ok. - - -%% ----------------------------------------------------- -%% A sequence number handler -%% The purpose is to be able to pair requests with responses. - -start_sequence_number_server() -> - proc_lib:spawn(fun() -> loop_sequence_number(1) end). - -loop_sequence_number(N) -> - receive - shutdown -> - ok; - {From, get_next} -> - From ! {next_is, N}, - loop_sequence_number(N + 1) - end. - -get_next_sequence_number(SeqNumServer) -> - SeqNumServer ! {self(), get_next}, - receive {next_is, N} -> N end. - -%% ----------------------------------------------------- -%% Client part -%% Sends requests randomly parallel - -run_clients(NumClients, ServerPort, SeqNumServer) -> - io:format("start clients when" - "~n NumClients: ~w" - "~n ServerPort: ~w" - "~n SeqNumServer: ~w" - "~n", [NumClients, ServerPort, SeqNumServer]), - set_random_seed(), - lists:map( - fun(Id) -> - io:format("starting client ~w~n", [Id]), - Req = f("req~3..0w", [get_next_sequence_number(SeqNumServer)]), - Url = f(?URL_START ++ "~w/~s", [ServerPort, Req]), - Pid = proc_lib:spawn( - fun() -> - io:format("[~w] client started - " - "issue request~n", [Id]), - case httpc:request(Url) of - {ok, {{_,200,_}, _, Resp}} -> - io:format("[~w] 200 response: " - "~p~n", [Id, Resp]), - case lists:prefix(Req++"->", Resp) of - true -> exit(normal); - false -> exit({bad_resp,Req,Resp}) - end; - {ok, {{_,EC,Reason},_,Resp}} -> - io:format("[~w] ~w response: " - "~s~n~s~n", - [Id, EC, Reason, Resp]), - exit({bad_resp,Req,Resp}); - Crap -> - io:format("[~w] bad response: ~p", - [Id, Crap]), - exit({bad_resp, Req, Crap}) - end - end), - MRef = erlang:monitor(process, Pid), - timer:sleep(10 + random:uniform(1334)), - {Id, Pid, MRef} - - end, - lists:seq(1, NumClients)). - -%% wait_for_clients(Clients) -> -%% lists:foreach( -%% fun({Id, Pid, MRef}) -> -%% io:format("waiting for client ~w termination~n", [Id]), -%% receive -%% {'DOWN', MRef, process, Pid, normal} -> -%% io:format("waiting for clients: " -%% "normal exit from ~w (~p)~n", -%% [Id, Pid]), -%% ok; -%% {'DOWN', MRef, process, Pid, Reason} -> -%% io:format("waiting for clients: " -%% "unexpected exit from ~w (~p):" -%% "~n Reason: ~p" -%% "~n", [Id, Pid, Reason]), -%% erlang:error(Reason) -%% end -%% end, -%% Clients). - - -wait4clients([], _Timeout) -> - ok; -wait4clients(Clients, Timeout) when Timeout > 0 -> - io:format("wait4clients -> entry with" - "~n length(Clients): ~w" - "~n Timeout: ~w" - "~n", [length(Clients), Timeout]), - T = t(), - receive - {'DOWN', _MRef, process, Pid, normal} -> - case lists:keysearch(Pid, 2, Clients) of - {value, {Id, _, _}} -> - io:format("receive normal exit message " - "from client ~p (~p)", [Id, Pid]), - NewClients = - lists:keydelete(Id, 1, Clients), - wait4clients(NewClients, - Timeout - (t() - T)); - false -> - io:format("receive normal exit message " - "from unknown process: ~p", [Pid]), - wait4clients(Clients, Timeout - (t() - T)) +timeout_memory_leak() -> + [{doc, "Check OTP-8739"}]. +timeout_memory_leak(Config) when is_list(Config) -> + {_DummyServerPid, Port} = otp_8739_dummy_server(), + {ok,Host} = inet:gethostname(), + Request = {?URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ "/dummy.html", []}, + case httpc:request(get, Request, [{connect_timeout, 500}, {timeout, 1}], [{sync, true}]) of + {error, timeout} -> + %% And now we check the size of the handler db + Info = httpc:info(), + ct:print("Info: ~p", [Info]), + {value, {handlers, Handlers}} = + lists:keysearch(handlers, 1, Info), + case Handlers of + [] -> + ok; + _ -> + ct:fail({unexpected_handlers, Handlers}) end; - - {'DOWN', _MRef, process, Pid, Reason} -> - case lists:keysearch(Pid, 2, Clients) of - {value, {Id, _, _}} -> - io:format("receive bad exit message " - "from client ~p (~p):" - "~n ~p", [Id, Pid, Reason]), - erlang:error({bad_client_termination, Id, Reason}); - false -> - io:format("receive normal exit message " - "from unknown process: ~p", [Pid]), - wait4clients(Clients, Timeout - (t() - T)) - end - - after Timeout -> - erlang:error({client_timeout, Clients}) - end; -wait4clients(Clients, _) -> - erlang:error({client_timeout, Clients}). - - -%% Time in milli seconds -t() -> - {A,B,C} = erlang:now(), - A*1000000000+B*1000+(C div 1000). - - -%% ----------------------------------------------------- -%% Webserver part: -%% Implements a web server that sends responses one character -%% at a time, with random delays between the characters. - -start_slow_server(SeqNumServer) -> - io:format("start slow server when" - "~n SeqNumServer: ~w" - "~n", [SeqNumServer]), - proc_lib:start( - erlang, apply, [fun() -> init_slow_server(SeqNumServer) end, []]). - -init_slow_server(SeqNumServer) -> - io:format("[webserver ~w] init slow server" - "~n", [SeqNumServer]), - {ok, LSock} = gen_tcp:listen(0, [binary, {packet,0}, {active,true}, - {backlog, 100}]), - io:format("[webserver ~w] LSock: ~p" - "~n", [SeqNumServer, LSock]), - {ok, {_IP, Port}} = inet:sockname(LSock), - io:format("[webserver ~w] Port: ~w" - "~n", [SeqNumServer, Port]), - proc_lib:init_ack({ok, self(), Port}), - loop_slow_server(LSock, SeqNumServer). - -loop_slow_server(LSock, SeqNumServer) -> - io:format("[webserver ~w] entry with" - "~n LSock: ~p" - "~n", [SeqNumServer, LSock]), - Master = self(), - Acceptor = proc_lib:spawn( - fun() -> client_handler(Master, LSock, SeqNumServer) end), - io:format("[webserver ~w] acceptor started" - "~n Acceptor: ~p" - "~n", [SeqNumServer, Acceptor]), - receive - {accepted, Acceptor} -> - io:format("[webserver ~w] accepted" - "~n", [SeqNumServer]), - loop_slow_server(LSock, SeqNumServer); - shutdown -> - gen_tcp:close(LSock), - exit(Acceptor, kill) - end. - - -%% Handle one client connection -client_handler(Master, LSock, SeqNumServer) -> - io:format("[acceptor ~w] await accept" - "~n", [SeqNumServer]), - {ok, CSock} = gen_tcp:accept(LSock), - io:format("[acceptor ~w] accepted" - "~n CSock: ~p" - "~n", [SeqNumServer, CSock]), - Master ! {accepted, self()}, - set_random_seed(), - loop_client(1, CSock, SeqNumServer). - -loop_client(N, CSock, SeqNumServer) -> - %% Await request, don't bother parsing it too much, - %% assuming the entire request arrives in one packet. - io:format("[acceptor ~w] await request" - "~n N: ~p" - "~n", [SeqNumServer, N]), - receive - {tcp, CSock, Req} -> - ReqNum = parse_req_num(Req), - RespSeqNum = get_next_sequence_number(SeqNumServer), - Response = f("~s->resp~3..0w/~2..0w", [ReqNum, RespSeqNum, N]), - Txt = f("Slow server (~p) got ~p, answering with ~p", - [self(), Req, Response]), - io:format("~s...~n", [Txt]), - slowly_send_response(CSock, Response), - case parse_connection_type(Req) of - keep_alive -> - io:format("~s...done~n", [Txt]), - loop_client(N+1, CSock, SeqNumServer); - close -> - io:format("~s...done (closing)~n", [Txt]), - gen_tcp:close(CSock) - end - end. - -slowly_send_response(CSock, Answer) -> - Response = f("HTTP/1.1 200 OK\r\nContent-Length: ~w\r\n\r\n~s", - [length(Answer), Answer]), - lists:foreach( - fun(Char) -> - timer:sleep(random:uniform(500)), - gen_tcp:send(CSock, <>) - end, - Response). - -parse_req_num(Request) -> - Opts = [caseless,{capture,all_but_first,list}], - {match, [ReqNum]} = re:run(Request, "GET /(.*) HTTP", Opts), - ReqNum. - -parse_connection_type(Request) -> - Opts = [caseless,{capture,all_but_first,list}], - {match,[CType]} = re:run(Request, "connection: *(keep-alive|close)", Opts), - case string:to_lower(CType) of - "close" -> close; - "keep-alive" -> keep_alive - end. - - -set_random_seed() -> - {_, _, Micros} = now(), - A = erlang:phash2([make_ref(), self(), Micros]), - random:seed(A, A, A). - -f(F, A) -> lists:flatten(io_lib:format(F,A)). - - - - -%%------------------------------------------------------------------------- - - - -otp_8106_pid(doc) -> - ["OTP-8106 - deliver reply info using \"other\" pid"]; -otp_8106_pid(suite) -> - []; -otp_8106_pid(Config) when is_list(Config) -> - case ?config(local_server, Config) of - ok -> - ReceiverPid = create_receiver(pid), - Receiver = ReceiverPid, - - otp8106(ReceiverPid, Receiver, Config), - - stop_receiver(ReceiverPid), - - ok; - _ -> - skip("Failed to start local http-server") - end. - - -otp_8106_fun(doc) -> - ["OTP-8106 - deliver reply info using fun"]; -otp_8106_fun(suite) -> - []; -otp_8106_fun(Config) when is_list(Config) -> - case ?config(local_server, Config) of - ok -> - ReceiverPid = create_receiver(function), - Receiver = otp_8106_deliver_fun(ReceiverPid), - - otp8106(ReceiverPid, Receiver, Config), - - stop_receiver(ReceiverPid), - - ok; - _ -> - skip("Failed to start local http-server") + Unexpected -> + ct:fail({unexpected, Unexpected}) end. +%%-------------------------------------------------------------------- -otp_8106_mfa(doc) -> - ["OTP-8106 - deliver reply info using mfa callback"]; -otp_8106_mfa(suite) -> - []; -otp_8106_mfa(Config) when is_list(Config) -> - case ?config(local_server, Config) of - ok -> - ReceiverPid = create_receiver(mfa), - Receiver = {?MODULE, otp_8106_deliver, [mfa, ReceiverPid]}, - - otp8106(ReceiverPid, Receiver, Config), - - stop_receiver(ReceiverPid), - - ok; - _ -> - skip("Failed to start local http-server") - end. - - - otp8106(ReceiverPid, Receiver, Config) -> - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - Request = {URL, []}, - HTTPOptions = [], - Options = [{sync, false}, {receiver, Receiver}], +wait_for_whole_response() -> + [{doc, "Check OTP-8154"}]. +wait_for_whole_response(Config) when is_list(Config) -> - {ok, RequestId} = - httpc:request(get, Request, HTTPOptions, Options), + ReqSeqNumServer = start_sequence_number_server(), + RespSeqNumServer = start_sequence_number_server(), + {ok, Server, Port} = start_slow_server(RespSeqNumServer), + Clients = run_clients(105, Port, ReqSeqNumServer), + ok = wait4clients(Clients, timer:minutes(3)), + Server ! shutdown, + RespSeqNumServer ! shutdown, + ReqSeqNumServer ! shutdown. - Body = - receive +%%-------------------------------------------------------------------- +%% Internal Functions ------------------------------------------------ +%%-------------------------------------------------------------------- +stream(ReceiverPid, Receiver, Config) -> + Request = {url(group_name(Config), "/dummy.html", Config), []}, + Options = [{sync, false}, {receiver, Receiver}], + {ok, RequestId} = + httpc:request(get, Request, [], Options), + Body = + receive {reply, ReceiverPid, {RequestId, {{_, 200, _}, _, B}}} -> B; {reply, ReceiverPid, Msg} -> - tsf(Msg); + ct:fail(Msg); {bad_reply, ReceiverPid, Msg} -> - tsf(Msg) + ct:fail(Msg) end, - inets_test_lib:check_body(binary_to_list(Body)), - ok. - + inets_test_lib:check_body(binary_to_list(Body)). create_receiver(Type) -> - Parent = self(), + Parent = self(), Receiver = fun() -> receiver(Type, Parent) end, spawn_link(Receiver). @@ -2678,8 +941,7 @@ stop_receiver(Pid) -> receiver(Type, Parent) -> receive {stop, Parent} -> - exit(normal); - + ok; {http, ReplyInfo} when (Type =:= pid) -> Parent ! {reply, self(), ReplyInfo}, receiver(Type, Parent); @@ -2687,266 +949,124 @@ receiver(Type, Parent) -> {Type, ReplyInfo} -> Parent ! {reply, self(), ReplyInfo}, receiver(Type, Parent); - + Crap -> Parent ! {reply, self(), {bad_reply, Crap}}, receiver(Type, Parent) end. +stream_deliver_fun(ReceiverPid) -> + fun(ReplyInfo) -> stream_deliver(ReplyInfo, function, ReceiverPid) end. -otp_8106_deliver_fun(ReceiverPid) -> - fun(ReplyInfo) -> otp_8106_deliver(ReplyInfo, function, ReceiverPid) end. - -otp_8106_deliver(ReplyInfo, Type, ReceiverPid) -> +stream_deliver(ReplyInfo, Type, ReceiverPid) -> ReceiverPid ! {Type, ReplyInfo}, ok. +stream_test(Request, To) -> + {ok, {{_,200,_}, [_ | _], Body}} = + httpc:request(get, Request, [], []), + {ok, RequestId} = + httpc:request(get, Request, [], [{sync, false}, To]), + StreamedBody = + receive + {http, {RequestId, stream_start, _Headers}} -> + receive_streamed_body(RequestId, <<>>); + {http, {RequestId, stream_start, _Headers, Pid}} -> + receive_streamed_body(RequestId, <<>>, Pid); + {http, Msg} -> + ct:fail(Msg) + end, -%%------------------------------------------------------------------------- - -otp_8056(doc) -> - "OTP-8056"; -otp_8056(suite) -> - []; -otp_8056(Config) when is_list(Config) -> - Method = get, - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - Request = {URL, []}, - HTTPOptions = [], - Options1 = [{sync, true}, {stream, {self, once}}], - Options2 = [{sync, true}, {stream, self}], - {error, streaming_error} = httpc:request(Method, Request, - HTTPOptions, Options1), - tsp("request 1 failed as expected"), - {error, streaming_error} = httpc:request(Method, Request, - HTTPOptions, Options2), - tsp("request 2 failed as expected"), - ok. - - -%%------------------------------------------------------------------------- - -otp_8352(doc) -> - "OTP-8352"; -otp_8352(suite) -> - []; -otp_8352(Config) when is_list(Config) -> - tsp("otp_8352 -> entry with" - "~n Config: ~p", [Config]), - case ?config(local_server, Config) of - ok -> - tsp("local-server running"), - - tsp("initial profile info(1): ~p", [httpc:info()]), - - MaxSessions = 5, - MaxKeepAlive = 10, - KeepAliveTimeout = timer:minutes(2), - ConnOptions = [{max_sessions, MaxSessions}, - {max_keep_alive_length, MaxKeepAlive}, - {keep_alive_timeout, KeepAliveTimeout}], - httpc:set_options(ConnOptions), - - Method = get, - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - Request = {URL, []}, - Timeout = timer:seconds(1), - ConnTimeout = Timeout + timer:seconds(1), - HttpOptions1 = [{timeout, Timeout}, {connect_timeout, ConnTimeout}], - Options1 = [{socket_opts, [{tos, 87}, - {recbuf, 16#FFFF}, - {sndbuf, 16#FFFF}]}], - case httpc:request(Method, Request, HttpOptions1, Options1) of - {ok, {{_,200,_}, [_ | _], ReplyBody1 = [_ | _]}} -> - %% equivaliant to httpc:request(get, {URL, []}, [], []), - inets_test_lib:check_body(ReplyBody1); - {ok, UnexpectedReply1} -> - tsf({unexpected_reply, UnexpectedReply1}); - {error, _} = Error1 -> - tsf({bad_reply, Error1}) - end, - - tsp("profile info (2): ~p", [httpc:info()]), - - HttpOptions2 = [], - Options2 = [{socket_opts, [{tos, 84}, - {recbuf, 32#1FFFF}, - {sndbuf, 32#1FFFF}]}], - case httpc:request(Method, Request, HttpOptions2, Options2) of - {ok, {{_,200,_}, [_ | _], ReplyBody2 = [_ | _]}} -> - %% equivaliant to httpc:request(get, {URL, []}, [], []), - inets_test_lib:check_body(ReplyBody2); - {ok, UnexpectedReply2} -> - tsf({unexpected_reply, UnexpectedReply2}); - {error, _} = Error2 -> - tsf({bad_reply, Error2}) - end, - tsp("profile info (3): ~p", [httpc:info()]), - ok; - - _ -> - skip("Failed to start local http-server") - end. - - -%%------------------------------------------------------------------------- - -otp_8371(doc) -> - ["OTP-8371"]; -otp_8371(suite) -> - []; -otp_8371(Config) when is_list(Config) -> - ok = httpc:set_options([{ipv6, disabled}]), % also test the old option - {DummyServerPid, Port} = dummy_server(ipv4), - - URL = ?URL_START ++ integer_to_list(Port) ++ - "/ensure_host_header_with_port.html", - - case httpc:request(get, {URL, []}, [], []) of - {ok, Result} -> - case Result of - {{_, 200, _}, _Headers, Body} -> - tsp("expected response with" - "~n Body: ~p", [Body]), - ok; - {StatusLine, Headers, Body} -> - tsp("expected response with" - "~n StatusLine: ~p" - "~n Headers: ~p" - "~n Body: ~p", [StatusLine, Headers, Body]), - tsf({unexpected_result, - [{status_line, StatusLine}, - {headers, Headers}, - {body, Body}]}); - _ -> - tsf({unexpected_result, Result}) - end; - Error -> - tsf({request_failed, Error}) - end, + Body == binary_to_list(StreamedBody). - DummyServerPid ! stop, - ok = httpc:set_options([{ipv6, enabled}]), +url(http, End, Config) -> + Port = ?config(port, Config), + {ok,Host} = inet:gethostname(), + ?URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End; +url(https, End, Config) -> + Port = ?config(port, Config), + {ok,Host} = inet:gethostname(), + ?TLS_URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End; +url(sim_http, End, Config) -> + url(http, End, Config); +url(sim_https, End, Config) -> + url(https, End, Config). +url(http, UserInfo, End, Config) -> + Port = ?config(port, Config), + ?URL_START ++ UserInfo ++ integer_to_list(Port) ++ End; +url(https, UserInfo, End, Config) -> + Port = ?config(port, Config), + ?TLS_URL_START ++ UserInfo ++ integer_to_list(Port) ++ End; +url(sim_http, UserInfo, End, Config) -> + url(http, UserInfo, End, Config); +url(sim_https, UserInfo, End, Config) -> + url(https, UserInfo, End, Config). + +group_name(Config) -> + GroupProp = ?config(tc_group_properties, Config), + proplists:get_value(name, GroupProp). + +server_start(sim_http, _) -> + Inet = inet_version(), + ok = httpc:set_options([{ipfamily, Inet}]), + {_Pid, Port} = dummy_server(Inet), + Port; + +server_start(sim_https, SslConfig) -> + Inet = inet_version(), + ok = httpc:set_options([{ipfamily, Inet}]), + {_Pid, Port} = dummy_server(ssl, Inet, SslConfig), + Port; + +server_start(_, HttpdConfig) -> + {ok, Pid} = inets:start(httpd, HttpdConfig), + Serv = inets:services_info(), + {value, {_, _, Info}} = lists:keysearch(Pid, 2, Serv), + proplists:get_value(port, Info). + +server_config(http, Config) -> + ServerRoot = ?config(server_root, Config), + [{port, 0}, + {server_name,"httpc_test"}, + {server_root, ServerRoot}, + {document_root, ?config(doc_root, Config)}, + {bind_address, any}, + {ipfamily, inet_version()}, + {mime_type, "text/plain"}, + {script_alias, {"/cgi-bin/", filename:join(ServerRoot, "cgi-bin") ++ "/"}} + ]; + +server_config(https, Config) -> + [{socket_type, {essl, ssl_config(Config)}} | server_config(http, Config)]; +server_config(sim_https, Config) -> + ssl_config(Config); +server_config(_, _) -> + []. + +start_apps(https) -> + inets_test_lib:start_apps([crypto, public_key, ssl]); +start_apps(_) -> ok. - -%%------------------------------------------------------------------------- - -otp_8739(doc) -> - ["OTP-8739"]; -otp_8739(suite) -> - []; -otp_8739(Config) when is_list(Config) -> - {_DummyServerPid, Port} = otp_8739_dummy_server(), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - Method = get, - Request = {URL, []}, - HttpOptions = [{connect_timeout, 500}, {timeout, 1}], - Options = [{sync, true}], - case httpc:request(Method, Request, HttpOptions, Options) of - {error, timeout} -> - %% And now we check the size of the handler db - Info = httpc:info(), - tsp("Info: ~p", [Info]), - {value, {handlers, Handlers}} = - lists:keysearch(handlers, 1, Info), - case Handlers of - [] -> - ok; - _ -> - tsf({unexpected_handlers, Handlers}) - end; - Unexpected -> - tsf({unexpected, Unexpected}) - end. - - -otp_8739_dummy_server() -> - Parent = self(), - Pid = spawn_link(fun() -> otp_8739_dummy_server_init(Parent) end), - receive - {port, Port} -> - {Pid, Port} - end. - -otp_8739_dummy_server_init(Parent) -> - {ok, ListenSocket} = - gen_tcp:listen(0, [binary, inet, {packet, 0}, - {reuseaddr,true}, - {active, false}]), - {ok, Port} = inet:port(ListenSocket), - Parent ! {port, Port}, - 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 - receive - {tcp_closed, Sock} -> - (catch gen_tcp:close(ListenSocket)), - exit(normal); - {tcp_error, Sock, Reason} -> - tsp("socket error: ~p", [Reason]), - (catch gen_tcp:close(ListenSocket)), - exit(normal) - after 10000 -> - %% Just in case - (catch gen_tcp:close(Sock)), - (catch gen_tcp:close(ListenSocket)), - exit(timeout) - end; - Error -> - 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) -> +ssl_config(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(), + [{certfile, filename:join(DataDir, "ssl_server_cert.pem")}, + {verify, verify_none} + ]. - DummyServerPid ! stop, - ok = httpc:set_options([{ipfamily, inet6fb4}]). - -%%-------------------------------------------------------------------- -%% Internal functions -%%-------------------------------------------------------------------- -setup_server_dirs(ServerRoot, DocRoot, DataDir) -> - ConfDir = filename:join(ServerRoot, "conf"), +setup_server_dirs(ServerRoot, DocRoot, DataDir) -> CgiDir = filename:join(ServerRoot, "cgi-bin"), ok = file:make_dir(ServerRoot), ok = file:make_dir(DocRoot), - ok = file:make_dir(ConfDir), ok = file:make_dir(CgiDir), {ok, Files} = file:list_dir(DataDir), lists:foreach(fun(File) -> case lists:suffix(".html", File) of true -> - inets_test_lib:copy_file(File, - DataDir, + inets_test_lib:copy_file(File, + DataDir, DocRoot); false -> ok @@ -2961,77 +1081,41 @@ setup_server_dirs(ServerRoot, DocRoot, DataDir) -> end, inets_test_lib:copy_file(Cgi, DataDir, CgiDir), - inets_test_lib:copy_file("mime.types", DataDir, ConfDir). - -create_config(FileName, ComType, Port, PrivDir, ServerRoot, DocRoot, - SSLDir) -> - MaxHdrSz = io_lib:format("~p", [256]), - MaxHdrAct = io_lib:format("~p", [close]), - SSL = - case ComType of - ssl -> - [cline(["SSLCertificateFile ", - filename:join(SSLDir, "ssl_server_cert.pem")]), - cline(["SSLCertificateKeyFile ", - filename:join(SSLDir, "ssl_server_cert.pem")]), - cline(["SSLVerifyClient 0"])]; - _ -> - [] - end, + AbsCgi = filename:join([CgiDir, Cgi]), + {ok, FileInfo} = file:read_file_info(AbsCgi), + ok = file:write_file_info(AbsCgi, FileInfo#file_info{mode = 8#00755}). - Mod_order = "Modules mod_alias mod_auth mod_esi mod_actions mod_cgi" - " mod_include mod_dir mod_get mod_head" - " mod_log mod_disk_log mod_trace", - - %% BindAddress = "*|inet", % Force the use of IPv4 - BindAddress = "*", % This corresponds to using IpFamily inet6fb4 - - HttpConfig = [ - cline(["BindAddress ", BindAddress]), - cline(["Port ", integer_to_list(Port)]), - cline(["ServerName ", "httpc_test"]), - cline(["SocketType ", atom_to_list(ComType)]), - cline([Mod_order]), - cline(["ServerRoot ", ServerRoot]), - cline(["DocumentRoot ", DocRoot]), - cline(["MaxHeaderSize ",MaxHdrSz]), - cline(["MaxHeaderAction ",MaxHdrAct]), - cline(["DirectoryIndex ", "index.html "]), - cline(["DefaultType ", "text/plain"]), - cline(["ScriptAlias /cgi-bin/ ", - filename:join(ServerRoot, "cgi-bin"), "/"]), - SSL], - ConfigFile = filename:join([PrivDir,FileName]), - {ok, Fd} = file:open(ConfigFile, [write]), - ok = file:write(Fd, lists:flatten(HttpConfig)), - ok = file:close(Fd). - -cline(List) -> - lists:flatten([List, "\r\n"]). -receive_streamed_body(RequestId, Body) -> - receive - {http, {RequestId, stream, BinBodyPart}} -> - receive_streamed_body(RequestId, - <>); - {http, {RequestId, stream_end, _Headers}} -> - Body; - {http, Msg} -> - tsf(Msg) - end. +keep_alive_requests(Request, Profile) -> + {ok, RequestIdA0} = + httpc:request(get, Request, [], [{sync, false}], Profile), + {ok, RequestIdA1} = + httpc:request(get, Request, [], [{sync, false}], Profile), + {ok, RequestIdA2} = + httpc:request(get, Request, [], [{sync, false}], Profile), -receive_streamed_body(RequestId, Body, Pid) -> - httpc:stream_next(Pid), - test_server:format("~p:receive_streamed_body -> requested next stream ~n", [?MODULE]), - receive - {http, {RequestId, stream, BinBodyPart}} -> - receive_streamed_body(RequestId, - <>, - Pid); - {http, {RequestId, stream_end, _Headers}} -> - Body; - {http, Msg} -> - tsf(Msg) + receive_replys([RequestIdA0, RequestIdA1, RequestIdA2]), + + {ok, RequestIdB0} = + httpc:request(get, Request, [], [{sync, false}], Profile), + {ok, RequestIdB1} = + httpc:request(get, Request, [], [{sync, false}], Profile), + {ok, RequestIdB2} = + httpc:request(get, Request, [], [{sync, false}], Profile), + + ok = httpc:cancel_request(RequestIdB1, Profile), + ct:print("Cancel ~p~n", [RequestIdB1]), + receive_replys([RequestIdB0, RequestIdB2]). + + +receive_replys([]) -> + ok; +receive_replys([ID|IDs]) -> + receive + {http, {ID, {{_, 200, _}, [_|_], _}}} -> + receive_replys(IDs); + {http, {Other, {{_, 200, _}, [_|_], _}}} -> + ct:fail({recived_canceld_id, Other}) end. %% Perform a synchronous stop @@ -3042,55 +1126,46 @@ dummy_server_stop(Pid) -> ok end. -dummy_server(IpV) -> - dummy_server(self(), ip_comm, IpV, []). +inet_version() -> + inet. %% Just run inet for now + %% case gen_tcp:listen(0,[inet6]) of + %% {ok, S} -> + %% gen_tcp:close(S), + %% inet6; + %% _ -> + %% inet + %%end. -dummy_server(SocketType, IpV, Extra) -> - dummy_server(self(), SocketType, IpV, Extra). +dummy_server(Inet) -> + dummy_server(self(), ip_comm, Inet, []). -dummy_server(Caller, SocketType, IpV, Extra) -> - Args = [Caller, SocketType, IpV, Extra], +dummy_server(SocketType, Inet, Extra) -> + dummy_server(self(), SocketType, Inet, Extra). + +dummy_server(Caller, SocketType, Inet, Extra) -> + Args = [Caller, SocketType, Inet, Extra], Pid = spawn(httpc_SUITE, dummy_server_init, Args), receive {port, Port} -> {Pid, Port} end. -dummy_server_init(Caller, ip_comm, IpV, _) -> +dummy_server_init(Caller, ip_comm, Inet, _) -> BaseOpts = [binary, {packet, 0}, {reuseaddr,true}, {active, false}], - {ok, ListenSocket} = - case IpV of - ipv4 -> - tsp("ip_comm ipv4 listen", []), - gen_tcp:listen(0, [inet | BaseOpts]); - ipv6 -> - tsp("ip_comm ipv6 listen", []), - gen_tcp:listen(0, [inet6 | BaseOpts]) - end, + {ok, ListenSocket} = gen_tcp:listen(0, [Inet | BaseOpts]), {ok, Port} = inet:port(ListenSocket), - tsp("dummy_server_init(ip_comm) -> Port: ~p", [Port]), Caller ! {port, Port}, dummy_ipcomm_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]}, [], ListenSocket); -dummy_server_init(Caller, essl, IpV, SSLOptions) -> - BaseOpts = [{ssl_imp, new}, - {backlog, 128}, binary, {reuseaddr,true}, {active, false} | + +dummy_server_init(Caller, ssl, Inet, SSLOptions) -> + BaseOpts = [binary, {reuseaddr,true}, {active, false} | SSLOptions], - dummy_ssl_server_init(Caller, BaseOpts, IpV). - -dummy_ssl_server_init(Caller, BaseOpts, IpV) -> - {ok, ListenSocket} = - case IpV of - ipv4 -> - tsp("dummy_ssl_server_init -> ssl ipv4 listen", []), - ssl:listen(0, [inet | BaseOpts]); - ipv6 -> - tsp("dummy_ssl_server_init -> ssl ipv6 listen", []), - ssl:listen(0, [inet6 | BaseOpts]) - end, - tsp("dummy_ssl_server_init -> ListenSocket: ~p", [ListenSocket]), + dummy_ssl_server_init(Caller, BaseOpts, Inet). + +dummy_ssl_server_init(Caller, BaseOpts, Inet) -> + {ok, ListenSocket} = ssl:listen(0, [Inet | BaseOpts]), {ok, {_, Port}} = ssl:sockname(ListenSocket), - tsp("dummy_ssl_server_init -> Port: ~p", [Port]), Caller ! {port, Port}, dummy_ssl_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]}, [], ListenSocket). @@ -3098,85 +1173,56 @@ dummy_ssl_server_init(Caller, BaseOpts, IpV) -> dummy_ipcomm_server_loop(MFA, Handlers, ListenSocket) -> receive stop -> - tsp("dummy_ipcomm_server_loop -> stop handlers", []), lists:foreach(fun(Handler) -> Handler ! stop end, Handlers); {stop, From} -> - tsp("dummy_ipcomm_server_loop -> " - "stop command from ~p for handlers (~p)", [From, Handlers]), Stopper = fun(Handler) -> Handler ! stop end, lists:foreach(Stopper, Handlers), From ! {stopped, self()} after 0 -> - tsp("dummy_ipcomm_server_loop -> await accept", []), {ok, Socket} = gen_tcp:accept(ListenSocket), - tsp("dummy_ipcomm_server_loop -> accepted: ~p", [Socket]), HandlerPid = dummy_request_handler(MFA, Socket), - tsp("dummy_icomm_server_loop -> handler created: ~p", [HandlerPid]), gen_tcp:controlling_process(Socket, HandlerPid), - tsp("dummy_ipcomm_server_loop -> " - "control transfered to handler", []), HandlerPid ! ipcomm_controller, - tsp("dummy_ipcomm_server_loop -> " - "handler informed about control transfer", []), dummy_ipcomm_server_loop(MFA, [HandlerPid | Handlers], - ListenSocket) + ListenSocket) end. dummy_ssl_server_loop(MFA, Handlers, ListenSocket) -> receive stop -> - tsp("dummy_ssl_server_loop -> stop handlers", []), lists:foreach(fun(Handler) -> Handler ! stop end, Handlers); {stop, From} -> - tsp("dummy_ssl_server_loop -> " - "stop command from ~p for handlers (~p)", [From, Handlers]), Stopper = fun(Handler) -> Handler ! stop end, lists:foreach(Stopper, Handlers), From ! {stopped, self()} after 0 -> - tsp("dummy_ssl_server_loop -> await accept", []), {ok, Socket} = ssl:transport_accept(ListenSocket), - tsp("dummy_ssl_server_loop -> accepted: ~p", [Socket]), HandlerPid = dummy_request_handler(MFA, Socket), - tsp("dummy_ssl_server_loop -> handler created: ~p", [HandlerPid]), ssl:controlling_process(Socket, HandlerPid), - tsp("dummy_ssl_server_loop -> control transfered to handler", []), HandlerPid ! ssl_controller, - tsp("dummy_ssl_server_loop -> " - "handler informed about control transfer", []), dummy_ssl_server_loop(MFA, [HandlerPid | Handlers], ListenSocket) end. dummy_request_handler(MFA, Socket) -> - tsp("spawn request handler", []), spawn(httpc_SUITE, dummy_request_handler_init, [MFA, Socket]). dummy_request_handler_init(MFA, Socket) -> SockType = receive ipcomm_controller -> - tsp("dummy_request_handler_init -> " - "received ip_comm controller - activate", []), inet:setopts(Socket, [{active, true}]), ip_comm; ssl_controller -> - tsp("dummy_request_handler_init -> " - "received ssl controller - activate", []), ssl:setopts(Socket, [{active, true}]), ssl end, dummy_request_handler_loop(MFA, SockType, Socket). dummy_request_handler_loop({Module, Function, Args}, SockType, Socket) -> - tsp("dummy_request_handler_loop -> entry with" - "~n Module: ~p" - "~n Function: ~p" - "~n Args: ~p", [Module, Function, Args]), receive {Proto, _, Data} when (Proto =:= tcp) orelse (Proto =:= ssl) -> - tsp("dummy_request_handler_loop -> [~w] Data ~p", [Proto, Data]), - case handle_request(Module, Function, [Data | Args], Socket, Proto) of + case handle_request(Module, Function, [Data | Args], Socket) of stop when Proto =:= tcp -> gen_tcp:close(Socket); stop when Proto =:= ssl -> @@ -3190,49 +1236,26 @@ dummy_request_handler_loop({Module, Function, Args}, SockType, Socket) -> ssl:close(Socket) end. - -mk_close(tcp) -> fun(Sock) -> gen_tcp:close(Sock) end; -mk_close(ssl) -> fun(Sock) -> ssl:close(Sock) end. - -mk_send(tcp) -> fun(Sock, Data) -> gen_tcp:send(Sock, Data) end; -mk_send(ssl) -> fun(Sock, Data) -> ssl:send(Sock, Data) end. - -handle_request(Module, Function, Args, Socket, Proto) -> - Close = mk_close(Proto), - Send = mk_send(Proto), - handle_request(Module, Function, Args, Socket, Close, Send). - -handle_request(Module, Function, Args, Socket, Close, Send) -> - tsp("handle_request -> entry with" - "~n Module: ~p" - "~n Function: ~p" - "~n Args: ~p", [Module, Function, Args]), +handle_request(Module, Function, Args, Socket) -> case Module:Function(Args) of {ok, Result} -> - tsp("handle_request -> ok" - "~n Result: ~p", [Result]), - case (catch handle_http_msg(Result, Socket, Close, Send)) of + case handle_http_msg(Result, Socket) of stop -> stop; <<>> -> - tsp("handle_request -> empty data"), {httpd_request, parse, [[<<>>, ?HTTP_MAX_HEADER_SIZE]]}; Data -> handle_request(httpd_request, parse, - [Data |[?HTTP_MAX_HEADER_SIZE]], Socket, - Close, Send) + [Data |[?HTTP_MAX_HEADER_SIZE]], Socket) end; NewMFA -> - tsp("handle_request -> " - "~n NewMFA: ~p", [NewMFA]), NewMFA end. -handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket, Close, Send) -> - tsp("handle_http_msg -> entry with: " - "~n RelUri: ~p" - "~n Headers: ~p" - "~n Body: ~p", [RelUri, Headers, Body]), +handle_http_msg({Method, RelUri, _, {_, Headers}, Body}, Socket) -> + + ct:print("Request: ~p ~p", [Method, RelUri]), + NextRequest = case RelUri of "/dummy_headers.html" -> @@ -3253,225 +1276,69 @@ handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket, Close, Send) -> end end, - tsp("handle_http_msg -> NextRequest: ~p", [NextRequest]), case (catch ets:lookup(cookie, cookies)) of [{cookies, true}]-> - tsp("handle_http_msg -> check cookies ~p", []), check_cookie(Headers); _ -> ok end, - + + {ok, {_, Port}} = sockname(Socket), + + DefaultResponse = "HTTP/1.1 200 ok\r\n" ++ "Content-Length:32\r\n\r\n" "foobar", - Msg = - case RelUri of - "/just_close.html" -> - close; - "/no_content.html" -> - "HTTP/1.0 204 No Content\r\n\r\n"; - "/no_headers.html" -> - "HTTP/1.0 200 OK\r\n\r\nTEST"; - "/ensure_host_header_with_port.html" -> - %% tsp("handle_http_msg -> validate host with port"), - case ensure_host_header_with_port(Headers) of - true -> - B = - "" ++ - "host with port" ++ - "", - Len = integer_to_list(length(B)), - "HTTP/1.1 200 ok\r\n" ++ - "Content-Length:" ++ Len ++ "\r\n\r\n" ++ B; - false -> - B = - "" ++ - "Internal Server Error - host without port" ++ - "", - Len = integer_to_list(length(B)), - "HTTP/1.1 500 Internal Server Error\r\n" ++ - "Content-Length:" ++ Len ++ "\r\n\r\n" ++ B - end; - "/300.html" -> - NewUri = ?URL_START ++ - integer_to_list(?IP_PORT) ++ "/dummy.html", - "HTTP/1.1 300 Multiple Choices\r\n" ++ - "Location:" ++ NewUri ++ "\r\n" ++ - "Content-Length:0\r\n\r\n"; - "/301.html" -> - NewUri = ?URL_START ++ - integer_to_list(?IP_PORT) ++ "/dummy.html", - "HTTP/1.1 301 Moved Permanently\r\n" ++ - "Location:" ++ NewUri ++ "\r\n" ++ - "Content-Length:80\r\n\r\n" ++ - "New place"; - "/302.html" -> - NewUri = ?URL_START ++ - integer_to_list(?IP_PORT) ++ "/dummy.html", - "HTTP/1.1 302 Found \r\n" ++ - "Location:" ++ NewUri ++ "\r\n" ++ - "Content-Length:80\r\n\r\n" ++ - "New place"; - "/303.html" -> - NewUri = ?URL_START ++ - integer_to_list(?IP_PORT) ++ "/dummy.html", - "HTTP/1.1 303 See Other \r\n" ++ - "Location:" ++ NewUri ++ "\r\n" ++ - "Content-Length:80\r\n\r\n" ++ - "New place"; - "/307.html" -> - NewUri = ?URL_START ++ - integer_to_list(?IP_PORT) ++ "/dummy.html", - "HTTP/1.1 307 Temporary Rediect \r\n" ++ - "Location:" ++ NewUri ++ "\r\n" ++ - "Content-Length:80\r\n\r\n" ++ - "New place"; - "/500.html" -> - "HTTP/1.1 500 Internal Server Error\r\n" ++ - "Content-Length:47\r\n\r\n" ++ - "Internal Server Error"; - "/503.html" -> - case ets:lookup(unavailable, 503) of - [{503, unavailable}] -> - ets:insert(unavailable, {503, available}), - "HTTP/1.1 503 Service Unavailable\r\n" ++ - "Retry-After:5\r\n" ++ - "Content-Length:47\r\n\r\n" ++ - "Internal Server Error"; - [{503, available}] -> - DefaultResponse; - [{503, long_unavailable}] -> - "HTTP/1.1 503 Service Unavailable\r\n" ++ - "Retry-After:120\r\n" ++ - "Content-Length:47\r\n\r\n" ++ - "Internal Server Error" - end; - "/redirectloop.html" -> %% Create a potential endless loop! - {ok, Port} = inet:port(Socket), - NewUri = ?URL_START ++ - integer_to_list(Port) ++ "/redirectloop.html", - "HTTP/1.1 300 Multiple Choices\r\n" ++ - "Location:" ++ NewUri ++ "\r\n" ++ - "Content-Length:0\r\n\r\n"; - "/userinfo.html" -> - Challange = "HTTP/1.1 401 Unauthorized \r\n" ++ - "WWW-Authenticate:Basic" ++"\r\n" ++ - "Content-Length:0\r\n\r\n", - case auth_header(Headers) of - {ok, Value} -> - handle_auth(Value, Challange, DefaultResponse); - _ -> - Challange - end; - "/dummy_headers.html" -> - %% The client will only care about the Transfer-Encoding - %% header the rest of these headers are left to the - %% user to evaluate. This is not a valid response - %% it only tests that the header handling code works. - Head = "HTTP/1.1 200 ok\r\n" ++ - "Content-Length:32\r\n" ++ - "Pragma:1#no-cache\r\n" ++ - "Via:1.0 fred, 1.1 nowhere.com (Apache/1.1)\r\n" ++ - "Warning:1#pseudonym foobar\r\n" ++ - "Vary:*\r\n" ++ - "Trailer:Other:inets_test\r\n" ++ - "Upgrade:HTTP/2.0\r\n" ++ - "Age:4711\r\n" ++ - "Transfer-Encoding:chunked\r\n" ++ - "Content-Encoding:foo\r\n" ++ - "Content-Language:en\r\n" ++ - "Content-Location:http://www.foobar.se\r\n" ++ - "Content-MD5:104528739076276072743283077410617235478\r\n" - ++ - "Content-Range:Sat, 29 Oct 1994 19:43:31 GMT\r\n" ++ - "Expires:Sat, 29 Oct 1994 19:43:31 GMT\r\n" ++ - "Proxy-Authenticate:#1Basic" ++ - "\r\n\r\n", - Send(Socket, Head), - Send(Socket, http_chunk:encode("fo")), - Send(Socket, http_chunk:encode("obar")), - http_chunk:encode_last(); - "/capital_transfer_encoding.html" -> - Head = "HTTP/1.1 200 ok\r\n" ++ - "Transfer-Encoding:Chunked\r\n\r\n", - Send(Socket, Head), - Send(Socket, http_chunk:encode("fo")), - Send(Socket, http_chunk:encode("obar")), - http_chunk:encode_last(); - "/cookie.html" -> - "HTTP/1.1 200 ok\r\n" ++ - "set-cookie:" ++ "test_cookie=true; path=/;" ++ - "max-age=60000\r\n" ++ - "Content-Length:32\r\n\r\n"++ - "foobar"; - "/missing_crlf.html" -> - "HTTP/1.1 200 ok" ++ - "Content-Length:32\r\n" ++ - "foobar"; - "/wrong_statusline.html" -> - "ok 200 HTTP/1.1\r\n\r\n" ++ - "Content-Length:32\r\n\r\n" ++ - "foobar"; - "/once_chunked.html" -> - Head = "HTTP/1.1 200 ok\r\n" ++ - "Transfer-Encoding:Chunked\r\n\r\n", - Send(Socket, Head), - Send(Socket, http_chunk:encode("fo")), - Send(Socket, - http_chunk:encode("obar")), - http_chunk:encode_last(); - "/once.html" -> - Head = "HTTP/1.1 200 ok\r\n" ++ - "Content-Length:32\r\n\r\n", - Send(Socket, Head), - Send(Socket, "fo"), - test_server:sleep(1000), - Send(Socket, "ob"), - test_server:sleep(1000), - Send(Socket, "ar"); - "/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"; - "/missing_reason_phrase.html" -> - "HTTP/1.1 200\r\n" ++ - "Content-Length: 32\r\n\r\n" - "foobar"; - "/missing_CR.html" -> - "HTTP/1.1 200 ok\n" ++ - "Content-Length:32\r\n\n" - "foobar"; - _ -> - DefaultResponse - end, - - tsp("handle_http_msg -> Msg: ~p", [Msg]), + Msg = handle_uri(Method,RelUri, Port, Headers, Socket, DefaultResponse), + case Msg of ok -> - %% Previously, this resulted in an {error, einval}. Now what? ok; close -> %% Nothing to send, just close - Close(Socket); + close(Socket); _ when is_list(Msg) orelse is_binary(Msg) -> - Send(Socket, Msg) + case Msg of + [] -> + ct:print("Empty Msg", []); + _ -> + ct:print("Response: ~p", [Msg]), + send(Socket, Msg) + end end, - tsp("handle_http_msg -> done"), NextRequest. +dummy_ssl_server_hang(Caller, Inet, SslOpt) -> + Pid = spawn(httpc_SUITE, dummy_ssl_server_hang_init, [Caller, Inet, SslOpt]), + receive + {port, Port} -> + {Pid, Port} + end. + +dummy_ssl_server_hang_init(Caller, Inet, SslOpt) -> + {ok, ListenSocket} = + ssl:listen(0, [binary, Inet, {packet, 0}, + {reuseaddr,true}, + {active, false}] ++ SslOpt), + {ok, {_,Port}} = ssl:sockname(ListenSocket), + 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. + ensure_host_header_with_port([]) -> false; ensure_host_header_with_port(["host: " ++ Host| _]) -> case string:tokens(Host, [$:]) of - [ActualHost, Port] -> - tsp("ensure_host_header_with_port -> " - "~n ActualHost: ~p" - "~n Port: ~p", [ActualHost, Port]), + [_ActualHost, _Port] -> true; _ -> false @@ -3489,15 +1356,15 @@ auth_header([_ | Tail]) -> handle_auth("Basic " ++ UserInfo, Challange, DefaultResponse) -> case string:tokens(base64:decode_to_string(UserInfo), ":") of ["alladin", "sesame"] = Auth -> - test_server:format("Auth: ~p~n", [Auth]), + ct:print("Auth: ~p~n", [Auth]), DefaultResponse; Other -> - test_server:format("UnAuth: ~p~n", [Other]), + ct:print("UnAuth: ~p~n", [Other]), Challange end. check_cookie([]) -> - tsf(no_cookie_header); + ct:fail(no_cookie_header); check_cookie(["cookie:" ++ _Value | _]) -> ok; check_cookie([_Head | Tail]) -> @@ -3510,86 +1377,532 @@ content_length(["content-length:" ++ Value | _]) -> content_length([_Head | Tail]) -> content_length(Tail). -%% ------------------------------------------------------------------------- - -simple_request_and_verify(Config, - Method, Request, HttpOpts, Opts, VerifyResult) - when (is_list(Config) andalso - is_atom(Method) andalso - is_list(HttpOpts) andalso - is_list(Opts) andalso - is_function(VerifyResult, 1)) -> - tsp("request_and_verify -> entry with" - "~n Method: ~p" - "~n Request: ~p" - "~n HttpOpts: ~p" - "~n Opts: ~p", [Method, Request, HttpOpts, Opts]), - case ?config(local_server, Config) of - ok -> - tsp("request_and_verify -> local-server running"), - Result = (catch httpc:request(Method, Request, HttpOpts, Opts)), - VerifyResult(Result); +handle_uri(_,"/just_close.html",_,_,_,_) -> + close; +handle_uri(_,"/no_content.html",_,_,_,_) -> + "HTTP/1.0 204 No Content\r\n\r\n"; + +handle_uri(_,"/no_headers.html",_,_,_,_) -> + "HTTP/1.0 200 OK\r\n\r\nTEST"; + +handle_uri("TRACE","/trace.html",_,_,_,_) -> + Body = "TRACE /trace.html simulate HTTP TRACE ", + "HTTP/1.1 200 OK\r\n" ++ "Content-Length:" ++ integer_to_list(length(Body)) ++ "\r\n\r\n" ++ Body; + +handle_uri(_,"/ensure_host_header_with_port.html",_,Headers,_,_) -> + case ensure_host_header_with_port(Headers) of + true -> + B = + "" ++ + "host with port" ++ + "", + Len = integer_to_list(length(B)), + "HTTP/1.1 200 ok\r\n" ++ + "Content-Length:" ++ Len ++ "\r\n\r\n" ++ B; + false -> + B = + "" ++ + "Internal Server Error - host without port" ++ + "", + Len = integer_to_list(length(B)), + "HTTP/1.1 500 Internal Server Error\r\n" ++ + "Content-Length:" ++ Len ++ "\r\n\r\n" ++ B + end; + +handle_uri(_,"/300.html",Port,_,Socket,_) -> + NewUri = url_start(Socket) ++ + integer_to_list(Port) ++ "/dummy.html", + Body = "New place", + "HTTP/1.1 300 Multiple Choices\r\n" ++ + "Location:" ++ NewUri ++ "\r\n" ++ + "Content-Length:" ++ integer_to_list(length(Body)) + ++ "\r\n\r\n" ++ Body; + +handle_uri("HEAD","/301.html",Port,_,Socket,_) -> + NewUri = url_start(Socket) ++ + integer_to_list(Port) ++ "/dummy.html", + "HTTP/1.1 301 Moved Permanently\r\n" ++ + "Location:" ++ NewUri ++ "\r\n" ++ + "Content-Length:0\r\n\r\n"; + +handle_uri(_,"/301.html",Port,_,Socket,_) -> + NewUri = url_start(Socket) ++ + integer_to_list(Port) ++ "/dummy.html", + Body = "New place", + "HTTP/1.1 301 Moved Permanently\r\n" ++ + "Location:" ++ NewUri ++ "\r\n" ++ + "Content-Length:" ++ integer_to_list(length(Body)) + ++ "\r\n\r\n" ++ Body; + +handle_uri("HEAD","/302.html",Port,_,Socket,_) -> + NewUri = url_start(Socket) ++ + integer_to_list(Port) ++ "/dummy.html", + "HTTP/1.1 302 Found \r\n" ++ + "Location:" ++ NewUri ++ "\r\n" ++ + "Content-Length:0\r\n\r\n"; + +handle_uri(_,"/302.html",Port, _,Socket,_) -> + NewUri = url_start(Socket) ++ + integer_to_list(Port) ++ "/dummy.html", + Body = "New place", + "HTTP/1.1 302 Found \r\n" ++ + "Location:" ++ NewUri ++ "\r\n" ++ + "Content-Length:" ++ integer_to_list(length(Body)) + ++ "\r\n\r\n" ++ Body; + +handle_uri("HEAD","/303.html",Port,_,Socket,_) -> + NewUri = url_start(Socket) ++ + integer_to_list(Port) ++ "/dummy.html", + "HTTP/1.1 302 See Other \r\n" ++ + "Location:" ++ NewUri ++ "\r\n" ++ + "Content-Length:0\r\n\r\n"; +handle_uri(_,"/303.html",Port,_,Socket,_) -> + NewUri = url_start(Socket) ++ + integer_to_list(Port) ++ "/dummy.html", + Body = "New place", + "HTTP/1.1 303 See Other \r\n" ++ + "Location:" ++ NewUri ++ "\r\n" ++ + "Content-Length:" ++ integer_to_list(length(Body)) + ++ "\r\n\r\n" ++ Body; +handle_uri("HEAD","/307.html",Port,_,Socket,_) -> + NewUri = url_start(Socket) ++ + integer_to_list(Port) ++ "/dummy.html", + "HTTP/1.1 307 Temporary Rediect \r\n" ++ + "Location:" ++ NewUri ++ "\r\n" ++ + "Content-Length:0\r\n\r\n"; +handle_uri(_,"/307.html",Port,_,Socket,_) -> + NewUri = url_start(Socket) ++ + integer_to_list(Port) ++ "/dummy.html", + Body = "New place", + "HTTP/1.1 307 Temporary Rediect \r\n" ++ + "Location:" ++ NewUri ++ "\r\n" ++ + "Content-Length:" ++ integer_to_list(length(Body)) + ++ "\r\n\r\n" ++ Body; + +handle_uri(_,"/500.html",_,_,_,_) -> + "HTTP/1.1 500 Internal Server Error\r\n" ++ + "Content-Length:47\r\n\r\n" ++ + "Internal Server Error"; + +handle_uri(_,"/503.html",_,_,_,DefaultResponse) -> + case ets:lookup(unavailable, 503) of + [{503, unavailable}] -> + ets:insert(unavailable, {503, available}), + "HTTP/1.1 503 Service Unavailable\r\n" ++ + "Retry-After:5\r\n" ++ + "Content-Length:47\r\n\r\n" ++ + "Internal Server Error"; + [{503, available}] -> + DefaultResponse; + [{503, long_unavailable}] -> + "HTTP/1.1 503 Service Unavailable\r\n" ++ + "Retry-After:120\r\n" ++ + "Content-Length:47\r\n\r\n" ++ + "Internal Server Error" + end; + +handle_uri(_,"/redirectloop.html",Port,_,Socket,_) -> + %% Create a potential endless loop! + NewUri = url_start(Socket) ++ + integer_to_list(Port) ++ "/redirectloop.html", + Body = "New place", + "HTTP/1.1 300 Multiple Choices\r\n" ++ + "Location:" ++ NewUri ++ "\r\n" ++ + "Content-Length:" ++ integer_to_list(length(Body)) + ++ "\r\n\r\n" ++ Body; + +handle_uri(_,"/userinfo.html", _,Headers,_, DefaultResponse) -> + Challange = "HTTP/1.1 401 Unauthorized \r\n" ++ + "WWW-Authenticate:Basic" ++"\r\n" ++ + "Content-Length:0\r\n\r\n", + case auth_header(Headers) of + {ok, Value} -> + handle_auth(Value, Challange, DefaultResponse); _ -> - tsp("request_and_verify -> local-server *not* running - skip"), - hard_skip("Local http-server not running") + Challange + end; + +handle_uri(_,"/dummy_headers.html",_,_,Socket,_) -> + %% The client will only care about the Transfer-Encoding + %% header the rest of these headers are left to the + %% user to evaluate. This is not a valid response + %% it only tests that the header handling code works. + Head = "HTTP/1.1 200 ok\r\n" ++ + "Content-Length:32\r\n" ++ + "Pragma:1#no-cache\r\n" ++ + "Via:1.0 fred, 1.1 nowhere.com (Apache/1.1)\r\n" ++ + "Warning:1#pseudonym foobar\r\n" ++ + "Vary:*\r\n" ++ + "Trailer:Other:inets_test\r\n" ++ + "Upgrade:HTTP/2.0\r\n" ++ + "Age:4711\r\n" ++ + "Transfer-Encoding:chunked\r\n" ++ + "Content-Encoding:foo\r\n" ++ + "Content-Language:en\r\n" ++ + "Content-Location:http://www.foobar.se\r\n" ++ + "Content-MD5:104528739076276072743283077410617235478\r\n" + ++ + "Content-Range:Sat, 29 Oct 1994 19:43:31 GMT\r\n" ++ + "Expires:Sat, 29 Oct 1994 19:43:31 GMT\r\n" ++ + "Proxy-Authenticate:#1Basic" ++ + "\r\n\r\n", + send(Socket, Head), + send(Socket, http_chunk:encode("fo")), + send(Socket, http_chunk:encode("obar")), + http_chunk:encode_last(); + +handle_uri(_,"/capital_transfer_encoding.html",_,_,Socket,_) -> + Head = "HTTP/1.1 200 ok\r\n" ++ + "Transfer-Encoding:Chunked\r\n\r\n", + send(Socket, Head), + send(Socket, http_chunk:encode("fo")), + send(Socket, http_chunk:encode("obar")), + http_chunk:encode_last(); + +handle_uri(_,"/cookie.html",_,_,_,_) -> + "HTTP/1.1 200 ok\r\n" ++ + "set-cookie:" ++ "test_cookie=true; path=/;" ++ + "max-age=60000\r\n" ++ + "Content-Length:32\r\n\r\n"++ + "foobar"; + +handle_uri(_,"/missing_crlf.html",_,_,_,_) -> + "HTTP/1.1 200 ok" ++ + "Content-Length:32\r\n" ++ + "foobar"; + +handle_uri(_,"/wrong_statusline.html",_,_,_,_) -> + "ok 200 HTTP/1.1\r\n\r\n" ++ + "Content-Length:32\r\n\r\n" ++ + "foobar"; + +handle_uri(_,"/once_chunked.html",_,_,Socket,_) -> + Head = "HTTP/1.1 200 ok\r\n" ++ + "Transfer-Encoding:Chunked\r\n\r\n", + send(Socket, Head), + send(Socket, http_chunk:encode("fo")), + send(Socket, + http_chunk:encode("obar")), + http_chunk:encode_last(); + +handle_uri(_,"/once.html",_,_,Socket,_) -> + Head = "HTTP/1.1 200 ok\r\n" ++ + "Content-Length:32\r\n\r\n", + send(Socket, Head), + send(Socket, "fo"), + test_server:sleep(1000), + send(Socket, "ob"), + test_server:sleep(1000), + send(Socket, "ar"); + +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(_,"/missing_reason_phrase.html",_,_,_,_) -> + "HTTP/1.1 200\r\n" ++ + "Content-Length: 32\r\n\r\n" + "foobar"; + +handle_uri(_,"/missing_CR.html",_,_,_,_) -> + "HTTP/1.1 200 ok\n" ++ + "Content-Length:32\r\n\n" ++ + "foobar"; + +handle_uri("HEAD",_,_,_,_,_) -> + "HTTP/1.1 200 ok\r\n" ++ + "Content-Length:0\r\n\r\n"; +handle_uri(_,_,_,_,_,DefaultResponse) -> + DefaultResponse. + +url_start(#sslsocket{}) -> + {ok,Host} = inet:gethostname(), + ?TLS_URL_START ++ Host ++ ":"; +url_start(_) -> + {ok,Host} = inet:gethostname(), + ?URL_START ++ Host ++ ":". + +send(#sslsocket{} = S, Msg) -> + ssl:send(S, Msg); +send(S, Msg) -> + gen_tcp:send(S, Msg). + +close(#sslsocket{} = S) -> + ssl:close(S); +close(S) -> + gen_tcp:close(S). + +sockname(#sslsocket{}= S) -> + ssl:sockname(S); +sockname(S) -> + inet:sockname(S). + +receive_streamed_body(RequestId, Body) -> + receive + {http, {RequestId, stream, BinBodyPart}} -> + receive_streamed_body(RequestId, + <>); + {http, {RequestId, stream_end, _Headers}} -> + Body; + {http, Msg} -> + ct:fail(Msg) + end. + +receive_streamed_body(RequestId, Body, Pid) -> + httpc:stream_next(Pid), + ct:print("~p:receive_streamed_body -> requested next stream ~n", [?MODULE]), + receive + {http, {RequestId, stream, BinBodyPart}} -> + receive_streamed_body(RequestId, + <>, + Pid); + {http, {RequestId, stream_end, _Headers}} -> + Body; + {http, Msg} -> + ct:fail(Msg) + end. + +%% ----------------------------------------------------- +%% A sequence number handler +%% The purpose is to be able to pair requests with responses. + +start_sequence_number_server() -> + proc_lib:spawn(fun() -> loop_sequence_number(1) end). + +loop_sequence_number(N) -> + receive + shutdown -> + ok; + {From, get_next} -> + From ! {next_is, N}, + loop_sequence_number(N + 1) + end. + +get_next_sequence_number(SeqNumServer) -> + SeqNumServer ! {self(), get_next}, + receive {next_is, N} -> N end. + +%% ----------------------------------------------------- +%% Client part +%% Sends requests randomly parallel + +run_clients(NumClients, ServerPort, SeqNumServer) -> + {ok,Host} = inet:gethostname(), + set_random_seed(), + lists:map( + fun(Id) -> + Req = lists:flatten(io_lib:format("req~3..0w", [get_next_sequence_number(SeqNumServer)])), + Url = ?URL_START ++ Host ++ ":" ++ integer_to_list(ServerPort) ++ "/" ++ Req, + Pid = proc_lib:spawn( + fun() -> + case httpc:request(Url) of + {ok, {{_,200,_}, _, Resp}} -> + ct:print("[~w] 200 response: " + "~p~n", [Id, Resp]), + case lists:prefix(Req++"->", Resp) of + true -> exit(normal); + false -> exit({bad_resp,Req,Resp}) + end; + {ok, {{_,EC,Reason},_,Resp}} -> + ct:print("[~w] ~w response: " + "~s~n~s~n", + [Id, EC, Reason, Resp]), + exit({bad_resp,Req,Resp}); + Crap -> + ct:print("[~w] bad response: ~p", + [Id, Crap]), + exit({bad_resp, Req, Crap}) + end + end), + MRef = erlang:monitor(process, Pid), + timer:sleep(10 + random:uniform(1334)), + {Id, Pid, MRef} + end, + lists:seq(1, NumClients)). + +wait4clients([], _Timeout) -> + ok; +wait4clients(Clients, Timeout) when Timeout > 0 -> + Time = now_ms(), + 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)); + {'DOWN', _MRef, process, Pid, Reason} -> + {value, {Id, _, _}} = lists:keysearch(Pid, 2, Clients), + ct:fail({bad_client_termination, Id, Reason}) + after Timeout -> + ct:fail({client_timeout, Clients}) + end; +wait4clients(Clients, _) -> + ct:fail({client_timeout, Clients}). + + +%% ----------------------------------------------------- +%% Webserver part: +%% Implements a web server that sends responses one character +%% at a time, with random delays between the characters. + +start_slow_server(SeqNumServer) -> + proc_lib:start( + erlang, apply, [fun() -> init_slow_server(SeqNumServer) end, []]). + +init_slow_server(SeqNumServer) -> + Inet = inet_version(), + {ok, LSock} = gen_tcp:listen(0, [binary, Inet, {packet,0}, {active,true}, + {backlog, 100}]), + {ok, {_IP, Port}} = inet:sockname(LSock), + proc_lib:init_ack({ok, self(), Port}), + loop_slow_server(LSock, SeqNumServer). + +loop_slow_server(LSock, SeqNumServer) -> + Master = self(), + Acceptor = proc_lib:spawn( + fun() -> client_handler(Master, LSock, SeqNumServer) end), + receive + {accepted, Acceptor} -> + loop_slow_server(LSock, SeqNumServer); + shutdown -> + gen_tcp:close(LSock), + exit(Acceptor, kill) end. +%% Handle one client connection +client_handler(Master, LSock, SeqNumServer) -> + {ok, CSock} = gen_tcp:accept(LSock), + Master ! {accepted, self()}, + set_random_seed(), + loop_client(1, CSock, SeqNumServer). + +loop_client(N, CSock, SeqNumServer) -> + %% Await request, don't bother parsing it too much, + %% assuming the entire request arrives in one packet. + receive + {tcp, CSock, Req} -> + ReqNum = parse_req_num(Req), + RespSeqNum = get_next_sequence_number(SeqNumServer), + Response = lists:flatten(io_lib:format("~s->resp~3..0w/~2..0w", [ReqNum, RespSeqNum, N])), + Txt = lists:flatten(io_lib:format("Slow server (~p) got ~p, answering with ~p", + [self(), Req, Response])), + ct:print("~s...~n", [Txt]), + slowly_send_response(CSock, Response), + case parse_connection_type(Req) of + keep_alive -> + ct:print("~s...done~n", [Txt]), + loop_client(N+1, CSock, SeqNumServer); + close -> + ct:print("~s...done (closing)~n", [Txt]), + gen_tcp:close(CSock) + end + end. +slowly_send_response(CSock, Answer) -> + Response = lists:flatten(io_lib:format("HTTP/1.1 200 OK\r\nContent-Length: ~w\r\n\r\n~s", + [length(Answer), Answer])), + lists:foreach( + fun(Char) -> + timer:sleep(random:uniform(500)), + gen_tcp:send(CSock, <>) + end, + Response). -not_implemented_yet() -> - exit(not_implemented_yet). +parse_req_num(Request) -> + Opts = [caseless,{capture,all_but_first,list}], + {match, [ReqNum]} = re:run(Request, "GET /(.*) HTTP", Opts), + ReqNum. -p(F) -> - p(F, []). +parse_connection_type(Request) -> + Opts = [caseless,{capture,all_but_first,list}], + {match,[CType]} = re:run(Request, "connection: *(keep-alive|close)", Opts), + case string:to_lower(CType) of + "close" -> close; + "keep-alive" -> keep_alive + end. -p(F, A) -> - io:format("~p ~w:" ++ F ++ "~n", [self(), ?MODULE | A]). +%% Time in milli seconds +now_ms() -> + {A,B,C} = erlang:now(), + A*1000000000+B*1000+(C div 1000). -tsp(F) -> - inets_test_lib:tsp("[~w]" ++ F, [?MODULE]). -tsp(F, A) -> - inets_test_lib:tsp("[~w]" ++ F, [?MODULE|A]). +set_random_seed() -> + {_, _, Micros} = now(), + A = erlang:phash2([make_ref(), self(), Micros]), + random:seed(A, A, A). -tsf(Reason) -> - test_server:fail(Reason). +otp_8739(doc) -> + ["OTP-8739"]; +otp_8739(suite) -> + []; +otp_8739(Config) when is_list(Config) -> + {_DummyServerPid, Port} = otp_8739_dummy_server(), + {ok,Host} = inet:gethostname(), + URL = ?URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ "/dummy.html", + Method = get, + Request = {URL, []}, + HttpOptions = [{connect_timeout, 500}, {timeout, 1}], + Options = [{sync, true}], + case httpc:request(Method, Request, HttpOptions, Options) of + {error, timeout} -> + %% And now we check the size of the handler db + Info = httpc:info(), + ct:print("Info: ~p", [Info]), + {value, {handlers, Handlers}} = + lists:keysearch(handlers, 1, Info), + case Handlers of + [] -> + ok; + _ -> + ct:fail({unexpected_handlers, Handlers}) + end; + Unexpected -> + ct:fail({unexpected, Unexpected}) + end. -dummy_ssl_server_hang(Caller, IpV, SslOpt) -> - Pid = spawn(httpc_SUITE, dummy_ssl_server_hang_init, [Caller, IpV, SslOpt]), +otp_8739_dummy_server() -> + Parent = self(), + Pid = spawn_link(fun() -> otp_8739_dummy_server_init(Parent) end), receive {port, Port} -> {Pid, Port} end. -dummy_ssl_server_hang_init(Caller, IpV, SslOpt) -> +otp_8739_dummy_server_init(Parent) -> + Inet = inet_version(), {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). + gen_tcp:listen(0, [binary, Inet, {packet, 0}, + {reuseaddr,true}, + {active, false}]), + {ok, Port} = inet:port(ListenSocket), + Parent ! {port, Port}, + otp_8739_dummy_server_main(Parent, ListenSocket). -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 +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 + receive + {tcp_closed, Sock} -> + (catch gen_tcp:close(ListenSocket)), + exit(normal); + {tcp_error, Sock, Reason} -> + ct:fail("socket error: ~p", [Reason]), + (catch gen_tcp:close(ListenSocket)), + exit(normal) + after 10000 -> + %% Just in case + (catch gen_tcp:close(Sock)), + (catch gen_tcp:close(ListenSocket)), + exit(timeout) + end; + Error -> + exit(Error) end. - -hard_skip(Reason) -> - throw(skip(Reason)). - -skip(Reason) -> - {skip, Reason}. diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index 93a878e019..42a5026d72 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2012. All Rights Reserved. +%% Copyright Ericsson AB 2005-2013. 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 @@ -67,97 +67,36 @@ ]). -export([ - pssl_mod_alias/1, essl_mod_alias/1, - - pssl_mod_actions/1, essl_mod_actions/1, - - pssl_mod_security/1, essl_mod_security/1, - - pssl_mod_auth/1, essl_mod_auth/1, - - pssl_mod_auth_api/1, essl_mod_auth_api/1, - - pssl_mod_auth_mnesia_api/1, essl_mod_auth_mnesia_api/1, - - pssl_mod_htaccess/1, essl_mod_htaccess/1, - - pssl_mod_cgi/1, essl_mod_cgi/1, - - pssl_mod_esi/1, essl_mod_esi/1, - - pssl_mod_get/1, essl_mod_get/1, - - pssl_mod_head/1, essl_mod_head/1, - - pssl_mod_all/1, essl_mod_all/1, - - pssl_load_light/1, essl_load_light/1, - - pssl_load_medium/1, essl_load_medium/1, - - pssl_load_heavy/1, essl_load_heavy/1, - - pssl_dos_hostname/1, essl_dos_hostname/1, - - pssl_time_test/1, essl_time_test/1, - - pssl_restart_no_block/1, essl_restart_no_block/1, - - pssl_restart_disturbing_block/1, essl_restart_disturbing_block/1, - - pssl_restart_non_disturbing_block/1, essl_restart_non_disturbing_block/1, - - pssl_block_disturbing_idle/1, essl_block_disturbing_idle/1, - - pssl_block_non_disturbing_idle/1, essl_block_non_disturbing_idle/1, - - pssl_block_503/1, essl_block_503/1, - - pssl_block_disturbing_active/1, essl_block_disturbing_active/1, - - pssl_block_non_disturbing_active/1, essl_block_non_disturbing_active/1, - - pssl_block_disturbing_active_timeout_not_released/1, essl_block_disturbing_active_timeout_not_released/1, - - pssl_block_disturbing_active_timeout_released/1, essl_block_disturbing_active_timeout_released/1, - - pssl_block_non_disturbing_active_timeout_not_released/1, essl_block_non_disturbing_active_timeout_not_released/1, - - pssl_block_non_disturbing_active_timeout_released/1, essl_block_non_disturbing_active_timeout_released/1, - - pssl_block_disturbing_blocker_dies/1, essl_block_disturbing_blocker_dies/1, - - pssl_block_non_disturbing_blocker_dies/1, essl_block_non_disturbing_blocker_dies/1 ]). @@ -242,26 +181,7 @@ groups() -> ip_block_non_disturbing_active_timeout_released, ip_block_disturbing_blocker_dies, ip_block_non_disturbing_blocker_dies]}, - {ssl, [], [{group, pssl}, {group, essl}]}, - {pssl, [], - [pssl_mod_alias, pssl_mod_actions, pssl_mod_security, - pssl_mod_auth, pssl_mod_auth_api, - pssl_mod_auth_mnesia_api, pssl_mod_htaccess, - pssl_mod_cgi, pssl_mod_esi, pssl_mod_get, pssl_mod_head, - pssl_mod_all, pssl_load_light, pssl_load_medium, - pssl_load_heavy, pssl_dos_hostname, pssl_time_test, - pssl_restart_no_block, pssl_restart_disturbing_block, - pssl_restart_non_disturbing_block, - pssl_block_disturbing_idle, - pssl_block_non_disturbing_idle, pssl_block_503, - pssl_block_disturbing_active, - pssl_block_non_disturbing_active, - pssl_block_disturbing_active_timeout_not_released, - pssl_block_disturbing_active_timeout_released, - pssl_block_non_disturbing_active_timeout_not_released, - pssl_block_non_disturbing_active_timeout_released, - pssl_block_disturbing_blocker_dies, - pssl_block_non_disturbing_blocker_dies]}, + {ssl, [], [{group, essl}]}, {essl, [], [essl_mod_alias, essl_mod_actions, essl_mod_security, essl_mod_auth, essl_mod_auth_api, @@ -375,8 +295,8 @@ init_per_testcase(Case, Config) -> init_per_testcase2(Case, Config) -> - tsp("init_per_testcase2 -> entry with" - "~n Config: ~p", [Config]), + %% tsp("init_per_testcase2 -> entry with" + %% "~n Config: ~p", [Config]), IpNormal = integer_to_list(?IP_PORT) ++ ".conf", IpHtaccess = integer_to_list(?IP_PORT) ++ "htaccess.conf", @@ -386,33 +306,33 @@ init_per_testcase2(Case, Config) -> DataDir = ?config(data_dir, Config), SuiteTopDir = ?config(suite_top_dir, Config), - tsp("init_per_testcase2 -> " - "~n SuiteDir: ~p" - "~n DataDir: ~p", [SuiteTopDir, DataDir]), + %% tsp("init_per_testcase2 -> " + %% "~n SuiteDir: ~p" + %% "~n DataDir: ~p", [SuiteTopDir, DataDir]), TcTopDir = filename:join(SuiteTopDir, Case), ?line ok = file:make_dir(TcTopDir), - tsp("init_per_testcase2 -> " - "~n TcTopDir: ~p", [TcTopDir]), + %% tsp("init_per_testcase2 -> " + %% "~n TcTopDir: ~p", [TcTopDir]), DataSrc = filename:join([DataDir, "server_root"]), ServerRoot = filename:join([TcTopDir, "server_root"]), - tsp("init_per_testcase2 -> " - "~n DataSrc: ~p" - "~n ServerRoot: ~p", [DataSrc, ServerRoot]), + %% tsp("init_per_testcase2 -> " + %% "~n DataSrc: ~p" + %% "~n ServerRoot: ~p", [DataSrc, ServerRoot]), ok = file:make_dir(ServerRoot), ok = file:make_dir(filename:join([TcTopDir, "logs"])), NewConfig = [{tc_top_dir, TcTopDir}, {server_root, ServerRoot} | Config], - tsp("init_per_testcase2 -> copy DataSrc to ServerRoot"), + %% tsp("init_per_testcase2 -> copy DataSrc to ServerRoot"), inets_test_lib:copy_dirs(DataSrc, ServerRoot), - tsp("init_per_testcase2 -> fix cgi"), + %% tsp("init_per_testcase2 -> fix cgi"), EnvCGI = filename:join([ServerRoot, "cgi-bin", "printenv.sh"]), {ok, FileInfo} = file:read_file_info(EnvCGI), ok = file:write_file_info(EnvCGI, @@ -432,14 +352,14 @@ init_per_testcase2(Case, Config) -> FileInfo1#file_info{mode = 8#00755}), %% To be used by IP test cases - tsp("init_per_testcase2 -> ip testcase setups"), + %% tsp("init_per_testcase2 -> ip testcase setups"), create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig], normal_access, IpNormal), create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig], mod_htaccess, IpHtaccess), %% To be used by SSL test cases - tsp("init_per_testcase2 -> ssl testcase setups"), + %% tsp("init_per_testcase2 -> ssl testcase setups"), SocketType = case atom_to_list(Case) of [X, $s, $s, $l | _] -> @@ -504,8 +424,8 @@ init_per_testcase2(Case, Config) -> NewConfig end, - tsp("init_per_testcase2 -> done when" - "~n NewConfig2: ~p", [NewConfig2]), + %% tsp("init_per_testcase2 -> done when" + %% "~n NewConfig2: ~p", [NewConfig2]), NewConfig2. @@ -537,6 +457,7 @@ init_per_testcase3(Case, Config) -> Dog = test_server:timetrap(inets_test_lib:minutes(10)), NewConfig = lists:keydelete(watchdog, 1, Config), TcTopDir = ?config(tc_top_dir, Config), + CaseRest = case atom_to_list(Case) of "ip_mod_htaccess" -> @@ -1074,13 +995,6 @@ ip_restart_non_disturbing_block(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -pssl_mod_alias(doc) -> - ["Module test: mod_alias - old SSL config"]; -pssl_mod_alias(suite) -> - []; -pssl_mod_alias(Config) when is_list(Config) -> - ssl_mod_alias(ssl, Config). - essl_mod_alias(doc) -> ["Module test: mod_alias - using new of configure new SSL"]; essl_mod_alias(suite) -> @@ -1097,13 +1011,6 @@ ssl_mod_alias(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_mod_actions(doc) -> - ["Module test: mod_actions - old SSL config"]; -pssl_mod_actions(suite) -> - []; -pssl_mod_actions(Config) when is_list(Config) -> - ssl_mod_actions(ssl, Config). - essl_mod_actions(doc) -> ["Module test: mod_actions - using new of configure new SSL"]; essl_mod_actions(suite) -> @@ -1122,13 +1029,6 @@ ssl_mod_actions(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_mod_security(doc) -> - ["Module test: mod_security - old SSL config"]; -pssl_mod_security(suite) -> - []; -pssl_mod_security(Config) when is_list(Config) -> - ssl_mod_security(ssl, Config). - essl_mod_security(doc) -> ["Module test: mod_security - using new of configure new SSL"]; essl_mod_security(suite) -> @@ -1148,13 +1048,6 @@ ssl_mod_security(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_mod_auth(doc) -> - ["Module test: mod_auth - old SSL config"]; -pssl_mod_auth(suite) -> - []; -pssl_mod_auth(Config) when is_list(Config) -> - ssl_mod_auth(ssl, Config). - essl_mod_auth(doc) -> ["Module test: mod_auth - using new of configure new SSL"]; essl_mod_auth(suite) -> @@ -1172,12 +1065,6 @@ ssl_mod_auth(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_mod_auth_api(doc) -> - ["Module test: mod_auth - old SSL config"]; -pssl_mod_auth_api(suite) -> - []; -pssl_mod_auth_api(Config) when is_list(Config) -> - ssl_mod_auth_api(ssl, Config). essl_mod_auth_api(doc) -> ["Module test: mod_auth - using new of configure new SSL"]; @@ -1198,12 +1085,6 @@ ssl_mod_auth_api(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_mod_auth_mnesia_api(doc) -> - ["Module test: mod_auth_mnesia_api - old SSL config"]; -pssl_mod_auth_mnesia_api(suite) -> - []; -pssl_mod_auth_mnesia_api(Config) when is_list(Config) -> - ssl_mod_auth_mnesia_api(ssl, Config). essl_mod_auth_mnesia_api(doc) -> ["Module test: mod_auth_mnesia_api - using new of configure new SSL"]; @@ -1222,13 +1103,6 @@ ssl_mod_auth_mnesia_api(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_mod_htaccess(doc) -> - ["Module test: mod_htaccess - old SSL config"]; -pssl_mod_htaccess(suite) -> - []; -pssl_mod_htaccess(Config) when is_list(Config) -> - ssl_mod_htaccess(ssl, Config). - essl_mod_htaccess(doc) -> ["Module test: mod_htaccess - using new of configure new SSL"]; essl_mod_htaccess(suite) -> @@ -1246,13 +1120,6 @@ ssl_mod_htaccess(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_mod_cgi(doc) -> - ["Module test: mod_cgi - old SSL config"]; -pssl_mod_cgi(suite) -> - []; -pssl_mod_cgi(Config) when is_list(Config) -> - ssl_mod_cgi(ssl, Config). - essl_mod_cgi(doc) -> ["Module test: mod_cgi - using new of configure new SSL"]; essl_mod_cgi(suite) -> @@ -1275,13 +1142,6 @@ ssl_mod_cgi(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_mod_esi(doc) -> - ["Module test: mod_esi - old SSL config"]; -pssl_mod_esi(suite) -> - []; -pssl_mod_esi(Config) when is_list(Config) -> - ssl_mod_esi(ssl, Config). - essl_mod_esi(doc) -> ["Module test: mod_esi - using new of configure new SSL"]; essl_mod_esi(suite) -> @@ -1299,13 +1159,6 @@ ssl_mod_esi(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_mod_get(doc) -> - ["Module test: mod_get - old SSL config"]; -pssl_mod_get(suite) -> - []; -pssl_mod_get(Config) when is_list(Config) -> - ssl_mod_get(ssl, Config). - essl_mod_get(doc) -> ["Module test: mod_get - using new of configure new SSL"]; essl_mod_get(suite) -> @@ -1323,13 +1176,6 @@ ssl_mod_get(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_mod_head(doc) -> - ["Module test: mod_head - old SSL config"]; -pssl_mod_head(suite) -> - []; -pssl_mod_head(Config) when is_list(Config) -> - ssl_mod_head(ssl, Config). - essl_mod_head(doc) -> ["Module test: mod_head - using new of configure new SSL"]; essl_mod_head(suite) -> @@ -1347,13 +1193,6 @@ ssl_mod_head(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_mod_all(doc) -> - ["All modules test - old SSL config"]; -pssl_mod_all(suite) -> - []; -pssl_mod_all(Config) when is_list(Config) -> - ssl_mod_all(ssl, Config). - essl_mod_all(doc) -> ["All modules test - using new of configure new SSL"]; essl_mod_all(suite) -> @@ -1371,13 +1210,6 @@ ssl_mod_all(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_load_light(doc) -> - ["Test light load - old SSL config"]; -pssl_load_light(suite) -> - []; -pssl_load_light(Config) when is_list(Config) -> - ssl_load_light(ssl, Config). - essl_load_light(doc) -> ["Test light load - using new of configure new SSL"]; essl_load_light(suite) -> @@ -1396,13 +1228,6 @@ ssl_load_light(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_load_medium(doc) -> - ["Test medium load - old SSL config"]; -pssl_load_medium(suite) -> - []; -pssl_load_medium(Config) when is_list(Config) -> - ssl_load_medium(ssl, Config). - essl_load_medium(doc) -> ["Test medium load - using new of configure new SSL"]; essl_load_medium(suite) -> @@ -1427,13 +1252,6 @@ ssl_load_medium(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_load_heavy(doc) -> - ["Test heavy load - old SSL config"]; -pssl_load_heavy(suite) -> - []; -pssl_load_heavy(Config) when is_list(Config) -> - ssl_load_heavy(ssl, Config). - essl_load_heavy(doc) -> ["Test heavy load - using new of configure new SSL"]; essl_load_heavy(suite) -> @@ -1458,12 +1276,6 @@ ssl_load_heavy(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_dos_hostname(doc) -> - ["Denial Of Service (DOS) attack test case - old SSL config"]; -pssl_dos_hostname(suite) -> - []; -pssl_dos_hostname(Config) when is_list(Config) -> - ssl_dos_hostname(ssl, Config). essl_dos_hostname(doc) -> ["Denial Of Service (DOS) attack test case - using new of configure new SSL"]; @@ -1483,12 +1295,6 @@ ssl_dos_hostname(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_time_test(doc) -> - ["old SSL config"]; -pssl_time_test(suite) -> - []; -pssl_time_test(Config) when is_list(Config) -> - ssl_time_test(ssl, Config). essl_time_test(doc) -> ["using new of configure new SSL"]; @@ -1521,13 +1327,6 @@ ssl_time_test(Tag, Config) when is_list(Config) -> %%------------------------------------------------------------------------- -pssl_block_503(doc) -> - ["Check that you will receive status code 503 when the server" - " is blocked and 200 when its not blocked - old SSL config."]; -pssl_block_503(suite) -> - []; -pssl_block_503(Config) when is_list(Config) -> - ssl_block_503(ssl, Config). essl_block_503(doc) -> ["Check that you will receive status code 503 when the server" @@ -1547,15 +1346,6 @@ ssl_block_503(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_block_disturbing_idle(doc) -> - ["Check that you can block/unblock an idle server. The strategy " - "distribing does not really make a difference in this case." - "Old SSL config"]; -pssl_block_disturbing_idle(suite) -> - []; -pssl_block_disturbing_idle(Config) when is_list(Config) -> - ssl_block_disturbing_idle(ssl, Config). - essl_block_disturbing_idle(doc) -> ["Check that you can block/unblock an idle server. The strategy " "distribing does not really make a difference in this case." @@ -1575,15 +1365,6 @@ ssl_block_disturbing_idle(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_block_non_disturbing_idle(doc) -> - ["Check that you can block/unblock an idle server. The strategy " - "non distribing does not really make a difference in this case." - "Old SSL config"]; -pssl_block_non_disturbing_idle(suite) -> - []; -pssl_block_non_disturbing_idle(Config) when is_list(Config) -> - ssl_block_non_disturbing_idle(ssl, Config). - essl_block_non_disturbing_idle(doc) -> ["Check that you can block/unblock an idle server. The strategy " "non distribing does not really make a difference in this case." @@ -1603,15 +1384,6 @@ ssl_block_non_disturbing_idle(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_block_disturbing_active(doc) -> - ["Check that you can block/unblock an active server. The strategy " - "distribing means ongoing requests should be terminated." - "Old SSL config"]; -pssl_block_disturbing_active(suite) -> - []; -pssl_block_disturbing_active(Config) when is_list(Config) -> - ssl_block_disturbing_active(ssl, Config). - essl_block_disturbing_active(doc) -> ["Check that you can block/unblock an active server. The strategy " "distribing means ongoing requests should be terminated." @@ -1631,15 +1403,6 @@ ssl_block_disturbing_active(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_block_non_disturbing_active(doc) -> - ["Check that you can block/unblock an idle server. The strategy " - "non distribing means the ongoing requests should be compleated." - "Old SSL config"]; -pssl_block_non_disturbing_active(suite) -> - []; -pssl_block_non_disturbing_active(Config) when is_list(Config) -> - ssl_block_non_disturbing_active(ssl, Config). - essl_block_non_disturbing_active(doc) -> ["Check that you can block/unblock an idle server. The strategy " "non distribing means the ongoing requests should be compleated." @@ -1659,17 +1422,6 @@ ssl_block_non_disturbing_active(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_block_disturbing_active_timeout_not_released(doc) -> - ["Check that you can block an active server. The strategy " - "distribing means ongoing requests should be compleated" - "if the timeout does not occur." - "Old SSL config"]; -pssl_block_disturbing_active_timeout_not_released(suite) -> - []; -pssl_block_disturbing_active_timeout_not_released(Config) - when is_list(Config) -> - ssl_block_disturbing_active_timeout_not_released(ssl, Config). - essl_block_disturbing_active_timeout_not_released(doc) -> ["Check that you can block an active server. The strategy " "distribing means ongoing requests should be compleated" @@ -1692,17 +1444,6 @@ ssl_block_disturbing_active_timeout_not_released(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_block_disturbing_active_timeout_released(doc) -> - ["Check that you can block an active server. The strategy " - "distribing means ongoing requests should be terminated when" - "the timeout occurs." - "Old SSL config"]; -pssl_block_disturbing_active_timeout_released(suite) -> - []; -pssl_block_disturbing_active_timeout_released(Config) - when is_list(Config) -> - ssl_block_disturbing_active_timeout_released(ssl, Config). - essl_block_disturbing_active_timeout_released(doc) -> ["Check that you can block an active server. The strategy " "distribing means ongoing requests should be terminated when" @@ -1727,16 +1468,6 @@ ssl_block_disturbing_active_timeout_released(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_block_non_disturbing_active_timeout_not_released(doc) -> - ["Check that you can block an active server. The strategy " - "non non distribing means ongoing requests should be completed." - "Old SSL config"]; -pssl_block_non_disturbing_active_timeout_not_released(suite) -> - []; -pssl_block_non_disturbing_active_timeout_not_released(Config) - when is_list(Config) -> - ssl_block_non_disturbing_active_timeout_not_released(ssl, Config). - essl_block_non_disturbing_active_timeout_not_released(doc) -> ["Check that you can block an active server. The strategy " "non non distribing means ongoing requests should be completed." @@ -1760,16 +1491,6 @@ ssl_block_non_disturbing_active_timeout_not_released(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_block_non_disturbing_active_timeout_released(doc) -> - ["Check that you can block an active server. The strategy " - "non distribing means ongoing requests should be completed. " - "When the timeout occurs the block operation sohould be canceled." - "Old SSL config"]; -pssl_block_non_disturbing_active_timeout_released(suite) -> - []; -pssl_block_non_disturbing_active_timeout_released(Config) - when is_list(Config) -> - ssl_block_non_disturbing_active_timeout_released(ssl, Config). essl_block_non_disturbing_active_timeout_released(doc) -> ["Check that you can block an active server. The strategy " @@ -1797,12 +1518,6 @@ ssl_block_non_disturbing_active_timeout_released(Tag, Config) %%------------------------------------------------------------------------- -pssl_block_disturbing_blocker_dies(doc) -> - ["old SSL config"]; -pssl_block_disturbing_blocker_dies(suite) -> - []; -pssl_block_disturbing_blocker_dies(Config) when is_list(Config) -> - ssl_block_disturbing_blocker_dies(ssl, Config). essl_block_disturbing_blocker_dies(doc) -> ["using new of configure new SSL"]; @@ -1821,13 +1536,6 @@ ssl_block_disturbing_blocker_dies(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_block_non_disturbing_blocker_dies(doc) -> - ["old SSL config"]; -pssl_block_non_disturbing_blocker_dies(suite) -> - []; -pssl_block_non_disturbing_blocker_dies(Config) when is_list(Config) -> - ssl_block_non_disturbing_blocker_dies(ssl, Config). - essl_block_non_disturbing_blocker_dies(doc) -> ["using new of configure new SSL"]; essl_block_non_disturbing_blocker_dies(suite) -> @@ -1845,12 +1553,6 @@ ssl_block_non_disturbing_blocker_dies(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_restart_no_block(doc) -> - ["old SSL config"]; -pssl_restart_no_block(suite) -> - []; -pssl_restart_no_block(Config) when is_list(Config) -> - ssl_restart_no_block(ssl, Config). essl_restart_no_block(doc) -> ["using new of configure new SSL"]; @@ -1869,12 +1571,6 @@ ssl_restart_no_block(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_restart_disturbing_block(doc) -> - ["old SSL config"]; -pssl_restart_disturbing_block(suite) -> - []; -pssl_restart_disturbing_block(Config) when is_list(Config) -> - ssl_restart_disturbing_block(ssl, Config). essl_restart_disturbing_block(doc) -> ["using new of configure new SSL"]; @@ -1926,12 +1622,6 @@ ssl_restart_disturbing_block(Tag, Config) -> %%------------------------------------------------------------------------- -pssl_restart_non_disturbing_block(doc) -> - ["old SSL config"]; -pssl_restart_non_disturbing_block(suite) -> - []; -pssl_restart_non_disturbing_block(Config) when is_list(Config) -> - ssl_restart_non_disturbing_block(ssl, Config). essl_restart_non_disturbing_block(doc) -> ["using new of configure new SSL"]; @@ -2324,7 +2014,7 @@ create_config(Config, Access, FileName) -> "~n Type: ~p" "~n Port: ~p" "~n Host: ~p" - "~n", [ServerRoot, TcTopDir, Port, Type, Host]), + "~n", [ServerRoot, TcTopDir, Type, Port, Host]), SSL = if diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl index 387263ce58..075409e2eb 100644 --- a/lib/inets/test/httpd_mod.erl +++ b/lib/inets/test/httpd_mod.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2012. All Rights Reserved. +%% Copyright Ericsson AB 2005-2013. 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 @@ -40,7 +40,6 @@ %%------------------------------------------------------------------------- alias(Type, Port, Host, Node) -> %% This is very crude, but... - tsp("alias -> Has IPv6 support: ~p", [inets_test_lib:has_ipv6_support()]), Opts = [], ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node, "GET /pics/icon.sheet.gif " @@ -85,16 +84,7 @@ actions(Type, Port, Host, Node) -> %%------------------------------------------------------------------------- security(ServerRoot, Type, Port, Host, Node) -> - tsp("security -> " - "entry with" - "~n ServerRoot: ~p" - "~n Type: ~p" - "~n Port: ~p" - "~n Host: ~p" - "~n Node: ~p", [ServerRoot, Type, Port, Host, Node]), - tsp("security -> " - "register - receive security events"), global:register_name(mod_security_test, self()), % Receive events tsp("security -> " @@ -333,12 +323,6 @@ security(ServerRoot, Type, Port, Host, Node) -> %%------------------------------------------------------------------------- auth(Type, Port, Host, Node) -> - tsp("auth -> " - "entry with" - "~n Type: ~p" - "~n Port: ~p" - "~n Host: ~p" - "~n Node: ~p", [Type, Port, Host, Node]), %% Authentication required! ok = httpd_test_lib:verify_request(Type,Host,Port,Node, @@ -750,11 +734,6 @@ htaccess(Type, Port, Host, Node) -> {header, "WWW-Authenticate"}]). %%-------------------------------------------------------------------- cgi(Type, Port, Host, Node) -> -%% tsp("cgi -> entry with" -%% "~n Type: ~p" -%% "~n Port: ~p" -%% "~n Host: ~p" -%% "~n Node: ~p", []), {Script, Script2, Script3} = case test_server:os_type() of {win32, _} -> diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl index 4b33350cf2..f9533e8af9 100644 --- a/lib/inets/test/httpd_test_lib.erl +++ b/lib/inets/test/httpd_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2012. All Rights Reserved. +%% Copyright Ericsson AB 2001-2013. 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 @@ -91,7 +91,7 @@ verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut) when (is_integer(TimeOut) orelse (TimeOut =:= infinity)) -> verify_request(SocketType, Host, Port, [], Node, RequestStr, Options, TimeOut). -verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, TimeOut) -> +verify_request(SocketType, Host, Port, TranspOpts0, Node, RequestStr, Options, TimeOut) -> tsp("verify_request -> entry with" "~n SocketType: ~p" "~n Host: ~p" @@ -100,7 +100,17 @@ verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, Ti "~n Node: ~p" "~n Options: ~p" "~n TimeOut: ~p", - [SocketType, Host, Port, TranspOpts, Node, Options, TimeOut]), + [SocketType, Host, Port, TranspOpts0, Node, Options, TimeOut]), + + %% For now, until we modernize the httpd tests + TranspOpts = + case lists:member(inet6, TranspOpts0) of + true -> + TranspOpts0; + false -> + [inet | TranspOpts0] + end, + try inets_test_lib:connect_bin(SocketType, Host, Port, TranspOpts) of {ok, Socket} -> tsp("verify_request -> connected - now send message"), @@ -293,8 +303,7 @@ validate(RequestStr, #state{status_line = {Version, StatusCode, _}, list_to_integer(Headers#http_response_h.'content-length'), Body). - -%%-------------------------------------------------------------------- +%-------------------------------------------------------------------- %% Internal functions %%------------------------------------------------------------------ check_version(Version, Options) -> diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl index 0f8671b682..3494269d70 100644 --- a/lib/inets/test/inets_test_lib.erl +++ b/lib/inets/test/inets_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2012. All Rights Reserved. +%% Copyright Ericsson AB 2001-2013. 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 @@ -22,26 +22,8 @@ -include("inets_test_lib.hrl"). -include_lib("inets/src/http_lib/http_internal.hrl"). -%% Various small utility functions --export([start_http_server/1, start_http_server/2]). --export([start_http_server_ssl/1, start_http_server_ssl/2]). --export([hostname/0]). --export([connect_bin/3, connect_bin/4, - connect_byte/3, connect_byte/4, - send/3, close/2]). --export([copy_file/3, copy_files/2, copy_dirs/2, del_dirs/1]). --export([info/4, log/4, debug/4, print/4]). --export([timestamp/0, formated_timestamp/0]). --export([tsp/1, tsp/2, tsf/1, tss/1]). --export([check_body/1]). --export([millis/0, millis_diff/2, hours/1, minutes/1, seconds/1, sleep/1]). --export([oscmd/1, has_ipv6_support/0, has_ipv6_support/1, print_system_info/1]). --export([run_on_os/2, run_on_windows/1]). --export([ensure_started/1]). --export([non_pc_tc_maybe_skip/4, os_based_skip/1, skip/3, fail/3]). --export([flush/0]). --export([start_node/1, stop_node/1]). - +%% Note: This directive should only be used in test suites. +-compile(export_all). %% -- Misc os command and stuff @@ -474,7 +456,7 @@ connect_bin(ssl, Host, Port, Opts0) -> Opts = [binary, {packet,0} | Opts0], connect(ssl, Host, Port, Opts); connect_bin(essl, Host, Port, Opts0) -> - Opts = [{ssl_imp, new}, binary, {packet,0}, {reuseaddr, true} | Opts0], + Opts = [{ssl_imp, new}, binary, {packet,0}| Opts0], connect(ssl, Host, Port, Opts); connect_bin(ip_comm, Host, Port, Opts0) -> Opts = [binary, {packet, 0} | Opts0], @@ -494,74 +476,10 @@ connect_byte(ip_comm, Host, Port, Opts0) -> Opts = [{packet,0} | Opts0], connect(ip_comm, Host, Port, Opts). - -%% This always falls back on IPV4, but tries IPV6 first. -connect(Proto, Host, Port, Opts0) -> - Opts = Opts0 -- [inet, inet6], - connect(Proto, Host, Port, Opts ++ [inet6], inet6). - -connect(ssl, Host, Port, Opts, Type) -> - tsp("connect(ssl) -> entry with" - "~n Host: ~p" - "~n Port: ~p" - "~n Opts: ~p" - "~n Type: ~p", [Host, Port, Opts, Type]), - ssl:start(), - %% We ignore this option for ssl... - %% ...maybe we should really treat this in the same way as ip_comm... - case ssl:connect(Host, Port, Opts) of - {ok, Socket} -> - {ok, Socket}; - {error, Reason} when Type =:= inet6 -> - tsp("connect(ssl) -> failed connecting with inet6: " - "~n Reason: ~p" - "~n trying inet", [Reason]), - connect(ssl, Host, Port, Opts -- [inet6], inet); - {error, Reason} -> - tsp("connect(ssl) -> failed connecting: " - "~n Reason: ~p", [Reason]), - {error, Reason}; - Error -> - Error - end; -connect(ip_comm, Host, Port, Opts, Type) -> - tsp("connect(ip_comm) -> entry with" - "~n Host: ~p" - "~n Port: ~p" - "~n Opts: ~p" - "~n Type: ~p", [Host, Port, Opts, Type]), - - case gen_tcp:connect(Host, Port, Opts, timer:seconds(10)) of - {ok, Socket} -> - tsp("connect success"), - {ok, Socket}; - - {error, Reason} when ((Type =:= inet6) andalso - ((Reason =:= timeout) orelse - (Reason =:= nxdomain) orelse - (Reason =:= eafnosupport) orelse - (Reason =:= econnreset) orelse - (Reason =:= enetunreach) orelse - (Reason =:= econnrefused) orelse - (Reason =:= ehostunreach))) -> - tsp("connect(ip_comm) -> Connect error: " - "~n Reason: ~p" - "~n Type: ~p" - "~n Opts: ~p", [Reason, Type, Opts]), - connect(ip_comm, Host, Port, Opts -- [inet6], inet); - - Error -> - tsp("connect(ip_comm) -> Fatal connect error: " - "~n Error: ~p" - "~nwhen" - "~n Host: ~p" - "~n Port: ~p" - "~n Opts: ~p" - "~n Type: ~p" - "~n", [Error, Host, Port, Opts, Type]), - Error - end. - +connect(ip_comm, Host, Port, Opts) -> + gen_tcp:connect(Host, Port, Opts); +connect(ssl, Host, Port, Opts) -> + ssl:connect(Host, Port, Opts). send(ssl, Socket, Data) -> ssl:send(Socket, Data); @@ -651,3 +569,12 @@ format_timestamp({_N1, _N2, N3} = Now) -> [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), lists:flatten(FormatDate). +start_apps(Apps) -> + lists:foreach(fun(App) -> + application:stop(App), + application:start(App) + end, Apps). +stop_apps(Apps) -> + lists:foreach(fun(App) -> + application:stop(App) + end, Apps). diff --git a/lib/inets/test/old_httpc_SUITE.erl b/lib/inets/test/old_httpc_SUITE.erl new file mode 100644 index 0000000000..3d7de71052 --- /dev/null +++ b/lib/inets/test/old_httpc_SUITE.erl @@ -0,0 +1,3602 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2013. 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. +%% +%% %CopyrightEnd% +%% +%% + +%% +%% ts:run(inets, httpc_SUITE, [batch]). +%% + +-module(old_httpc_SUITE). + +-include_lib("common_test/include/ct.hrl"). +-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). + +%% Test server specific exports +-define(IP_PORT, 8998). +-define(SSL_PORT, 8999). +-define(NOT_IN_USE_PORT, 8997). +-define(LOCAL_HOST, {127,0,0,1}). +-define(IPV6_LOCAL_HOST, "0:0:0:0:0:0:0:1"). +-define(URL_START, "http://localhost:"). +-define(SSL_URL_START, "https://localhost:"). +-define(CR, $\r). +-define(LF, $\n). +-define(HTTP_MAX_HEADER_SIZE, 10240). + + +%%-------------------------------------------------------------------- +%% all(Arg) -> [Doc] | [Case] | {skip, Comment} +%% Arg - doc | suite +%% Doc - string() +%% Case - atom() +%% Name of a test case function. +%% Comment - string() +%% Description: Returns documentation/test cases in this test suite +%% or a skip tuple if the platform is not supported. +%%-------------------------------------------------------------------- + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ + 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, + + 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, + %%parse_url, + options, + %%headers_as_is, + selecting_session, + + %%{group, ssl}, + %%{group, stream}, + {group, ipv6}, + {group, tickets}, + initial_server_connect + ]. + +groups() -> + [ + %% {ssl, [], [ssl_head, + %% essl_head, + %% ssl_get, + %% essl_get, + %% ssl_trace, + %% essl_trace]}, + %% {stream, [], [http_stream, + %% http_stream_once]}, + {tickets, [], [%%hexed_query_otp_6191, + %%empty_body_otp_6243, + %%empty_response_header_otp_6830, + %%transfer_encoding_otp_6807, + %%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]}, + {ipv6, [], [ipv6_ipcomm, ipv6_essl]} + ]. + + +init_per_group(ipv6 = _GroupName, Config) -> + case inets_test_lib:has_ipv6_support() of + {ok, _} -> + Config; + _ -> + {skip, "Host does not support IPv6"} + end; +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config) -> Config +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Initiation before the whole suite +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + + ?PRINT_SYSTEM_INFO([]), + + PrivDir = ?config(priv_dir, Config), + DataDir = ?config(data_dir, Config), + ServerRoot = filename:join(PrivDir, "server_root"), + DocRoot = filename:join(ServerRoot, "htdocs"), + IpConfFile = integer_to_list(?IP_PORT) ++ ".conf", + SslConfFile = integer_to_list(?SSL_PORT) ++ ".conf", + + setup_server_dirs(ServerRoot, DocRoot, DataDir), + create_config(IpConfFile, ip_comm, ?IP_PORT, PrivDir, ServerRoot, + DocRoot, DataDir), + create_config(SslConfFile, ssl, ?SSL_PORT, PrivDir, ServerRoot, + DocRoot, DataDir), + + Cgi = case test_server:os_type() of + {win32, _} -> + filename:join([ServerRoot, "cgi-bin", "cgi_echo.exe"]); + _ -> + filename:join([ServerRoot, "cgi-bin", "cgi_echo"]) + end, + + {ok, FileInfo} = file:read_file_info(Cgi), + ok = file:write_file_info(Cgi, FileInfo#file_info{mode = 8#00755}), + + [{has_ipv6_support, inets_test_lib:has_ipv6_support()}, + {server_root, ServerRoot}, + {doc_root, DocRoot}, + {local_port, ?IP_PORT}, + {local_ssl_port, ?SSL_PORT} | Config]. + + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config) -> _ +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after the whole suite +%%-------------------------------------------------------------------- +end_per_suite(Config) -> + PrivDir = ?config(priv_dir, Config), + inets_test_lib:del_dirs(PrivDir), + application:stop(inets), + application:stop(ssl), + ok. + + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(Case, Config) -> Config +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Initiation before each test case +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- + +init_per_testcase(otp_8154_1 = Case, Config) -> + init_per_testcase(Case, 5, Config); + +init_per_testcase(initial_server_connect = Case, Config) -> + %% Try to check if crypto actually exist or not, + %% this test case does not work unless it does + try + begin + ?ENSURE_STARTED([crypto, public_key, ssl]), + inets:start(), + Config + end + catch + throw:{error, {failed_starting, App, ActualError}} -> + tsp("init_per_testcase(~w) -> failed starting ~w: " + "~n ~p", [Case, App, ActualError]), + SkipString = + "Could not start " ++ atom_to_list(App), + skip(SkipString); + _:X -> + SkipString = + lists:flatten( + io_lib:format("Failed starting apps: ~p", [X])), + skip(SkipString) + end; + +init_per_testcase(Case, Config) -> + init_per_testcase(Case, 2, Config). + +init_per_testcase(Case, Timeout, Config) -> + io:format(user, + "~n~n*** INIT ~w:~w[~w] ***" + "~n~n", [?MODULE, Case, Timeout]), + + PrivDir = ?config(priv_dir, Config), + application:stop(inets), + Dog = test_server:timetrap(inets_test_lib:minutes(Timeout)), + TmpConfig = lists:keydelete(watchdog, 1, Config), + IpConfFile = integer_to_list(?IP_PORT) ++ ".conf", + SslConfFile = integer_to_list(?SSL_PORT) ++ ".conf", + + %% inets:enable_trace(max, io, httpd), + %% inets:enable_trace(max, io, httpc), + %% inets:enable_trace(max, io, all), + + NewConfig = + case atom_to_list(Case) of + [$s, $s, $l | _] -> + ?ENSURE_STARTED([crypto, public_key, ssl]), + init_per_testcase_ssl(ssl, PrivDir, SslConfFile, + [{watchdog, Dog} | TmpConfig]); + + [$e, $s, $s, $l | _] -> + ?ENSURE_STARTED([crypto, public_key, ssl]), + init_per_testcase_ssl(essl, PrivDir, SslConfFile, + [{watchdog, Dog} | TmpConfig]); + + "ipv6_" ++ _Rest -> + %% Ensure needed apps (crypto, public_key and ssl) are started + try ?ENSURE_STARTED([crypto, public_key, ssl]) of + ok -> + Profile = ipv6, + %% A stand-alone profile is represented by a pid() + {ok, ProfilePid} = + inets:start(httpc, + [{profile, Profile}, + {data_dir, PrivDir}], stand_alone), + ok = httpc:set_options([{ipfamily, inet6}], + ProfilePid), + tsp("httpc profile pid: ~p", [ProfilePid]), + [{watchdog, Dog}, {profile, ProfilePid}| TmpConfig] + catch + throw:{error, {failed_starting, App, ActualError}} -> + tsp("init_per_testcase(~w) -> failed starting ~w: " + "~n ~p", [Case, App, ActualError]), + SkipString = + "Could not start " ++ atom_to_list(App), + skip(SkipString); + _:X -> + SkipString = + lists:flatten( + io_lib:format("Failed starting apps: ~p", [X])), + skip(SkipString) + end; + + _ -> + %% Try inet6fb4 on windows... + %% No need? Since it is set above? + + %% tsp("init_per_testcase -> allways try IPv6 on windows"), + %% ?RUN_ON_WINDOWS( + %% fun() -> + %% tsp("init_per_testcase:set_options_fun -> " + %% "set-option ipfamily to inet6fb4"), + %% Res = httpc:set_options([{ipfamily, inet6fb4}]), + %% tsp("init_per_testcase:set_options_fun -> " + %% "~n Res: ~p", [Res]), + %% Res + %% end), + + TmpConfig2 = lists:keydelete(local_server, 1, TmpConfig), + %% Will start inets + tsp("init_per_testcase -> try start server"), + Server = start_http_server(PrivDir, IpConfFile), + [{watchdog, Dog}, {local_server, Server} | TmpConfig2] + end, + + %% + %% Set default ipfamily to the same as the main server has by default + %% This makes the client try w/ ipv6 before falling back to ipv4, + %% as that is what the server is configured to do. + %% Note that this is required for the tests to run on *BSD w/ ipv6 enabled + %% as well as on Windows. The Linux behaviour of allowing ipv4 connects + %% to ipv6 sockets is not required or even encouraged. + + tsp("init_per_testcase -> Options before ipfamily set: ~n~p", + [httpc:get_options(all)]), + ok = httpc:set_options([{ipfamily, inet6fb4}]), + tsp("init_per_testcase -> Options after ipfamily set: ~n~p", + [httpc:get_options(all)]), + + %% Note that the IPv6 test case(s) *must* use inet6, + %% so this value will be overwritten (see "ipv6_" below). + %% + + %% inets:enable_trace(max, io, all), + %% snmp:set_trace([gen_tcp]), + tsp("init_per_testcase(~w) -> done when" + "~n NewConfig: ~p" + "~n~n", [Case, NewConfig]), + NewConfig. + + +init_per_testcase_ssl(Tag, PrivDir, SslConfFile, Config) -> + tsp("init_per_testcase_ssl(~w) -> stop ssl", [Tag]), + application:stop(ssl), + Config2 = lists:keydelete(local_ssl_server, 1, Config), + %% Will start inets + tsp("init_per_testcase_ssl(~w) -> try start http server (including inets)", + [Tag]), + Server = inets_test_lib:start_http_server( + filename:join(PrivDir, SslConfFile), Tag), + tsp("init_per_testcase(~w) -> Server: ~p", [Tag, Server]), + [{local_ssl_server, Server} | Config2]. + +start_http_server(ConfDir, ConfFile) -> + inets_test_lib:start_http_server( filename:join(ConfDir, ConfFile) ). + + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(Case, Config) -> _ +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after each test case +%%-------------------------------------------------------------------- +end_per_testcase(http_save_to_file = Case, Config) -> + io:format(user, "~n~n*** END ~w:~w ***~n~n", + [?MODULE, Case]), + PrivDir = ?config(priv_dir, Config), + FullPath = filename:join(PrivDir, "dummy.html"), + file:delete(FullPath), + finish(Config); + +end_per_testcase(Case, Config) -> + io:format(user, "~n~n*** END ~w:~w ***~n~n", + [?MODULE, Case]), + case atom_to_list(Case) of + "ipv6_" ++ _Rest -> + tsp("end_per_testcase(~w) -> stop ssl", [Case]), + application:stop(ssl), + tsp("end_per_testcase(~w) -> stop public_key", [Case]), + application:stop(public_key), + tsp("end_per_testcase(~w) -> stop crypto", [Case]), + application:stop(crypto), + ProfilePid = ?config(profile, Config), + tsp("end_per_testcase(~w) -> stop httpc profile (~p)", + [Case, ProfilePid]), + unlink(ProfilePid), + inets:stop(stand_alone, ProfilePid), + tsp("end_per_testcase(~w) -> httpc profile (~p) stopped", + [Case, ProfilePid]), + ok; + _ -> + ok + end, + finish(Config). + +finish(Config) -> + Dog = ?config(watchdog, Config), + case Dog of + undefined -> + ok; + _ -> + tsp("finish -> stop watchdog (~p)", [Dog]), + test_server:timetrap_cancel(Dog) + end. + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- + + + +%%------------------------------------------------------------------------- + +http_options(doc) -> + ["Test http options request against local server."]; +http_options(suite) -> + []; +http_options(Config) when is_list(Config) -> + skip("Not supported by httpd"). + +http_head(doc) -> + ["Test http head request against local server."]; +http_head(suite) -> + []; +http_head(Config) when is_list(Config) -> + tsp("http_head -> entry with" + "~n Config: ~p", [Config]), + Method = head, + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + Request = {URL, []}, + HttpOpts = [], + Opts = [], + VerifyResult = + fun({ok, {{_,200,_}, [_ | _], []}}) -> + ok; + ({ok, UnexpectedReply}) -> + tsp("http_head:verify_fun -> Unexpected Reply: " + "~n ~p", [UnexpectedReply]), + tsf({unexpected_reply, UnexpectedReply}); + ({error, Reason} = Error) -> + tsp("http_head:verify_fun -> Error reply: " + "~n Reason: ~p", [Reason]), + tsf({bad_reply, Error}) + end, + simple_request_and_verify(Config, + Method, Request, HttpOpts, Opts, VerifyResult). + + +%%------------------------------------------------------------------------- + +http_get(doc) -> + ["Test http get request against local server"]; +http_get(suite) -> + []; +http_get(Config) when is_list(Config) -> + tsp("http_get -> entry with" + "~n Config: ~p", [Config]), + case ?config(local_server, Config) of + ok -> + tsp("local-server running"), + Method = get, + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + Request = {URL, []}, + Timeout = timer:seconds(1), + ConnTimeout = Timeout + timer:seconds(1), + HttpOptions1 = [{timeout, Timeout}, + {connect_timeout, ConnTimeout}], + Options1 = [], + Body = + case httpc:request(Method, Request, HttpOptions1, Options1) of + {ok, {{_,200,_}, [_ | _], ReplyBody = [_ | _]}} -> + ReplyBody; + {ok, UnexpectedReply1} -> + tsf({unexpected_reply, UnexpectedReply1}); + {error, _} = Error1 -> + tsf({bad_reply, Error1}) + end, + + %% eqvivivalent to httpc:request(get, {URL, []}, [], []), + inets_test_lib:check_body(Body), + + HttpOptions2 = [], + Options2 = [{body_format, binary}], + case httpc:request(Method, Request, HttpOptions2, Options2) of + {ok, {{_,200,_}, [_ | _], Bin}} when is_binary(Bin) -> + ok; + {ok, {{_,200,_}, [_ | _], BadBin}} -> + tsf({body_format_not_binary, BadBin}); + {ok, UnexpectedReply2} -> + tsf({unexpected_reply, UnexpectedReply2}); + {error, _} = Error2 -> + tsf({bad_reply, Error2}) + end; + _ -> + skip("Failed to start local http-server") + end. + +%%------------------------------------------------------------------------- + +http_post(doc) -> + ["Test http post request against local server. We do in this case " + "only care about the client side of the the post. The server " + "script will not actually use the post data."]; +http_post(suite) -> + []; +http_post(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 + Body = lists:duplicate(100, "1"), + + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + httpc:request(post, {URL, [{"expect","100-continue"}], + "text/plain", Body}, [], []), + + {ok, {{_,504,_}, [_ | _], []}} = + httpc:request(post, {URL, [{"expect","100-continue"}], + "text/plain", "foobar"}, [], []); + _ -> + skip("Failed to start local http-server") + 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) -> + []; +http_emulate_lower_versions(Config) when is_list(Config) -> + case ?config(local_server, Config) of + ok -> + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + {ok, Body0} = + httpc:request(get, {URL, []}, [{version, "HTTP/0.9"}], []), + inets_test_lib:check_body(Body0), + {ok, {{"HTTP/1.0", 200, _}, [_ | _], Body1 = [_ | _]}} = + httpc:request(get, {URL, []}, [{version, "HTTP/1.0"}], []), + inets_test_lib:check_body(Body1), + {ok, {{"HTTP/1.1", 200, _}, [_ | _], Body2 = [_ | _]}} = + httpc:request(get, {URL, []}, [{version, "HTTP/1.1"}], []), + inets_test_lib:check_body(Body2); + _-> + skip("Failed to start local http-server") + end. + + +%%------------------------------------------------------------------------- + +http_relaxed(doc) -> + ["Test relaxed mode"]; +http_relaxed(suite) -> + []; +http_relaxed(Config) when is_list(Config) -> + ok = httpc:set_options([{ipv6, disabled}]), % also test the old option + %% ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), + + URL = ?URL_START ++ integer_to_list(Port) ++ + "/missing_reason_phrase.html", + + {error, Reason} = + httpc:request(get, {URL, []}, [{relaxed, false}], []), + + test_server:format("Not relaxed: ~p~n", [Reason]), + + {ok, {{_, 200, _}, [_ | _], [_ | _]}} = + httpc:request(get, {URL, []}, [{relaxed, true}], []), + + DummyServerPid ! stop, + ok = httpc:set_options([{ipv6, enabled}]), + %% ok = httpc:set_options([{ipfamily, inet6fb4}]), + ok. + + +%%------------------------------------------------------------------------- +http_dummy_pipe(doc) -> + ["Test pipelining code."]; +http_dummy_pipe(suite) -> + []; +http_dummy_pipe(Config) when is_list(Config) -> + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), + + URL = ?URL_START ++ integer_to_list(Port) ++ "/foobar.html", + + test_pipeline(URL), + + DummyServerPid ! stop, + ok = httpc:set_options([{ipfamily, inet6fb4}]), + ok. + +http_inets_pipe(doc) -> + ["Test pipelining code."]; +http_inets_pipe(suite) -> + []; +http_inets_pipe(Config) when is_list(Config) -> + + case ?config(local_server, Config) of + ok -> + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + test_pipeline(URL); + _ -> + skip("Failed to start local http-server") + end. + + +test_pipeline(URL) -> + p("test_pipeline -> entry with" + "~n URL: ~p", [URL]), + + httpc:set_options([{pipeline_timeout, 50000}, + {max_pipeline_length, 5}]), + + p("test_pipeline -> issue (async) request 1" + "~n when profile info: ~p", [httpc:info()]), + {ok, RequestIdA1} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + tsp("RequestIdA1: ~p", [RequestIdA1]), + p("test_pipeline -> RequestIdA1: ~p" + "~n when profile info: ~p", [RequestIdA1, httpc:info()]), + + %% Make sure pipeline is initiated + p("test_pipeline -> sleep some", []), + test_server:sleep(4000), + + p("test_pipeline -> issue (async) request A2, A3 and A4" + "~n when profile info: ~p", [httpc:info()]), + {ok, RequestIdA2} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + {ok, RequestIdA3} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + {ok, RequestIdA4} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + tsp("RequestIdAs => A2: ~p, A3: ~p and A4: ~p", + [RequestIdA2, RequestIdA3, RequestIdA4]), + p("test_pipeline -> RequestIds => A2: ~p, A3: ~p and A4: ~p" + "~n when profile info: ~p", + [RequestIdA2, RequestIdA3, RequestIdA4, httpc:info()]), + + p("test_pipeline -> issue (sync) request 3"), + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + httpc:request(get, {URL, []}, [], []), + + p("test_pipeline -> expect reply for (async) request A1, A2, A3 and A4" + "~n when profile info: ~p", [httpc:info()]), + pipeline_await_async_reply([{RequestIdA1, a1, 200}, + {RequestIdA2, a2, 200}, + {RequestIdA3, a3, 200}, + {RequestIdA4, a4, 200}], ?MINS(2)), + + p("test_pipeline -> sleep some" + "~n when profile info: ~p", [httpc:info()]), + test_server:sleep(4000), + + p("test_pipeline -> issue (async) request B1, B2, B3 and B4" + "~n when profile info: ~p", [httpc:info()]), + {ok, RequestIdB1} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + {ok, RequestIdB2} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + {ok, RequestIdB3} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + {ok, RequestIdB4} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + tsp("RequestIdBs => B1: ~p, B2: ~p, B3: ~p and B4: ~p", + [RequestIdB1, RequestIdB2, RequestIdB3, RequestIdB4]), + p("test_pipeline -> RequestIdBs => B1: ~p, B2: ~p, B3: ~p and B4: ~p" + "~n when profile info: ~p", + [RequestIdB1, RequestIdB2, RequestIdB3, RequestIdB4, httpc:info()]), + + p("test_pipeline -> cancel (async) request B2" + "~n when profile info: ~p", [httpc:info()]), + ok = httpc:cancel_request(RequestIdB2), + + p("test_pipeline -> " + "expect *no* reply for cancelled (async) request B2 (for 3 secs)" + "~n when profile info: ~p", [httpc:info()]), + receive + {http, {RequestIdB2, _}} -> + tsf(http_cancel_request_failed) + after 3000 -> + ok + end, + + p("test_pipeline -> expect reply for (async) request B1, B3 and B4" + "~n when profile info: ~p", [httpc:info()]), + Bodies = pipeline_await_async_reply([{RequestIdB1, b1, 200}, + {RequestIdB3, b3, 200}, + {RequestIdB4, b4, 200}], ?MINS(1)), + [{b1, Body}|_] = Bodies, + + p("test_pipeline -> check reply for (async) request B1" + "~n when profile info: ~p", [httpc:info()]), + inets_test_lib:check_body(binary_to_list(Body)), + + p("test_pipeline -> ensure no unexpected incomming" + "~n when profile info: ~p", [httpc:info()]), + receive + {http, Any} -> + tsf({unexpected_message, Any}) + after 500 -> + ok + end, + + p("test_pipeline -> done" + "~n when profile info: ~p", [httpc:info()]), + ok. + +pipeline_await_async_reply(ReqIds, Timeout) -> + pipeline_await_async_reply(ReqIds, Timeout, []). + +pipeline_await_async_reply([], _, Acc) -> + lists:keysort(1, Acc); +pipeline_await_async_reply(ReqIds, Timeout, Acc) when Timeout > 0 -> + T1 = inets_test_lib:timestamp(), + p("pipeline_await_async_reply -> await replies" + "~n ReqIds: ~p" + "~n Timeout: ~p", [ReqIds, Timeout]), + receive + {http, {RequestId, {{_, Status, _}, _, Body}}} -> + p("pipeline_await_async_reply -> received reply for" + "~n RequestId: ~p" + "~n Status: ~p", [RequestId, Status]), + case lists:keysearch(RequestId, 1, ReqIds) of + {value, {RequestId, N, Status}} -> + p("pipeline_await_async_reply -> " + "found expected request ~w", [N]), + ReqIds2 = lists:keydelete(RequestId, 1, ReqIds), + NewTimeout = Timeout - (inets_test_lib:timestamp()-T1), + pipeline_await_async_reply(ReqIds2, NewTimeout, + [{N, Body} | Acc]); + {value, {RequestId, N, WrongStatus}} -> + p("pipeline_await_async_reply -> " + "found request ~w with wrong status", [N]), + tsf({reply_with_unexpected_status, + {RequestId, N, WrongStatus}}); + false -> + tsf({unexpected_reply, {RequestId, Status}}) + end; + {http, Msg} -> + tsf({unexpected_reply, Msg}) + after Timeout -> + receive + Any -> + tsp("pipeline_await_async_reply -> " + "received unknown data after timeout: " + "~n ~p", [Any]), + tsf({timeout, {unknown, Any}}) + end + end; +pipeline_await_async_reply(ReqIds, _, Acc) -> + tsp("pipeline_await_async_reply -> " + "timeout: " + "~n ~p" + "~nwhen" + "~n ~p", [ReqIds, Acc]), + tsf({timeout, ReqIds, Acc}). + + + +%%------------------------------------------------------------------------- +http_trace(doc) -> + ["Perform a TRACE request."]; +http_trace(suite) -> + []; +http_trace(Config) when is_list(Config) -> + case ?config(local_server, Config) of + ok -> + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + case httpc:request(trace, {URL, []}, [], []) of + {ok, {{_,200,_}, [_ | _], "TRACE /dummy.html" ++ _}} -> + ok; + {ok, {{_,200,_}, [_ | _], WrongBody}} -> + tsf({wrong_body, WrongBody}); + {ok, WrongReply} -> + tsf({wrong_reply, WrongReply}); + Error -> + tsf({failed, Error}) + end; + _ -> + skip("Failed to start local http-server") + end. +%%------------------------------------------------------------------------- +http_async(doc) -> + ["Test an asynchrony http request."]; +http_async(suite) -> + []; +http_async(Config) when is_list(Config) -> + case ?config(local_server, Config) of + ok -> + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + {ok, RequestId} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + + Body = + receive + {http, {RequestId, {{_, 200, _}, _, BinBody}}} -> + BinBody; + {http, Msg} -> + tsf(Msg) + end, + + inets_test_lib:check_body(binary_to_list(Body)), + + {ok, NewRequestId} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + ok = httpc:cancel_request(NewRequestId), + receive + {http, {NewRequestId, _NewResult}} -> + tsf(http_cancel_request_failed) + after 3000 -> + ok + end; + _ -> + skip("Failed to start local http-server") + end. + +%%------------------------------------------------------------------------- +http_save_to_file(doc) -> + ["Test to save the http body to a file"]; +http_save_to_file(suite) -> + []; +http_save_to_file(Config) when is_list(Config) -> + case ?config(local_server, Config) of + ok -> + PrivDir = ?config(priv_dir, Config), + FilePath = filename:join(PrivDir, "dummy.html"), + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + {ok, saved_to_file} + = httpc:request(get, {URL, []}, [], [{stream, FilePath}]), + {ok, Bin} = file:read_file(FilePath), + {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL), + Bin == Body; + _ -> + skip("Failed to start local http-server") + end. + + +%%------------------------------------------------------------------------- +http_save_to_file_async(doc) -> + ["Test to save the http body to a file"]; +http_save_to_file_async(suite) -> + []; +http_save_to_file_async(Config) when is_list(Config) -> + case ?config(local_server, Config) of + ok -> + PrivDir = ?config(priv_dir, Config), + FilePath = filename:join(PrivDir, "dummy.html"), + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + {ok, RequestId} = httpc:request(get, {URL, []}, [], + [{stream, FilePath}, + {sync, false}]), + receive + {http, {RequestId, saved_to_file}} -> + ok; + {http, Msg} -> + tsf(Msg) + end, + + {ok, Bin} = file:read_file(FilePath), + {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL), + Bin == Body; + _ -> + skip("Failed to start local http-server") + end. +%%------------------------------------------------------------------------- +http_headers(doc) -> + ["Use as many request headers as possible not used in proxy_headers"]; +http_headers(suite) -> + []; +http_headers(Config) when is_list(Config) -> + + case ?config(local_server, Config) of + ok -> + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + DocRoot = ?config(doc_root, Config), + {ok, FileInfo} = + file:read_file_info(filename:join([DocRoot,"dummy.html"])), + CreatedSec = + calendar:datetime_to_gregorian_seconds( + FileInfo#file_info.mtime), + + Mod = httpd_util:rfc1123_date( + calendar:gregorian_seconds_to_datetime( + CreatedSec-1)), + + Date = httpd_util:rfc1123_date({date(), time()}), + + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + httpc:request(get, {URL, [{"If-Modified-Since", + Mod}, + {"From","webmaster@erlang.se"}, + {"Date", Date} + ]}, [], []), + + Mod1 = httpd_util:rfc1123_date( + calendar:gregorian_seconds_to_datetime( + CreatedSec+1)), + + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + httpc:request(get, {URL, [{"If-UnModified-Since", + Mod1} + ]}, [], []), + + Tag = httpd_util:create_etag(FileInfo), + + + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + httpc:request(get, {URL, [{"If-Match", + Tag} + ]}, [], []), + + {ok, {{_,200,_}, [_ | _], _}} = + httpc:request(get, {URL, [{"If-None-Match", + "NotEtag,NeihterEtag"}, + {"Connection", "Close"} + ]}, [], []), + ok; + _ -> + skip("Failed to start local http-server") + end. + +%%------------------------------------------------------------------------- +http_headers_dummy(doc) -> + ["Test the code for handling headers we do not want/can send " + "to a real server. Note it is not logical to send" + "all of these headers together, we only want to test that" + "the code for handling headers will not crash."]; +http_headers_dummy(suite) -> + []; +http_headers_dummy(Config) when is_list(Config) -> + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), + + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy_headers.html", + + Foo = http_chunk:encode("foobar") ++ + binary_to_list(http_chunk:encode_last()), + FooBar = Foo ++ "\r\n\r\nOther:inets_test\r\n\r\n", + + UserPasswd = base64:encode_to_string("Alladin:Sesame"), + Auth = "Basic " ++ UserPasswd, + + %% The dummy server will ignore the headers, we only want to test + %% that the client header-handling code. This would not + %% be a vaild http-request! + {ok, {{_,200,_}, [_ | _], [_|_]}} = + httpc:request(post, + {URL, + [{"Via", + "1.0 fred, 1.1 nowhere.com (Apache/1.1)"}, + {"Warning","1#pseudonym foobar"}, + {"Vary","*"}, + {"Upgrade","HTTP/2.0"}, + {"Pragma", "1#no-cache"}, + {"Cache-Control", "no-cache"}, + {"Connection", "close"}, + {"Date", "Sat, 29 Oct 1994 19:43:31 GMT"}, + {"Accept", " text/plain; q=0.5, text/html"}, + {"Accept-Language", "en"}, + {"Accept-Encoding","chunked"}, + {"Accept-Charset", "ISO8859-1"}, + {"Authorization", Auth}, + {"Expect", "1#100-continue"}, + {"User-Agent","inets"}, + {"Transfer-Encoding","chunked"}, + {"Range", " bytes=0-499"}, + {"If-Range", "Sat, 29 Oct 1994 19:43:31 GMT"}, + {"If-Match", "*"}, + {"Content-Type", "text/plain"}, + {"Content-Encoding", "chunked"}, + {"Content-Length", "6"}, + {"Content-Language", "en"}, + {"Content-Location", "http://www.foobar.se"}, + {"Content-MD5", + "104528739076276072743283077410617235478"}, + {"Content-Range", "bytes 0-499/1234"}, + {"Allow", "GET"}, + {"Proxy-Authorization", Auth}, + {"Expires", "Sat, 29 Oct 1994 19:43:31 GMT"}, + {"Upgrade", "HTTP/2.0"}, + {"Last-Modified", "Sat, 29 Oct 1994 19:43:31 GMT"}, + {"Trailer","1#User-Agent"} + ], "text/plain", FooBar}, + [], []), + DummyServerPid ! stop, + ok = httpc:set_options([{ipfamily, inet6fb4}]), + ok. + + +%%------------------------------------------------------------------------- +http_bad_response(doc) -> + ["Test what happens when the server does not follow the protocol"]; +http_bad_response(suite) -> + []; +http_bad_response(Config) when is_list(Config) -> + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), + + URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_crlf.html", + + URL1 = ?URL_START ++ integer_to_list(Port) ++ "/wrong_statusline.html", + + {error, timeout} = httpc:request(get, {URL, []}, [{timeout, 400}], []), + + {error, Reason} = httpc:request(URL1), + + test_server:format("Wrong Statusline: ~p~n", [Reason]), + + DummyServerPid ! stop, + ok = httpc:set_options([{ipfamily, inet6fb4}]), + ok. + + +%%------------------------------------------------------------------------- +ssl_head(doc) -> + ["Same as http_head/1 but over ssl sockets."]; +ssl_head(suite) -> + []; +ssl_head(Config) when is_list(Config) -> + ssl_head(ssl, Config). + +essl_head(doc) -> + ["Same as http_head/1 but over ssl sockets."]; +essl_head(suite) -> + []; +essl_head(Config) when is_list(Config) -> + ssl_head(essl, Config). + +ssl_head(SslTag, Config) -> + tsp("ssl_head -> entry with" + "~n SslTag: ~p" + "~n Config: ~p", [SslTag, Config]), + case ?config(local_ssl_server, Config) of + ok -> + DataDir = ?config(data_dir, Config), + Port = ?config(local_ssl_port, Config), + URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html", + CertFile = filename:join(DataDir, "ssl_client_cert.pem"), + SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], + SSLConfig = + case SslTag of + ssl -> + SSLOptions; + essl -> + {essl, SSLOptions} + end, + tsp("ssl_head -> make request using: " + "~n URL: ~p" + "~n SslTag: ~p" + "~n SSLOptions: ~p", [URL, SslTag, SSLOptions]), + {ok, {{_,200, _}, [_ | _], []}} = + httpc:request(head, {URL, []}, [{ssl, SSLConfig}], []); + {ok, _} -> + skip("local http-server not started"); + _ -> + skip("SSL not started") + end. + + +%%------------------------------------------------------------------------- +ssl_get(doc) -> + ["Same as http_get/1 but over ssl sockets."]; +ssl_get(suite) -> + []; +ssl_get(Config) when is_list(Config) -> + ssl_get(ssl, Config). + +essl_get(doc) -> + ["Same as http_get/1 but over ssl sockets."]; +essl_get(suite) -> + []; +essl_get(Config) when is_list(Config) -> + ssl_get(essl, Config). + +ssl_get(SslTag, Config) when is_list(Config) -> + case ?config(local_ssl_server, Config) of + ok -> + DataDir = ?config(data_dir, Config), + Port = ?config(local_ssl_port, Config), + URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html", + CertFile = filename:join(DataDir, "ssl_client_cert.pem"), + SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], + SSLConfig = + case SslTag of + ssl -> + SSLOptions; + essl -> + {essl, SSLOptions} + end, + tsp("ssl_get -> make request using: " + "~n URL: ~p" + "~n SslTag: ~p" + "~n SSLOptions: ~p", [URL, SslTag, SSLOptions]), + case httpc:request(get, {URL, []}, [{ssl, SSLConfig}], []) of + {ok, {{_,200, _}, [_ | _], Body = [_ | _]}} -> + inets_test_lib:check_body(Body), + ok; + {ok, {StatusLine, Headers, _Body}} -> + tsp("ssl_get -> unexpected result: " + "~n StatusLine: ~p" + "~n Headers: ~p", [StatusLine, Headers]), + tsf({unexpected_response, StatusLine, Headers}); + {error, Reason} -> + tsp("ssl_get -> request failed: " + "~n Reason: ~p", [Reason]), + tsf({request_failed, Reason}) + end; + {ok, _} -> + skip("local http-server not started"); + _ -> + skip("SSL not started") + end. + + +%%------------------------------------------------------------------------- +ssl_trace(doc) -> + ["Same as http_trace/1 but over ssl sockets."]; +ssl_trace(suite) -> + []; +ssl_trace(Config) when is_list(Config) -> + ssl_trace(ssl, Config). + +essl_trace(doc) -> + ["Same as http_trace/1 but over ssl sockets."]; +essl_trace(suite) -> + []; +essl_trace(Config) when is_list(Config) -> + ssl_trace(essl, Config). + +ssl_trace(SslTag, Config) when is_list(Config) -> + case ?config(local_ssl_server, Config) of + ok -> + DataDir = ?config(data_dir, Config), + Port = ?config(local_ssl_port, Config), + URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html", + CertFile = filename:join(DataDir, "ssl_client_cert.pem"), + SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], + SSLConfig = + case SslTag of + ssl -> + SSLOptions; + essl -> + {essl, SSLOptions} + end, + tsp("ssl_trace -> make request using: " + "~n URL: ~p" + "~n SslTag: ~p" + "~n SSLOptions: ~p", [URL, SslTag, SSLOptions]), + case httpc:request(trace, {URL, []}, [{ssl, SSLConfig}], []) of + {ok, {{_,200, _}, [_ | _], "TRACE /dummy.html" ++ _}} -> + ok; + {ok, {{_,200,_}, [_ | _], WrongBody}} -> + tsf({wrong_body, WrongBody}); + {ok, WrongReply} -> + tsf({wrong_reply, WrongReply}); + Error -> + tsf({failed, Error}) + end; + {ok, _} -> + skip("local http-server not started"); + _ -> + skip("SSL not started") + end. + + +%%------------------------------------------------------------------------- +http_redirect(doc) -> + ["Test redirect with dummy server as httpd does not implement" + " server redirect"]; +http_redirect(suite) -> + []; +http_redirect(Config) when is_list(Config) -> + tsp("http_redirect -> entry with" + "~n Config: ~p", [Config]), + case ?config(local_server, Config) of + ok -> + %% tsp("http_redirect -> set ipfamily option to inet"), + %% ok = httpc:set_options([{ipfamily, inet}]), + + tsp("http_redirect -> start dummy server inet"), + {DummyServerPid, Port} = dummy_server(ipv4), + tsp("http_redirect -> server port = ~p", [Port]), + + URL300 = ?URL_START ++ integer_to_list(Port) ++ "/300.html", + + tsp("http_redirect -> issue request 1: " + "~n ~p", [URL300]), + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, {URL300, []}, [], []), + + tsp("http_redirect -> issue request 2: " + "~n ~p", [URL300]), + {ok, {{_,300,_}, [_ | _], _}} = + httpc:request(get, {URL300, []}, [{autoredirect, false}], []), + + URL301 = ?URL_START ++ integer_to_list(Port) ++ "/301.html", + + tsp("http_redirect -> issue request 3: " + "~n ~p", [URL301]), + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, {URL301, []}, [], []), + + tsp("http_redirect -> issue request 4: " + "~n ~p", [URL301]), + {ok, {{_,200,_}, [_ | _], []}} + = httpc:request(head, {URL301, []}, [], []), + + tsp("http_redirect -> issue request 5: " + "~n ~p", [URL301]), + {ok, {{_,301,_}, [_ | _], [_|_]}} + = httpc:request(post, {URL301, [],"text/plain", "foobar"}, + [], []), + + URL302 = ?URL_START ++ integer_to_list(Port) ++ "/302.html", + + tsp("http_redirect -> issue request 6: " + "~n ~p", [URL302]), + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, {URL302, []}, [], []), + case httpc:request(get, {URL302, []}, [], []) of + {ok, Reply7} -> + case Reply7 of + {{_,200,_}, [_ | _], [_|_]} -> + tsp("http_redirect -> " + "expected reply for request 7"), + ok; + {StatusLine, Headers, Body} -> + tsp("http_redirect -> " + "unexpected reply for request 7: " + "~n StatusLine: ~p" + "~n Headers: ~p" + "~n Body: ~p", + [StatusLine, Headers, Body]), + tsf({unexpected_reply, Reply7}) + end; + Error7 -> + tsp("http_redirect -> " + "unexpected result for request 7: " + "~n Error7: ~p", + [Error7]), + tsf({unexpected_result, Error7}) + end, + + tsp("http_redirect -> issue request 7: " + "~n ~p", [URL302]), + {ok, {{_,200,_}, [_ | _], []}} + = httpc:request(head, {URL302, []}, [], []), + + tsp("http_redirect -> issue request 8: " + "~n ~p", [URL302]), + {ok, {{_,302,_}, [_ | _], [_|_]}} + = httpc:request(post, {URL302, [],"text/plain", "foobar"}, + [], []), + + URL303 = ?URL_START ++ integer_to_list(Port) ++ "/303.html", + + tsp("http_redirect -> issue request 9: " + "~n ~p", [URL303]), + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, {URL303, []}, [], []), + + tsp("http_redirect -> issue request 10: " + "~n ~p", [URL303]), + {ok, {{_,200,_}, [_ | _], []}} + = httpc:request(head, {URL303, []}, [], []), + + tsp("http_redirect -> issue request 11: " + "~n ~p", [URL303]), + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(post, {URL303, [],"text/plain", "foobar"}, + [], []), + + URL307 = ?URL_START ++ integer_to_list(Port) ++ "/307.html", + + tsp("http_redirect -> issue request 12: " + "~n ~p", [URL307]), + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, {URL307, []}, [], []), + + tsp("http_redirect -> issue request 13: " + "~n ~p", [URL307]), + {ok, {{_,200,_}, [_ | _], []}} + = httpc:request(head, {URL307, []}, [], []), + + tsp("http_redirect -> issue request 14: " + "~n ~p", [URL307]), + {ok, {{_,307,_}, [_ | _], [_|_]}} + = httpc:request(post, {URL307, [],"text/plain", "foobar"}, + [], []), + + tsp("http_redirect -> stop dummy server"), + DummyServerPid ! stop, + tsp("http_redirect -> reset ipfamily option (to inet6fb4)"), + ok = httpc:set_options([{ipfamily, inet6fb4}]), + tsp("http_redirect -> done"), + ok; + + _ -> + skip("Failed to start local http-server") + end. + + + +%%------------------------------------------------------------------------- +http_redirect_loop(doc) -> + ["Test redirect loop detection"]; +http_redirect_loop(suite) -> + []; +http_redirect_loop(Config) when is_list(Config) -> + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), + + URL = ?URL_START ++ integer_to_list(Port) ++ "/redirectloop.html", + + {ok, {{_,300,_}, [_ | _], _}} + = httpc:request(get, {URL, []}, [], []), + DummyServerPid ! stop, + ok = httpc:set_options([{ipfamily, inet6fb4}]), + ok. + +%%------------------------------------------------------------------------- +http_internal_server_error(doc) -> + ["Test 50X codes"]; +http_internal_server_error(suite) -> + []; +http_internal_server_error(Config) when is_list(Config) -> + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), + + URL500 = ?URL_START ++ integer_to_list(Port) ++ "/500.html", + + {ok, {{_,500,_}, [_ | _], _}} + = httpc:request(get, {URL500, []}, [], []), + + + URL503 = ?URL_START ++ integer_to_list(Port) ++ "/503.html", + + %% Used to be able to make the service available after retry. + ets:new(unavailable, [named_table, public, set]), + ets:insert(unavailable, {503, unavailable}), + + {ok, {{_,200, _}, [_ | _], [_|_]}} = + httpc:request(get, {URL503, []}, [], []), + + ets:insert(unavailable, {503, long_unavailable}), + + {ok, {{_,503, _}, [_ | _], [_|_]}} = + httpc:request(get, {URL503, []}, [], []), + + ets:delete(unavailable), + DummyServerPid ! stop, + ok = httpc:set_options([{ipfamily, inet6fb4}]), + ok. + + +%%------------------------------------------------------------------------- +http_userinfo(doc) -> + ["Test user info e.i. http://user:passwd@host:port/"]; +http_userinfo(suite) -> + []; +http_userinfo(Config) when is_list(Config) -> + ok = httpc:set_options([{ipfamily, inet}]), + + {DummyServerPid, Port} = dummy_server(ipv4), + + URLAuth = "http://alladin:sesame@localhost:" + ++ integer_to_list(Port) ++ "/userinfo.html", + + {ok, {{_,200,_}, [_ | _], _}} + = httpc:request(get, {URLAuth, []}, [], []), + + URLUnAuth = "http://alladin:foobar@localhost:" + ++ integer_to_list(Port) ++ "/userinfo.html", + + {ok, {{_,401, _}, [_ | _], _}} = + httpc:request(get, {URLUnAuth, []}, [], []), + + DummyServerPid ! stop, + ok = httpc:set_options([{ipfamily, inet6fb4}]), + ok. + + +%%------------------------------------------------------------------------- +http_cookie(doc) -> + ["Test cookies."]; +http_cookie(suite) -> + []; +http_cookie(Config) when is_list(Config) -> + ok = httpc:set_options([{cookies, enabled}, {ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), + + URLStart = ?URL_START + ++ integer_to_list(Port), + + URLCookie = URLStart ++ "/cookie.html", + + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, {URLCookie, []}, [], []), + + ets:new(cookie, [named_table, public, set]), + ets:insert(cookie, {cookies, true}), + + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, {URLStart ++ "/", []}, [], []), + + ets:delete(cookie), + + ok = httpc:set_options([{cookies, disabled}]), + DummyServerPid ! stop, + ok = httpc:set_options([{ipfamily, inet6fb4}]), + ok. + +%%------------------------------------------------------------------------- +http_server_does_not_exist(doc) -> + ["Test that we get an error message back when the server " + "does note exist."]; +http_server_does_not_exist(suite) -> + []; +http_server_does_not_exist(Config) when is_list(Config) -> + {error, _} = + httpc:request(get, {"http://localhost:" ++ + integer_to_list(?NOT_IN_USE_PORT) + ++ "/", []},[], []), + ok. + + +%%------------------------------------------------------------------------- +page_does_not_exist(doc) -> + ["Test that we get a 404 when the page is not found."]; +page_does_not_exist(suite) -> + []; +page_does_not_exist(Config) when is_list(Config) -> + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/doesnotexist.html", + {ok, {{_,404,_}, [_ | _], [_ | _]}} + = httpc:request(get, {URL, []}, [], []), + ok. + + +%%------------------------------------------------------------------------- + +http_stream(doc) -> + ["Test the option stream for asynchrony requests"]; +http_stream(suite) -> + []; +http_stream(Config) when is_list(Config) -> + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + {ok, {{_,200,_}, [_ | _], Body}} = + httpc:request(get, {URL, []}, [], []), + + {ok, RequestId} = + httpc:request(get, {URL, []}, [], [{sync, false}, + {stream, self}]), + + receive + {http, {RequestId, stream_start, _Headers}} -> + ok; + {http, Msg} -> + tsf(Msg) + end, + + StreamedBody = receive_streamed_body(RequestId, <<>>), + + Body == binary_to_list(StreamedBody). + + +%%------------------------------------------------------------------------- + +http_stream_once(doc) -> + ["Test the option stream for asynchrony requests"]; +http_stream_once(suite) -> + []; +http_stream_once(Config) when is_list(Config) -> + p("http_stream_once -> entry with" + "~n Config: ~p", [Config]), + + p("http_stream_once -> set ipfamily to inet", []), + ok = httpc:set_options([{ipfamily, inet}]), + p("http_stream_once -> start dummy server", []), + {DummyServerPid, Port} = dummy_server(ipv4), + + PortStr = integer_to_list(Port), + p("http_stream_once -> once", []), + once(?URL_START ++ PortStr ++ "/once.html"), + p("http_stream_once -> once_chunked", []), + once(?URL_START ++ PortStr ++ "/once_chunked.html"), + p("http_stream_once -> dummy", []), + once(?URL_START ++ PortStr ++ "/dummy.html"), + + p("http_stream_once -> stop dummy server", []), + DummyServerPid ! stop, + p("http_stream_once -> set ipfamily to inet6fb4", []), + ok = httpc:set_options([{ipfamily, inet6fb4}]), + p("http_stream_once -> done", []), + ok. + +once(URL) -> + p("once -> issue sync request for ~p", [URL]), + {ok, {{_,200,_}, [_ | _], Body}} = + httpc:request(get, {URL, []}, [], []), + + p("once -> issue async (self stream) request for ~p", [URL]), + {ok, RequestId} = + httpc:request(get, {URL, []}, [], [{sync, false}, + {stream, {self, once}}]), + + p("once -> await stream_start reply for (async) request ~p", [RequestId]), + NewPid = + receive + {http, {RequestId, stream_start, _Headers, Pid}} -> + p("once -> received stream_start reply for (async) request ~p: ~p", + [RequestId, Pid]), + Pid; + {http, Msg} -> + tsf(Msg) + end, + + tsp("once -> request handler: ~p", [NewPid]), + + p("once -> await stream reply for (async) request ~p", [RequestId]), + BodyPart = + receive + {http, {RequestId, stream, BinBodyPart}} -> + p("once -> received stream reply for (async) request ~p: " + "~n~p", [RequestId, binary_to_list(BinBodyPart)]), + BinBodyPart + end, + + tsp("once -> first body part '~p' received", [binary_to_list(BodyPart)]), + + StreamedBody = receive_streamed_body(RequestId, BinBodyPart, NewPid), + + Body = binary_to_list(StreamedBody), + + p("once -> done when Bode: ~p", [Body]), + ok. + + +%%------------------------------------------------------------------------- +parse_url(doc) -> + ["Test that an url is parsed correctly"]; +parse_url(suite) -> + []; +parse_url(Config) when is_list(Config) -> + %% ipv6 + {ok, {http,[],"2010:836B:4179::836B:4179",80,"/foobar.html",[]}} = + http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html"), + {ok, {http,[],"[2010:836B:4179::836B:4179]",80,"/foobar.html",[]}} = + http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html", + [{ipv6_host_with_brackets, true}]), + {ok, {http,[],"2010:836B:4179::836B:4179",80,"/foobar.html",[]}} = + http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html", + [{ipv6_host_with_brackets, false}]), + {ok, {http,[],"2010:836B:4179::836B:4179",80,"/foobar.html",[]}} = + http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html", + [{foo, false}]), + {error, + {malformed_url, _, "http://2010:836B:4179::836B:4179/foobar.html"}} = + http_uri:parse("http://2010:836B:4179::836B:4179/foobar.html"), + + %% ipv4 + {ok, {http,[],"127.0.0.1",80,"/foobar.html",[]}} = + http_uri:parse("http://127.0.0.1/foobar.html"), + + %% host + {ok, {http,[],"localhost",8888,"/foobar.html",[]}} = + http_uri:parse("http://localhost:8888/foobar.html"), + + %% Userinfo + {ok, {http,"nisse:foobar","localhost",8888,"/foobar.html",[]}} = + http_uri:parse("http://nisse:foobar@localhost:8888/foobar.html"), + + %% Scheme error + {error, no_scheme} = http_uri:parse("localhost/foobar.html"), + {error, {malformed_url, _, _}} = + http_uri:parse("localhost:8888/foobar.html"), + + %% Query + {ok, {http,[],"localhost",8888,"/foobar.html","?foo=bar&foobar=42"}} = + http_uri:parse("http://localhost:8888/foobar.html?foo=bar&foobar=42"), + + %% Esc chars + {ok, {http,[],"www.somedomain.com",80,"/%2Eabc",[]}} = + http_uri:parse("http://www.somedomain.com/%2Eabc"), + {ok, {http,[],"www.somedomain.com",80,"/%252Eabc",[]}} = + http_uri:parse("http://www.somedomain.com/%252Eabc"), + {ok, {http,[],"www.somedomain.com",80,"/%25abc",[]}} = + http_uri:parse("http://www.somedomain.com/%25abc"), + {ok, {http,[],"www.somedomain.com",80,"/%25abc", "?foo=bar"}} = + http_uri:parse("http://www.somedomain.com/%25abc?foo=bar"), + + + ok. + + +%%------------------------------------------------------------------------- + +ipv6_ipcomm() -> + [{require, ipv6_hosts}]. +ipv6_ipcomm(doc) -> + ["Test ip_comm ipv6."]; +ipv6_ipcomm(suite) -> + []; +ipv6_ipcomm(Config) when is_list(Config) -> + HTTPOptions = [], + SocketType = ip_comm, + Scheme = "http", + Extra = [], + ipv6(SocketType, Scheme, HTTPOptions, Extra, Config). + + +%%------------------------------------------------------------------------- + +ipv6_essl() -> + [{require, ipv6_hosts}]. +ipv6_essl(doc) -> + ["Test essl ipv6."]; +ipv6_essl(suite) -> + []; +ipv6_essl(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + CertFile = filename:join(DataDir, "ssl_client_cert.pem"), + SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], + SSLConfig = {essl, SSLOptions}, + tsp("ssl_ipv6 -> make request using: " + "~n SSLOptions: ~p", [SSLOptions]), + HTTPOptions = [{ssl, SSLConfig}], + SocketType = essl, + Scheme = "https", + Extra = SSLOptions, + ipv6(SocketType, Scheme, HTTPOptions, Extra, Config). + + +%%------------------------------------------------------------------------- + +ipv6(SocketType, Scheme, HTTPOptions, Extra, Config) -> + %% Check if we are a IPv6 host + tsp("ipv6 -> verify ipv6 support"), + case inets_test_lib:has_ipv6_support(Config) of + {ok, Addr} -> + tsp("ipv6 -> ipv6 supported: ~p", [Addr]), + {DummyServerPid, Port} = dummy_server(SocketType, ipv6, Extra), + Profile = ?config(profile, Config), + URL = + Scheme ++ + "://[" ++ http_transport:ipv6_name(Addr) ++ "]:" ++ + integer_to_list(Port) ++ "/foobar.html", + tsp("ipv6 -> issue request with: " + "~n URL: ~p" + "~n HTTPOptions: ~p", [URL, HTTPOptions]), + case httpc:request(get, {URL, []}, HTTPOptions, [], Profile) of + {ok, {{_,200,_}, [_ | _], [_|_]}} -> + tsp("ipv6 -> expected result"), + DummyServerPid ! stop, + ok; + {ok, Unexpected} -> + tsp("ipv6 -> unexpected result: " + "~n ~p", [Unexpected]), + DummyServerPid ! stop, + tsf({unexpected_result, Unexpected}); + {error, Reason} -> + tsp("ipv6 -> error: " + "~n Reason: ~p", [Reason]), + DummyServerPid ! stop, + tsf(Reason) + end, + ok; + _ -> + tsp("ipv6 -> ipv6 not supported"), + skip("Host does not support IPv6") + end. + + +%%------------------------------------------------------------------------- + +headers_as_is(doc) -> + ["Test the option headers_as_is"]; +headers_as_is(suite) -> + []; +headers_as_is(Config) when is_list(Config) -> + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + {ok, {{_,200,_}, [_|_], [_|_]}} = + httpc:request(get, {URL, [{"Host", "localhost"},{"Te", ""}]}, + [], [{headers_as_is, true}]), + + {ok, {{_,400,_}, [_|_], [_|_]}} = + httpc:request(get, {URL, [{"Te", ""}]},[], [{headers_as_is, true}]), + ok. + + +%%------------------------------------------------------------------------- + +selecting_session(doc) -> + ["Test selection of sessions - OTP-9847"]; +selecting_session(suite) -> + []; +selecting_session(Config) when is_list(Config) -> + tsp("selecting_session -> entry with" + "~n Config: ~p", [Config]), + + tsp("selecting_session -> set ipfamily to inet"), + ok = httpc:set_options([{ipfamily, inet}]), + + tsp("selecting_session -> start server"), + {ServerPid, Port} = otp_9847_server(), + + PortStr = integer_to_list(Port), + URL = ?URL_START ++ PortStr ++ "/index.html", + + tsp("selecting_session -> issue the first batch (three) requests"), + lists:foreach(fun(P) -> + tsp("selecting_session:fun1 -> " + "send stop request to ~p", [P]), + P ! stop + end, + reqs(URL, ServerPid, 3, 3, false)), + tsp("selecting_session -> sleep some (1) to make sure nothing lingers"), + ?SLEEP(5000), + tsp("selecting_session -> " + "instruct the server to reply to the first request"), + ServerPid ! {answer, true}, + receive + {answer, true} -> + tsp("selecting_session -> " + "received ack from server to reply to the first request"), + ok + end, + tsp("selecting_session -> issue the second batch (four) requests"), + lists:foreach(fun(P) -> + tsp("selecting_session:fun2 -> " + "send stop request to ~p", [P]), + P ! stop + end, + reqs(URL, ServerPid, 4, 1, true)), + tsp("selecting_session -> sleep some (2) to make sure nothing lingers"), + ?SLEEP(5000), + + tsp("selecting_session -> stop server"), + ServerPid ! stop, + tsp("selecting_session -> set ipfamily (back) to inet6fb4"), + ok = httpc:set_options([{ipfamily, inet6fb4}]), + tsp("selecting_session -> done"), + ok. + +reqs(URL, ServerPid, NumReqs, NumHandlers, InitialSync) -> + tsp("reqs -> entry with" + "~n URL: ~p" + "~n ServerPid: ~w" + "~n NumReqs: ~w" + "~n NumHandlers: ~w" + "~n InitialSync: ~w", + [URL, ServerPid, NumReqs, NumHandlers, InitialSync]), + Handlers = reqs2(URL, NumReqs, [], InitialSync), + tsp("reqs -> " + "~n Handlers: ~w", [Handlers]), + case length(Handlers) of + NumHandlers -> + tsp("reqs -> " + "~n NumHandlers: ~w", [NumHandlers]), + ServerPid ! num_handlers, + receive + {num_handlers, NumHandlers} -> + tsp("reqs -> received num_handlers with" + "~n NumHandlers: ~w", [NumHandlers]), + Handlers; + {num_handlers, WrongNumHandlers} -> + tsp("reqs -> received num_handlers with" + "~n WrongNumHandlers: ~w", [WrongNumHandlers]), + exit({wrong_num_handlers1, WrongNumHandlers, NumHandlers}) + end; + WrongNumHandlers -> + tsp("reqs -> " + "~n WrongNumHandlers: ~w", [WrongNumHandlers]), + exit({wrong_num_handlers2, WrongNumHandlers, NumHandlers}) + end. + + +reqs2(_URL, 0, Acc, _Sync) -> + lists:reverse(Acc); +reqs2(URL, Num, Acc, Sync) -> + tsp("reqs2 -> entry with" + "~n Num: ~w" + "~n Sync: ~w", [Num, Sync]), + case httpc:request(get, {URL, []}, [], [{sync, Sync}]) of + {ok, _Reply} -> + tsp("reqs2 -> successful request: ~p", [_Reply]), + receive + {handler, Handler, _Manager} -> + %% This is when a new handler is created + tsp("reqs2 -> received handler: ~p", [Handler]), + case lists:member(Handler, Acc) of + true -> + tsp("reqs2 -> duplicate handler"), + exit({duplicate_handler, Handler, Num, Acc}); + false -> + tsp("reqs2 -> wait for data ack"), + receive + {data_received, Handler} -> + tsp("reqs2 -> " + "received data ack from ~p", [Handler]), + case Sync of + true -> + reqs2(URL, Num-1, [Handler|Acc], + false); + false -> + reqs2(URL, Num-1, [Handler|Acc], + Sync) + end + end + end; + + {data_received, Handler} -> + tsp("reqs2 -> " + "received data ack from ~p", [Handler]), + reqs2(URL, Num-1, Acc, false) + + end; + + {error, Reason} -> + tsp("reqs2 -> request ~w failed: ~p", [Num, Reason]), + exit({request_failed, Reason, Num, Acc}) + end. + +otp_9847_server() -> + TC = self(), + Pid = spawn_link(fun() -> otp_9847_server_init(TC) end), + receive + {port, Port} -> + {Pid, Port} + end. + +otp_9847_server_init(TC) -> + tsp("otp_9847_server_init -> entry with" + "~n TC: ~p", [TC]), + {ok, ListenSocket} = + gen_tcp:listen(0, [binary, inet, {packet, 0}, + {reuseaddr,true}, + {active, false}]), + tsp("otp_9847_server_init -> listen socket created: " + "~n ListenSocket: ~p", [ListenSocket]), + {ok, Port} = inet:port(ListenSocket), + tsp("otp_9847_server_init -> Port: ~p", [Port]), + TC ! {port, Port}, + otp_9847_server_main(TC, ListenSocket, false, []). + +otp_9847_server_main(TC, ListenSocket, Answer, Handlers) -> + tsp("otp_9847_server_main -> entry with" + "~n TC: ~p" + "~n ListenSocket: ~p" + "~n Answer: ~p" + "~n Handlers: ~p", [TC, ListenSocket, Answer, Handlers]), + case gen_tcp:accept(ListenSocket, 1000) of + {ok, Sock} -> + tsp("otp_9847_server_main -> accepted" + "~n Sock: ~p", [Sock]), + {Handler, Mon, Port} = otp_9847_handler(TC, Sock, Answer), + tsp("otp_9847_server_main -> handler ~p created for ~w", + [Handler, Port]), + gen_tcp:controlling_process(Sock, Handler), + tsp("otp_9847_server_main -> control transfer"), + Handler ! owner, + tsp("otp_9847_server_main -> " + "handler ~p informed of owner transfer", [Handler]), + TC ! {handler, Handler, self()}, + tsp("otp_9847_server_main -> " + "TC ~p informed of handler ~p", [TC, Handler]), + otp_9847_server_main(TC, ListenSocket, Answer, + [{Handler, Mon, Sock, Port}|Handlers]); + + {error, timeout} -> + tsp("otp_9847_server_main -> timeout"), + receive + {answer, true} -> + tsp("otp_9847_server_main -> received answer request"), + TC ! {answer, true}, + otp_9847_server_main(TC, ListenSocket, true, Handlers); + + {'DOWN', _Mon, process, Pid, _Reason} -> + %% Could be one of the handlers + tsp("otp_9847_server_main -> received DOWN for ~p", [Pid]), + otp_9847_server_main(TC, ListenSocket, Answer, + lists:keydelete(Pid, 1, Handlers)); + + num_handlers -> + tsp("otp_9847_server_main -> " + "received request for number of handlers (~w)", + [length(Handlers)]), + TC ! {num_handlers, length(Handlers)}, + otp_9847_server_main(TC, ListenSocket, Answer, Handlers); + + stop -> + tsp("otp_9847_server_main -> received stop request"), + %% Stop all handlers (just in case) + Pids = [Handler || {Handler, _, _} <- Handlers], + lists:foreach(fun(Pid) -> Pid ! stop end, Pids), + exit(normal); + + Any -> + tsp("otp_9847_server_main -> received" + "~n Any: ~p", [Any]), + exit({crap, Any}) + + after 0 -> + tsp("otp_9847_server_main -> nothing in queue"), + otp_9847_server_main(TC, ListenSocket, Answer, Handlers) + end; + + Error -> + exit(Error) + end. + + +otp_9847_handler(TC, Sock, Answer) -> + tsp("otp_9847_handler -> entry with" + "~n TC: ~p" + "~n Sock: ~p" + "~n Answer: ~p", [TC, Sock, Answer]), + Self = self(), + {Pid, Mon} = + spawn_opt(fun() -> + otp_9847_handler_init(TC, Self, Sock, Answer) + end, + [monitor]), + receive + {port, Port} -> + tsp("otp_9847_handler -> received port message (from ~p)" + "~n Port: ~p", [Pid, Port]), + {Pid, Mon, Port} + end. + + +otp_9847_handler_init(TC, Server, Sock, Answer) -> + tsp("otp_9847_handler_init -> entry with" + "~n TC: ~p" + "~n Server: ~p" + "~n Sock: ~p" + "~n Answer: ~p", [TC, Server, Sock, Answer]), + {ok, Port} = inet:port(Sock), + Server ! {port, Port}, + receive + owner -> + tsp("otp_9847_handler_init -> " + "received owner message - activate socket"), + inet:setopts(Sock, [{active, true}]), + otp_9847_handler_main(TC, Server, Sock, Answer, [?HTTP_MAX_HEADER_SIZE]) + end. + +otp_9847_handler_main(TC, Server, Sock, Answer, ParseArgs) -> + tsp("otp_9847_handler_main -> entry with" + "~n TC: ~p" + "~n Server: ~p" + "~n Sock: ~p" + "~n Answer: ~p" + "~n ParseArgs: ~p", [TC, Server, Sock, Answer, ParseArgs]), + receive + stop -> + tsp("otp_9847_handler_main -> received stop request"), + exit(normal); + + {tcp, Sock, _Data} when Answer =:= false -> + tsp("otp_9847_handler_main -> received tcp data - no answer"), + TC ! {data_received, self()}, + inet:setopts(Sock, [{active, true}]), + %% Ignore all data + otp_9847_handler_main(TC, Server, Sock, Answer, ParseArgs); + + {tcp, Sock, Data} when Answer =:= true -> + tsp("otp_9847_handler_main -> received tcp data - answer"), + TC ! {data_received, self()}, + inet:setopts(Sock, [{active, true}]), + NewParseArgs = otp_9847_handler_request(Sock, [Data|ParseArgs]), + otp_9847_handler_main(TC, Server, Sock, Answer, NewParseArgs); + + {tcp_closed, Sock} -> + tsp("otp_9847_handler_main -> received tcp socket closed"), + exit(normal); + + {tcp_error, Sock, Reason} -> + tsp("otp_9847_handler_main -> socket error: ~p", [Reason]), + (catch gen_tcp:close(Sock)), + exit(normal) + + %% after 30000 -> + %% gen_tcp:close(Sock), + %% exit(normal) + end. + +otp_9847_handler_request(Sock, Args) -> + Msg = + case httpd_request:parse(Args) of + {ok, {_, "/index.html" = _RelUrl, _, _, _}} -> + B = + "" ++ + "...some body part..." ++ + "", + Len = integer_to_list(length(B)), + "HTTP/1.1 200 ok\r\n" ++ + "Content-Length:" ++ Len ++ "\r\n\r\n" ++ B + end, + gen_tcp:send(Sock, Msg), + [?HTTP_MAX_HEADER_SIZE]. + + + +%%------------------------------------------------------------------------- + +options(doc) -> + ["Test the option parameters."]; +options(suite) -> + []; +options(Config) when is_list(Config) -> + case ?config(local_server, Config) of + ok -> + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + {ok, {{_,200,_}, [_ | _], Bin}} + = httpc:request(get, {URL, []}, [{foo, bar}], + %% Ignore unknown options + [{body_format, binary}, {foo, bar}]), + + true = is_binary(Bin), + {ok, {200, [_|_]}} + = httpc:request(get, {URL, []}, [{timeout, infinity}], + [{full_result, false}]); + _ -> + skip("Failed to start local http-server") + end. + + +%%------------------------------------------------------------------------- + +http_invalid_http(doc) -> + ["Test parse error"]; +http_invalid_http(suite) -> + []; +http_invalid_http(Config) when is_list(Config) -> + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), + + URL = ?URL_START ++ integer_to_list(Port) ++ "/invalid_http.html", + + {error, {could_not_parse_as_http, _} = Reason} = + httpc:request(get, {URL, []}, [], []), + + test_server:format("Parse error: ~p ~n", [Reason]), + DummyServerPid ! stop, + ok = httpc:set_options([{ipfamily, inet6fb4}]), + ok. + + +%%------------------------------------------------------------------------- + +-define(GOOGLE, "www.google.com"). + +hexed_query_otp_6191(doc) -> + []; +hexed_query_otp_6191(suite) -> + []; +hexed_query_otp_6191(Config) when is_list(Config) -> + Google = ?GOOGLE, + GoogleSearch = "http://" ++ Google ++ "/search", + Search1 = "?hl=en&q=a%D1%85%D1%83%D0%B9&btnG=Google+Search", + URI1 = GoogleSearch ++ Search1, + Search2 = "?hl=en&q=%25%25", + URI2 = GoogleSearch ++ Search2, + Search3 = "?hl=en&q=%foo", + URI3 = GoogleSearch ++ Search3, + + Verify1 = + fun({http, [], ?GOOGLE, 80, "/search", _}) -> ok; + (_) -> error + end, + Verify2 = Verify1, + Verify3 = Verify1, + verify_uri(URI1, Verify1), + verify_uri(URI2, Verify2), + verify_uri(URI3, Verify3), + ok. + +verify_uri(URI, Verify) -> + case http_uri:parse(URI) of + {ok, ParsedURI} -> + case Verify(ParsedURI) of + ok -> + ok; + error -> + Reason = {unexpected_parse_result, URI, ParsedURI}, + ERROR = {error, Reason}, + throw(ERROR) + end; + {error, _} = ERROR -> + throw(ERROR) + end. + + +%%------------------------------------------------------------------------- + +empty_body_otp_6243(doc) -> + ["An empty body was not returned directly. There was a delay for several" + "seconds."]; +empty_body_otp_6243(suite) -> + []; +empty_body_otp_6243(Config) when is_list(Config) -> + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/empty.html", + {ok, {{_,200,_}, [_ | _], []}} = + httpc:request(get, {URL, []}, [{timeout, 500}], []). + + +%%------------------------------------------------------------------------- + +transfer_encoding_otp_6807(doc) -> + ["Transfer encoding is case insensitive"]; +transfer_encoding_otp_6807(suite) -> + []; +transfer_encoding_otp_6807(Config) when is_list(Config) -> + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), + + URL = ?URL_START ++ integer_to_list(Port) ++ + "/capital_transfer_encoding.html", + {ok, {{_,200,_}, [_|_], [_ | _]}} = httpc:request(URL), + DummyServerPid ! stop, + ok = httpc:set_options([{ipfamily, inet6fb4}]), + ok. + + +%%------------------------------------------------------------------------- + +empty_response_header_otp_6830(doc) -> + ["Test the case that the HTTP server does not send any headers"]; +empty_response_header_otp_6830(suite) -> + []; +empty_response_header_otp_6830(Config) when is_list(Config) -> + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), + + URL = ?URL_START ++ integer_to_list(Port) ++ "/no_headers.html", + {ok, {{_,200,_}, [], [_ | _]}} = httpc:request(URL), + DummyServerPid ! stop, + ok = httpc:set_options([{ipfamily, inet6fb4}]), + ok. + + +%%------------------------------------------------------------------------- + +no_content_204_otp_6982(doc) -> + ["Test the case that the HTTP 204 no content header"]; +no_content_204_otp_6982(suite) -> + []; +no_content_204_otp_6982(Config) when is_list(Config) -> + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), + + URL = ?URL_START ++ integer_to_list(Port) ++ "/no_content.html", + {ok, {{_,204,_}, [], []}} = httpc:request(URL), + DummyServerPid ! stop, + ok = httpc:set_options([{ipfamily, inet6fb4}]), + ok. + + +%%------------------------------------------------------------------------- + +missing_CR_otp_7304(doc) -> + ["Test the case that the HTTP server uses only LF instead of CRLF" + "as delimitor"]; +missing_CR_otp_7304(suite) -> + []; +missing_CR_otp_7304(Config) when is_list(Config) -> + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), + + URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_CR.html", + {ok, {{_,200,_}, _, [_ | _]}} = httpc:request(URL), + DummyServerPid ! stop, + ok = httpc:set_options([{ipfamily, inet6fb4}]), + ok. + + +%%------------------------------------------------------------------------- + + +otp_7883_1(doc) -> + ["OTP-7883-sync"]; +otp_7883_1(suite) -> + []; +otp_7883_1(Config) when is_list(Config) -> + ok = httpc:set_options([{ipfamily, inet}]), + + {DummyServerPid, Port} = dummy_server(ipv4), + + URL = ?URL_START ++ integer_to_list(Port) ++ "/just_close.html", + {error, socket_closed_remotely} = httpc:request(URL), + DummyServerPid ! stop, + + ok = httpc:set_options([{ipfamily, inet6fb4}]), + ok. + +otp_7883_2(doc) -> + ["OTP-7883-async"]; +otp_7883_2(suite) -> + []; +otp_7883_2(Config) when is_list(Config) -> + ok = httpc:set_options([{ipfamily, inet}]), + + {DummyServerPid, Port} = dummy_server(ipv4), + + URL = ?URL_START ++ integer_to_list(Port) ++ "/just_close.html", + Method = get, + Request = {URL, []}, + HttpOptions = [], + Options = [{sync, false}], + Profile = httpc:default_profile(), + {ok, RequestId} = + httpc:request(Method, Request, HttpOptions, Options, Profile), + ok = + receive + {http, {RequestId, {error, socket_closed_remotely}}} -> + ok + end, + DummyServerPid ! stop, + + ok = httpc:set_options([{ipfamily, inet6fb4}]), + ok. + + +%%------------------------------------------------------------------------- + + +otp_8154_1(doc) -> + ["OTP-8154"]; +otp_8154_1(suite) -> + []; +otp_8154_1(Config) when is_list(Config) -> + start_inets(), + ReqSeqNumServer = start_sequence_number_server(), + RespSeqNumServer = start_sequence_number_server(), + {ok, Server, Port} = start_slow_server(RespSeqNumServer), + Clients = run_clients(105, Port, ReqSeqNumServer), + %% ok = wait_for_clients(Clients), + ok = wait4clients(Clients, timer:minutes(3)), + Server ! shutdown, + RespSeqNumServer ! shutdown, + ReqSeqNumServer ! shutdown, + ok. + +start_inets() -> + inets:start(), + ok. + + +%% ----------------------------------------------------- +%% A sequence number handler +%% The purpose is to be able to pair requests with responses. + +start_sequence_number_server() -> + proc_lib:spawn(fun() -> loop_sequence_number(1) end). + +loop_sequence_number(N) -> + receive + shutdown -> + ok; + {From, get_next} -> + From ! {next_is, N}, + loop_sequence_number(N + 1) + end. + +get_next_sequence_number(SeqNumServer) -> + SeqNumServer ! {self(), get_next}, + receive {next_is, N} -> N end. + +%% ----------------------------------------------------- +%% Client part +%% Sends requests randomly parallel + +run_clients(NumClients, ServerPort, SeqNumServer) -> + io:format("start clients when" + "~n NumClients: ~w" + "~n ServerPort: ~w" + "~n SeqNumServer: ~w" + "~n", [NumClients, ServerPort, SeqNumServer]), + set_random_seed(), + lists:map( + fun(Id) -> + io:format("starting client ~w~n", [Id]), + Req = f("req~3..0w", [get_next_sequence_number(SeqNumServer)]), + Url = f(?URL_START ++ "~w/~s", [ServerPort, Req]), + Pid = proc_lib:spawn( + fun() -> + io:format("[~w] client started - " + "issue request~n", [Id]), + case httpc:request(Url) of + {ok, {{_,200,_}, _, Resp}} -> + io:format("[~w] 200 response: " + "~p~n", [Id, Resp]), + case lists:prefix(Req++"->", Resp) of + true -> exit(normal); + false -> exit({bad_resp,Req,Resp}) + end; + {ok, {{_,EC,Reason},_,Resp}} -> + io:format("[~w] ~w response: " + "~s~n~s~n", + [Id, EC, Reason, Resp]), + exit({bad_resp,Req,Resp}); + Crap -> + io:format("[~w] bad response: ~p", + [Id, Crap]), + exit({bad_resp, Req, Crap}) + end + end), + MRef = erlang:monitor(process, Pid), + timer:sleep(10 + random:uniform(1334)), + {Id, Pid, MRef} + + end, + lists:seq(1, NumClients)). + +%% wait_for_clients(Clients) -> +%% lists:foreach( +%% fun({Id, Pid, MRef}) -> +%% io:format("waiting for client ~w termination~n", [Id]), +%% receive +%% {'DOWN', MRef, process, Pid, normal} -> +%% io:format("waiting for clients: " +%% "normal exit from ~w (~p)~n", +%% [Id, Pid]), +%% ok; +%% {'DOWN', MRef, process, Pid, Reason} -> +%% io:format("waiting for clients: " +%% "unexpected exit from ~w (~p):" +%% "~n Reason: ~p" +%% "~n", [Id, Pid, Reason]), +%% erlang:error(Reason) +%% end +%% end, +%% Clients). + + +wait4clients([], _Timeout) -> + ok; +wait4clients(Clients, Timeout) when Timeout > 0 -> + io:format("wait4clients -> entry with" + "~n length(Clients): ~w" + "~n Timeout: ~w" + "~n", [length(Clients), Timeout]), + T = t(), + receive + {'DOWN', _MRef, process, Pid, normal} -> + case lists:keysearch(Pid, 2, Clients) of + {value, {Id, _, _}} -> + io:format("receive normal exit message " + "from client ~p (~p)", [Id, Pid]), + NewClients = + lists:keydelete(Id, 1, Clients), + wait4clients(NewClients, + Timeout - (t() - T)); + false -> + io:format("receive normal exit message " + "from unknown process: ~p", [Pid]), + wait4clients(Clients, Timeout - (t() - T)) + end; + + {'DOWN', _MRef, process, Pid, Reason} -> + case lists:keysearch(Pid, 2, Clients) of + {value, {Id, _, _}} -> + io:format("receive bad exit message " + "from client ~p (~p):" + "~n ~p", [Id, Pid, Reason]), + erlang:error({bad_client_termination, Id, Reason}); + false -> + io:format("receive normal exit message " + "from unknown process: ~p", [Pid]), + wait4clients(Clients, Timeout - (t() - T)) + end + + after Timeout -> + erlang:error({client_timeout, Clients}) + end; +wait4clients(Clients, _) -> + erlang:error({client_timeout, Clients}). + + +%% Time in milli seconds +t() -> + {A,B,C} = erlang:now(), + A*1000000000+B*1000+(C div 1000). + + +%% ----------------------------------------------------- +%% Webserver part: +%% Implements a web server that sends responses one character +%% at a time, with random delays between the characters. + +start_slow_server(SeqNumServer) -> + io:format("start slow server when" + "~n SeqNumServer: ~w" + "~n", [SeqNumServer]), + proc_lib:start( + erlang, apply, [fun() -> init_slow_server(SeqNumServer) end, []]). + +init_slow_server(SeqNumServer) -> + io:format("[webserver ~w] init slow server" + "~n", [SeqNumServer]), + {ok, LSock} = gen_tcp:listen(0, [binary, {packet,0}, {active,true}, + {backlog, 100}]), + io:format("[webserver ~w] LSock: ~p" + "~n", [SeqNumServer, LSock]), + {ok, {_IP, Port}} = inet:sockname(LSock), + io:format("[webserver ~w] Port: ~w" + "~n", [SeqNumServer, Port]), + proc_lib:init_ack({ok, self(), Port}), + loop_slow_server(LSock, SeqNumServer). + +loop_slow_server(LSock, SeqNumServer) -> + io:format("[webserver ~w] entry with" + "~n LSock: ~p" + "~n", [SeqNumServer, LSock]), + Master = self(), + Acceptor = proc_lib:spawn( + fun() -> client_handler(Master, LSock, SeqNumServer) end), + io:format("[webserver ~w] acceptor started" + "~n Acceptor: ~p" + "~n", [SeqNumServer, Acceptor]), + receive + {accepted, Acceptor} -> + io:format("[webserver ~w] accepted" + "~n", [SeqNumServer]), + loop_slow_server(LSock, SeqNumServer); + shutdown -> + gen_tcp:close(LSock), + exit(Acceptor, kill) + end. + + +%% Handle one client connection +client_handler(Master, LSock, SeqNumServer) -> + io:format("[acceptor ~w] await accept" + "~n", [SeqNumServer]), + {ok, CSock} = gen_tcp:accept(LSock), + io:format("[acceptor ~w] accepted" + "~n CSock: ~p" + "~n", [SeqNumServer, CSock]), + Master ! {accepted, self()}, + set_random_seed(), + loop_client(1, CSock, SeqNumServer). + +loop_client(N, CSock, SeqNumServer) -> + %% Await request, don't bother parsing it too much, + %% assuming the entire request arrives in one packet. + io:format("[acceptor ~w] await request" + "~n N: ~p" + "~n", [SeqNumServer, N]), + receive + {tcp, CSock, Req} -> + ReqNum = parse_req_num(Req), + RespSeqNum = get_next_sequence_number(SeqNumServer), + Response = f("~s->resp~3..0w/~2..0w", [ReqNum, RespSeqNum, N]), + Txt = f("Slow server (~p) got ~p, answering with ~p", + [self(), Req, Response]), + io:format("~s...~n", [Txt]), + slowly_send_response(CSock, Response), + case parse_connection_type(Req) of + keep_alive -> + io:format("~s...done~n", [Txt]), + loop_client(N+1, CSock, SeqNumServer); + close -> + io:format("~s...done (closing)~n", [Txt]), + gen_tcp:close(CSock) + end + end. + +slowly_send_response(CSock, Answer) -> + Response = f("HTTP/1.1 200 OK\r\nContent-Length: ~w\r\n\r\n~s", + [length(Answer), Answer]), + lists:foreach( + fun(Char) -> + timer:sleep(random:uniform(500)), + gen_tcp:send(CSock, <>) + end, + Response). + +parse_req_num(Request) -> + Opts = [caseless,{capture,all_but_first,list}], + {match, [ReqNum]} = re:run(Request, "GET /(.*) HTTP", Opts), + ReqNum. + +parse_connection_type(Request) -> + Opts = [caseless,{capture,all_but_first,list}], + {match,[CType]} = re:run(Request, "connection: *(keep-alive|close)", Opts), + case string:to_lower(CType) of + "close" -> close; + "keep-alive" -> keep_alive + end. + + +set_random_seed() -> + {_, _, Micros} = now(), + A = erlang:phash2([make_ref(), self(), Micros]), + random:seed(A, A, A). + +f(F, A) -> lists:flatten(io_lib:format(F,A)). + + + + +%%------------------------------------------------------------------------- + + + +otp_8106_pid(doc) -> + ["OTP-8106 - deliver reply info using \"other\" pid"]; +otp_8106_pid(suite) -> + []; +otp_8106_pid(Config) when is_list(Config) -> + case ?config(local_server, Config) of + ok -> + ReceiverPid = create_receiver(pid), + Receiver = ReceiverPid, + + otp8106(ReceiverPid, Receiver, Config), + + stop_receiver(ReceiverPid), + + ok; + _ -> + skip("Failed to start local http-server") + end. + + +otp_8106_fun(doc) -> + ["OTP-8106 - deliver reply info using fun"]; +otp_8106_fun(suite) -> + []; +otp_8106_fun(Config) when is_list(Config) -> + case ?config(local_server, Config) of + ok -> + ReceiverPid = create_receiver(function), + Receiver = otp_8106_deliver_fun(ReceiverPid), + + otp8106(ReceiverPid, Receiver, Config), + + stop_receiver(ReceiverPid), + + ok; + _ -> + skip("Failed to start local http-server") + end. + + +otp_8106_mfa(doc) -> + ["OTP-8106 - deliver reply info using mfa callback"]; +otp_8106_mfa(suite) -> + []; +otp_8106_mfa(Config) when is_list(Config) -> + case ?config(local_server, Config) of + ok -> + ReceiverPid = create_receiver(mfa), + Receiver = {?MODULE, otp_8106_deliver, [mfa, ReceiverPid]}, + + otp8106(ReceiverPid, Receiver, Config), + + stop_receiver(ReceiverPid), + + ok; + _ -> + skip("Failed to start local http-server") + end. + + + otp8106(ReceiverPid, Receiver, Config) -> + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + Request = {URL, []}, + HTTPOptions = [], + Options = [{sync, false}, {receiver, Receiver}], + + {ok, RequestId} = + httpc:request(get, Request, HTTPOptions, Options), + + Body = + receive + {reply, ReceiverPid, {RequestId, {{_, 200, _}, _, B}}} -> + B; + {reply, ReceiverPid, Msg} -> + tsf(Msg); + {bad_reply, ReceiverPid, Msg} -> + tsf(Msg) + end, + + inets_test_lib:check_body(binary_to_list(Body)), + ok. + + +create_receiver(Type) -> + Parent = self(), + Receiver = fun() -> receiver(Type, Parent) end, + spawn_link(Receiver). + +stop_receiver(Pid) -> + Pid ! {stop, self()}. + +receiver(Type, Parent) -> + receive + {stop, Parent} -> + exit(normal); + + {http, ReplyInfo} when (Type =:= pid) -> + Parent ! {reply, self(), ReplyInfo}, + receiver(Type, Parent); + + {Type, ReplyInfo} -> + Parent ! {reply, self(), ReplyInfo}, + receiver(Type, Parent); + + Crap -> + Parent ! {reply, self(), {bad_reply, Crap}}, + receiver(Type, Parent) + end. + + +otp_8106_deliver_fun(ReceiverPid) -> + fun(ReplyInfo) -> otp_8106_deliver(ReplyInfo, function, ReceiverPid) end. + +otp_8106_deliver(ReplyInfo, Type, ReceiverPid) -> + ReceiverPid ! {Type, ReplyInfo}, + ok. + + + +%%------------------------------------------------------------------------- + +otp_8056(doc) -> + "OTP-8056"; +otp_8056(suite) -> + []; +otp_8056(Config) when is_list(Config) -> + Method = get, + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + Request = {URL, []}, + HTTPOptions = [], + Options1 = [{sync, true}, {stream, {self, once}}], + Options2 = [{sync, true}, {stream, self}], + {error, streaming_error} = httpc:request(Method, Request, + HTTPOptions, Options1), + tsp("request 1 failed as expected"), + {error, streaming_error} = httpc:request(Method, Request, + HTTPOptions, Options2), + tsp("request 2 failed as expected"), + ok. + + +%%------------------------------------------------------------------------- + +otp_8352(doc) -> + "OTP-8352"; +otp_8352(suite) -> + []; +otp_8352(Config) when is_list(Config) -> + tsp("otp_8352 -> entry with" + "~n Config: ~p", [Config]), + case ?config(local_server, Config) of + ok -> + tsp("local-server running"), + + tsp("initial profile info(1): ~p", [httpc:info()]), + + MaxSessions = 5, + MaxKeepAlive = 10, + KeepAliveTimeout = timer:minutes(2), + ConnOptions = [{max_sessions, MaxSessions}, + {max_keep_alive_length, MaxKeepAlive}, + {keep_alive_timeout, KeepAliveTimeout}], + httpc:set_options(ConnOptions), + + Method = get, + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + Request = {URL, []}, + Timeout = timer:seconds(1), + ConnTimeout = Timeout + timer:seconds(1), + HttpOptions1 = [{timeout, Timeout}, {connect_timeout, ConnTimeout}], + Options1 = [{socket_opts, [{tos, 87}, + {recbuf, 16#FFFF}, + {sndbuf, 16#FFFF}]}], + case httpc:request(Method, Request, HttpOptions1, Options1) of + {ok, {{_,200,_}, [_ | _], ReplyBody1 = [_ | _]}} -> + %% equivaliant to httpc:request(get, {URL, []}, [], []), + inets_test_lib:check_body(ReplyBody1); + {ok, UnexpectedReply1} -> + tsf({unexpected_reply, UnexpectedReply1}); + {error, _} = Error1 -> + tsf({bad_reply, Error1}) + end, + + tsp("profile info (2): ~p", [httpc:info()]), + + HttpOptions2 = [], + Options2 = [{socket_opts, [{tos, 84}, + {recbuf, 32#1FFFF}, + {sndbuf, 32#1FFFF}]}], + case httpc:request(Method, Request, HttpOptions2, Options2) of + {ok, {{_,200,_}, [_ | _], ReplyBody2 = [_ | _]}} -> + %% equivaliant to httpc:request(get, {URL, []}, [], []), + inets_test_lib:check_body(ReplyBody2); + {ok, UnexpectedReply2} -> + tsf({unexpected_reply, UnexpectedReply2}); + {error, _} = Error2 -> + tsf({bad_reply, Error2}) + end, + tsp("profile info (3): ~p", [httpc:info()]), + ok; + + _ -> + skip("Failed to start local http-server") + end. + + +%%------------------------------------------------------------------------- + +otp_8371(doc) -> + ["OTP-8371"]; +otp_8371(suite) -> + []; +otp_8371(Config) when is_list(Config) -> + ok = httpc:set_options([{ipv6, disabled}]), % also test the old option + {DummyServerPid, Port} = dummy_server(ipv4), + + URL = ?URL_START ++ integer_to_list(Port) ++ + "/ensure_host_header_with_port.html", + + case httpc:request(get, {URL, []}, [], []) of + {ok, Result} -> + case Result of + {{_, 200, _}, _Headers, Body} -> + tsp("expected response with" + "~n Body: ~p", [Body]), + ok; + {StatusLine, Headers, Body} -> + tsp("expected response with" + "~n StatusLine: ~p" + "~n Headers: ~p" + "~n Body: ~p", [StatusLine, Headers, Body]), + tsf({unexpected_result, + [{status_line, StatusLine}, + {headers, Headers}, + {body, Body}]}); + _ -> + tsf({unexpected_result, Result}) + end; + Error -> + tsf({request_failed, Error}) + end, + + DummyServerPid ! stop, + ok = httpc:set_options([{ipv6, enabled}]), + ok. + + +%%------------------------------------------------------------------------- + +otp_8739(doc) -> + ["OTP-8739"]; +otp_8739(suite) -> + []; +otp_8739(Config) when is_list(Config) -> + {_DummyServerPid, Port} = otp_8739_dummy_server(), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + Method = get, + Request = {URL, []}, + HttpOptions = [{connect_timeout, 500}, {timeout, 1}], + Options = [{sync, true}], + case httpc:request(Method, Request, HttpOptions, Options) of + {error, timeout} -> + %% And now we check the size of the handler db + Info = httpc:info(), + tsp("Info: ~p", [Info]), + {value, {handlers, Handlers}} = + lists:keysearch(handlers, 1, Info), + case Handlers of + [] -> + ok; + _ -> + tsf({unexpected_handlers, Handlers}) + end; + Unexpected -> + tsf({unexpected, Unexpected}) + end. + + +otp_8739_dummy_server() -> + Parent = self(), + Pid = spawn_link(fun() -> otp_8739_dummy_server_init(Parent) end), + receive + {port, Port} -> + {Pid, Port} + end. + +otp_8739_dummy_server_init(Parent) -> + {ok, ListenSocket} = + gen_tcp:listen(0, [binary, inet, {packet, 0}, + {reuseaddr,true}, + {active, false}]), + {ok, Port} = inet:port(ListenSocket), + Parent ! {port, Port}, + 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 + receive + {tcp_closed, Sock} -> + (catch gen_tcp:close(ListenSocket)), + exit(normal); + {tcp_error, Sock, Reason} -> + tsp("socket error: ~p", [Reason]), + (catch gen_tcp:close(ListenSocket)), + exit(normal) + after 10000 -> + %% Just in case + (catch gen_tcp:close(Sock)), + (catch gen_tcp:close(ListenSocket)), + exit(timeout) + end; + Error -> + 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 +%%-------------------------------------------------------------------- +setup_server_dirs(ServerRoot, DocRoot, DataDir) -> + ConfDir = filename:join(ServerRoot, "conf"), + CgiDir = filename:join(ServerRoot, "cgi-bin"), + ok = file:make_dir(ServerRoot), + ok = file:make_dir(DocRoot), + ok = file:make_dir(ConfDir), + ok = file:make_dir(CgiDir), + + {ok, Files} = file:list_dir(DataDir), + + lists:foreach(fun(File) -> case lists:suffix(".html", File) of + true -> + inets_test_lib:copy_file(File, + DataDir, + DocRoot); + false -> + ok + end + end, Files), + + Cgi = case test_server:os_type() of + {win32, _} -> + "cgi_echo.exe"; + _ -> + "cgi_echo" + end, + + inets_test_lib:copy_file(Cgi, DataDir, CgiDir), + inets_test_lib:copy_file("mime.types", DataDir, ConfDir). + +create_config(FileName, ComType, Port, PrivDir, ServerRoot, DocRoot, + SSLDir) -> + MaxHdrSz = io_lib:format("~p", [256]), + MaxHdrAct = io_lib:format("~p", [close]), + SSL = + case ComType of + ssl -> + [cline(["SSLCertificateFile ", + filename:join(SSLDir, "ssl_server_cert.pem")]), + cline(["SSLCertificateKeyFile ", + filename:join(SSLDir, "ssl_server_cert.pem")]), + cline(["SSLVerifyClient 0"])]; + _ -> + [] + end, + + Mod_order = "Modules mod_alias mod_auth mod_esi mod_actions mod_cgi" + " mod_include mod_dir mod_get mod_head" + " mod_log mod_disk_log mod_trace", + + %% BindAddress = "*|inet", % Force the use of IPv4 + BindAddress = "*", % This corresponds to using IpFamily inet6fb4 + + HttpConfig = [ + cline(["BindAddress ", BindAddress]), + cline(["Port ", integer_to_list(Port)]), + cline(["ServerName ", "httpc_test"]), + cline(["SocketType ", atom_to_list(ComType)]), + cline([Mod_order]), + cline(["ServerRoot ", ServerRoot]), + cline(["DocumentRoot ", DocRoot]), + cline(["MaxHeaderSize ",MaxHdrSz]), + cline(["MaxHeaderAction ",MaxHdrAct]), + cline(["DirectoryIndex ", "index.html "]), + cline(["DefaultType ", "text/plain"]), + cline(["ScriptAlias /cgi-bin/ ", + filename:join(ServerRoot, "cgi-bin"), "/"]), + SSL], + ConfigFile = filename:join([PrivDir,FileName]), + {ok, Fd} = file:open(ConfigFile, [write]), + ok = file:write(Fd, lists:flatten(HttpConfig)), + ok = file:close(Fd). + +cline(List) -> + lists:flatten([List, "\r\n"]). + +receive_streamed_body(RequestId, Body) -> + receive + {http, {RequestId, stream, BinBodyPart}} -> + receive_streamed_body(RequestId, + <>); + {http, {RequestId, stream_end, _Headers}} -> + Body; + {http, Msg} -> + tsf(Msg) + end. + +receive_streamed_body(RequestId, Body, Pid) -> + httpc:stream_next(Pid), + test_server:format("~p:receive_streamed_body -> requested next stream ~n", [?MODULE]), + receive + {http, {RequestId, stream, BinBodyPart}} -> + receive_streamed_body(RequestId, + <>, + Pid); + {http, {RequestId, stream_end, _Headers}} -> + Body; + {http, Msg} -> + tsf(Msg) + end. + +%% Perform a synchronous stop +dummy_server_stop(Pid) -> + Pid ! {stop, self()}, + receive + {stopped, Pid} -> + ok + end. + +dummy_server(IpV) -> + dummy_server(self(), ip_comm, IpV, []). + +dummy_server(SocketType, IpV, Extra) -> + dummy_server(self(), SocketType, IpV, Extra). + +dummy_server(Caller, SocketType, IpV, Extra) -> + Args = [Caller, SocketType, IpV, Extra], + Pid = spawn(httpc_SUITE, dummy_server_init, Args), + receive + {port, Port} -> + {Pid, Port} + end. + +dummy_server_init(Caller, ip_comm, IpV, _) -> + BaseOpts = [binary, {packet, 0}, {reuseaddr,true}, {active, false}], + {ok, ListenSocket} = + case IpV of + ipv4 -> + tsp("ip_comm ipv4 listen", []), + gen_tcp:listen(0, [inet | BaseOpts]); + ipv6 -> + tsp("ip_comm ipv6 listen", []), + gen_tcp:listen(0, [inet6 | BaseOpts]) + end, + {ok, Port} = inet:port(ListenSocket), + tsp("dummy_server_init(ip_comm) -> Port: ~p", [Port]), + Caller ! {port, Port}, + dummy_ipcomm_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]}, + [], ListenSocket); +dummy_server_init(Caller, essl, IpV, SSLOptions) -> + BaseOpts = [{ssl_imp, new}, + {backlog, 128}, binary, {reuseaddr,true}, {active, false} | + SSLOptions], + dummy_ssl_server_init(Caller, BaseOpts, IpV). + +dummy_ssl_server_init(Caller, BaseOpts, IpV) -> + {ok, ListenSocket} = + case IpV of + ipv4 -> + tsp("dummy_ssl_server_init -> ssl ipv4 listen", []), + ssl:listen(0, [inet | BaseOpts]); + ipv6 -> + tsp("dummy_ssl_server_init -> ssl ipv6 listen", []), + ssl:listen(0, [inet6 | BaseOpts]) + end, + tsp("dummy_ssl_server_init -> ListenSocket: ~p", [ListenSocket]), + {ok, {_, Port}} = ssl:sockname(ListenSocket), + tsp("dummy_ssl_server_init -> Port: ~p", [Port]), + Caller ! {port, Port}, + dummy_ssl_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]}, + [], ListenSocket). + +dummy_ipcomm_server_loop(MFA, Handlers, ListenSocket) -> + receive + stop -> + tsp("dummy_ipcomm_server_loop -> stop handlers", []), + lists:foreach(fun(Handler) -> Handler ! stop end, Handlers); + {stop, From} -> + tsp("dummy_ipcomm_server_loop -> " + "stop command from ~p for handlers (~p)", [From, Handlers]), + Stopper = fun(Handler) -> Handler ! stop end, + lists:foreach(Stopper, Handlers), + From ! {stopped, self()} + after 0 -> + tsp("dummy_ipcomm_server_loop -> await accept", []), + {ok, Socket} = gen_tcp:accept(ListenSocket), + tsp("dummy_ipcomm_server_loop -> accepted: ~p", [Socket]), + HandlerPid = dummy_request_handler(MFA, Socket), + tsp("dummy_icomm_server_loop -> handler created: ~p", [HandlerPid]), + gen_tcp:controlling_process(Socket, HandlerPid), + tsp("dummy_ipcomm_server_loop -> " + "control transfered to handler", []), + HandlerPid ! ipcomm_controller, + tsp("dummy_ipcomm_server_loop -> " + "handler informed about control transfer", []), + dummy_ipcomm_server_loop(MFA, [HandlerPid | Handlers], + ListenSocket) + end. + +dummy_ssl_server_loop(MFA, Handlers, ListenSocket) -> + receive + stop -> + tsp("dummy_ssl_server_loop -> stop handlers", []), + lists:foreach(fun(Handler) -> Handler ! stop end, Handlers); + {stop, From} -> + tsp("dummy_ssl_server_loop -> " + "stop command from ~p for handlers (~p)", [From, Handlers]), + Stopper = fun(Handler) -> Handler ! stop end, + lists:foreach(Stopper, Handlers), + From ! {stopped, self()} + after 0 -> + tsp("dummy_ssl_server_loop -> await accept", []), + {ok, Socket} = ssl:transport_accept(ListenSocket), + tsp("dummy_ssl_server_loop -> accepted: ~p", [Socket]), + HandlerPid = dummy_request_handler(MFA, Socket), + tsp("dummy_ssl_server_loop -> handler created: ~p", [HandlerPid]), + ssl:controlling_process(Socket, HandlerPid), + tsp("dummy_ssl_server_loop -> control transfered to handler", []), + HandlerPid ! ssl_controller, + tsp("dummy_ssl_server_loop -> " + "handler informed about control transfer", []), + dummy_ssl_server_loop(MFA, [HandlerPid | Handlers], + ListenSocket) + end. + +dummy_request_handler(MFA, Socket) -> + tsp("spawn request handler", []), + spawn(httpc_SUITE, dummy_request_handler_init, [MFA, Socket]). + +dummy_request_handler_init(MFA, Socket) -> + SockType = + receive + ipcomm_controller -> + tsp("dummy_request_handler_init -> " + "received ip_comm controller - activate", []), + inet:setopts(Socket, [{active, true}]), + ip_comm; + ssl_controller -> + tsp("dummy_request_handler_init -> " + "received ssl controller - activate", []), + ssl:setopts(Socket, [{active, true}]), + ssl + end, + dummy_request_handler_loop(MFA, SockType, Socket). + +dummy_request_handler_loop({Module, Function, Args}, SockType, Socket) -> + tsp("dummy_request_handler_loop -> entry with" + "~n Module: ~p" + "~n Function: ~p" + "~n Args: ~p", [Module, Function, Args]), + receive + {Proto, _, Data} when (Proto =:= tcp) orelse (Proto =:= ssl) -> + tsp("dummy_request_handler_loop -> [~w] Data ~p", [Proto, Data]), + case handle_request(Module, Function, [Data | Args], Socket, Proto) of + stop when Proto =:= tcp -> + gen_tcp:close(Socket); + stop when Proto =:= ssl -> + ssl:close(Socket); + NewMFA -> + dummy_request_handler_loop(NewMFA, SockType, Socket) + end; + stop when SockType =:= ip_comm -> + gen_tcp:close(Socket); + stop when SockType =:= ssl -> + ssl:close(Socket) + end. + + +mk_close(tcp) -> fun(Sock) -> gen_tcp:close(Sock) end; +mk_close(ssl) -> fun(Sock) -> ssl:close(Sock) end. + +mk_send(tcp) -> fun(Sock, Data) -> gen_tcp:send(Sock, Data) end; +mk_send(ssl) -> fun(Sock, Data) -> ssl:send(Sock, Data) end. + +handle_request(Module, Function, Args, Socket, Proto) -> + Close = mk_close(Proto), + Send = mk_send(Proto), + handle_request(Module, Function, Args, Socket, Close, Send). + +handle_request(Module, Function, Args, Socket, Close, Send) -> + tsp("handle_request -> entry with" + "~n Module: ~p" + "~n Function: ~p" + "~n Args: ~p", [Module, Function, Args]), + case Module:Function(Args) of + {ok, Result} -> + tsp("handle_request -> ok" + "~n Result: ~p", [Result]), + case (catch handle_http_msg(Result, Socket, Close, Send)) of + stop -> + stop; + <<>> -> + tsp("handle_request -> empty data"), + {httpd_request, parse, [[<<>>, ?HTTP_MAX_HEADER_SIZE]]}; + Data -> + handle_request(httpd_request, parse, + [Data |[?HTTP_MAX_HEADER_SIZE]], Socket, + Close, Send) + end; + NewMFA -> + tsp("handle_request -> " + "~n NewMFA: ~p", [NewMFA]), + NewMFA + end. + +handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket, Close, Send) -> + tsp("handle_http_msg -> entry with: " + "~n RelUri: ~p" + "~n Headers: ~p" + "~n Body: ~p", [RelUri, Headers, Body]), + NextRequest = + case RelUri of + "/dummy_headers.html" -> + <<>>; + "/no_headers.html" -> + stop; + "/just_close.html" -> + stop; + _ -> + ContentLength = content_length(Headers), + case size(Body) - ContentLength of + 0 -> + <<>>; + _ -> + <<_BodyThisReq:ContentLength/binary, + Next/binary>> = Body, + Next + end + end, + + tsp("handle_http_msg -> NextRequest: ~p", [NextRequest]), + case (catch ets:lookup(cookie, cookies)) of + [{cookies, true}]-> + tsp("handle_http_msg -> check cookies ~p", []), + check_cookie(Headers); + _ -> + ok + end, + + DefaultResponse = "HTTP/1.1 200 ok\r\n" ++ + "Content-Length:32\r\n\r\n" + "foobar", + + Msg = + case RelUri of + "/just_close.html" -> + close; + "/no_content.html" -> + "HTTP/1.0 204 No Content\r\n\r\n"; + "/no_headers.html" -> + "HTTP/1.0 200 OK\r\n\r\nTEST"; + "/ensure_host_header_with_port.html" -> + %% tsp("handle_http_msg -> validate host with port"), + case ensure_host_header_with_port(Headers) of + true -> + B = + "" ++ + "host with port" ++ + "", + Len = integer_to_list(length(B)), + "HTTP/1.1 200 ok\r\n" ++ + "Content-Length:" ++ Len ++ "\r\n\r\n" ++ B; + false -> + B = + "" ++ + "Internal Server Error - host without port" ++ + "", + Len = integer_to_list(length(B)), + "HTTP/1.1 500 Internal Server Error\r\n" ++ + "Content-Length:" ++ Len ++ "\r\n\r\n" ++ B + end; + "/300.html" -> + NewUri = ?URL_START ++ + integer_to_list(?IP_PORT) ++ "/dummy.html", + "HTTP/1.1 300 Multiple Choices\r\n" ++ + "Location:" ++ NewUri ++ "\r\n" ++ + "Content-Length:0\r\n\r\n"; + "/301.html" -> + NewUri = ?URL_START ++ + integer_to_list(?IP_PORT) ++ "/dummy.html", + "HTTP/1.1 301 Moved Permanently\r\n" ++ + "Location:" ++ NewUri ++ "\r\n" ++ + "Content-Length:80\r\n\r\n" ++ + "New place"; + "/302.html" -> + NewUri = ?URL_START ++ + integer_to_list(?IP_PORT) ++ "/dummy.html", + "HTTP/1.1 302 Found \r\n" ++ + "Location:" ++ NewUri ++ "\r\n" ++ + "Content-Length:80\r\n\r\n" ++ + "New place"; + "/303.html" -> + NewUri = ?URL_START ++ + integer_to_list(?IP_PORT) ++ "/dummy.html", + "HTTP/1.1 303 See Other \r\n" ++ + "Location:" ++ NewUri ++ "\r\n" ++ + "Content-Length:80\r\n\r\n" ++ + "New place"; + "/307.html" -> + NewUri = ?URL_START ++ + integer_to_list(?IP_PORT) ++ "/dummy.html", + "HTTP/1.1 307 Temporary Rediect \r\n" ++ + "Location:" ++ NewUri ++ "\r\n" ++ + "Content-Length:80\r\n\r\n" ++ + "New place"; + "/500.html" -> + "HTTP/1.1 500 Internal Server Error\r\n" ++ + "Content-Length:47\r\n\r\n" ++ + "Internal Server Error"; + "/503.html" -> + case ets:lookup(unavailable, 503) of + [{503, unavailable}] -> + ets:insert(unavailable, {503, available}), + "HTTP/1.1 503 Service Unavailable\r\n" ++ + "Retry-After:5\r\n" ++ + "Content-Length:47\r\n\r\n" ++ + "Internal Server Error"; + [{503, available}] -> + DefaultResponse; + [{503, long_unavailable}] -> + "HTTP/1.1 503 Service Unavailable\r\n" ++ + "Retry-After:120\r\n" ++ + "Content-Length:47\r\n\r\n" ++ + "Internal Server Error" + end; + "/redirectloop.html" -> %% Create a potential endless loop! + {ok, Port} = inet:port(Socket), + NewUri = ?URL_START ++ + integer_to_list(Port) ++ "/redirectloop.html", + "HTTP/1.1 300 Multiple Choices\r\n" ++ + "Location:" ++ NewUri ++ "\r\n" ++ + "Content-Length:0\r\n\r\n"; + "/userinfo.html" -> + Challange = "HTTP/1.1 401 Unauthorized \r\n" ++ + "WWW-Authenticate:Basic" ++"\r\n" ++ + "Content-Length:0\r\n\r\n", + case auth_header(Headers) of + {ok, Value} -> + handle_auth(Value, Challange, DefaultResponse); + _ -> + Challange + end; + "/dummy_headers.html" -> + %% The client will only care about the Transfer-Encoding + %% header the rest of these headers are left to the + %% user to evaluate. This is not a valid response + %% it only tests that the header handling code works. + Head = "HTTP/1.1 200 ok\r\n" ++ + "Content-Length:32\r\n" ++ + "Pragma:1#no-cache\r\n" ++ + "Via:1.0 fred, 1.1 nowhere.com (Apache/1.1)\r\n" ++ + "Warning:1#pseudonym foobar\r\n" ++ + "Vary:*\r\n" ++ + "Trailer:Other:inets_test\r\n" ++ + "Upgrade:HTTP/2.0\r\n" ++ + "Age:4711\r\n" ++ + "Transfer-Encoding:chunked\r\n" ++ + "Content-Encoding:foo\r\n" ++ + "Content-Language:en\r\n" ++ + "Content-Location:http://www.foobar.se\r\n" ++ + "Content-MD5:104528739076276072743283077410617235478\r\n" + ++ + "Content-Range:Sat, 29 Oct 1994 19:43:31 GMT\r\n" ++ + "Expires:Sat, 29 Oct 1994 19:43:31 GMT\r\n" ++ + "Proxy-Authenticate:#1Basic" ++ + "\r\n\r\n", + Send(Socket, Head), + Send(Socket, http_chunk:encode("fo")), + Send(Socket, http_chunk:encode("obar")), + http_chunk:encode_last(); + "/capital_transfer_encoding.html" -> + Head = "HTTP/1.1 200 ok\r\n" ++ + "Transfer-Encoding:Chunked\r\n\r\n", + Send(Socket, Head), + Send(Socket, http_chunk:encode("fo")), + Send(Socket, http_chunk:encode("obar")), + http_chunk:encode_last(); + "/cookie.html" -> + "HTTP/1.1 200 ok\r\n" ++ + "set-cookie:" ++ "test_cookie=true; path=/;" ++ + "max-age=60000\r\n" ++ + "Content-Length:32\r\n\r\n"++ + "foobar"; + "/missing_crlf.html" -> + "HTTP/1.1 200 ok" ++ + "Content-Length:32\r\n" ++ + "foobar"; + "/wrong_statusline.html" -> + "ok 200 HTTP/1.1\r\n\r\n" ++ + "Content-Length:32\r\n\r\n" ++ + "foobar"; + "/once_chunked.html" -> + Head = "HTTP/1.1 200 ok\r\n" ++ + "Transfer-Encoding:Chunked\r\n\r\n", + Send(Socket, Head), + Send(Socket, http_chunk:encode("fo")), + Send(Socket, + http_chunk:encode("obar")), + http_chunk:encode_last(); + "/once.html" -> + Head = "HTTP/1.1 200 ok\r\n" ++ + "Content-Length:32\r\n\r\n", + Send(Socket, Head), + Send(Socket, "fo"), + test_server:sleep(1000), + Send(Socket, "ob"), + test_server:sleep(1000), + Send(Socket, "ar"); + "/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"; + "/missing_reason_phrase.html" -> + "HTTP/1.1 200\r\n" ++ + "Content-Length: 32\r\n\r\n" + "foobar"; + "/missing_CR.html" -> + "HTTP/1.1 200 ok\n" ++ + "Content-Length:32\r\n\n" + "foobar"; + _ -> + DefaultResponse + end, + + tsp("handle_http_msg -> Msg: ~p", [Msg]), + case Msg of + ok -> + %% Previously, this resulted in an {error, einval}. Now what? + ok; + close -> + %% Nothing to send, just close + Close(Socket); + _ when is_list(Msg) orelse is_binary(Msg) -> + Send(Socket, Msg) + end, + tsp("handle_http_msg -> done"), + NextRequest. + +ensure_host_header_with_port([]) -> + false; +ensure_host_header_with_port(["host: " ++ Host| _]) -> + case string:tokens(Host, [$:]) of + [ActualHost, Port] -> + tsp("ensure_host_header_with_port -> " + "~n ActualHost: ~p" + "~n Port: ~p", [ActualHost, Port]), + true; + _ -> + false + end; +ensure_host_header_with_port([_|T]) -> + ensure_host_header_with_port(T). + +auth_header([]) -> + auth_header_not_found; +auth_header(["authorization:" ++ Value | _]) -> + {ok, string:strip(Value)}; +auth_header([_ | Tail]) -> + auth_header(Tail). + +handle_auth("Basic " ++ UserInfo, Challange, DefaultResponse) -> + case string:tokens(base64:decode_to_string(UserInfo), ":") of + ["alladin", "sesame"] = Auth -> + test_server:format("Auth: ~p~n", [Auth]), + DefaultResponse; + Other -> + test_server:format("UnAuth: ~p~n", [Other]), + Challange + end. + +check_cookie([]) -> + tsf(no_cookie_header); +check_cookie(["cookie:" ++ _Value | _]) -> + ok; +check_cookie([_Head | Tail]) -> + check_cookie(Tail). + +content_length([]) -> + 0; +content_length(["content-length:" ++ Value | _]) -> + list_to_integer(string:strip(Value)); +content_length([_Head | Tail]) -> + content_length(Tail). + +%% ------------------------------------------------------------------------- + +simple_request_and_verify(Config, + Method, Request, HttpOpts, Opts, VerifyResult) + when (is_list(Config) andalso + is_atom(Method) andalso + is_list(HttpOpts) andalso + is_list(Opts) andalso + is_function(VerifyResult, 1)) -> + tsp("request_and_verify -> entry with" + "~n Method: ~p" + "~n Request: ~p" + "~n HttpOpts: ~p" + "~n Opts: ~p", [Method, Request, HttpOpts, Opts]), + case ?config(local_server, Config) of + ok -> + tsp("request_and_verify -> local-server running"), + Result = (catch httpc:request(Method, Request, HttpOpts, Opts)), + VerifyResult(Result); + _ -> + tsp("request_and_verify -> local-server *not* running - skip"), + hard_skip("Local http-server not running") + end. + + + + +not_implemented_yet() -> + exit(not_implemented_yet). + +p(F) -> + p(F, []). + +p(F, A) -> + io:format("~p ~w:" ++ F ++ "~n", [self(), ?MODULE | A]). + +tsp(F) -> + inets_test_lib:tsp("[~w]" ++ F, [?MODULE]). +tsp(F, A) -> + inets_test_lib:tsp("[~w]" ++ F, [?MODULE|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. + +hard_skip(Reason) -> + throw(skip(Reason)). + +skip(Reason) -> + {skip, Reason}. diff --git a/lib/inets/test/uri_SUITE.erl b/lib/inets/test/uri_SUITE.erl new file mode 100644 index 0000000000..9ba09e1474 --- /dev/null +++ b/lib/inets/test/uri_SUITE.erl @@ -0,0 +1,159 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2013. 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. +%% +%% %CopyrightEnd% +%% +%% + +%% +%% ct:run("../inets_test", uri_SUITE). +%% + +-module(uri_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). + +-define(GOOGLE, "www.google.com"). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- +suite() -> + [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ + ipv4, + ipv6, + host, + userinfo, + scheme, + queries, + escaped, + hexed_query + ]. + +%%-------------------------------------------------------------------- + +init_per_suite(Config) -> + Config. +end_per_suite(_Config) -> + ok. + +%%-------------------------------------------------------------------- + +init_per_testcase(_Case, Config) -> + Config. +end_per_testcase(_Case, _Config) -> + ok. + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- + +ipv4(Config) when is_list(Config) -> + {ok, {http,[],"127.0.0.1",80,"/foobar.html",[]}} = + http_uri:parse("http://127.0.0.1/foobar.html"). + +ipv6(Config) when is_list(Config) -> + {ok, {http,[],"2010:836B:4179::836B:4179",80,"/foobar.html",[]}} = + http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html"), + {ok, {http,[],"[2010:836B:4179::836B:4179]",80,"/foobar.html",[]}} = + http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html", + [{ipv6_host_with_brackets, true}]), + {ok, {http,[],"2010:836B:4179::836B:4179",80,"/foobar.html",[]}} = + http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html", + [{ipv6_host_with_brackets, false}]), + {ok, {http,[],"2010:836B:4179::836B:4179",80,"/foobar.html",[]}} = + http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html", + [{foo, false}]), + {error, + {malformed_url, _, "http://2010:836B:4179::836B:4179/foobar.html"}} = + http_uri:parse("http://2010:836B:4179::836B:4179/foobar.html"). + +host(Config) when is_list(Config) -> + {ok, {http,[],"localhost",8888,"/foobar.html",[]}} = + http_uri:parse("http://localhost:8888/foobar.html"). + +userinfo(Config) when is_list(Config) -> + {ok, {http,"nisse:foobar","localhost",8888,"/foobar.html",[]}} = + http_uri:parse("http://nisse:foobar@localhost:8888/foobar.html"). + +scheme(Config) when is_list(Config) -> + {error, no_scheme} = http_uri:parse("localhost/foobar.html"), + {error, {malformed_url, _, _}} = + http_uri:parse("localhost:8888/foobar.html"). + +queries(Config) when is_list(Config) -> + {ok, {http,[],"localhost",8888,"/foobar.html","?foo=bar&foobar=42"}} = + http_uri:parse("http://localhost:8888/foobar.html?foo=bar&foobar=42"). + +escaped(Config) when is_list(Config) -> + {ok, {http,[],"www.somedomain.com",80,"/%2Eabc",[]}} = + http_uri:parse("http://www.somedomain.com/%2Eabc"), + {ok, {http,[],"www.somedomain.com",80,"/%252Eabc",[]}} = + http_uri:parse("http://www.somedomain.com/%252Eabc"), + {ok, {http,[],"www.somedomain.com",80,"/%25abc",[]}} = + http_uri:parse("http://www.somedomain.com/%25abc"), + {ok, {http,[],"www.somedomain.com",80,"/%25abc", "?foo=bar"}} = + http_uri:parse("http://www.somedomain.com/%25abc?foo=bar"). + +hexed_query(doc) -> + [{doc, "Solves OTP-6191"}]; +hexed_query(Config) when is_list(Config) -> + Google = ?GOOGLE, + GoogleSearch = "http://" ++ Google ++ "/search", + Search1 = "?hl=en&q=a%D1%85%D1%83%D0%B9&btnG=Google+Search", + URI1 = GoogleSearch ++ Search1, + Search2 = "?hl=en&q=%25%25", + URI2 = GoogleSearch ++ Search2, + Search3 = "?hl=en&q=%foo", + URI3 = GoogleSearch ++ Search3, + + Verify1 = + fun({http, [], ?GOOGLE, 80, "/search", _}) -> ok; + (_) -> error + end, + Verify2 = Verify1, + Verify3 = Verify1, + verify_uri(URI1, Verify1), + verify_uri(URI2, Verify2), + verify_uri(URI3, Verify3). + + +%%-------------------------------------------------------------------- +%% Internal Functions ------------------------------------------------ +%%-------------------------------------------------------------------- + + +verify_uri(URI, Verify) -> + case http_uri:parse(URI) of + {ok, ParsedURI} -> + case Verify(ParsedURI) of + ok -> + ok; + error -> + Reason = {unexpected_parse_result, URI, ParsedURI}, + ERROR = {error, Reason}, + throw(ERROR) + end; + {error, _} = ERROR -> + throw(ERROR) + end. -- cgit v1.2.3 From eb816c14be5d0d70de45e250f92ff5c69834af78 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Wed, 20 Feb 2013 17:18:43 +0100 Subject: inets httpd: Handle ipfamily option correctly when listning to port 0 Conflicts: lib/inets/src/http_lib/http_transport.erl --- lib/inets/src/http_lib/http_transport.erl | 104 +++++++---------------- lib/inets/src/http_server/httpd_acceptor.erl | 38 ++++----- lib/inets/src/http_server/httpd_acceptor_sup.erl | 20 ++--- lib/inets/src/http_server/httpd_manager.erl | 8 +- lib/inets/src/http_server/httpd_sup.erl | 5 +- 5 files changed, 66 insertions(+), 109 deletions(-) diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index 1692dcad78..6ac95a9572 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2013. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. 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 @@ -23,7 +23,7 @@ -export([ start/1, connect/3, connect/4, - listen/2, listen/3, listen/4, + listen/4, listen/5, accept/2, accept/3, close/2, send/3, @@ -155,21 +155,25 @@ connect({essl, SslConfig}, {Host, Port}, Opts0, Timeout) -> %% reason for this to enable a HTTP-server not running as root to use %% port 80. %%------------------------------------------------------------------------- -listen(SocketType, Port) -> - listen(SocketType, undefined, Port). +listen(ip_comm = _SocketType, Addr, Port, Fd, IpFamily) -> + listen_ip_comm(Addr, Port, Fd, IpFamily); -listen(ip_comm = _SocketType, Addr, Port) -> - listen_ip_comm(Addr, Port, undefined); +listen({essl, SSLConfig}, Addr, Port, Fd, IpFamily) -> + listen_ssl(Addr, Port, Fd, SSLConfig, IpFamily). + +listen(ip_comm = _SocketType, Addr, Port, IpFamily) -> + listen_ip_comm(Addr, Port, undefined, IpFamily); %% Wrapper for backaward compatibillity -listen({ssl, SSLConfig}, Addr, Port) -> +listen({ssl, SSLConfig}, Addr, Port, IpFamily) -> ?hlrt("listen (wrapper)", [{addr, Addr}, {port, Port}, {ssl_config, SSLConfig}]), - listen({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Addr, Port); + listen({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Addr, Port, IpFamily); + -listen({essl, SSLConfig}, Addr, Port) -> +listen({essl, SSLConfig}, Addr, Port, IpFamily) -> ?hlrt("listen (essl)", [{addr, Addr}, {port, Port}, @@ -180,24 +184,18 @@ listen({essl, SSLConfig}, Addr, Port) -> LogAlert -> {proplists:delete(log_alert, SSLConfig), [{log_alert, LogAlert}]} end, - listen_ssl(Addr, Port, [{ssl_imp, new}, {reuseaddr, true} | SSLConfig2], ExtraOpts). - + listen_ssl(Addr, Port, undefined, SSLConfig2, IpFamily, ExtraOpts). -listen(ip_comm, Addr, Port, Fd) -> - listen_ip_comm(Addr, Port, Fd); -listen({_, SSLConfig}, Addr, Port, _Fd) -> - listen_ssl(Addr, Port, SSLConfig). - -listen_ip_comm(Addr, Port, Fd) -> - case (catch do_listen_ip_comm(Addr, Port, Fd)) of +listen_ip_comm(Addr, Port, Fd, IpFamily) -> + case (catch do_listen_ip_comm(Addr, Port, Fd, IpFamily)) of {'EXIT', Reason} -> {error, {exit, Reason}}; Else -> Else end. -do_listen_ip_comm(Addr, Port, Fd) -> - {NewPort, Opts, IpFamily} = get_socket_info(Addr, Port, Fd), +do_listen_ip_comm(Addr, Port, Fd, IpFamily) -> + {NewPort, Opts} = get_socket_info(Addr, Port, Fd), case IpFamily of inet6fb4 -> Opts2 = [inet6 | Opts], @@ -229,11 +227,9 @@ do_listen_ip_comm(Addr, Port, Fd) -> gen_tcp:listen(NewPort, Opts2) end. - -listen_ssl(Addr, Port, Opts0, ExtraOpts) -> - IpFamily = ipfamily_default(Addr, Port), - BaseOpts = [{backlog, 128}, {reuseaddr, true} | Opts0], - Opts = sock_opts(Addr, BaseOpts), +listen_ssl(Addr, Port, Fd, Opts0, IpFamily, ExtraOpts) -> + {NewPort, SockOpt} = get_socket_info(Addr, Port, Fd), + Opts = SockOpt ++ Opts0, case IpFamily of inet6fb4 -> Opts2 = [inet6 | Opts] ++ ExtraOpts, @@ -244,13 +240,13 @@ listen_ssl(Addr, Port, Opts0, ExtraOpts) -> Opts3 = [inet | Opts] ++ ExtraOpts, ?hlrt("ipv6 listen failed - try ipv4 instead", [{reason, Reason}, {opts, Opts3}]), - ssl:listen(Port, Opts3); + ssl:listen(NewPort, Opts3); {'EXIT', Reason} -> Opts3 = [inet | Opts] ++ ExtraOpts, ?hlrt("ipv6 listen exit - try ipv4 instead", [{reason, Reason}, {opts, Opts3}]), - ssl:listen(Port, Opts3); + ssl:listen(NewPort, Opts3); Other -> ?hlrt("ipv6 listen done", [{other, Other}]), @@ -260,61 +256,19 @@ listen_ssl(Addr, Port, Opts0, ExtraOpts) -> _ -> Opts2 = [IpFamily | Opts], ?hlrt("listen", [{opts, Opts2}]), - ssl:listen(Port, Opts2 ++ ExtraOpts) + ssl:listen(NewPort, Opts2 ++ ExtraOpts) end. - -ipfamily_default(Addr, Port) -> - httpd_conf:lookup(Addr, Port, ipfamily, inet6fb4). - -get_socket_info(Addr, Port, Fd0) -> +get_socket_info(Addr, Port, Fd) -> BaseOpts = [{backlog, 128}, {reuseaddr, true}], - IpFamilyDefault = ipfamily_default(Addr, Port), %% The presence of a file descriptor takes precedence - case get_fd(Port, Fd0, IpFamilyDefault) of - {Fd, IpFamily} -> - {0, sock_opts(Addr, [{fd, Fd} | BaseOpts]), IpFamily}; + case Fd of undefined -> - {Port, sock_opts(Addr, BaseOpts), IpFamilyDefault} + {Port, sock_opts(Addr, BaseOpts)}; + Fd -> + {0, sock_opts(Addr, [{fd, Fd} | BaseOpts])} end. -get_fd(Port, undefined = _Fd, IpFamilyDefault) -> - FdKey = list_to_atom("httpd_" ++ integer_to_list(Port)), - case init:get_argument(FdKey) of - {ok, [[Value]]} -> - case string:tokens(Value, [$|]) of - [FdStr, IpFamilyStr] -> - {fd_of(FdStr), ip_family_of(IpFamilyStr)}; - [FdStr] -> - {fd_of(FdStr), IpFamilyDefault}; - _ -> - throw({error, {bad_descriptor, Value}}) - end; - error -> - undefined - end; -get_fd(_Port, Fd, IpFamilyDefault) -> - {Fd, IpFamilyDefault}. - - -fd_of(FdStr) -> - case (catch list_to_integer(FdStr)) of - Fd when is_integer(Fd) -> - Fd; - _ -> - throw({error, {bad_descriptor, FdStr}}) - end. - -ip_family_of(IpFamilyStr) -> - IpFamily = list_to_atom(IpFamilyStr), - case lists:member(IpFamily, [inet, inet6, inet6fb4]) of - true -> - IpFamily; - false -> - throw({error, {bad_ipfamily, IpFamilyStr}}) - end. - - %%------------------------------------------------------------------------- %% accept(SocketType, ListenSocket) -> {ok, Socket} | {error, Reason} %% accept(SocketType, ListenSocket, Timeout) -> ok | {error, Reason} diff --git a/lib/inets/src/http_server/httpd_acceptor.erl b/lib/inets/src/http_server/httpd_acceptor.erl index 08ee9ee0d0..e79dfce0cb 100644 --- a/lib/inets/src/http_server/httpd_acceptor.erl +++ b/lib/inets/src/http_server/httpd_acceptor.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. 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 @@ -24,10 +24,10 @@ -include("inets_internal.hrl"). %% Internal application API --export([start_link/5, start_link/6]). +-export([start_link/6, start_link/7]). %% Other exports (for spawn's etc.) --export([acceptor_init/6, acceptor_init/7, acceptor_loop/5]). +-export([acceptor_init/7, acceptor_init/8, acceptor_loop/6]). %% %% External API @@ -35,27 +35,27 @@ %% start_link -start_link(Manager, SocketType, Addr, Port, ConfigDb, AcceptTimeout) -> +start_link(Manager, SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout) -> ?hdrd("start link", [{manager, Manager}, {socket_type, SocketType}, {address, Addr}, {port, Port}, {timeout, AcceptTimeout}]), - Args = [self(), Manager, SocketType, Addr, Port, ConfigDb, AcceptTimeout], + Args = [self(), Manager, SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout], proc_lib:start_link(?MODULE, acceptor_init, Args). -start_link(Manager, SocketType, ListenSocket, ConfigDb, AcceptTimeout) -> +start_link(Manager, SocketType, ListenSocket, IpFamily, ConfigDb, AcceptTimeout) -> ?hdrd("start link", [{manager, Manager}, {socket_type, SocketType}, {listen_socket, ListenSocket}, {timeout, AcceptTimeout}]), - Args = [self(), Manager, SocketType, ListenSocket, + Args = [self(), Manager, SocketType, ListenSocket, IpFamily, ConfigDb, AcceptTimeout], proc_lib:start_link(?MODULE, acceptor_init, Args). -acceptor_init(Parent, Manager, SocketType, {ListenOwner, ListenSocket}, +acceptor_init(Parent, Manager, SocketType, {ListenOwner, ListenSocket}, IpFamily, ConfigDb, AcceptTimeout) -> ?hdrd("acceptor init", [{parent, Parent}, @@ -66,9 +66,9 @@ acceptor_init(Parent, Manager, SocketType, {ListenOwner, ListenSocket}, {timeout, AcceptTimeout}]), link(ListenOwner), proc_lib:init_ack(Parent, {ok, self()}), - acceptor_loop(Manager, SocketType, ListenSocket, ConfigDb, AcceptTimeout). + acceptor_loop(Manager, SocketType, ListenSocket, IpFamily, ConfigDb, AcceptTimeout). -acceptor_init(Parent, Manager, SocketType, Addr, Port, +acceptor_init(Parent, Manager, SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout) -> ?hdrd("acceptor init", [{parent, Parent}, @@ -77,20 +77,20 @@ acceptor_init(Parent, Manager, SocketType, Addr, Port, {address, Addr}, {port, Port}, {timeout, AcceptTimeout}]), - case (catch do_init(SocketType, Addr, Port)) of + case (catch do_init(SocketType, Addr, Port, IpFamily)) of {ok, ListenSocket} -> proc_lib:init_ack(Parent, {ok, self()}), acceptor_loop(Manager, SocketType, - ListenSocket, ConfigDb, AcceptTimeout); + ListenSocket, IpFamily,ConfigDb, AcceptTimeout); Error -> proc_lib:init_ack(Parent, Error), error end. -do_init(SocketType, Addr, Port) -> +do_init(SocketType, Addr, Port, IpFamily) -> ?hdrt("do init", []), do_socket_start(SocketType), - ListenSocket = do_socket_listen(SocketType, Addr, Port), + ListenSocket = do_socket_listen(SocketType, Addr, Port, IpFamily), {ok, ListenSocket}. @@ -105,9 +105,9 @@ do_socket_start(SocketType) -> end. -do_socket_listen(SocketType, Addr, Port) -> +do_socket_listen(SocketType, Addr, Port, IpFamily) -> ?hdrt("do socket listen", []), - case http_transport:listen(SocketType, Addr, Port) of + case http_transport:listen(SocketType, Addr, Port, IpFamily) of {ok, ListenSocket} -> ListenSocket; {error, Reason} -> @@ -121,7 +121,7 @@ do_socket_listen(SocketType, Addr, Port) -> %% acceptor -acceptor_loop(Manager, SocketType, ListenSocket, ConfigDb, AcceptTimeout) -> +acceptor_loop(Manager, SocketType, ListenSocket, IpFamily, ConfigDb, AcceptTimeout) -> ?hdrd("awaiting accept", [{manager, Manager}, {socket_type, SocketType}, @@ -133,12 +133,12 @@ acceptor_loop(Manager, SocketType, ListenSocket, ConfigDb, AcceptTimeout) -> handle_connection(Manager, ConfigDb, AcceptTimeout, SocketType, Socket), ?MODULE:acceptor_loop(Manager, SocketType, - ListenSocket, ConfigDb,AcceptTimeout); + ListenSocket, IpFamily, ConfigDb,AcceptTimeout); {error, Reason} -> ?hdri("accept failed", [{reason, Reason}]), handle_error(Reason, ConfigDb), ?MODULE:acceptor_loop(Manager, SocketType, ListenSocket, - ConfigDb, AcceptTimeout); + IpFamily, ConfigDb, AcceptTimeout); {'EXIT', Reason} -> ?hdri("accept exited", [{reason, Reason}]), ReasonString = diff --git a/lib/inets/src/http_server/httpd_acceptor_sup.erl b/lib/inets/src/http_server/httpd_acceptor_sup.erl index 8b1e4b6c4f..0cae8dbe47 100644 --- a/lib/inets/src/http_server/httpd_acceptor_sup.erl +++ b/lib/inets/src/http_server/httpd_acceptor_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. 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 @@ -27,7 +27,7 @@ -behaviour(supervisor). %% API --export([start_link/2, start_acceptor/5, start_acceptor/6, stop_acceptor/2]). +-export([start_link/2, start_acceptor/6, start_acceptor/7, stop_acceptor/2]). %% Supervisor callback -export([init/1]). @@ -43,11 +43,11 @@ start_link(Addr, Port) -> %% Function: [start|stop]_acceptor/5 %% Description: Starts/stops an [auth | security] worker (child) process %%---------------------------------------------------------------------- -start_acceptor(SocketType, Addr, Port, ConfigDb, AcceptTimeout) -> - start_worker(httpd_acceptor, SocketType, Addr, Port, +start_acceptor(SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout) -> + start_worker(httpd_acceptor, SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout, self(), []). -start_acceptor(SocketType, Addr, Port, ConfigDb, AcceptTimeout, ListenSocket) -> - start_worker(httpd_acceptor, SocketType, Addr, Port, +start_acceptor(SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout, ListenSocket) -> + start_worker(httpd_acceptor, SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout, ListenSocket, self(), []). @@ -69,18 +69,18 @@ init(_) -> make_name(Addr,Port) -> httpd_util:make_name("httpd_acc_sup", Addr, Port). -start_worker(M, SocketType, Addr, Port, ConfigDB, AcceptTimeout, Manager, Modules) -> +start_worker(M, SocketType, Addr, Port, IpFamily, ConfigDB, AcceptTimeout, Manager, Modules) -> SupName = make_name(Addr, Port), - Args = [Manager, SocketType, Addr, Port, ConfigDB, AcceptTimeout], + Args = [Manager, SocketType, Addr, Port, IpFamily, ConfigDB, AcceptTimeout], Spec = {{M, Addr, Port}, {M, start_link, Args}, permanent, timer:seconds(1), worker, [M] ++ Modules}, supervisor:start_child(SupName, Spec). -start_worker(M, SocketType, Addr, Port, ConfigDB, AcceptTimeout, ListenSocket, +start_worker(M, SocketType, Addr, Port, IpFamily, ConfigDB, AcceptTimeout, ListenSocket, Manager, Modules) -> SupName = make_name(Addr, Port), - Args = [Manager, SocketType, ListenSocket, ConfigDB, AcceptTimeout], + Args = [Manager, SocketType, ListenSocket, IpFamily, ConfigDB, AcceptTimeout], Spec = {{M, Addr, Port}, {M, start_link, Args}, permanent, timer:seconds(1), worker, [M] ++ Modules}, diff --git a/lib/inets/src/http_server/httpd_manager.erl b/lib/inets/src/http_server/httpd_manager.erl index 7bafc3f7f3..524e579ec0 100644 --- a/lib/inets/src/http_server/httpd_manager.erl +++ b/lib/inets/src/http_server/httpd_manager.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% Copyright Ericsson AB 2000-2014. 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 @@ -264,11 +264,12 @@ init([ConfigFile, ConfigList, AcceptTimeout, Addr, Port, ListenInfo]) -> end. do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port) -> + IpFamily = proplists:get_value(ipfamily, ConfigList, inet6fb4), NewConfigFile = proplists:get_value(file, ConfigList, ConfigFile), ConfigDB = do_initial_store(ConfigList), SocketType = httpd_conf:lookup_socket_type(ConfigDB), case httpd_acceptor_sup:start_acceptor(SocketType, Addr, - Port, ConfigDB, AcceptTimeout) of + Port, IpFamily, ConfigDB, AcceptTimeout) of {ok, _Pid} -> Status = [{max_conn, 0}, {last_heavy_load, never}, @@ -284,11 +285,12 @@ do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port) -> end. do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port, ListenInfo) -> + IpFamily = proplists:get_value(ipfamily, ConfigList, inet6fb4), NewConfigFile = proplists:get_value(file, ConfigList, ConfigFile), ConfigDB = do_initial_store(ConfigList), SocketType = httpd_conf:lookup_socket_type(ConfigDB), case httpd_acceptor_sup:start_acceptor(SocketType, Addr, - Port, ConfigDB, + Port, IpFamily, ConfigDB, AcceptTimeout, ListenInfo) of {ok, _Pid} -> Status = [{max_conn,0}, {last_heavy_load,never}, diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl index 4be298939f..da641cf533 100644 --- a/lib/inets/src/http_server/httpd_sup.erl +++ b/lib/inets/src/http_server/httpd_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2013. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. 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 @@ -248,7 +248,8 @@ listen(Address, Port, Config) -> case http_transport:start(SocketType) of ok -> Fd = proplists:get_value(fd, Config), - case http_transport:listen(SocketType, Address, Port, Fd) of + IpFamily = proplists:get_value(ipfamily, Config, inet6fb4), + case http_transport:listen(SocketType, Address, Port, Fd, IpFamily) of {ok, ListenSocket} -> NewConfig = proplists:delete(port, Config), {NewPort, _} = http_transport:sockname(SocketType, ListenSocket), -- cgit v1.2.3 From 86949cb7c32d1c07f9867e49d4fdcc6d47a7d216 Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Fri, 15 Feb 2013 15:01:10 +0100 Subject: Removed case clause for length = 0 for both HTTP1.0 and HTTP1.1 --- lib/inets/src/http_client/httpc_response.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/inets/src/http_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl index 37f5f2ce6d..7fd595c071 100644 --- a/lib/inets/src/http_client/httpc_response.erl +++ b/lib/inets/src/http_client/httpc_response.erl @@ -430,8 +430,6 @@ format_response({StatusLine, Headers, Body}) -> Length = list_to_integer(Headers#http_response_h.'content-length'), {NewBody, Data} = case Length of - 0 -> - {Body, <<>>}; -1 -> % When no lenght indicator is provided {Body, <<>>}; Length when (Length =< size(Body)) -> -- cgit v1.2.3 From 023a3b53e6011e05f46013104146fcd6df97fd75 Mon Sep 17 00:00:00 2001 From: serge Date: Thu, 11 Oct 2012 23:53:55 -0400 Subject: Handle correctly the "No files found or file unavailable" error code. --- lib/inets/src/ftp/ftp.erl | 4 ++++ lib/inets/src/ftp/ftp_response.erl | 3 +++ 2 files changed, 7 insertions(+) diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index fe25c23316..132a384a49 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -1899,6 +1899,10 @@ ctrl_result_response(pos_compl, #state{client = From} = State, _) -> gen_server:reply(From, ok), {noreply, State#state{client = undefined, caller = undefined}}; +ctrl_result_response(enofile, #state{client = From} = State, _) -> + gen_server:reply(From, {error, enofile}), + {noreply, State#state{client = undefined, caller = undefined}}; + ctrl_result_response(Status, #state{client = From} = State, _) when (Status =:= etnospc) orelse (Status =:= epnospc) orelse diff --git a/lib/inets/src/ftp/ftp_response.erl b/lib/inets/src/ftp/ftp_response.erl index faeacb31ab..364f534737 100644 --- a/lib/inets/src/ftp/ftp_response.erl +++ b/lib/inets/src/ftp/ftp_response.erl @@ -162,6 +162,7 @@ error_string(epath) -> "No such file or directory, already exists, " error_string(etype) -> "No such type."; error_string(euser) -> "User name or password not valid."; error_string(etnospc) -> "Insufficient storage space in system."; +error_string(enofile) -> "No files found or file unavailable"; error_string(epnospc) -> "Exceeded storage allocation " "(for current directory or dataset)."; error_string(efnamena) -> "File name not allowed."; @@ -180,6 +181,8 @@ interpret_status(?POS_COMPL,_,_) -> pos_compl; interpret_status(?POS_INTERM,?AUTH_ACC,2) -> pos_interm_acct; %% Positive Intermediate Reply interpret_status(?POS_INTERM,_,_) -> pos_interm; +%% No files found or file not available +interpret_status(?TRANS_NEG_COMPL,?FILE_SYSTEM,0) -> enofile; %% No storage area no action taken interpret_status(?TRANS_NEG_COMPL,?FILE_SYSTEM,2) -> etnospc; %% Temporary Error, no action taken -- cgit v1.2.3 From 68cecbfa6a9d79b9137c2cf797b0499c9daed27b Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Tue, 12 Mar 2013 16:19:59 +0100 Subject: Added guard for invalid field and values in headers --- lib/inets/src/http_client/httpc.erl | 16 +++++++++++++--- lib/inets/test/httpc_SUITE.erl | 7 ++++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index eb7102d046..fc2081239e 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -163,8 +163,13 @@ request(Method, {error, Reason} -> {error, Reason}; {ok, ParsedUrl} -> - handle_request(Method, Url, ParsedUrl, Headers, [], [], - HTTPOptions, Options, Profile) + case header_parse(Headers) of + {error, Reason} -> + {error, Reason}; + _ -> + handle_request(Method, Url, ParsedUrl, Headers, [], [], + HTTPOptions, Options, Profile) + end end; request(Method, @@ -1247,7 +1252,12 @@ uri_parse(URI, Opts) -> %%-------------------------------------------------------------------------- - +header_parse([]) -> + ok; +header_parse([{Field, Value}|T]) when is_list(Field), is_list(Value) -> + header_parse(T); +header_parse(_) -> + {error, {headers_error, not_strings}}. child_name2info(undefined) -> {error, no_such_service}; child_name2info(httpc_manager) -> diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 43eaa48961..15792a1b36 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -83,7 +83,8 @@ real_requests()-> stream_through_fun, stream_through_mfa, streaming_error, - inet_opts + inet_opts, + invalid_headers ]. only_simulated() -> @@ -795,6 +796,10 @@ headers_dummy(Config) when is_list(Config) -> %%------------------------------------------------------------------------- +invalid_headers(Config) -> + Request = {url(group_name(Config), "/dummy.html", Config), [{"cookie", undefined}]}, + {error, _} = httpc:request(get, Request, [], []). + remote_socket_close(Config) when is_list(Config) -> URL = url(group_name(Config), "/just_close.html", Config), {error, socket_closed_remotely} = httpc:request(URL). -- cgit v1.2.3 From 808502d8122a7e5c136f2e0079db68af5690e959 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Mon, 18 Mar 2013 10:22:03 +0100 Subject: inets httpc: Revert incorrect commit regarding cookie handling. --- lib/inets/src/http_client/httpc_handler.erl | 2 +- lib/inets/test/httpc_SUITE.erl | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index 54db6a5c81..6b8f33f9b8 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -1142,7 +1142,7 @@ handle_response(#state{request = Request, {session, Session}, {status_line, StatusLine}]), - handle_cookies(Headers, Request, Options, httpc_manager), %% FOO profile_name + handle_cookies(Headers, Request, Options, ProfileName), case httpc_response:result({StatusLine, Headers, Body}, Request) of %% 100-continue continue -> diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 15792a1b36..bf993c2705 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -90,6 +90,7 @@ real_requests()-> only_simulated() -> [ cookie, + cookie_profile, trace, stream_once, no_content_204, @@ -489,9 +490,37 @@ cookie(Config) when is_list(Config) -> {ok, {{_,200,_}, [_ | _], [_|_]}} = httpc:request(get, Request1, [], []), + [{session_cookies, [_|_]}] = httpc:which_cookies(httpc:default_profile()), + ets:delete(cookie), ok = httpc:set_options([{cookies, disabled}]). + + +%%------------------------------------------------------------------------- +cookie_profile() -> + [{doc, "Test cookies on a non default profile."}]. +cookie_profile(Config) when is_list(Config) -> + inets:start(httpc, [{profile, cookie_test}]), + ok = httpc:set_options([{cookies, enabled}], cookie_test), + + Request0 = {url(group_name(Config), "/cookie.html", Config), []}, + + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, Request0, [], [], cookie_test), + + %% Populate table to be used by the "dummy" server + ets:new(cookie, [named_table, public, set]), + ets:insert(cookie, {cookies, true}), + + Request1 = {url(group_name(Config), "/", Config), []}, + + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, Request1, [], [], cookie_test), + + ets:delete(cookie), + inets:stop(httpc, cookie_test). + %%------------------------------------------------------------------------- headers_as_is(doc) -> ["Test the option headers_as_is"]; -- cgit v1.2.3 From 1cce96320d8e56dba2bf2357eef5a23f9bca1fa7 Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Mon, 15 Apr 2013 10:32:15 +0200 Subject: inets: stop block timer with the right arguments --- lib/inets/src/http_server/httpd_manager.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/inets/src/http_server/httpd_manager.erl b/lib/inets/src/http_server/httpd_manager.erl index 524e579ec0..3a486614b8 100644 --- a/lib/inets/src/http_server/httpd_manager.erl +++ b/lib/inets/src/http_server/httpd_manager.erl @@ -662,11 +662,11 @@ handle_unblock(S, FromA) -> handle_unblock(S, _FromA, unblocked) -> {ok,S}; handle_unblock(S, FromA, _AdminState) -> - stop_block_tmr(S#state.blocking_tmr), case S#state.blocking_tmr of - {_Tmr,FromB,Ref} -> + {Tmr,FromB,Ref} -> %% Another process is trying to unblock %% Inform the blocker + stop_block_tmr(Tmr), FromB ! {block_reply, {error,{unblocked,FromA}},Ref}; _ -> ok -- cgit v1.2.3 From 0f1fc86b2528b2f58b868dace3aa053e3867aa77 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 28 Jun 2013 10:51:27 +0200 Subject: Update release notes Conflicts: lib/inets/doc/src/notes.xml --- lib/inets/doc/src/notes.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 80c06ffadd..f99bb14c5d 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -4,7 +4,7 @@
- 20022012 + 20022014 Ericsson AB. All Rights Reserved. @@ -32,7 +32,6 @@ notes.xml
-
Inets 5.9.2.2
Improvements and New Features @@ -50,7 +49,6 @@
Inets 5.9.2.1 -
Improvements and New Features -- cgit v1.2.3 From 0188fe68b8e163e6f04579cb1a9ab80ff4893cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Wei=C3=9Fl?= Date: Wed, 24 Jul 2013 00:12:08 +0200 Subject: Fix some errors in the inets documentation --- lib/inets/doc/src/httpc.xml | 6 +++--- lib/inets/doc/src/httpd.xml | 22 +++++++++++----------- lib/inets/doc/src/httpd_util.xml | 4 ++-- lib/inets/doc/src/mod_alias.xml | 4 ++-- lib/inets/doc/src/notes_history.xml | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index b1f4e6dfe4..91685db49f 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -300,11 +300,11 @@ filename() = string() process or to a file. When streaming to the calling process using the option self the following stream messages will be sent to that process: {http, {RequestId, - stream_start, Headers}, {http, {RequestId, stream, - BinBodyPart}, {http, {RequestId, stream_end, Headers}. When + stream_start, Headers}}, {http, {RequestId, stream, + BinBodyPart}}, {http, {RequestId, stream_end, Headers}}. When streaming to to the calling processes using the option {self, once} the first message will have an additional - element e.i. {http, {RequestId, stream_start, Headers, Pid}, + element e.i. {http, {RequestId, stream_start, Headers, Pid}}, this is the process id that should be used as an argument to http:stream_next/1 to trigger the next message to be sent to the calling process.

diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index 58293f79fc..e02959f4c1 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -406,7 +406,7 @@ bytes begins with url-path is mapped to local files that begins with directory-filename, for example: - {alias, {"/image", "/ftp/pub/image"} + {alias, {"/image", "/ftp/pub/image"}} and an access to http://your.server.org/image/foo.gif would refer to the file /ftp/pub/image/foo.gif.

@@ -421,7 +421,7 @@ bytes by re:replace/3 to produce a path in the local filesystem. For example: - {re_write, {"^/[~]([^/]+)(.*)$", "/home/\\1/public\\2"} + {re_write, {"^/[~]([^/]+)(.*)$", "/home/\\1/public\\2"}} and an access to http://your.server.org/~bob/foo.gif would refer to the file /home/bob/public/foo.gif. @@ -468,7 +468,7 @@ bytes scripts. URLs with a path beginning with url-path are mapped to scripts beginning with directory-filename, for example: - {script_alias, {"/cgi-bin/", "/web/cgi-bin/"} + {script_alias, {"/cgi-bin/", "/web/cgi-bin/"}} and an access to http://your.server.org/cgi-bin/foo would cause the server to run the script /web/cgi-bin/foo.

@@ -483,7 +483,7 @@ bytes scripts. URLs with a path beginning with url-path are mapped to scripts beginning with directory-filename, for example: - {script_re_write, {"^/cgi-bin/(\\d+)/", "/web/\\1/cgi-bin/"} + {script_re_write, {"^/cgi-bin/(\\d+)/", "/web/\\1/cgi-bin/"}} and an access to http://your.server.org/cgi-bin/17/foo would cause the server to run the script /web/17/cgi-bin/foo.

@@ -517,7 +517,7 @@ bytes the standard CGI PATH_INFO and PATH_TRANSLATED environment variables. - {action, {"text/plain", "/cgi-bin/log_and_deliver_text"} + {action, {"text/plain", "/cgi-bin/log_and_deliver_text"}}

@@ -532,7 +532,7 @@ bytes the standard CGI PATH_INFO and PATH_TRANSLATED environment variables. - {script, {"PUT", "/cgi-bin/put"} + {script, {"PUT", "/cgi-bin/put"}}

@@ -549,7 +549,7 @@ bytes scheme scripts. A matching URL is mapped into a specific module and function. For example: - {erl_script_alias, {"/cgi-bin/example", [httpd_example]} + {erl_script_alias, {"/cgi-bin/example", [httpd_example]}} and a request to @@ -706,7 +706,7 @@ bytes For example: - {allow_from, ["123.34.56.11", "150.100.23"] + {allow_from, ["123.34.56.11", "150.100.23"]} The host 123.34.56.11 and all machines on the 150.100.23 subnet are allowed access.

@@ -719,7 +719,7 @@ bytes which should be denied access to a given directory. For example: - {deny_from, ["123.34.56.11", "150.100.23"] + {deny_from, ["123.34.56.11", "150.100.23"]} The host 123.34.56.11 and all machines on the 150.100.23 subnet are not allowed access.

@@ -835,7 +835,7 @@ bytes

Security properties - requires mod_security

-

{security_directory, {path(), [{property(), term()}]}

+

{security_directory, {path(), [{property(), term()}]}}

Here follows the valid properties for security directories

@@ -1067,7 +1067,7 @@ bytes Called for each request to the Web server. OldData = list() - NewData = [{response,{StatusCode,Body}}] | [{response,{response,Head,Body}}] | [{response,{already_sent,Statuscode,Size}] + NewData = [{response,{StatusCode,Body}}] | [{response,{response,Head,Body}}] | [{response,{already_sent,Statuscode,Size}}] StausCode = integer() Body = io_list() | nobody | {Fun, Arg} Head = [HeaderOption] diff --git a/lib/inets/doc/src/httpd_util.xml b/lib/inets/doc/src/httpd_util.xml index 9f290084d2..9218ee91e2 100644 --- a/lib/inets/doc/src/httpd_util.xml +++ b/lib/inets/doc/src/httpd_util.xml @@ -337,10 +337,10 @@ rfc1123_date() -> RFC1123Date - rfc1123_date({{YYYY,MM,DD},{Hour,Min,Sec}}}) -> RFC1123Date + rfc1123_date({{YYYY,MM,DD},{Hour,Min,Sec}}) -> RFC1123Date Return the current date in RFC 1123 format. - YYYY = MM = DD = Hour = Min =Sec = integer() + YYYY = MM = DD = Hour = Min = Sec = integer() RFC1123Date = string() diff --git a/lib/inets/doc/src/mod_alias.xml b/lib/inets/doc/src/mod_alias.xml index 265a1b8e76..b38be5db28 100644 --- a/lib/inets/doc/src/mod_alias.xml +++ b/lib/inets/doc/src/mod_alias.xml @@ -118,7 +118,7 @@ - real_script_name(ConfigDB,RequestURI,ScriptAliases) -> Ret + real_script_name(ConfigDB, RequestURI, ScriptAliases) -> Ret Expand a request uri using ScriptAlias config directives. ConfigDB = config_db() @@ -129,7 +129,7 @@ -

real_name/3 traverses ScriptAliases, +

real_script_name/3 traverses ScriptAliases, typically extracted from ConfigDB, and matches each FakeName with RequestURI. If a match is found FakeName is replaced with RealName in the diff --git a/lib/inets/doc/src/notes_history.xml b/lib/inets/doc/src/notes_history.xml index bd59c1ba47..4162ab97bb 100644 --- a/lib/inets/doc/src/notes_history.xml +++ b/lib/inets/doc/src/notes_history.xml @@ -834,7 +834,7 @@

[ftp, client] - A new option {progress, {CBmodule, - CBFunction, InitProgressTerm} has been added to allow + CBFunction, InitProgressTerm}} has been added to allow users to create things such as progress bars in there GUI's. The option affects ftp:send/[3,4] and ftp:recv/[3,4].

-- cgit v1.2.3 From 5bf88f6341c8414ce59b0c069b106ed044cf1bc4 Mon Sep 17 00:00:00 2001 From: tomaon Date: Fri, 2 Aug 2013 12:13:48 +0900 Subject: fix: typo, inets/doc/src/httpd --- lib/inets/doc/src/httpd.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index e02959f4c1..776d6e439b 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -251,14 +251,14 @@ - {max_uri, integer()} + {max_uri_size, integer()}

Limits the size of the HTTP request URI. By default there is no limit.

- {max_keep_alive_requests, integer()} + {max_keep_alive_request, integer()}

The number of request that a client can do on one connection. When the server has responded to the number of @@ -632,7 +632,7 @@ bytes - {error_disk_log, internal | external} + {error_disk_log, path()}

Defines the filename of the (disk_log(3)) error log file to be used to log server errors. If the filename does not begin -- cgit v1.2.3 From 1623d54ce6a552eb6802519c0ffe1f3616fab4aa Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Thu, 15 Aug 2013 15:10:31 +0200 Subject: [inets, ssl]: make log_alert configurable as option in ssl, SSLLogLevel added as option to inets conf file Conflicts: lib/inets/src/http_lib/http_transport.erl lib/inets/src/inets_app/inets.appup.src lib/ssl/src/ssl.appup.src --- lib/inets/src/http_lib/http_transport.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index 6ac95a9572..b59b3a781b 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -159,7 +159,7 @@ listen(ip_comm = _SocketType, Addr, Port, Fd, IpFamily) -> listen_ip_comm(Addr, Port, Fd, IpFamily); listen({essl, SSLConfig}, Addr, Port, Fd, IpFamily) -> - listen_ssl(Addr, Port, Fd, SSLConfig, IpFamily). + listen_ssl(Addr, Port, Fd, SSLConfig, IpFamily, []). listen(ip_comm = _SocketType, Addr, Port, IpFamily) -> listen_ip_comm(Addr, Port, undefined, IpFamily); -- cgit v1.2.3 From 79377d58e3f42a3f866998f2135c42c05a51d945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Wei=C3=9Fl?= Date: Sat, 22 Jun 2013 20:21:45 +0200 Subject: Fix httpd config option 'erl_script_nocache' This fixes the usage of the httpd configuration option 'erl_script_nocache', which got ignored before. Attention: Before, the option 'script_nocache' also changed the behavior for mod_esi, now it only affects mod_cgi. --- lib/inets/src/http_server/httpd_response.erl | 6 +++--- lib/inets/src/http_server/mod_cgi.erl | 2 +- lib/inets/src/http_server/mod_esi.erl | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl index 6b6532266b..a45b04f275 100644 --- a/lib/inets/src/http_server/httpd_response.erl +++ b/lib/inets/src/http_server/httpd_response.erl @@ -20,7 +20,7 @@ -module(httpd_response). -export([generate_and_send_response/1, send_status/3, send_header/3, send_body/3, send_chunk/3, send_final_chunk/2, split_header/2, - is_disable_chunked_send/1, cache_headers/1]). + is_disable_chunked_send/1, cache_headers/2]). -export([map_status_code/2]). -include("httpd.hrl"). @@ -266,8 +266,8 @@ get_connection(false,"HTTP/1.1") -> get_connection(_,_) -> "". -cache_headers(#mod{config_db = Db}) -> - case httpd_util:lookup(Db, script_nocache, false) of +cache_headers(#mod{config_db = Db}, NoCacheType) -> + case httpd_util:lookup(Db, NoCacheType, false) of true -> Date = httpd_util:rfc1123_date(), [{"cache-control", "no-cache"}, diff --git a/lib/inets/src/http_server/mod_cgi.erl b/lib/inets/src/http_server/mod_cgi.erl index c854166c29..f1b73810e6 100644 --- a/lib/inets/src/http_server/mod_cgi.erl +++ b/lib/inets/src/http_server/mod_cgi.erl @@ -295,7 +295,7 @@ receive_headers(Port, Module, Function, Args, Timeout) -> end. send_headers(ModData, {StatusCode, _}, HTTPHeaders) -> - ExtraHeaders = httpd_response:cache_headers(ModData), + ExtraHeaders = httpd_response:cache_headers(ModData, script_nocache), httpd_response:send_header(ModData, StatusCode, ExtraHeaders ++ HTTPHeaders). diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl index e36c33b282..b11df34f9e 100644 --- a/lib/inets/src/http_server/mod_esi.erl +++ b/lib/inets/src/http_server/mod_esi.erl @@ -440,7 +440,7 @@ receive_headers(Timeout) -> end. send_headers(ModData, StatusCode, HTTPHeaders) -> - ExtraHeaders = httpd_response:cache_headers(ModData), + ExtraHeaders = httpd_response:cache_headers(ModData, erl_script_nocache), httpd_response:send_header(ModData, StatusCode, ExtraHeaders ++ HTTPHeaders). -- cgit v1.2.3 From 459c933d7c186ea7d862b9fe4f308e9ea632a3d2 Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Wed, 21 Aug 2013 11:23:20 +0200 Subject: inets: added simple testcase for option erl_script_nocache --- lib/inets/test/httpd_basic_SUITE.erl | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl index 523cf9d38c..92024b6fdf 100644 --- a/lib/inets/test/httpd_basic_SUITE.erl +++ b/lib/inets/test/httpd_basic_SUITE.erl @@ -33,7 +33,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [ uri_too_long_414, - header_too_long_413, + header_too_long_413, + erl_script_nocache_opt, escaped_url_in_error_body, slowdose ]. @@ -178,6 +179,28 @@ header_too_long_413(Config) when is_list(Config) -> {version, "HTTP/1.1"}]), inets:stop(httpd, Pid). +%%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- + +erl_script_nocache_opt(doc) -> + ["Test that too long headers's get 413 HTTP code"]; +erl_script_nocache_opt(suite) -> + []; +erl_script_nocache_opt(Config) when is_list(Config) -> + HttpdConf = ?config(httpd_conf, Config), + {ok, Pid} = inets:start(httpd, [{port, 0}, {erl_script_nocache, true} | HttpdConf]), + Info = httpd:info(Pid), + Port = proplists:get_value(port, Info), + _Address = proplists:get_value(bind_address, Info), + URL1 = ?URL_START ++ integer_to_list(Port), + case httpc:request(get, {URL1 ++ "/dummy.html", []}, + [{url_encode, false}, + {version, "HTTP/1.0"}], + [{full_result, false}]) of + {ok, {200, _}} -> + ok + end, + inets:stop(httpd, Pid). %%------------------------------------------------------------------------- %%------------------------------------------------------------------------- -- cgit v1.2.3 From e5764c3299eb5c500b1f4e3d743ec2c780e4b6bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Wei=C3=9Fl?= Date: Sat, 31 Aug 2013 04:08:13 +0200 Subject: Test nocache option of mod_cgi and mod_esi Tests that the nocache options work and can be used independently from each other. This was broken before c8ef69c. Conflicts: lib/inets/test/httpd_basic_SUITE.erl --- lib/inets/test/Makefile | 2 +- lib/inets/test/httpd_SUITE.erl | 2 +- lib/inets/test/httpd_basic_SUITE.erl | 63 +++++++++++++++++++++- lib/inets/test/httpd_basic_SUITE_data/printenv.bat | 1 + lib/inets/test/httpd_basic_SUITE_data/printenv.sh | 1 + lib/inets/test/httpd_mod.erl | 15 ++++++ lib/inets/test/httpd_test_lib.erl | 2 +- 7 files changed, 82 insertions(+), 4 deletions(-) create mode 120000 lib/inets/test/httpd_basic_SUITE_data/printenv.bat create mode 120000 lib/inets/test/httpd_basic_SUITE_data/printenv.sh diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile index 0e7954fba8..b1daf51e25 100644 --- a/lib/inets/test/Makefile +++ b/lib/inets/test/Makefile @@ -215,7 +215,7 @@ INETS_FILES = inets.config $(INETS_SPECS) # inets_tftp_suite INETS_DATADIRS = inets_SUITE_data inets_sup_SUITE_data -HTTPD_DATADIRS = httpd_test_data httpd_SUITE_data +HTTPD_DATADIRS = httpd_test_data httpd_SUITE_data httpd_basic_SUITE_data HTTPC_DATADIRS = httpc_SUITE_data httpc_proxy_SUITE_data FTP_DATADIRS = ftp_SUITE_data diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index 42a5026d72..b84a806305 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -1929,7 +1929,7 @@ ticket_5865(Config) -> " HTTP/1.1\r\nHost:" ++Host++"\r\n\r\n", [{statuscode, 200}, - {no_last_modified, + {no_header, "last-modified"}]), ok; {error, Reason} -> diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl index 92024b6fdf..d2d043a89f 100644 --- a/lib/inets/test/httpd_basic_SUITE.erl +++ b/lib/inets/test/httpd_basic_SUITE.erl @@ -19,6 +19,7 @@ %% -module(httpd_basic_SUITE). +-include_lib("kernel/include/file.hrl"). -include_lib("common_test/include/ct.hrl"). -include("inets_test_lib.hrl"). @@ -35,6 +36,7 @@ all() -> uri_too_long_414, header_too_long_413, erl_script_nocache_opt, + script_nocache, escaped_url_in_error_body, slowdose ]. @@ -63,6 +65,7 @@ init_per_suite(Config) -> "~n Config: ~p", [Config]), ok = inets:start(), PrivDir = ?config(priv_dir, Config), + DataDir = ?config(data_dir, Config), Dummy = " @@ -75,6 +78,18 @@ DUMMY ", DummyFile = filename:join([PrivDir,"dummy.html"]), + CgiDir = filename:join(PrivDir, "cgi-bin"), + ok = file:make_dir(CgiDir), + Cgi = case test_server:os_type() of + {win32, _} -> + "printenv.bat"; + _ -> + "printenv.sh" + end, + inets_test_lib:copy_file(Cgi, DataDir, CgiDir), + AbsCgi = filename:join([CgiDir, Cgi]), + {ok, FileInfo} = file:read_file_info(AbsCgi), + ok = file:write_file_info(AbsCgi, FileInfo#file_info{mode = 8#00755}), {ok, Fd} = file:open(DummyFile, [write]), ok = file:write(Fd, Dummy), ok = file:close(Fd), @@ -85,7 +100,7 @@ DUMMY {document_root, PrivDir}, {bind_address, "localhost"}], - [{httpd_conf, HttpdConf} | Config]. + [{httpd_conf, HttpdConf}, {cgi_dir, CgiDir}, {cgi_script, Cgi} | Config]. %%-------------------------------------------------------------------- %% Function: end_per_suite(Config) -> _ @@ -205,6 +220,52 @@ erl_script_nocache_opt(Config) when is_list(Config) -> %%------------------------------------------------------------------------- %%------------------------------------------------------------------------- +script_nocache(doc) -> + ["Test nocache option for mod_cgi and mod_esi"]; +script_nocache(suite) -> + []; +script_nocache(Config) when is_list(Config) -> + Normal = {no_header, "cache-control"}, + NoCache = {header, "cache-control", "no-cache"}, + verify_script_nocache(Config, false, false, Normal, Normal), + verify_script_nocache(Config, true, false, NoCache, Normal), + verify_script_nocache(Config, false, true, Normal, NoCache), + verify_script_nocache(Config, true, true, NoCache, NoCache), + ok. + +verify_script_nocache(Config, CgiNoCache, EsiNoCache, CgiOption, EsiOption) -> + HttpdConf = ?config(httpd_conf, Config), + CgiScript = ?config(cgi_script, Config), + CgiDir = ?config(cgi_dir, Config), + {ok, Pid} = inets:start(httpd, [{port, 0}, + {script_alias, + {"/cgi-bin/", CgiDir ++ "/"}}, + {script_nocache, CgiNoCache}, + {erl_script_alias, + {"/cgi-bin/erl", [httpd_example,io]}}, + {erl_script_nocache, EsiNoCache} + | HttpdConf]), + Info = httpd:info(Pid), + Port = proplists:get_value(port, Info), + Address = proplists:get_value(bind_address, Info), + ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(), + "GET /cgi-bin/" ++ CgiScript ++ + " HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + CgiOption, + {version, "HTTP/1.0"}]), + ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(), + "GET /cgi-bin/erl/httpd_example:get " + "HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + EsiOption, + {version, "HTTP/1.0"}]), + inets:stop(httpd, Pid). + + +%%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- + escaped_url_in_error_body(doc) -> ["Test Url-encoding see OTP-8940"]; escaped_url_in_error_body(suite) -> diff --git a/lib/inets/test/httpd_basic_SUITE_data/printenv.bat b/lib/inets/test/httpd_basic_SUITE_data/printenv.bat new file mode 120000 index 0000000000..1bc8e52059 --- /dev/null +++ b/lib/inets/test/httpd_basic_SUITE_data/printenv.bat @@ -0,0 +1 @@ +../httpd_SUITE_data/server_root/cgi-bin/printenv.bat \ No newline at end of file diff --git a/lib/inets/test/httpd_basic_SUITE_data/printenv.sh b/lib/inets/test/httpd_basic_SUITE_data/printenv.sh new file mode 120000 index 0000000000..0136a3fa23 --- /dev/null +++ b/lib/inets/test/httpd_basic_SUITE_data/printenv.sh @@ -0,0 +1 @@ +../httpd_SUITE_data/server_root/cgi-bin/printenv.sh \ No newline at end of file diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl index 075409e2eb..558958424a 100644 --- a/lib/inets/test/httpd_mod.erl +++ b/lib/inets/test/httpd_mod.erl @@ -842,6 +842,14 @@ cgi(Type, Port, Host, Node) -> {version, "HTTP/1.0"}]), %% tsp("cgi -> done"), + + %% Check "ScriptNoCache" directive (default: false) + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/" ++ Script ++ + " HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {no_header, "cache-control"}, + {version, "HTTP/1.0"}]), ok. @@ -899,6 +907,13 @@ esi(Type, Port, Host, Node) -> " HTTP/1.0\r\n\r\n", [{statuscode, 302}, {version, "HTTP/1.0"}]), + %% Check "ErlScriptNoCache" directive (default: false) + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/erl/httpd_example:get" + " HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {no_header, "cache-control"}, + {version, "HTTP/1.0"}]), ok. diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl index f9533e8af9..11d41726e5 100644 --- a/lib/inets/test/httpd_test_lib.erl +++ b/lib/inets/test/httpd_test_lib.erl @@ -361,7 +361,7 @@ do_validate(Header, [{header, HeaderField, Value}|Rest],N,P) -> tsf({wrong_header_field_value, LowerHeaderField, Header}) end, do_validate(Header, Rest, N, P); -do_validate(Header,[{no_last_modified, HeaderField}|Rest],N,P) -> +do_validate(Header,[{no_header, HeaderField}|Rest],N,P) -> case lists:keysearch(HeaderField,1,Header) of {value,_} -> tsf({wrong_header_field_value, HeaderField, Header}); -- cgit v1.2.3 From 55d5420de8a4b765cc32dfbe839ac7134f8f0e22 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Mon, 2 Sep 2013 15:49:30 +0200 Subject: inets: Add stronger RSA test-keys The ones provided will fail inets test cases as default ssl will negotiate stronger cipher suites now that will fail the with the weak keys. --- .../test/httpc_SUITE_data/ssl_client_cert.pem | 45 +++++++++++++--------- .../test/httpc_SUITE_data/ssl_server_cert.pem | 45 +++++++++++++--------- .../server_root/ssl/ssl_client.pem | 45 +++++++++++++--------- .../server_root/ssl/ssl_server.pem | 45 +++++++++++++--------- 4 files changed, 108 insertions(+), 72 deletions(-) diff --git a/lib/inets/test/httpc_SUITE_data/ssl_client_cert.pem b/lib/inets/test/httpc_SUITE_data/ssl_client_cert.pem index f274d2021d..427447958d 100644 --- a/lib/inets/test/httpc_SUITE_data/ssl_client_cert.pem +++ b/lib/inets/test/httpc_SUITE_data/ssl_client_cert.pem @@ -1,22 +1,31 @@ -----BEGIN RSA PRIVATE KEY----- -MIIBOwIBAAJBANz7eFvORmJDi1XJMM2U3uHC5wmp/DXTLMw08XaEvtZ73wgVg84E -V0oyX3Kh1thRE3Hch9AyrHjgpizCj9/Ra38CAwEAAQJACzpz2SZYCTIpaEh6xFdm -I86FcsZCXHHIeu/NvRntoHQ+nfM7Np379+z6XNJWIcWh/QgG/jNJalR1BO+eyc6/ -YQIhAP3m8M0LDxJwSgHFtGAGatQqaqw9l48Kq5xdMFqvdpiHAiEA3s7lld6yCJYu -6q7fZjTH+eKUwgg0vpgJutP7Fsok60kCIHHesQBEhW3vjkFdOZgXSLH+k/jLZr1w -O6bU5GrHZpjhAiEAyTvGYcjDtTunXjDY9l+fadK6FlEBCk8ZIpNIiTnDhHkCIQDr -QxxLLuNHRj8iWNbuVVZ99SJy8zC33pMgPFaFKaZesQ== +MIICXQIBAAKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSVwC+n +0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53h2Zr +3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwIDAQAB +AoGACdIVYe/LTeydUihtInC8lZ2QuPgJmoBNocRjqJFipEihoL4scHAx25n1bBvB +I0HZphffzBkGp28oBAtl2LRPWXqu527unc/RWRfLMqSK1xNSq1DxD1a30zkrZPna +QiV65vEJuNSJTtlDy/Zqc/BVZXCpxWlzYQedZgkmf0Qse8ECQQCmaz02Yur8zC9f +eSQKU5OSzGw3bSIumEzziCfHdTheK6MEoccf5TCAyLXhZwA7QlKja4tFXfeyVxws +/LlnUJN9AkEA4j+xnOeYUyGKXL5i+BAbnqpI4MzPiq+IoCYkaRlD/wAws24r5HNI +ZQmEHWqD/NNzOf/A2XuyLtMiTGJPW/DftwJBAKKpJP6Ytuh6xz8BUCnLwO12Y7vV +LtjuQiCzD3aUa5EYA9HOMqxJPxxRkf0LyR0i2VUkE8+sZiPpov+R0cJa7p0CQQCj +40GUiArGRSiF7/+e84QeVfl+pb29F1QftiFv5DZmFEwy3Z572KpbTh5edJbxYHY6 +UDHxGHJFCvnwXNJhpkVXAkBJqfEfiMJ3Q/E5Gpf3sQizacouW92iiN8ojlF1oB80 +t34RysJH7SgI3gdMhTribCo2UUaV0StjR6yodPN+TB2J -----END RSA PRIVATE KEY----- -----BEGIN CERTIFICATE----- -MIIB7jCCAZgCAQAwDQYJKoZIhvcNAQEEBQAwgYExCzAJBgNVBAYTAlNFMRIwEAYD -VQQHEwlTdG9ja2hvbG0xETAPBgNVBAoTCEVyaWNzc29uMQwwCgYDVQQLEwNFVFgx -FjAUBgNVBAMTDUhlbGVuIEFpcml5YW4xJTAjBgkqhkiG9w0BCQEWFmhlbGVuQGVy -aXguZXJpY3Nzb24uc2UwHhcNOTcwNzI4MDcxNDI1WhcNOTgxMjEwMDcxNDI1WjCB -gTELMAkGA1UEBhMCU0UxEjAQBgNVBAcTCVN0b2NraG9sbTERMA8GA1UEChMIRXJp -Y3Nzb24xDDAKBgNVBAsTA0VUWDEWMBQGA1UEAxMNSGVsZW4gQWlyaXlhbjElMCMG -CSqGSIb3DQEJARYWaGVsZW5AZXJpeC5lcmljc3Nvbi5zZTBcMA0GCSqGSIb3DQEB -AQUAA0sAMEgCQQDc+3hbzkZiQ4tVyTDNlN7hwucJqfw10yzMNPF2hL7We98IFYPO -BFdKMl9yodbYURNx3IfQMqx44KYswo/f0Wt/AgMBAAEwDQYJKoZIhvcNAQEEBQAD -QQC2++hLIaQJ4ChCjFE9UCfXO9cZ3Vq/FT9VjE+G4MRBDo4LQ5mBKNXcPF6EFZmi -7XrlvopXkVPlRguTi2SLRPkY +MIIChzCCAfCgAwIBAgIGAIsapa8BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT +BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE +BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD +VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw +MDAwWjB7MQ8wDQYDVQQDEwZjbGllbnQxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl +cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD +VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSV +wC+n0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53 +h2Zr3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwID +AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAG8t6f1A +PF7xayGxtUpG2r6W5ETylC3ZIKPS2kfJk9aYi7AZNTp7/xTU6SgqvFBN8aBPzxCD +4jHrSNC8DSb4X1x9uimarb6qdZDHEdij+DRAd2eygJHZxEf7+8B4Fx34thQeU9hZ +S1Izke5AlsyFMkvB7h0anE4k9BfuU70vl6v5 -----END CERTIFICATE----- diff --git a/lib/inets/test/httpc_SUITE_data/ssl_server_cert.pem b/lib/inets/test/httpc_SUITE_data/ssl_server_cert.pem index f01b6c992b..4aac86db49 100644 --- a/lib/inets/test/httpc_SUITE_data/ssl_server_cert.pem +++ b/lib/inets/test/httpc_SUITE_data/ssl_server_cert.pem @@ -1,22 +1,31 @@ -----BEGIN RSA PRIVATE KEY----- -MIIBOQIBAAJBAMe2WhP6s+JeKOwWPEjI9susfN4Vjn2dd1X4QUlOETcWVLoF916m -M4JU+ms7+ciMR8GRNCsIeqZGY8/GSqm74ccCAwEAAQJAF08YKlbLYfM9cXiS5qfV -7iWemUkIzW5wfC8yZ3zeE4Cp6R9ViUfs/dadQ/23Cw0Bpo2t8UdTUdCa4KpmqOem -cQIhAOnxTWZ5eo6h6PXDp7L5FZUACg8+wT3qf5f2is2mbSZPAiEA2orUY8JZDTSk -Rm7q9WxLiLNtORsXdTCmnCWhqBOYpwkCIErdowRxScxNekz0IT3AQqzdR1rbnWHg -IpcSGhd39CQ3AiA1XvQxjLP8wp9fyBS/bPwhXVhOOuyGpSP7PEF3b5m3KQIgGQWc -/a5wuWx3pc3mLx0ILwNoJr2ubFEuW1PJPsPJPv0= +MIICXQIBAAKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9Adq6 +7k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ4UAt +NHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQIDAQAB +AoGAQIlma0r6W6bcRj4+Wd4fXCFvHuq5Psu1fYEeC5Yvz8761xVjjSfbrDHJZ9pm +FjOEgedK+s5lbDXqYVyjbdyZSugStBRocSmbG8SQHcAsxR2ZIkNzX2hYzB+lslWo +T3YJojDyB134O7XJznCu+ZFXP86jyJ1JT6k6a+OIHcwnJ+ECQQDYn57dY4Px3mEd +VBLStN3YkRF5oFyT+xk7IaKeLLB6n4gCnoVbBoHut7PFbPYPzoNzEwPk3MQKDIHb +Kig3S5CpAkEAvPA1VmoJWAlN6kUi+F2L8HXEArzE8x7vwdsslrwMKUe4dFS+ZC/7 +5iDOaxcZ7TYkCgwzBt341++DCgP6j3fY1QJBALB6AcOcwi52m6l4B8mu3ZkEPjdX +BHTuONTqhv/TqoaLlxODL2NDvvDKqeMp7KBd/srt79swW2lQXS4+fvrlTdkCQQCm +zxj4O1QWkthkfje6ubSkTwUIOatUzrp1F9GNH2dJRtX2dx9FCwxGCC7WY6XzRXqa +GF0wsedSllbGD+82nWQlAkAicMGqCqRq4hKR/cVmFatOqKVWCVkx6OFF2FhuiI5Z +h5eIOPGCt8dVRs1P9DNSld/D98Sfm65m85z8BtXovvYV -----END RSA PRIVATE KEY----- -----BEGIN CERTIFICATE----- -MIIB7jCCAZgCAQAwDQYJKoZIhvcNAQEEBQAwgYExCzAJBgNVBAYTAlNFMRIwEAYD -VQQHEwlTdG9ja2hvbG0xETAPBgNVBAoTCEVyaWNzc29uMQwwCgYDVQQLEwNFVFgx -FjAUBgNVBAMTDUhlbGVuIEFpcml5YW4xJTAjBgkqhkiG9w0BCQEWFmhlbGVuQGVy -aXguZXJpY3Nzb24uc2UwHhcNOTcwNzI4MDcyMTAwWhcNOTgxMjEwMDcyMTAwWjCB -gTELMAkGA1UEBhMCU0UxEjAQBgNVBAcTCVN0b2NraG9sbTERMA8GA1UEChMIRXJp -Y3Nzb24xDDAKBgNVBAsTA0VUWDEWMBQGA1UEAxMNSGVsZW4gQWlyaXlhbjElMCMG -CSqGSIb3DQEJARYWaGVsZW5AZXJpeC5lcmljc3Nvbi5zZTBcMA0GCSqGSIb3DQEB -AQUAA0sAMEgCQQDHtloT+rPiXijsFjxIyPbLrHzeFY59nXdV+EFJThE3FlS6Bfde -pjOCVPprO/nIjEfBkTQrCHqmRmPPxkqpu+HHAgMBAAEwDQYJKoZIhvcNAQEEBQAD -QQCnU1TkxmfbLdUwjdECb5x9QHCevAR7AmTms4Csn2oOEyPX+bgF2d94xhrV1sxO -Rs0yigk1PtN17Ci0Dey0LYkR +MIIChzCCAfCgAwIBAgIGANUxXM9BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT +BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE +BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD +VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw +MDAwWjB7MQ8wDQYDVQQDEwZzZXJ2ZXIxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl +cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD +VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9 +Adq67k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ +4UAtNHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQID +AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAGF5Pfwk +QDdwJup/mVITPxbBls4Yl7anDooUQsq8066lA1g54H/PRfXscGkyCFGh1ifXvf1L +psMRoBAdDHL/wSJplk3rRavkC94eBgnTFZmfKL6844g1j53yameiYL8IEVExYMBg +/XGyc0qwq57WT8B/K4aElrvlBlQ0wF3wN54M -----END CERTIFICATE----- diff --git a/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_client.pem b/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_client.pem index 8221139eb4..427447958d 100644 --- a/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_client.pem +++ b/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_client.pem @@ -1,22 +1,31 @@ -----BEGIN RSA PRIVATE KEY----- -MIIBPAIBAAJBAL6Ym/bgUvhhnPkw08sggGg8Tnp759ThGMEjkmDzhuJ3w3PfnF65 -mgHcgunku4G6LxAQfEUougJWf9Phmjj3oRUCAwEAAQJBAKMjvVvzZxFzfAlP4flc -OI0AEayFokp04dtvtzuFN09f+aBo2dP18xHmKLCZvxrBOaRAROoQYscALiIVpN07 -GAECIQDfi+sSfAFaDlT3vzpL3xE5UEH6IzY8jWpaZfM1QaToJQIhANpEF50H4wGO -8Sbh7dUutNd+s+NYUjsMySW2DjLKMsoxAiEAzzb2ftrdsempD0F+O0gZwiPIFKLB -Kp33YLYyHEKuJtUCIDGi+pvDh2R7VWw6RRQOIyI+tjolg83aAoSI+oGiahqBAiEA -xzmNNajwoaokvWvlaz0na8rhxu45grOvDrflBT9XvSQ= +MIICXQIBAAKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSVwC+n +0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53h2Zr +3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwIDAQAB +AoGACdIVYe/LTeydUihtInC8lZ2QuPgJmoBNocRjqJFipEihoL4scHAx25n1bBvB +I0HZphffzBkGp28oBAtl2LRPWXqu527unc/RWRfLMqSK1xNSq1DxD1a30zkrZPna +QiV65vEJuNSJTtlDy/Zqc/BVZXCpxWlzYQedZgkmf0Qse8ECQQCmaz02Yur8zC9f +eSQKU5OSzGw3bSIumEzziCfHdTheK6MEoccf5TCAyLXhZwA7QlKja4tFXfeyVxws +/LlnUJN9AkEA4j+xnOeYUyGKXL5i+BAbnqpI4MzPiq+IoCYkaRlD/wAws24r5HNI +ZQmEHWqD/NNzOf/A2XuyLtMiTGJPW/DftwJBAKKpJP6Ytuh6xz8BUCnLwO12Y7vV +LtjuQiCzD3aUa5EYA9HOMqxJPxxRkf0LyR0i2VUkE8+sZiPpov+R0cJa7p0CQQCj +40GUiArGRSiF7/+e84QeVfl+pb29F1QftiFv5DZmFEwy3Z572KpbTh5edJbxYHY6 +UDHxGHJFCvnwXNJhpkVXAkBJqfEfiMJ3Q/E5Gpf3sQizacouW92iiN8ojlF1oB80 +t34RysJH7SgI3gdMhTribCo2UUaV0StjR6yodPN+TB2J -----END RSA PRIVATE KEY----- -----BEGIN CERTIFICATE----- -MIICDDCCAbYCAQAwDQYJKoZIhvcNAQEEBQAwgZAxCzAJBgNVBAYTAlNFMRIwEAYD -VQQIEwlTdG9ja2hvbG0xDzANBgNVBAcTBkFsdnNqbzEMMAoGA1UEChMDRVRYMQ4w -DAYDVQQLEwVETi9TUDEXMBUGA1UEAxMOSm9ha2ltIEdyZWJlbm8xJTAjBgkqhkiG -9w0BCQEWFmpvY2tlQGVyaXguZXJpY3Nzb24uc2UwHhcNOTcwNzE1MTUzNDM2WhcN -MDMwMjIyMTUzNDM2WjCBkDELMAkGA1UEBhMCU0UxEjAQBgNVBAgTCVN0b2NraG9s -bTEPMA0GA1UEBxMGQWx2c2pvMQwwCgYDVQQKEwNFVFgxDjAMBgNVBAsTBUROL1NQ -MRcwFQYDVQQDEw5Kb2FraW0gR3JlYmVubzElMCMGCSqGSIb3DQEJARYWam9ja2VA -ZXJpeC5lcmljc3Nvbi5zZTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC+mJv24FL4 -YZz5MNPLIIBoPE56e+fU4RjBI5Jg84bid8Nz35xeuZoB3ILp5LuBui8QEHxFKLoC -Vn/T4Zo496EVAgMBAAEwDQYJKoZIhvcNAQEEBQADQQBYxQVfTydyZCE0UXvZd7Ei -josNsAaWJk9fFIJaG9uyXCEfg2dVgoT2eBk3D9DI+7OB+78isM5CVlFbL7hilvP8 +MIIChzCCAfCgAwIBAgIGAIsapa8BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT +BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE +BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD +VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw +MDAwWjB7MQ8wDQYDVQQDEwZjbGllbnQxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl +cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD +VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSV +wC+n0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53 +h2Zr3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwID +AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAG8t6f1A +PF7xayGxtUpG2r6W5ETylC3ZIKPS2kfJk9aYi7AZNTp7/xTU6SgqvFBN8aBPzxCD +4jHrSNC8DSb4X1x9uimarb6qdZDHEdij+DRAd2eygJHZxEf7+8B4Fx34thQeU9hZ +S1Izke5AlsyFMkvB7h0anE4k9BfuU70vl6v5 -----END CERTIFICATE----- diff --git a/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_server.pem b/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_server.pem index fe739c15f7..4aac86db49 100644 --- a/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_server.pem +++ b/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_server.pem @@ -1,22 +1,31 @@ -----BEGIN RSA PRIVATE KEY----- -MIIBOwIBAAJBAL9Bozj3BIjL5Cy8b3rjMT2kPZRychX4wz9bHoIIiKnKo1xXHYjw -g3N9zWM1f1ZzMADwVry1uAInA8q09+7hL20CAwEAAQJACwu2ao7RozjrV64WXimK -6X131P/7GMvCMwGHNIlbozqoOqmZcYrbKaF61l+XuwA2QvTo3ywW1Ivxcyr6TeAr -PQIhAOX+WXT6yiqqwjt08kjBCJyMgfZtdAO6pc/6pKjNWiZfAiEA1OH1iPW/OQe5 -tlQXpiRVdLyneNsPygPRJc4Bdwu3hbMCIQDbI5pA56QxOzqOREOGJsb5wrciAfAE -jZbnr72sSN2YqQIgAWFpvzagw9Tp/mWzNY+cwkIK7/yzsIKv04fveH8p9IMCIQCr -td4IiukeUwXmPSvYM4uCE/+J89wEL9qU8Mlc3gDLXA== +MIICXQIBAAKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9Adq6 +7k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ4UAt +NHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQIDAQAB +AoGAQIlma0r6W6bcRj4+Wd4fXCFvHuq5Psu1fYEeC5Yvz8761xVjjSfbrDHJZ9pm +FjOEgedK+s5lbDXqYVyjbdyZSugStBRocSmbG8SQHcAsxR2ZIkNzX2hYzB+lslWo +T3YJojDyB134O7XJznCu+ZFXP86jyJ1JT6k6a+OIHcwnJ+ECQQDYn57dY4Px3mEd +VBLStN3YkRF5oFyT+xk7IaKeLLB6n4gCnoVbBoHut7PFbPYPzoNzEwPk3MQKDIHb +Kig3S5CpAkEAvPA1VmoJWAlN6kUi+F2L8HXEArzE8x7vwdsslrwMKUe4dFS+ZC/7 +5iDOaxcZ7TYkCgwzBt341++DCgP6j3fY1QJBALB6AcOcwi52m6l4B8mu3ZkEPjdX +BHTuONTqhv/TqoaLlxODL2NDvvDKqeMp7KBd/srt79swW2lQXS4+fvrlTdkCQQCm +zxj4O1QWkthkfje6ubSkTwUIOatUzrp1F9GNH2dJRtX2dx9FCwxGCC7WY6XzRXqa +GF0wsedSllbGD+82nWQlAkAicMGqCqRq4hKR/cVmFatOqKVWCVkx6OFF2FhuiI5Z +h5eIOPGCt8dVRs1P9DNSld/D98Sfm65m85z8BtXovvYV -----END RSA PRIVATE KEY----- -----BEGIN CERTIFICATE----- -MIICDDCCAbYCAQAwDQYJKoZIhvcNAQEEBQAwgZAxCzAJBgNVBAYTAlNFMRIwEAYD -VQQIEwlTdG9ja2hvbG0xDzANBgNVBAcTBkFsdnNqbzEMMAoGA1UEChMDRVRYMQ4w -DAYDVQQLEwVETi9TUDEXMBUGA1UEAxMOSm9ha2ltIEdyZWJlbm8xJTAjBgkqhkiG -9w0BCQEWFmpvY2tlQGVyaXguZXJpY3Nzb24uc2UwHhcNOTcwNzE1MTUzMzQxWhcN -MDMwMjIyMTUzMzQxWjCBkDELMAkGA1UEBhMCU0UxEjAQBgNVBAgTCVN0b2NraG9s -bTEPMA0GA1UEBxMGQWx2c2pvMQwwCgYDVQQKEwNFVFgxDjAMBgNVBAsTBUROL1NQ -MRcwFQYDVQQDEw5Kb2FraW0gR3JlYmVubzElMCMGCSqGSIb3DQEJARYWam9ja2VA -ZXJpeC5lcmljc3Nvbi5zZTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC/QaM49wSI -y+QsvG964zE9pD2UcnIV+MM/Wx6CCIipyqNcVx2I8INzfc1jNX9WczAA8Fa8tbgC -JwPKtPfu4S9tAgMBAAEwDQYJKoZIhvcNAQEEBQADQQAmXDY1CyJjzvQZX442kkHG -ic9QFY1UuVfzokzNMwlHYl1Qx9zaodx0cJCrcH5GF9O9LJbhhV77LzoxT1Q5wZp5 +MIIChzCCAfCgAwIBAgIGANUxXM9BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT +BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE +BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD +VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw +MDAwWjB7MQ8wDQYDVQQDEwZzZXJ2ZXIxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl +cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD +VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9 +Adq67k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ +4UAtNHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQID +AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAGF5Pfwk +QDdwJup/mVITPxbBls4Yl7anDooUQsq8066lA1g54H/PRfXscGkyCFGh1ifXvf1L +psMRoBAdDHL/wSJplk3rRavkC94eBgnTFZmfKL6844g1j53yameiYL8IEVExYMBg +/XGyc0qwq57WT8B/K4aElrvlBlQ0wF3wN54M -----END CERTIFICATE----- -- cgit v1.2.3 From f97cd6965ac35d1cbe510de23592956857d144c8 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Wed, 11 Sep 2013 12:04:36 +0200 Subject: inets: httpc improve pipelining --- lib/inets/src/http_client/httpc_handler.erl | 42 ++++++------------------- lib/inets/src/http_client/httpc_internal.hrl | 6 ++-- lib/inets/src/http_client/httpc_manager.erl | 46 +++++++++++++++++++--------- lib/inets/test/httpc_SUITE.erl | 6 ---- 4 files changed, 44 insertions(+), 56 deletions(-) diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index 6b8f33f9b8..1b5ac46408 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -1124,7 +1124,7 @@ handle_http_body(Body, #state{headers = Headers, handle_response(#state{status = new} = State) -> ?hcrd("handle response - status = new", []), - handle_response(try_to_enable_pipeline_or_keep_alive(State)); + handle_response(check_persistent(State)); handle_response(#state{request = Request, status = Status, @@ -1399,39 +1399,22 @@ is_keep_alive_enabled_server(_,_) -> is_keep_alive_connection(Headers, #session{client_close = ClientClose}) -> (not ((ClientClose) orelse httpc_response:is_server_closing(Headers))). -try_to_enable_pipeline_or_keep_alive( - #state{session = Session, - request = #request{method = Method}, +check_persistent( + #state{session = #session{type = Type} = Session, status_line = {Version, _, _}, headers = Headers, profile_name = ProfileName} = State) -> - ?hcrd("try to enable pipeline or keep-alive", - [{version, Version}, - {headers, Headers}, - {session, Session}]), case is_keep_alive_enabled_server(Version, Headers) andalso - is_keep_alive_connection(Headers, Session) of + is_keep_alive_connection(Headers, Session) of true -> - case (is_pipeline_enabled_client(Session) andalso - httpc_request:is_idempotent(Method)) of - true -> - insert_session(Session, ProfileName), - State#state{status = pipeline}; - false -> - insert_session(Session, ProfileName), - %% Make sure type is keep_alive in session - %% as it in this case might be pipeline - NewSession = Session#session{type = keep_alive}, - State#state{status = keep_alive, - session = NewSession} - end; + mark_persistent(ProfileName, Session), + State#state{status = Type}; false -> State#state{status = close} end. answer_request(#request{id = RequestId, from = From} = Request, Msg, - #state{session = Session, - timers = Timers, + #state{timers = Timers, profile_name = ProfileName} = State) -> ?hcrt("answer request", [{request, Request}, {msg, Msg}]), httpc_response:send(From, Msg), @@ -1441,19 +1424,14 @@ answer_request(#request{id = RequestId, from = From} = Request, Msg, Timer = {RequestId, TimerRef}, cancel_timer(TimerRef, {timeout, Request#request.id}), httpc_manager:request_done(RequestId, ProfileName), - NewSession = maybe_make_session_available(ProfileName, Session), Timers2 = Timers#timers{request_timers = lists:delete(Timer, RequestTimers)}, State#state{request = Request#request{from = answer_sent}, - session = NewSession, timers = Timers2}. -maybe_make_session_available(ProfileName, - #session{available = false} = Session) -> - update_session(ProfileName, Session, #session.available, true), - Session#session{available = true}; -maybe_make_session_available(_ProfileName, Session) -> - Session. +mark_persistent(ProfileName, Session) -> + update_session(ProfileName, Session, #session.persistent, true), + Session#session{persistent = true}. cancel_timers(#timers{request_timers = ReqTmrs, queue_timer = QTmr}) -> cancel_timer(QTmr, timeout_queue), diff --git a/lib/inets/src/http_client/httpc_internal.hrl b/lib/inets/src/http_client/httpc_internal.hrl index 30e2742e9d..d5b3dd2a2a 100644 --- a/lib/inets/src/http_client/httpc_internal.hrl +++ b/lib/inets/src/http_client/httpc_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2012. All Rights Reserved. +%% Copyright Ericsson AB 2005-2013. 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 @@ -143,8 +143,8 @@ %% true | false %% This will be true, when a response has been received for - %% the first request. See type above. - available = false + %% the first request and the server has not closed the connection + persistent = false }). diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl index 0d5157894b..d44235fc4a 100644 --- a/lib/inets/src/http_client/httpc_manager.erl +++ b/lib/inets/src/http_client/httpc_manager.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2012. All Rights Reserved. +%% Copyright Ericsson AB 2002-2013. 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 @@ -467,7 +467,7 @@ do_init(ProfileName, CookiesDir) -> %%-------------------------------------------------------------------- handle_call({request, Request}, _, State) -> ?hcri("request", [{request, Request}]), - case (catch handle_request(Request, State)) of + case (catch handle_request(Request, State, false)) of {reply, Msg, NewState} -> {reply, Msg, NewState}; Error -> @@ -547,7 +547,7 @@ handle_cast({retry_or_redirect_request, {Time, Request}}, {noreply, State}; handle_cast({retry_or_redirect_request, Request}, State) -> - case (catch handle_request(Request, State)) of + case (catch handle_request(Request, State, true)) of {reply, {ok, _}, NewState} -> {noreply, NewState}; Error -> @@ -774,7 +774,7 @@ get_handler_info(Tab) -> handle_request(#request{settings = #http_options{version = "HTTP/0.9"}} = Request, - State) -> + State, _) -> %% Act as an HTTP/0.9 client that does not know anything %% about persistent connections @@ -787,7 +787,7 @@ handle_request(#request{settings = handle_request(#request{settings = #http_options{version = "HTTP/1.0"}} = Request, - State) -> + State, _) -> %% Act as an HTTP/1.0 client that does not %% use persistent connections @@ -798,13 +798,13 @@ handle_request(#request{settings = start_handler(NewRequest#request{headers = NewHeaders}, State), {reply, {ok, NewRequest#request.id}, State}; -handle_request(Request, State = #state{options = Options}) -> +handle_request(Request, State = #state{options = Options}, Retry) -> NewRequest = handle_cookies(generate_request_id(Request), State), SessionType = session_type(Options), case select_session(Request#request.method, Request#request.address, - Request#request.scheme, SessionType, State) of + Request#request.scheme, SessionType, State, Retry) of {ok, HandlerPid} -> pipeline_or_keep_alive(NewRequest, HandlerPid, State); no_connection -> @@ -828,6 +828,7 @@ start_handler(#request{id = Id, #state{profile_name = ProfileName, handler_db = HandlerDb, options = Options}) -> + ClientClose = httpc_request:is_client_closing(Request#request.headers), {ok, Pid} = case is_inets_manager() of true -> @@ -838,13 +839,18 @@ start_handler(#request{id = Id, end, HandlerInfo = {Id, Pid, From}, ets:insert(HandlerDb, HandlerInfo), + insert_session(#session{id = {Request#request.address, Pid}, + scheme = Request#request.scheme, + client_close = ClientClose, + type = session_type(Options) + }, ProfileName), erlang:monitor(process, Pid). select_session(Method, HostPort, Scheme, SessionType, #state{options = #options{max_pipeline_length = MaxPipe, max_keep_alive_length = MaxKeepAlive}, - session_db = SessionDb}) -> + session_db = SessionDb}, Retry) -> ?hcrd("select session", [{session_type, SessionType}, {max_pipeline_length, MaxPipe}, {max_keep_alive_length, MaxKeepAlive}]), @@ -857,13 +863,23 @@ select_session(Method, HostPort, Scheme, SessionType, %% client_close, scheme and type specified. %% The fields id (part of: HandlerPid) and queue_length %% specified. - Pattern = #session{id = {HostPort, '$1'}, - client_close = false, - scheme = Scheme, - queue_length = '$2', - type = SessionType, - available = true, - _ = '_'}, + Pattern = case (Retry andalso SessionType == pipeline) of + true -> + #session{id = {HostPort, '$1'}, + client_close = false, + scheme = Scheme, + queue_length = '$2', + type = SessionType, + persistent = true, + _ = '_'}; + false -> + #session{id = {HostPort, '$1'}, + client_close = false, + scheme = Scheme, + queue_length = '$2', + type = SessionType, + _ = '_'} + end, %% {'_', {HostPort, '$1'}, false, Scheme, '_', '$2', SessionTyp}, Candidates = ets:match(SessionDb, Pattern), ?hcrd("select session", [{host_port, HostPort}, diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index bf993c2705..299c429d5e 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -277,9 +277,6 @@ trace(Config) when is_list(Config) -> pipeline(Config) when is_list(Config) -> Request = {url(group_name(Config), "/dummy.html", Config), []}, {ok, _} = httpc:request(get, Request, [], [], pipeline), - - %% Make sure pipeline session is registerd - test_server:sleep(4000), keep_alive_requests(Request, pipeline). %%-------------------------------------------------------------------- @@ -287,9 +284,6 @@ pipeline(Config) when is_list(Config) -> persistent_connection(Config) when is_list(Config) -> Request = {url(group_name(Config), "/dummy.html", Config), []}, {ok, _} = httpc:request(get, Request, [], [], persistent), - - %% Make sure pipeline session is registerd - test_server:sleep(4000), keep_alive_requests(Request, persistent). %%------------------------------------------------------------------------- -- cgit v1.2.3 From 34a95a40294b9fd62c1bb0de6cd5286dba8f15f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Wei=C3=9Fl?= Date: Thu, 5 Sep 2013 09:26:55 +0200 Subject: Add test for httpd config option 'script_timeout' The option got ignored before 720721e. --- lib/inets/test/httpd_basic_SUITE.erl | 66 ++++++++++++++++++---- lib/inets/test/httpd_basic_SUITE_data/Makefile.src | 14 +++++ lib/inets/test/httpd_basic_SUITE_data/cgi_sleep.c | 26 +++++++++ 3 files changed, 94 insertions(+), 12 deletions(-) create mode 100644 lib/inets/test/httpd_basic_SUITE_data/Makefile.src create mode 100644 lib/inets/test/httpd_basic_SUITE_data/cgi_sleep.c diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl index d2d043a89f..ddb79297b8 100644 --- a/lib/inets/test/httpd_basic_SUITE.erl +++ b/lib/inets/test/httpd_basic_SUITE.erl @@ -38,6 +38,7 @@ all() -> erl_script_nocache_opt, script_nocache, escaped_url_in_error_body, + script_timeout, slowdose ]. @@ -80,16 +81,19 @@ DUMMY DummyFile = filename:join([PrivDir,"dummy.html"]), CgiDir = filename:join(PrivDir, "cgi-bin"), ok = file:make_dir(CgiDir), - Cgi = case test_server:os_type() of - {win32, _} -> - "printenv.bat"; - _ -> - "printenv.sh" - end, - inets_test_lib:copy_file(Cgi, DataDir, CgiDir), - AbsCgi = filename:join([CgiDir, Cgi]), - {ok, FileInfo} = file:read_file_info(AbsCgi), - ok = file:write_file_info(AbsCgi, FileInfo#file_info{mode = 8#00755}), + {CgiPrintEnv, CgiSleep} = case test_server:os_type() of + {win32, _} -> + {"printenv.bat", "cgi_sleep.exe"}; + _ -> + {"printenv.sh", "cgi_sleep"} + end, + lists:foreach( + fun(Cgi) -> + inets_test_lib:copy_file(Cgi, DataDir, CgiDir), + AbsCgi = filename:join([CgiDir, Cgi]), + {ok, FileInfo} = file:read_file_info(AbsCgi), + ok = file:write_file_info(AbsCgi, FileInfo#file_info{mode = 8#00755}) + end, [CgiPrintEnv, CgiSleep]), {ok, Fd} = file:open(DummyFile, [write]), ok = file:write(Fd, Dummy), ok = file:close(Fd), @@ -100,7 +104,8 @@ DUMMY {document_root, PrivDir}, {bind_address, "localhost"}], - [{httpd_conf, HttpdConf}, {cgi_dir, CgiDir}, {cgi_script, Cgi} | Config]. + [{httpd_conf, HttpdConf}, {cgi_dir, CgiDir}, + {cgi_printenv, CgiPrintEnv}, {cgi_sleep, CgiSleep} | Config]. %%-------------------------------------------------------------------- %% Function: end_per_suite(Config) -> _ @@ -235,7 +240,7 @@ script_nocache(Config) when is_list(Config) -> verify_script_nocache(Config, CgiNoCache, EsiNoCache, CgiOption, EsiOption) -> HttpdConf = ?config(httpd_conf, Config), - CgiScript = ?config(cgi_script, Config), + CgiScript = ?config(cgi_printenv, Config), CgiDir = ?config(cgi_dir, Config), {ok, Pid} = inets:start(httpd, [{port, 0}, {script_alias, @@ -363,6 +368,43 @@ escaped_url_in_error_body(Config) when is_list(Config) -> inets:stop(httpd, Pid), tsp("escaped_url_in_error_body -> done"), ok. + + +%%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- + +script_timeout(doc) -> + ["Test the httpd script_timeout option"]; +script_timeout(suite) -> + []; +script_timeout(Config) when is_list(Config) -> + verify_script_timeout(Config, 20, 200), + verify_script_timeout(Config, 5, 403), + ok. + +verify_script_timeout(Config, ScriptTimeout, StatusCode) -> + HttpdConf = ?config(httpd_conf, Config), + CgiScript = ?config(cgi_sleep, Config), + CgiDir = ?config(cgi_dir, Config), + {ok, Pid} = inets:start(httpd, [{port, 0}, + {script_alias, + {"/cgi-bin/", CgiDir ++ "/"}}, + {script_timeout, ScriptTimeout} + | HttpdConf]), + Info = httpd:info(Pid), + Port = proplists:get_value(port, Info), + Address = proplists:get_value(bind_address, Info), + ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(), + "GET /cgi-bin/" ++ CgiScript ++ + " HTTP/1.0\r\n\r\n", + [{statuscode, StatusCode}, + {version, "HTTP/1.0"}]), + inets:stop(httpd, Pid). + + +%%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- + slowdose(doc) -> ["Testing minimum bytes per second option"]; slowdose(Config) when is_list(Config) -> diff --git a/lib/inets/test/httpd_basic_SUITE_data/Makefile.src b/lib/inets/test/httpd_basic_SUITE_data/Makefile.src new file mode 100644 index 0000000000..9da2ed583f --- /dev/null +++ b/lib/inets/test/httpd_basic_SUITE_data/Makefile.src @@ -0,0 +1,14 @@ +CC = @CC@ +LD = @LD@ +CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@ +CROSSLDFLAGS = @CROSSLDFLAGS@ + +PROGS = cgi_sleep@exe@ + +all: $(PROGS) + +cgi_sleep@exe@: cgi_sleep@obj@ + $(LD) $(CROSSLDFLAGS) -o cgi_sleep cgi_sleep@obj@ @LIBS@ + +cgi_sleep@obj@: cgi_sleep.c + $(CC) -c -o cgi_sleep@obj@ $(CFLAGS) cgi_sleep.c diff --git a/lib/inets/test/httpd_basic_SUITE_data/cgi_sleep.c b/lib/inets/test/httpd_basic_SUITE_data/cgi_sleep.c new file mode 100644 index 0000000000..126bb23987 --- /dev/null +++ b/lib/inets/test/httpd_basic_SUITE_data/cgi_sleep.c @@ -0,0 +1,26 @@ +#include +#include + +#ifdef __WIN32__ +#include +#include +#include +#else +#include +#endif + +int main(void) +{ + unsigned int seconds = 10; + +#ifdef __WIN32__ + Sleep(seconds * 1000); + _setmode(_fileno(stdout), _O_BINARY); +#else + sleep(seconds); +#endif + + printf("Content-type: text/plain\r\n\r\n"); + printf("Slept for %u seconds.\r\n", seconds); + exit(EXIT_SUCCESS); +} -- cgit v1.2.3 From dfb5449c3a3ac2dee8b71434cba0afdba29a6567 Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Mon, 30 Sep 2013 16:16:59 +0200 Subject: inets: added testcase for keep_alive_timeout --- lib/inets/test/httpd_basic_SUITE.erl | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl index ddb79297b8..fa5c58c509 100644 --- a/lib/inets/test/httpd_basic_SUITE.erl +++ b/lib/inets/test/httpd_basic_SUITE.erl @@ -39,7 +39,8 @@ all() -> script_nocache, escaped_url_in_error_body, script_timeout, - slowdose + slowdose, + keep_alive_timeout ]. groups() -> @@ -373,6 +374,26 @@ escaped_url_in_error_body(Config) when is_list(Config) -> %%------------------------------------------------------------------------- %%------------------------------------------------------------------------- +keep_alive_timeout(doc) -> + ["Test the keep_alive_timeout option"]; +keep_alive_timeout(suite) -> + []; +keep_alive_timeout(Config) when is_list(Config) -> + HttpdConf = ?config(httpd_conf, Config), + {ok, Pid} = inets:start(httpd, [{port, 0}, {keep_alive, true}, {keep_alive_timeout, 2} | HttpdConf]), + Info = httpd:info(Pid), + Port = proplists:get_value(port, Info), + _Address = proplists:get_value(bind_address, Info), + {ok, S} = gen_tcp:connect("localhost", Port, []), + receive + after 3000 -> + {error, closed} = gen_tcp:send(S, "hey") + end, + inets:stop(httpd, Pid). + +%%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- + script_timeout(doc) -> ["Test the httpd script_timeout option"]; script_timeout(suite) -> -- cgit v1.2.3 From df096a3f447080d74d831cee795d456da8a569c4 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Wed, 11 Sep 2013 15:58:51 +0200 Subject: httpc: Enhanched error handling --- lib/inets/src/http_client/httpc_handler.erl | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index 1b5ac46408..d15d93676e 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -483,19 +483,12 @@ handle_info({Proto, _Socket, Data}, activate_once(Session), {noreply, State#state{mfa = NewMFA}} catch - exit:_Exit -> - ?hcrd("data processing exit", [{exit, _Exit}]), - ClientReason = {could_not_parse_as_http, Data}, - ClientErrMsg = httpc_response:error(Request, ClientReason), - NewState = answer_request(Request, ClientErrMsg, State), - {stop, normal, NewState}; - error:_Error -> - ?hcrd("data processing error", [{error, _Error}]), + _:_Reason -> + ?hcrd("data processing exit", [{exit, _Reason}]), ClientReason = {could_not_parse_as_http, Data}, ClientErrMsg = httpc_response:error(Request, ClientReason), NewState = answer_request(Request, ClientErrMsg, State), {stop, normal, NewState} - end, ?hcri("data processed", [{final_result, FinalResult}]), FinalResult; -- cgit v1.2.3 From 2c0dabc1ea0330c765e7621483e7a96d751db0aa Mon Sep 17 00:00:00 2001 From: James Wheare Date: Fri, 14 Jun 2013 17:45:36 +0100 Subject: httpc: Allow content body in DELETE requests --- lib/inets/src/http_client/httpc.erl | 2 +- lib/inets/test/httpc_proxy_SUITE.erl | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index fc2081239e..6cb66cc94d 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -175,7 +175,7 @@ request(Method, request(Method, {Url, Headers, ContentType, Body}, HTTPOptions, Options, Profile) - when ((Method =:= post) orelse (Method =:= put)) andalso + when ((Method =:= post) orelse (Method =:= put) orelse (Method =:= delete)) andalso (is_atom(Profile) orelse is_pid(Profile)) -> ?hcrt("request", [{method, Method}, {url, Url}, diff --git a/lib/inets/test/httpc_proxy_SUITE.erl b/lib/inets/test/httpc_proxy_SUITE.erl index 2475489bb3..ef277f2b01 100644 --- a/lib/inets/test/httpc_proxy_SUITE.erl +++ b/lib/inets/test/httpc_proxy_SUITE.erl @@ -69,6 +69,7 @@ local_proxy_cases() -> http_post, http_put, http_delete, + http_delete_body, http_headers, http_proxy_auth, http_doesnotexist, @@ -262,6 +263,22 @@ http_delete(Config) when is_list(Config) -> %%-------------------------------------------------------------------- +http_delete_body(doc) -> + ["Perform a DELETE request with a content body. The server will not allow it " + "but we only test sending the request."]; +http_delete_body(Config) when is_list(Config) -> + Method = delete, + URL = url("/delete.html", Config), + Content = "foo=bar", + Request = {URL,[],"application/x-www-form-urlencoded",Content}, + HttpOpts = [], + Opts = [], + {ok,{{_,405,_},[_|_],[_|_]}} = + httpc:request(Method, Request, HttpOpts, Opts), + ok. + +%%-------------------------------------------------------------------- + http_headers(doc) -> ["Use as many request headers as possible"]; http_headers(Config) when is_list(Config) -> -- cgit v1.2.3 From 7a97e384ea8d4d17ed6a24508a58359ece175e16 Mon Sep 17 00:00:00 2001 From: Masatake Daimon Date: Fri, 17 May 2013 13:01:20 +0900 Subject: Fix {stream, {self, once}} in httpc Previously the only difference between {stream, self} and {stream, {self, once}} was an extra Pid in the stream_start message due to a bug in httpc_handler. It was immediately sending a bunch of messages till the end instead of waiting for httpc:stream_next/1 being called. --- lib/inets/src/http_client/httpc_handler.erl | 54 +++++++++++++++++++++++++---- lib/inets/test/httpc_SUITE.erl | 9 +++++ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index d15d93676e..3d1e1def67 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -65,7 +65,7 @@ options, % #options{} timers = #timers{}, % #timers{} profile_name, % atom() - id of httpc_manager process. - once % send | undefined + once = inactive % inactive | once }). @@ -231,6 +231,8 @@ init([Parent, Request, Options, ProfileName]) -> ProxyOptions = handle_proxy_options(Request#request.scheme, Options), Address = handle_proxy(Request#request.address, ProxyOptions), {ok, State} = + %% #state.once should initially be 'inactive' because we + %% activate the socket at first regardless of the state. case {Address /= Request#request.address, Request#request.scheme} of {true, https} -> connect_and_send_upgrade_request(Address, Request, @@ -425,7 +427,9 @@ handle_cast({cancel, RequestId, From}, handle_cast(stream_next, #state{session = Session} = State) -> activate_once(Session), - {noreply, State#state{once = once}}. + %% Inactivate the #state.once here because we don't want + %% next_body_chunk/1 to activate the socket twice. + {noreply, State#state{once = inactive}}. %%-------------------------------------------------------------------- @@ -478,6 +482,41 @@ handle_info({Proto, _Socket, Data}, NewMFA = {Module, whole_body, [NewBody, NewLength]}, {noreply, NewState#state{mfa = NewMFA, request = NewRequest}}; + {Module, decode_size, + [TotalChunk, HexList, + {MaxBodySize, BodySoFar, AccLength, MaxHeaderSize}]} + when BodySoFar =/= <<>> -> + ?hcrd("data processed - decode_size", []), + %% The response body is chunk-encoded. Steal decoded + %% chunks as much as possible to stream. + {_, Code, _} = StatusLine, + {NewBody, NewRequest} = stream(BodySoFar, Request, Code), + NewState = next_body_chunk(State), + NewMFA = {Module, decode_size, + [TotalChunk, HexList, + {MaxBodySize, NewBody, AccLength, MaxHeaderSize}]}, + {noreply, NewState#state{mfa = NewMFA, + request = NewRequest}}; + {Module, decode_data, + [ChunkSize, TotalChunk, + {MaxBodySize, BodySoFar, AccLength, MaxHeaderSize, false}]} + when TotalChunk =/= <<>> orelse BodySoFar =/= <<>> -> + ?hcrd("data processed - decode_data", []), + %% The response body is chunk-encoded. Steal decoded + %% chunks as much as possible to stream. + ChunkSizeToSteal = min(ChunkSize, byte_size(TotalChunk)), + <> = TotalChunk, + StolenBody = <>, + NewChunkSize = ChunkSize - ChunkSizeToSteal, + {_, Code, _} = StatusLine, + + {NewBody, NewRequest} = stream(StolenBody, Request, Code), + NewState = next_body_chunk(State), + NewMFA = {Module, decode_data, + [NewChunkSize, NewTotalChunk, + {MaxBodySize, NewBody, AccLength, MaxHeaderSize, false}]}, + {noreply, NewState#state{mfa = NewMFA, + request = NewRequest}}; NewMFA -> ?hcrd("data processed - new mfa", []), activate_once(Session), @@ -1020,11 +1059,15 @@ handle_http_msg({Version, StatusCode, ReasonPharse, Headers, Body}, status_line = StatusLine, headers = Headers}) end; -handle_http_msg({ChunkedHeaders, Body}, #state{headers = Headers} = State) -> +handle_http_msg({ChunkedHeaders, Body}, + #state{status_line = {_, Code, _}, headers = Headers} = State) -> ?hcrt("handle_http_msg", [{chunked_headers, ChunkedHeaders}, {headers, Headers}]), NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders), - handle_response(State#state{headers = NewHeaders, body = Body}); + {NewBody, NewRequest} = stream(Body, State#state.request, Code), + handle_response(State#state{headers = NewHeaders, + body = NewBody, + request = NewRequest}); handle_http_msg(Body, #state{status_line = {_,Code, _}} = State) -> ?hcrt("handle_http_msg", [{code, Code}]), {NewBody, NewRequest} = stream(Body, State#state.request, Code), @@ -1063,8 +1106,7 @@ handle_http_body(Body, #state{headers = Headers, "chunked" -> ?hcrt("handle_http_body - chunked", []), case http_chunk:decode(Body, State#state.max_body_size, - State#state.max_header_size, - {Code, Request}) of + State#state.max_header_size) of {Module, Function, Args} -> ?hcrt("handle_http_body - new mfa", [{module, Module}, diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 299c429d5e..51dc7c0f87 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -1687,6 +1687,15 @@ receive_streamed_body(RequestId, Body, Pid) -> ct:print("~p:receive_streamed_body -> requested next stream ~n", [?MODULE]), receive {http, {RequestId, stream, BinBodyPart}} -> + %% Make sure the httpc hasn't sent us the next 'stream' + %% without our request. + receive + {http, {RequestId, stream, _}} = Msg -> + ct:fail({unexpected_flood_of_stream, Msg}) + after + 1000 -> + ok + end, receive_streamed_body(RequestId, <>, Pid); -- cgit v1.2.3 From 8db1d1d3d38948dd118632b6617b8120299ab4b6 Mon Sep 17 00:00:00 2001 From: Masatake Daimon Date: Fri, 17 May 2013 13:28:47 +0900 Subject: Remove http_chunk:decode/4 as it is no longer used http_chunk:decode/4, the side-effecting chunk decoder, was only used by httpc_handler:handle_http_body/2 but now it's completely unused. Conflicts: lib/inets/src/http_client/httpc_handler.erl --- lib/inets/src/http_client/httpc_handler.erl | 5 ++- lib/inets/src/http_lib/http_chunk.erl | 55 +++++++++-------------------- 2 files changed, 18 insertions(+), 42 deletions(-) diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index 3d1e1def67..e48adaa4cb 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -33,7 +33,6 @@ %% connect_and_send/2, send/2, cancel/3, - stream/3, stream_next/1, info/1 ]). @@ -499,7 +498,7 @@ handle_info({Proto, _Socket, Data}, request = NewRequest}}; {Module, decode_data, [ChunkSize, TotalChunk, - {MaxBodySize, BodySoFar, AccLength, MaxHeaderSize, false}]} + {MaxBodySize, BodySoFar, AccLength, MaxHeaderSize}]} when TotalChunk =/= <<>> orelse BodySoFar =/= <<>> -> ?hcrd("data processed - decode_data", []), %% The response body is chunk-encoded. Steal decoded @@ -514,7 +513,7 @@ handle_info({Proto, _Socket, Data}, NewState = next_body_chunk(State), NewMFA = {Module, decode_data, [NewChunkSize, NewTotalChunk, - {MaxBodySize, NewBody, AccLength, MaxHeaderSize, false}]}, + {MaxBodySize, NewBody, AccLength, MaxHeaderSize}]}, {noreply, NewState#state{mfa = NewMFA, request = NewRequest}}; NewMFA -> diff --git a/lib/inets/src/http_lib/http_chunk.erl b/lib/inets/src/http_lib/http_chunk.erl index 57647438e9..34234f0c14 100644 --- a/lib/inets/src/http_lib/http_chunk.erl +++ b/lib/inets/src/http_lib/http_chunk.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. 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 @@ -24,7 +24,7 @@ -include("http_internal.hrl"). %% API --export([decode/3, decode/4, encode/1, encode_last/0, handle_headers/2]). +-export([decode/3, encode/1, encode_last/0, handle_headers/2]). %% Callback API - used for example if the chunkedbody is received a %% little at a time on a socket. -export([decode_size/1, ignore_extensions/1, decode_data/1, decode_trailer/1]). @@ -34,20 +34,14 @@ %%% API %%%========================================================================= %%------------------------------------------------------------------------- -%% decode(ChunkedBody, MaxBodySize, MaxHeaderSize, ) -> +%% decode(ChunkedBody, MaxBodySize, MaxHeaderSize) -> %% {ok, {Headers, Body}} | {Module, Function, Args} %% %% Headers = ["Header:Value"] %% ChunkedBody = binary() %% MaxBodySize = integer() %% MaxHeaderSize = integer() -%% Stream = {Code, Request} - if Request#request.stream =/= none -%% and Code == 200 the side effect of sending each decode chunk to the -%% client/file before the whole body is received will take place. %% -%% Note: decode/4 should only be used from httpc_handler module. -%% Otherwhise use the side effect free decode/3. -%% %% Description: Decodes a body encoded by the chunked transfer %% encoding. If the ChunkedBody is not compleate it returns {Module, %% Function, Args} so that decoding can be continued when more of the @@ -61,12 +55,9 @@ %% the next pass in the loop. %%------------------------------------------------------------------------- decode(ChunkedBody, MaxBodySize, MaxHeaderSize) -> - decode(ChunkedBody, MaxBodySize, MaxHeaderSize, false). - -decode(ChunkedBody, MaxBodySize, MaxHeaderSize, Stream) -> %% Note decode_size will call decode_data. - decode_size([ChunkedBody, <<>>, [], - {MaxBodySize, <<>>, 0, MaxHeaderSize, Stream}]). + decode_size([ChunkedBody, <<>>, [], + {MaxBodySize, <<>>, 0, MaxHeaderSize}]). %%------------------------------------------------------------------------- %% encode(Chunk) -> EncodedChunk @@ -150,7 +141,7 @@ decode_size(<<>>, HexList, Info) -> decode_size(Data = <>, HexList, {MaxBodySize, Body, AccLength, - MaxHeaderSize, Stream}) -> + MaxHeaderSize}) -> ChunkSize = http_util:hexlist_to_integer(lists:reverse(HexList)), case ChunkSize of 0 -> % Last chunk, there was no data @@ -164,7 +155,7 @@ decode_size(Data = <>, HexList, %% to this function comes in. decode_data(ChunkSize, ChunkRest, {MaxBodySize, Body, ChunkSize + AccLength , - MaxHeaderSize, Stream}) + MaxHeaderSize}) end; decode_size(<<";", Rest/binary>>, HexList, Info) -> %% Note ignore_extensions will call decode_size/1 again when @@ -189,50 +180,42 @@ ignore_extensions(<<_Octet, Rest/binary>>, NextFunction) -> ignore_extensions(Rest, NextFunction). decode_data(ChunkSize, TotalChunk, - Info = {MaxBodySize, BodySoFar, AccLength, MaxHeaderSize, Stream}) + Info = {MaxBodySize, BodySoFar, AccLength, MaxHeaderSize}) when ChunkSize =< size(TotalChunk) -> case TotalChunk of %% Last chunk <> -> %% Note ignore_extensions will call decode_trailer/1 %% once it ignored all extensions. - {NewBody, _} = - stream(<>, Stream), {?MODULE, ignore_extensions, [<<>>, {?MODULE, decode_trailer, [<<>>, [],[], MaxHeaderSize, - NewBody, + <>, integer_to_list(AccLength)]}]}; <> -> %% Note ignore_extensions will call decode_trailer/1 %% once it ignored all extensions. - {NewBody, _} = stream(<>, Stream), ignore_extensions(Rest, {?MODULE, decode_trailer, [<<>>, [],[], MaxHeaderSize, - NewBody, + <>, integer_to_list(AccLength)]}); <> -> - {NewBody, _} = stream(<>, Stream), {?MODULE, decode_trailer, [<>, [],[], MaxHeaderSize, - NewBody, + <>, integer_to_list(AccLength)]}; <> -> - {NewBody,_}= stream(<>, Stream), decode_trailer(<>, [],[], MaxHeaderSize, - NewBody, + <>, integer_to_list(AccLength)); %% There are more chunks, so here we go agin... <> -> - {NewBody, NewStream} = - stream(<>, Stream), - {?MODULE, decode_size, [<<>>, [], {MaxBodySize, NewBody, AccLength, MaxHeaderSize, NewStream}]}; + NewBody = <>, + {?MODULE, decode_size, [<<>>, [], {MaxBodySize, NewBody, AccLength, MaxHeaderSize}]}; <> when (AccLength < MaxBodySize) or (MaxBodySize == nolimit) -> - {NewBody, NewStream} = - stream(<>, Stream), decode_size(Rest, [], - {MaxBodySize, NewBody, - AccLength, MaxHeaderSize, NewStream}); + {MaxBodySize, <>, + AccLength, MaxHeaderSize}); <<_:ChunkSize/binary, ?CR, ?LF, _/binary>> -> throw({error, body_too_big}); _ -> @@ -286,9 +269,3 @@ decode_trailer(<>, Header, Headers, MaxHeaderSize, Body, BodyLength) -> decode_trailer(Rest, [Octet | Header], Headers, MaxHeaderSize, Body, BodyLength). - -stream(BodyPart, false) -> - {BodyPart, false}; -stream(BodyPart, {Code, Request}) -> - {NewBody, NewRequest} = httpc_handler:stream(BodyPart, Request, Code), - {NewBody, {Code, NewRequest}}. -- cgit v1.2.3 From b68cc1bd8995e53cec04a37be23bd1709e57bff9 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Wed, 11 Sep 2013 15:56:31 +0200 Subject: inets: httpc make httpc_cancel_request/[1,2] asynchronous Conflicts: lib/inets/src/http_client/httpc_handler.erl --- lib/inets/doc/src/httpc.xml | 7 ++- lib/inets/src/http_client/httpc.erl | 12 +---- lib/inets/src/http_client/httpc_handler.erl | 16 +++--- lib/inets/src/http_client/httpc_manager.erl | 78 ++++++----------------------- lib/inets/test/httpc_SUITE.erl | 13 ++--- 5 files changed, 31 insertions(+), 95 deletions(-) diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index 91685db49f..b08c219603 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -4,7 +4,7 @@

- 20042012 + 20042014 Ericsson AB. All Rights Reserved. @@ -440,7 +440,10 @@ apply(Module, Function, [ReplyInfo | Args]) Profile = profile() | pid() (when started stand_alone) -

Cancels an asynchronous HTTP-request.

+

Cancels an asynchronous HTTP-request. Note this does not guarantee + that the request response will not be delivered, as it is asynchronous the + the request may already have been completed when the cancellation arrives. +

diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index 6cb66cc94d..b1bac55871 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2012. All Rights Reserved. +%% Copyright Ericsson AB 2009-2014. 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 @@ -208,15 +208,7 @@ cancel_request(RequestId) -> cancel_request(RequestId, Profile) when is_atom(Profile) orelse is_pid(Profile) -> ?hcrt("cancel request", [{request_id, RequestId}, {profile, Profile}]), - ok = httpc_manager:cancel_request(RequestId, profile_name(Profile)), - receive - %% If the request was already fulfilled throw away the - %% answer as the request has been canceled. - {http, {RequestId, _}} -> - ok - after 0 -> - ok - end. + httpc_manager:cancel_request(RequestId, profile_name(Profile)). %%-------------------------------------------------------------------------- diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index e48adaa4cb..e4b05a18df 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2013. All Rights Reserved. +%% Copyright Ericsson AB 2002-2014. 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 @@ -32,7 +32,7 @@ start_link/4, %% connect_and_send/2, send/2, - cancel/3, + cancel/2, stream_next/1, info/1 ]). @@ -117,8 +117,8 @@ send(Request, Pid) -> %% Description: Cancels a request. Intended to be called by the httpc %% manager process. %%-------------------------------------------------------------------- -cancel(RequestId, Pid, From) -> - cast({cancel, RequestId, From}, Pid). +cancel(RequestId, Pid) -> + cast({cancel, RequestId}, Pid). %%-------------------------------------------------------------------- @@ -400,19 +400,17 @@ handle_call(info, _, State) -> %% handle_keep_alive_queue/2 on the other hand will just skip the %% request as if it was never issued as in this case the request will %% not have been sent. -handle_cast({cancel, RequestId, From}, +handle_cast({cancel, RequestId}, #state{request = #request{id = RequestId} = Request, profile_name = ProfileName, canceled = Canceled} = State) -> ?hcrv("cancel current request", [{request_id, RequestId}, {profile, ProfileName}, {canceled, Canceled}]), - httpc_manager:request_canceled(RequestId, ProfileName, From), - ?hcrv("canceled", []), {stop, normal, State#state{canceled = [RequestId | Canceled], request = Request#request{from = answer_sent}}}; -handle_cast({cancel, RequestId, From}, +handle_cast({cancel, RequestId}, #state{profile_name = ProfileName, request = #request{id = CurrId}, canceled = Canceled} = State) -> @@ -420,8 +418,6 @@ handle_cast({cancel, RequestId, From}, {curr_req_id, CurrId}, {profile, ProfileName}, {canceled, Canceled}]), - httpc_manager:request_canceled(RequestId, ProfileName, From), - ?hcrv("canceled", []), {noreply, State#state{canceled = [RequestId | Canceled]}}; handle_cast(stream_next, #state{session = Session} = State) -> diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl index d44235fc4a..4927bd3a33 100644 --- a/lib/inets/src/http_client/httpc_manager.erl +++ b/lib/inets/src/http_client/httpc_manager.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2013. All Rights Reserved. +%% Copyright Ericsson AB 2002-2014. 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 @@ -29,7 +29,6 @@ start_link/3, request/2, cancel_request/2, - request_canceled/3, request_done/2, retry_request/2, redirect_request/2, @@ -144,22 +143,7 @@ redirect_request(Request, ProfileName) -> %%-------------------------------------------------------------------- cancel_request(RequestId, ProfileName) -> - call(ProfileName, {cancel_request, RequestId}). - - -%%-------------------------------------------------------------------- -%% Function: request_canceled(RequestId, ProfileName) -> ok -%% RequestId - ref() -%% ProfileName = atom() -%% -%% Description: Confirms that a request has been canceld. Intended to -%% be called by the httpc handler process. -%%-------------------------------------------------------------------- - -request_canceled(RequestId, ProfileName, From) -> - gen_server:reply(From, ok), - cast(ProfileName, {request_canceled, RequestId}). - + cast(ProfileName, {cancel_request, RequestId}). %%-------------------------------------------------------------------- %% Function: request_done(RequestId, ProfileName) -> ok @@ -474,26 +458,6 @@ handle_call({request, Request}, _, State) -> {stop, Error, httpc_response:error(Request, Error), State} end; -handle_call({cancel_request, RequestId}, From, - #state{handler_db = HandlerDb} = State) -> - ?hcri("cancel_request", [{request_id, RequestId}]), - case ets:lookup(HandlerDb, RequestId) of - [] -> - %% The request has allready compleated make sure - %% it is deliverd to the client process queue so - %% it can be thrown away by httpc:cancel_request - %% This delay is hopfully a temporary workaround. - %% Note that it will not not delay the manager, - %% only the client that called httpc:cancel_request - timer:apply_after(?DELAY, gen_server, reply, [From, ok]), - {noreply, State}; - [{_, Pid, _}] -> - httpc_handler:cancel(RequestId, Pid, From), - {noreply, - State#state{cancel = - [{RequestId, Pid, From} | State#state.cancel]}} - end; - handle_call(reset_cookies, _, #state{cookie_db = CookieDb} = State) -> ?hcrv("reset cookies", []), httpc_cookie:reset_db(CookieDb), @@ -555,17 +519,17 @@ handle_cast({retry_or_redirect_request, Request}, State) -> {stop, Error, State} end; -handle_cast({request_canceled, RequestId}, State) -> - ?hcrv("request canceled", [{request_id, RequestId}]), - ets:delete(State#state.handler_db, RequestId), - case lists:keysearch(RequestId, 1, State#state.cancel) of - {value, Entry = {RequestId, _, From}} -> - ?hcrt("found in cancel", [{from, From}]), - {noreply, - State#state{cancel = lists:delete(Entry, State#state.cancel)}}; - Else -> - ?hcrt("not found in cancel", [{else, Else}]), - {noreply, State} +handle_cast({cancel_request, RequestId}, + #state{handler_db = HandlerDb} = State) -> + case ets:lookup(HandlerDb, RequestId) of + [] -> + %% Request already compleated nothing to + %% cancel + {noreply, State}; + [{_, Pid, _}] -> + httpc_handler:cancel(RequestId, Pid), + ets:delete(State#state.handler_db, RequestId), + {noreply, State} end; handle_cast({request_done, RequestId}, State) -> @@ -630,21 +594,7 @@ handle_info({'EXIT', _, _}, State) -> {noreply, State}; handle_info({'DOWN', _, _, Pid, _}, State) -> ets:match_delete(State#state.handler_db, {'_', Pid, '_'}), - - %% If there where any canceled request, handled by the - %% the process that now has terminated, the - %% cancelation can be viewed as sucessfull! - NewCanceldList = - lists:foldl(fun(Entry = {_, HandlerPid, From}, Acc) -> - case HandlerPid of - Pid -> - gen_server:reply(From, ok), - lists:delete(Entry, Acc); - _ -> - Acc - end - end, State#state.cancel, State#state.cancel), - {noreply, State#state{cancel = NewCanceldList}}; + {noreply, State}; handle_info(Info, State) -> Report = io_lib:format("Unknown message in " "httpc_manager:handle_info ~p~n", [Info]), diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 51dc7c0f87..c5a9b6168e 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2012. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. 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 @@ -305,13 +305,8 @@ async(Config) when is_list(Config) -> {ok, NewRequestId} = httpc:request(get, Request, [], [{sync, false}]), - ok = httpc:cancel_request(NewRequestId), - receive - {http, {NewRequestId, _}} -> - ct:fail(http_cancel_request_failed) - after 3000 -> - ok - end. + ok = httpc:cancel_request(NewRequestId). + %%------------------------------------------------------------------------- save_to_file() -> [{doc, "Test to save the http body to a file"}]. @@ -1143,7 +1138,7 @@ receive_replys([ID|IDs]) -> {http, {ID, {{_, 200, _}, [_|_], _}}} -> receive_replys(IDs); {http, {Other, {{_, 200, _}, [_|_], _}}} -> - ct:fail({recived_canceld_id, Other}) + ct:pal({recived_canceld_id, Other}) end. %% Perform a synchronous stop -- cgit v1.2.3 From 1b065c9329a4497208299dbf214bba90269399ab Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 24 Sep 2013 16:31:06 +0200 Subject: inets: httpc - Remove dead error handling code Some error handling is only relevant for functions that end up in gen_server:call and not in gen_server:cast. --- lib/inets/src/http_client/httpc.erl | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index b1bac55871..14b5c494e8 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -233,14 +233,7 @@ set_options(Options, Profile) when is_atom(Profile) orelse is_pid(Profile) -> ?hcrt("set options", [{options, Options}, {profile, Profile}]), case validate_options(Options) of {ok, Opts} -> - try - begin - httpc_manager:set_options(Opts, profile_name(Profile)) - end - catch - exit:{noproc, _} -> - {error, inets_not_started} - end; + httpc_manager:set_options(Opts, profile_name(Profile)); {error, Reason} -> {error, Reason} end. @@ -335,8 +328,6 @@ store_cookies(SetCookieHeaders, Url, Profile) ok end catch - exit:{noproc, _} -> - {error, {not_started, Profile}}; error:{badmatch, Bad} -> {error, {parse_failed, Bad}} end. -- cgit v1.2.3 From b8c86e76187aad7888325c57e021ae498816f5e0 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 13 Sep 2013 12:50:45 +0200 Subject: [inets/httpd] Fixed mod_head handling of "new" response --- lib/inets/src/http_server/httpd_response.erl | 33 ++++++++++++++++------------ lib/inets/src/http_server/mod_head.erl | 4 ++++ lib/inets/src/inets_app/inets_internal.hrl | 2 ++ 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl index a45b04f275..cbf28c7d60 100644 --- a/lib/inets/src/http_server/httpd_response.erl +++ b/lib/inets/src/http_server/httpd_response.erl @@ -23,9 +23,10 @@ is_disable_chunked_send/1, cache_headers/2]). -export([map_status_code/2]). --include("httpd.hrl"). --include("http_internal.hrl"). --include("httpd_internal.hrl"). +-include_lib("inets/src/inets_app/inets_internal.hrl"). +-include_lib("inets/include/httpd.hrl"). +-include_lib("inets/src/http_lib/http_internal.hrl"). +-include_lib("inets/src/http_server/httpd_internal.hrl"). -define(VMODULE,"RESPONSE"). @@ -35,7 +36,7 @@ generate_and_send_response(#mod{init_data = #init_data{peername = {_,"unknown"}}}) -> ok; generate_and_send_response(#mod{config_db = ConfigDB} = ModData) -> - Modules = httpd_util:lookup(ConfigDB,modules, ?DEFAULT_MODS), + Modules = httpd_util:lookup(ConfigDB, modules, ?DEFAULT_MODS), case traverse_modules(ModData, Modules) of done -> ok; @@ -68,16 +69,7 @@ traverse_modules(ModData,[]) -> {proceed,ModData#mod.data}; traverse_modules(ModData,[Module|Rest]) -> ?hdrd("traverse modules", [{callback_module, Module}]), - case (catch apply(Module, do, [ModData])) of - {'EXIT', Reason} -> - String = - lists:flatten( - io_lib:format("traverse exit from apply: ~p:do => ~n~p", - [Module, Reason])), - report_error(mod_log, ModData#mod.config_db, String), - report_error(mod_disk_log, ModData#mod.config_db, String), - send_status(ModData, 500, none), - done; + try apply(Module, do, [ModData]) of done -> ?hdrt("traverse modules - done", []), done; @@ -87,6 +79,19 @@ traverse_modules(ModData,[Module|Rest]) -> {proceed, NewData} -> ?hdrt("traverse modules - proceed", [{new_data, NewData}]), traverse_modules(ModData#mod{data = NewData}, Rest) + catch + T:E -> + String = + lists:flatten( + io_lib:format("module traverse failed: ~p:do => " + "~n Error Type: ~p" + "~n Error: ~p" + "~n Stack trace: ~p", + [Module, T, E, ?STACK()])), + report_error(mod_log, ModData#mod.config_db, String), + report_error(mod_disk_log, ModData#mod.config_db, String), + send_status(ModData, 500, none), + done end. %% send_status %% diff --git a/lib/inets/src/http_server/mod_head.erl b/lib/inets/src/http_server/mod_head.erl index c346fd4d23..1378063ae5 100644 --- a/lib/inets/src/http_server/mod_head.erl +++ b/lib/inets/src/http_server/mod_head.erl @@ -42,6 +42,10 @@ do(Info) -> %% A response has been sent! Nothing to do about it! {already_sent, _StatusCode, _Size} -> {proceed,Info#mod.data}; + {response, Header, _Body} -> %% New way + {proceed, + lists:keyreplace(response, 1, Info#mod.data, + {response, Header, nobody})}; %% A response has been generated! {_StatusCode, _Response} -> {proceed,Info#mod.data} diff --git a/lib/inets/src/inets_app/inets_internal.hrl b/lib/inets/src/inets_app/inets_internal.hrl index e56af3b59d..06843f2275 100644 --- a/lib/inets/src/inets_app/inets_internal.hrl +++ b/lib/inets/src/inets_app/inets_internal.hrl @@ -21,6 +21,8 @@ -ifndef(inets_internal_hrl). -define(inets_internal_hrl, true). +-define(STACK(), erlang:get_stacktrace()). + %% Various trace macros -define(report(Severity, Label, Service, Content), -- cgit v1.2.3 From a3ebf39cb29110be5d216ff0fc36a608b2ef27c0 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 16 Sep 2013 11:47:06 +0200 Subject: [inets/httpd] Logging cleanup Remove logging when fo keep-alive connection timeout. This is a normal event (feature) not an error. --- lib/inets/src/http_server/httpd_request_handler.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index cb20159794..57beb1e403 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -268,8 +268,8 @@ handle_info({ssl_error, _, _} = Reason, State) -> %% Timeouts handle_info(timeout, #state{mod = ModData, mfa = {_, parse, _}} = State) -> - error_log("No request received on keep-alive connection " - "before server side timeout", ModData), + %% error_log("No request received on keep-alive connection " + %% "before server side timeout", ModData), %% No response should be sent! {stop, normal, State#state{response_sent = true}}; handle_info(timeout, #state{mod = ModData} = State) -> -- cgit v1.2.3 From 260a528990de016d32e80c32fbfbf3ee365df999 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 16 Sep 2013 12:44:50 +0200 Subject: [inets/httpd] Improved access log entry Sometimes the size of a response could be as a string. --- lib/inets/src/http_server/httpd_log.erl | 15 +++++++++++---- lib/inets/src/http_server/httpd_request_handler.erl | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/inets/src/http_server/httpd_log.erl b/lib/inets/src/http_server/httpd_log.erl index a34435e0e8..693a681fd4 100644 --- a/lib/inets/src/http_server/httpd_log.erl +++ b/lib/inets/src/http_server/httpd_log.erl @@ -39,14 +39,21 @@ Size :: 0 | pos_integer() | string()) -> {Log :: atom() | pid(), Entry :: string()} | term() . -access_entry(Log, NoLog, Info, RFC931, AuthUser, Date, StatusCode, SizeStr) - when is_list(SizeStr) -> +%% Somethime the size in the form of the content_length is put here, which +%% is actually in the form of a string +%% So it can either be the size as an integer, the size as a string +%% or, worst case scenario, bytes. +access_entry(Log, NoLog, Info, RFC931, AuthUser, Date, StatusCode, + SizeStrOrBytes) + when is_list(SizeStrOrBytes) -> Size = - case (catch list_to_integer(SizeStr)) of + case (catch list_to_integer(SizeStrOrBytes)) of I when is_integer(I) -> + %% This is from using the content_length (which is a string) I; _ -> - SizeStr % This is better then nothing + %% This is better than nothing + httpd_util:flatlength(SizeStrOrBytes) end, access_entry(Log, NoLog, Info, RFC931, AuthUser, Date, StatusCode, Size); access_entry(Log, NoLog, diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index 57beb1e403..2e4a35d031 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -267,7 +267,7 @@ handle_info({ssl_error, _, _} = Reason, State) -> {stop, Reason, State}; %% Timeouts -handle_info(timeout, #state{mod = ModData, mfa = {_, parse, _}} = State) -> +handle_info(timeout, #state{mfa = {_, parse, _}} = State) -> %% error_log("No request received on keep-alive connection " %% "before server side timeout", ModData), %% No response should be sent! -- cgit v1.2.3 From 021ede611d20f60e64936763048c271986bb2997 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 16 Sep 2013 17:52:27 +0200 Subject: [inets/httpd] Improved request handler terminate error-log entry If a request handler terminates abnormally (terminate reason not normal), the error-log entry was a bit obtuse ("Internal Server Error"). This has been improved so that the log entry now also include the actual terminate reason. --- lib/inets/src/http_server/httpd_request_handler.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index 2e4a35d031..fb458f61cd 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -316,7 +316,10 @@ terminate(normal, State) -> do_terminate(State); terminate(Reason, #state{response_sent = false, mod = ModData} = State) -> httpd_response:send_status(ModData, 500, none), - error_log(httpd_util:reason_phrase(500), ModData), + ReasonStr = + lists:flatten(io_lib:format("~s - ~p", + [httpd_util:reason_phrase(500), Reason])), + error_log(ReasonStr, ModData), terminate(Reason, State#state{response_sent = true, mod = ModData}); terminate(_Reason, State) -> do_terminate(State). -- cgit v1.2.3 From c500946862bbad092367799428b2658bf7f82861 Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Thu, 7 Nov 2013 16:40:18 +0100 Subject: [eldap, inets]: fix testcases when ssl not is present Conflicts: lib/eldap/test/eldap_basic_SUITE.erl --- lib/eldap/test/eldap_basic_SUITE.erl | 183 ++++++++++++++++++++++++++--------- lib/inets/test/httpc_SUITE.erl | 23 ++++- 2 files changed, 160 insertions(+), 46 deletions(-) diff --git a/lib/eldap/test/eldap_basic_SUITE.erl b/lib/eldap/test/eldap_basic_SUITE.erl index c7e3052b29..3e809b0f68 100644 --- a/lib/eldap/test/eldap_basic_SUITE.erl +++ b/lib/eldap/test/eldap_basic_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2012. All Rights Reserved. +%% Copyright Ericsson AB 2012-2014. 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 @@ -27,39 +27,45 @@ -define(TIMEOUT, 120000). % 2 min -init_per_suite(Config0) -> - {{EldapHost,Port}, Config1} = - case catch ct:get_config(eldap_server, undefined) of - undefined -> %% Dev test only - Server = {"localhost", 9876}, - {Server, [{eldap_server, {"localhost", 9876}}|Config0]}; - {'EXIT', _} -> %% Dev test only - Server = {"localhost", 9876}, - {Server, [{eldap_server, {"localhost", 9876}}|Config0]}; - Server -> - {Server, [{eldap_server, Server}|Config0]} - end, - %% Add path for this test run +init_per_suite(Config) -> + StartSsl = try ssl:start() + catch + Error:Reason -> + {skip, lists:flatten(io_lib:format("eldap init_per_suite failed to start ssl Error=~p Reason=~p", [Error, Reason]))} + end, + case StartSsl of + ok -> + chk_config(ldap_server, {"localhost",9876}, + chk_config(ldaps_server, {"localhost",9877}, + Config)); + _ -> + StartSsl + end. + +end_per_suite(_Config) -> + ok. + +init_per_testcase(_TestCase, Config0) -> + {EldapHost,Port} = proplists:get_value(ldap_server,Config0), try - {ok, Handle} = eldap:open([EldapHost], [{port, Port}]), + {ok, Handle} = eldap:open([EldapHost], [{port,Port}]), ok = eldap:simple_bind(Handle, "cn=Manager,dc=ericsson,dc=se", "hejsan"), {ok, MyHost} = inet:gethostname(), Path = "dc="++MyHost++",dc=ericsson,dc=se", - Config = [{eldap_path,Path}|Config1], eldap:add(Handle,"dc=ericsson,dc=se", [{"objectclass", ["dcObject", "organization"]}, {"dc", ["ericsson"]}, {"o", ["Testing"]}]), eldap:add(Handle,Path, [{"objectclass", ["dcObject", "organization"]}, {"dc", [MyHost]}, {"o", ["Test machine"]}]), - Config + [{eldap_path,Path}|Config0] catch error:{badmatch,Error} -> io:format("Eldap init error ~p~n ~p~n",[Error, erlang:get_stacktrace()]), - {skip, lists:flatten(io_lib:format("Ldap init failed with host ~p", [EldapHost]))} + {skip, lists:flatten(io_lib:format("Ldap init failed with host ~p:~p. Error=~p", [EldapHost,Port,Error]))} end. -end_per_suite(Config) -> - %% Cleanup everything - {EHost, Port} = proplists:get_value(eldap_server, Config), + +end_per_testcase(_TestCase, Config) -> + {EHost, Port} = proplists:get_value(ldap_server, Config), Path = proplists:get_value(eldap_path, Config), {ok, H} = eldap:open([EHost], [{port, Port}]), ok = eldap:simple_bind(H, "cn=Manager,dc=ericsson,dc=se", "hejsan"), @@ -71,16 +77,20 @@ end_per_suite(Config) -> [ok = eldap:delete(H, Entry) || {eldap_entry, Entry, _} <- Entries]; _ -> ignore end, - ok. -init_per_testcase(_TestCase, Config) -> Config. -end_per_testcase(_TestCase, _Config) -> ok. + ok. %% suite() -> all() -> [app, - api]. + api, + ssl_api, + start_tls, + tls_operations, + start_tls_twice, + start_tls_on_ssl + ]. app(doc) -> "Test that the eldap app file is ok"; app(suite) -> []; @@ -90,21 +100,89 @@ app(Config) when is_list(Config) -> api(doc) -> "Basic test that all api functions works as expected"; api(suite) -> []; api(Config) -> - {Host,Port} = proplists:get_value(eldap_server, Config), + {Host,Port} = proplists:get_value(ldap_server, Config), {ok, H} = eldap:open([Host], [{port,Port}]), %% {ok, H} = eldap:open([Host], [{port,Port+1}, {ssl, true}]), + do_api_checks(H, Config), + eldap:close(H), + ok. + + +ssl_api(doc) -> "Basic test that all api functions works as expected"; +ssl_api(suite) -> []; +ssl_api(Config) -> + {Host,Port} = proplists:get_value(ldaps_server, Config), + {ok, H} = eldap:open([Host], [{port,Port}, {ssl,true}]), + do_api_checks(H, Config), + eldap:close(H), + ok. + + +start_tls(doc) -> "Test that an existing (tcp) connection can be upgraded to tls"; +start_tls(suite) -> []; +start_tls(Config) -> + {Host,Port} = proplists:get_value(ldap_server, Config), + {ok, H} = eldap:open([Host], [{port,Port}]), + ok = eldap:start_tls(H, [ + {keyfile, filename:join([proplists:get_value(data_dir,Config), + "certs/client/key.pem"])} + ]), + eldap:close(H). + + +tls_operations(doc) -> "Test that an upgraded connection is usable for ldap stuff"; +tls_operations(suite) -> []; +tls_operations(Config) -> + {Host,Port} = proplists:get_value(ldap_server, Config), + {ok, H} = eldap:open([Host], [{port,Port}]), + ok = eldap:start_tls(H, [ + {keyfile, filename:join([proplists:get_value(data_dir,Config), + "certs/client/key.pem"])} + ]), + do_api_checks(H, Config), + eldap:close(H). + +start_tls_twice(doc) -> "Test that start_tls on an already upgraded connection fails"; +start_tls_twice(suite) -> []; +start_tls_twice(Config) -> + {Host,Port} = proplists:get_value(ldap_server, Config), + {ok, H} = eldap:open([Host], [{port,Port}]), + ok = eldap:start_tls(H, []), + {error,tls_already_started} = eldap:start_tls(H, []), + do_api_checks(H, Config), + eldap:close(H). + + +start_tls_on_ssl(doc) -> "Test that start_tls on an ldaps connection fails"; +start_tls_on_ssl(suite) -> []; +start_tls_on_ssl(Config) -> + {Host,Port} = proplists:get_value(ldaps_server, Config), + {ok, H} = eldap:open([Host], [{port,Port}, {ssl,true}]), + {error,tls_already_started} = eldap:start_tls(H, []), + do_api_checks(H, Config), + eldap:close(H). + + +%%%-------------------------------------------------------------------------------- +chk_config(Key, Default, Config) -> + case catch ct:get_config(ldap_server, undefined) of + undefined -> [{Key,Default} | Config ]; + {'EXIT',_} -> [{Key,Default} | Config ]; + Value -> [{Key,Value} | Config] + end. + + + +do_api_checks(H, Config) -> BasePath = proplists:get_value(eldap_path, Config), + All = fun(Where) -> eldap:search(H, #eldap_search{base=Where, filter=eldap:present("objectclass"), scope= eldap:wholeSubtree()}) end, - Search = fun(Filter) -> - eldap:search(H, #eldap_search{base=BasePath, - filter=Filter, - scope=eldap:singleLevel()}) - end, - {ok, #eldap_search_result{entries=[_]}} = All(BasePath), + {ok, #eldap_search_result{entries=[_XYZ]}} = All(BasePath), +%% ct:log("XYZ=~p",[_XYZ]), {error, noSuchObject} = All("cn=Bar,"++BasePath), {error, _} = eldap:add(H, "cn=Jonas Jonsson," ++ BasePath, @@ -112,52 +190,67 @@ api(Config) -> {"cn", ["Jonas Jonsson"]}, {"sn", ["Jonsson"]}]), eldap:simple_bind(H, "cn=Manager,dc=ericsson,dc=se", "hejsan"), - %% Add + chk_add(H, BasePath), + {ok,FB} = chk_search(H, BasePath), + chk_modify(H, FB), + chk_delete(H, BasePath), + chk_modify_dn(H, FB). + + +chk_add(H, BasePath) -> ok = eldap:add(H, "cn=Jonas Jonsson," ++ BasePath, [{"objectclass", ["person"]}, {"cn", ["Jonas Jonsson"]}, {"sn", ["Jonsson"]}]), + {error, entryAlreadyExists} = eldap:add(H, "cn=Jonas Jonsson," ++ BasePath, + [{"objectclass", ["person"]}, + {"cn", ["Jonas Jonsson"]}, {"sn", ["Jonsson"]}]), ok = eldap:add(H, "cn=Foo Bar," ++ BasePath, [{"objectclass", ["person"]}, {"cn", ["Foo Bar"]}, {"sn", ["Bar"]}, {"telephoneNumber", ["555-1232", "555-5432"]}]), ok = eldap:add(H, "ou=Team," ++ BasePath, [{"objectclass", ["organizationalUnit"]}, - {"ou", ["Team"]}]), + {"ou", ["Team"]}]). - %% Search +chk_search(H, BasePath) -> + Search = fun(Filter) -> + eldap:search(H, #eldap_search{base=BasePath, + filter=Filter, + scope=eldap:singleLevel()}) + end, JJSR = {ok, #eldap_search_result{entries=[#eldap_entry{}]}} = Search(eldap:equalityMatch("sn", "Jonsson")), JJSR = Search(eldap:substrings("sn", [{any, "ss"}])), FBSR = {ok, #eldap_search_result{entries=[#eldap_entry{object_name=FB}]}} = Search(eldap:substrings("sn", [{any, "a"}])), FBSR = Search(eldap:substrings("sn", [{initial, "B"}])), FBSR = Search(eldap:substrings("sn", [{final, "r"}])), - F_AND = eldap:'and'([eldap:present("objectclass"), eldap:present("ou")]), {ok, #eldap_search_result{entries=[#eldap_entry{}]}} = Search(F_AND), F_NOT = eldap:'and'([eldap:present("objectclass"), eldap:'not'(eldap:present("ou"))]), {ok, #eldap_search_result{entries=[#eldap_entry{}, #eldap_entry{}]}} = Search(F_NOT), + {ok,FB}. %% FIXME - %% MODIFY +chk_modify(H, FB) -> Mod = [eldap:mod_replace("telephoneNumber", ["555-12345"]), eldap:mod_add("description", ["Nice guy"])], %% io:format("MOD ~p ~p ~n",[FB, Mod]), ok = eldap:modify(H, FB, Mod), %% DELETE ATTR - ok = eldap:modify(H, FB, [eldap:mod_delete("telephoneNumber", [])]), + ok = eldap:modify(H, FB, [eldap:mod_delete("telephoneNumber", [])]). - %% DELETE + +chk_delete(H, BasePath) -> {error, entryAlreadyExists} = eldap:add(H, "cn=Jonas Jonsson," ++ BasePath, [{"objectclass", ["person"]}, {"cn", ["Jonas Jonsson"]}, {"sn", ["Jonsson"]}]), ok = eldap:delete(H, "cn=Jonas Jonsson," ++ BasePath), - {error, noSuchObject} = eldap:delete(H, "cn=Jonas Jonsson," ++ BasePath), + {error, noSuchObject} = eldap:delete(H, "cn=Jonas Jonsson," ++ BasePath). - %% MODIFY_DN - ok = eldap:modify_dn(H, FB, "cn=Niclas Andre", true, ""), - %%io:format("Res ~p~n ~p~n",[R, All(BasePath)]), +chk_modify_dn(H, FB) -> + ok = eldap:modify_dn(H, FB, "cn=Niclas Andre", true, ""). + %%io:format("Res ~p~n ~p~n",[R, All(BasePath)]). - eldap:close(H), - ok. +%%%---------------- add(H, Attr, Value, Path0, Attrs, Class) -> Path = case Path0 of [] -> Attr ++ "=" ++ Value; diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index c5a9b6168e..50025b1d9d 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -145,6 +145,22 @@ init_per_group(misc = Group, Config) -> ok = httpc:set_options([{ipfamily, Inet}]), Config; +init_per_group(Group, Config0) when Group =:= sim_https; Group =:= https-> + start_apps(Group), + StartSsl = try ssl:start() + catch + Error:Reason -> + {skip, lists:flatten(io_lib:format("Failed to start apps for https Error=~p Reason=~p", [Error, Reason]))} + end, + case StartSsl of + {error, {already_started, _}} -> + do_init_per_group(Group, Config0); + ok -> + do_init_per_group(Group, Config0); + _ -> + StartSsl + end; + init_per_group(Group, Config0) -> start_apps(Group), Config = proplists:delete(port, Config0), @@ -153,7 +169,10 @@ init_per_group(Group, Config0) -> end_per_group(_, _Config) -> ok. - +do_init_per_group(Group, Config0) -> + Config = proplists:delete(port, Config0), + Port = server_start(Group, server_config(Group, Config)), + [{port, Port} | Config]. %%-------------------------------------------------------------------- init_per_testcase(pipeline, Config) -> inets:start(httpc, [{profile, pipeline}]), @@ -1069,6 +1088,8 @@ server_config(_, _) -> start_apps(https) -> inets_test_lib:start_apps([crypto, public_key, ssl]); +start_apps(sim_https) -> + inets_test_lib:start_apps([crypto, public_key, ssl]); start_apps(_) -> ok. -- cgit v1.2.3 From d5ceee89f1f479ae9dba2b8c6a811403843d07f0 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Fri, 27 Sep 2013 16:04:45 +0200 Subject: ftp: Implement ftps (upgrade to TLS) --- lib/inets/src/ftp/ftp.erl | 338 +++++++++++++--------- lib/inets/src/ftp/ftp_response.erl | 1 + lib/inets/test/Makefile | 1 + lib/inets/test/ftp_SUITE.erl | 146 ++++------ lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel | 18 -- lib/inets/test/old_ftp_SUITE.erl | 127 ++++++++ lib/inets/test/old_ftp_SUITE_data/ftpd_hosts.skel | 18 ++ 7 files changed, 408 insertions(+), 241 deletions(-) delete mode 100644 lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel create mode 100644 lib/inets/test/old_ftp_SUITE.erl create mode 100644 lib/inets/test/old_ftp_SUITE_data/ftpd_hosts.skel diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index 132a384a49..b3cf1544f5 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2012. All Rights Reserved. +%% Copyright Ericsson AB 1997-2013. 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 @@ -67,7 +67,8 @@ %% Internal state -record(state, { csock = undefined, % socket() - Control connection socket - dsock = undefined, % socket() - Data connection socket + dsock = undefined, % socket() - Data connection socket + tls_options = undefined, % list() verbose = false, % boolean() ldir = undefined, % string() - Current local directory type = ftp_server_default, % atom() - binary | ascii @@ -90,7 +91,8 @@ caller = undefined, % term() ipfamily, % inet | inet6 | inet6fb4 progress = ignore, % ignore | pid() - dtimeout = ?DATA_ACCEPT_TIMEOUT % non_neg_integer() | infinity + dtimeout = ?DATA_ACCEPT_TIMEOUT, % non_neg_integer() | infinity + tls_upgrading_data_connection }). @@ -154,8 +156,7 @@ open(Host, Opts) when is_list(Opts) -> ?fcrt("open", [{open_options, OpenOptions}]), case start_link(StartOptions, []) of {ok, Pid} -> - ?fcrt("open - ok", [{pid, Pid}]), - call(Pid, {open, ip_comm, OpenOptions}, plain); + do_open(Pid, OpenOptions, tls_options(Opts)); Error1 -> ?fcrt("open - error", [{error1, Error1}]), Error1 @@ -166,7 +167,13 @@ open(Host, Opts) when is_list(Opts) -> Error2 end. - +do_open(Pid, OpenOptions, TLSOpts) -> + case call(Pid, {open, ip_comm, OpenOptions}, plain) of + {ok, Pid} -> + maybe_tls_upgrade(Pid, TLSOpts); + Error -> + Error + end. %%-------------------------------------------------------------------------- %% user(Pid, User, Pass, ) -> ok | {error, euser} | {error, econn} %% | {error, eacct} @@ -905,6 +912,10 @@ open_options(Options) -> {progress, ValidateProgress, false, ?PROGRESS_DEFAULT}], validate_options(Options, ValidOptions, []). +tls_options(Options) -> + %% Options will be validated by ssl application + proplists:get_value(tls, Options, undefined). + validate_options([], [], Acc) -> ?fcrt("validate_options -> done", [{acc, Acc}]), {ok, lists:reverse(Acc)}; @@ -1021,8 +1032,8 @@ handle_call({_, info}, _, #state{verbose = Verbose, ipfamily = IpFamily, csock = Socket, progress = Progress} = State) -> - {ok, {_, LocalPort}} = inet:sockname(Socket), - {ok, {Address, Port}} = inet:peername(Socket), + {ok, {_, LocalPort}} = sockname(Socket), + {ok, {Address, Port}} = peername(Socket), Options = [{verbose, Verbose}, {ipfamily, IpFamily}, {mode, Mode}, @@ -1091,6 +1102,10 @@ handle_call({_, {open, ip_comm, Host, Opts}}, From, State) -> {stop, normal, State2#state{client = undefined}} end; +handle_call({_, {open, tls_upgrade, TLSOptions}}, From, State) -> + send_ctrl_message(State, mk_cmd("AUTH TLS", [])), + {noreply, State#state{client = From, caller = open, tls_options = TLSOptions}}; + handle_call({_, {user, User, Password}}, From, #state{csock = CSock} = State) when (CSock =/= undefined) -> handle_user(User, Password, "", State#state{client = From}); @@ -1196,8 +1211,8 @@ handle_call({_,{recv_chunk_start, RemoteFile}}, From, #state{chunk = false} handle_call({_, recv_chunk}, _, #state{chunk = false} = State) -> {reply, {error, "ftp:recv_chunk_start/2 not called"}, State}; -handle_call({_, recv_chunk}, From, #state{chunk = true} = State) -> - activate_data_connection(State), +handle_call({_, recv_chunk}, From, #state{chunk = true} = State0) -> + State = activate_data_connection(State0), {noreply, State#state{client = From, caller = recv_chunk}}; handle_call({_, {send, LocalFile, RemoteFile}}, From, @@ -1301,10 +1316,10 @@ handle_info(timeout, State) -> %%% Data socket messages %%% handle_info({tcp, Socket, Data}, #state{dsock = Socket, - caller = {recv_file, Fd}} = State) -> + caller = {recv_file, Fd}} = State0) -> file_write(binary_to_list(Data), Fd), - progress_report({binary, Data}, State), - activate_data_connection(State), + progress_report({binary, Data}, State0), + State = activate_data_connection(State0), {noreply, State}; handle_info({tcp, Socket, Data}, #state{dsock = Socket, client = From, @@ -1313,8 +1328,8 @@ handle_info({tcp, Socket, Data}, #state{dsock = Socket, client = From, gen_server:reply(From, {ok, Data}), {noreply, State#state{client = undefined, data = <<>>}}; -handle_info({tcp, Socket, Data}, #state{dsock = Socket} = State) -> - activate_data_connection(State), +handle_info({tcp, Socket, Data}, #state{dsock = Socket} = State0) -> + State = activate_data_connection(State0), {noreply, State#state{data = <<(State#state.data)/binary, Data/binary>>}}; @@ -1357,12 +1372,12 @@ handle_info({tcp_error, Socket, Reason}, #state{dsock = Socket, data = <<>>, caller = undefined, chunk = false}}; %%% Ctrl socket messages %%% -handle_info({tcp, Socket, Data}, #state{csock = Socket, - verbose = Verbose, - caller = Caller, - client = From, - ctrl_data = {CtrlData, AccLines, - LineStatus}} +handle_info({Transport, Socket, Data}, #state{csock = {Transport, Socket}, + verbose = Verbose, + caller = Caller, + client = From, + ctrl_data = {CtrlData, AccLines, + LineStatus}} = State) -> case ftp_response:parse_lines(<>, AccLines, LineStatus) of @@ -1386,10 +1401,12 @@ handle_info({tcp, Socket, Data}, #state{csock = Socket, {noreply, State#state{ctrl_data = NewCtrlData}} end; -handle_info({tcp_closed, Socket}, #state{csock = Socket}) -> - %% If the server closes the control channel it is - %% the expected behavior that connection process terminates. +%% If the server closes the control channel it is +%% the expected behavior that connection process terminates. +handle_info({tcp_closed, Socket}, #state{csock = {tcp, Socket}}) -> exit(normal); %% User will get error message from terminate/2 +handle_info({ssl_closed, Socket}, #state{csock = {ssl, Socket}}) -> + exit(normal); handle_info({tcp_error, Socket, Reason}, _) -> Report = @@ -1399,6 +1416,8 @@ handle_info({tcp_error, Socket, Reason}, _) -> %% If tcp does not work the only option is to terminate, %% this is the expected behavior under these circumstances. exit(normal); %% User will get error message from terminate/2 +handle_info({ssl_error, _Socket, _Reason}, _) -> + exit(normal); %% TODO?? %% Monitor messages - if the process owning the ftp connection goes %% down there is no point in continuing. @@ -1568,6 +1587,40 @@ handle_user_account(Acc, State) -> %%-------------------------------------------------------------------------- %%-------------------------------------------------------------------------- %% Handling of control connection setup +handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, pbsz, _}} = State) -> + send_ctrl_message(State, mk_cmd("PROT P", [])), + activate_ctrl_connection(State), + {noreply, State#state{tls_upgrading_data_connection = {true, prot}}}; + +handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, prot, NextAction}, + dsock = {tcp, Socket}, client = From, + tls_options = TLSOptions} = State0) -> + case ssl:connect(Socket, TLSOptions) of + {ok, TLSSocket} -> + State = State0#state{dsock = {ssl, TLSSocket}, + tls_upgrading_data_connection = {false, undefined}}, + next_action_after_tls_upgrade(NextAction, State); + {error, _} = Error -> + gen_server:reply(From, {Error, self()}), + {stop, normal, State0#state{client = undefined, + caller = undefined}} + end; + +handle_ctrl_result({tls_upgrade, _}, #state{csock = {tcp, Socket}, + tls_options = TLSOptions, + caller = open, client = From} + = State) -> + case ssl:connect(Socket, TLSOptions) of + {ok, TLSSocket} -> + gen_server:reply(From, {ok, self()}), + {noreply, State#state{csock = {ssl, TLSSocket}, + client = undefined, + caller = undefined }}; + {error, _} = Error -> + gen_server:reply(From, {Error, self()}), + {stop, normal, State#state{client = undefined, + caller = undefined}} + end; handle_ctrl_result({pos_compl, _}, #state{caller = open, client = From} = State) -> gen_server:reply(From, {ok, self()}), @@ -1601,10 +1654,10 @@ handle_ctrl_result({pos_compl, Lines}, timeout = Timeout} = State) -> [_, PortStr | _] = lists:reverse(string:tokens(Lines, "|")), - {ok, {IP, _}} = inet:peername(CSock), + {ok, {IP, _}} = peername(CSock), case connect(IP, list_to_integer(PortStr), Timeout, State) of {ok, _, Socket} -> - handle_caller(State#state{caller = Caller, dsock = Socket}); + handle_caller(State#state{caller = Caller, dsock = {tcp, Socket}}); {error, _Reason} = Error -> gen_server:reply(From, Error), {noreply, State#state{client = undefined, caller = undefined}} @@ -1669,18 +1722,18 @@ handle_ctrl_result({pos_compl, Lines}, %%-------------------------------------------------------------------------- %% Directory listing -handle_ctrl_result({pos_prel, _}, #state{caller = {dir, Dir}} = State) -> - case accept_data_connection(State) of - {ok, NewState} -> - activate_data_connection(NewState), - {noreply, NewState#state{caller = {handle_dir_result, Dir}}}; +handle_ctrl_result({pos_prel, _}, #state{caller = {dir, Dir}} = State0) -> + case accept_data_connection(State0) of + {ok, State1} -> + State = activate_data_connection(State1), + {noreply, State#state{caller = {handle_dir_result, Dir}}}; {error, _Reason} = ERROR -> - case State#state.client of + case State0#state.client of undefined -> - {stop, ERROR, State}; + {stop, ERROR, State0}; From -> gen_server:reply(From, ERROR), - {stop, normal, State#state{client = undefined}} + {stop, normal, State0#state{client = undefined}} end end; @@ -1778,18 +1831,18 @@ handle_ctrl_result({Status, _}, %%-------------------------------------------------------------------------- %% File handling - recv_bin -handle_ctrl_result({pos_prel, _}, #state{caller = recv_bin} = State) -> - case accept_data_connection(State) of - {ok, NewState} -> - activate_data_connection(NewState), - {noreply, NewState}; +handle_ctrl_result({pos_prel, _}, #state{caller = recv_bin} = State0) -> + case accept_data_connection(State0) of + {ok, State1} -> + State = activate_data_connection(State1), + {noreply, State}; {error, _Reason} = ERROR -> - case State#state.client of + case State0#state.client of undefined -> - {stop, ERROR, State}; + {stop, ERROR, State0}; From -> gen_server:reply(From, ERROR), - {stop, normal, State#state{client = undefined}} + {stop, normal, State0#state{client = undefined}} end end; @@ -1812,36 +1865,35 @@ handle_ctrl_result({Status, _}, #state{caller = {recv_bin, _}} = State) -> %% File handling - start_chunk_transfer handle_ctrl_result({pos_prel, _}, #state{client = From, caller = start_chunk_transfer} - = State) -> - case accept_data_connection(State) of - {ok, NewState} -> - gen_server:reply(From, ok), - {noreply, NewState#state{chunk = true, client = undefined, - caller = undefined}}; + = State0) -> + case accept_data_connection(State0) of + {ok, State1} -> + State = start_chunk(State1), + {noreply, State}; {error, _Reason} = ERROR -> - case State#state.client of + case State0#state.client of undefined -> - {stop, ERROR, State}; + {stop, ERROR, State0}; From -> gen_server:reply(From, ERROR), - {stop, normal, State#state{client = undefined}} + {stop, normal, State0#state{client = undefined}} end end; %%-------------------------------------------------------------------------- %% File handling - recv_file -handle_ctrl_result({pos_prel, _}, #state{caller = {recv_file, _}} = State) -> - case accept_data_connection(State) of - {ok, NewState} -> - activate_data_connection(NewState), - {noreply, NewState}; +handle_ctrl_result({pos_prel, _}, #state{caller = {recv_file, _}} = State0) -> + case accept_data_connection(State0) of + {ok, State1} -> + State = activate_data_connection(State1), + {noreply, State}; {error, _Reason} = ERROR -> - case State#state.client of + case State0#state.client of undefined -> - {stop, ERROR, State}; + {stop, ERROR, State0}; From -> gen_server:reply(From, ERROR), - {stop, normal, State#state{client = undefined}} + {stop, normal, State0#state{client = undefined}} end end; @@ -1853,36 +1905,32 @@ handle_ctrl_result({Status, _}, #state{caller = {recv_file, Fd}} = State) -> %%-------------------------------------------------------------------------- %% File handling - transfer_* handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_file, Fd}} - = State) -> - case accept_data_connection(State) of - {ok, NewState} -> - send_file(Fd, NewState); + = State0) -> + case accept_data_connection(State0) of + {ok, State1} -> + send_file(State1, Fd); {error, _Reason} = ERROR -> - case State#state.client of + case State0#state.client of undefined -> - {stop, ERROR, State}; + {stop, ERROR, State0}; From -> gen_server:reply(From, ERROR), - {stop, normal, State#state{client = undefined}} + {stop, normal, State0#state{client = undefined}} end end; handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_data, Bin}} - = State) -> - case accept_data_connection(State) of - {ok, NewState} -> - send_data_message(NewState, Bin), - close_data_connection(NewState), - activate_ctrl_connection(NewState), - {noreply, NewState#state{caller = transfer_data_second_phase, - dsock = undefined}}; + = State0) -> + case accept_data_connection(State0) of + {ok, State} -> + send_bin(State, Bin); {error, _Reason} = ERROR -> - case State#state.client of + case State0#state.client of undefined -> - {stop, ERROR, State}; + {stop, ERROR, State0}; From -> gen_server:reply(From, ERROR), - {stop, normal, State#state{client = undefined}} + {stop, normal, State0#state{client = undefined}} end end; @@ -1975,7 +2023,7 @@ setup_ctrl_connection(Host, Port, Timeout, State) -> MsTime = millisec_time(), case connect(Host, Port, Timeout, State) of {ok, IpFam, CSock} -> - NewState = State#state{csock = CSock, ipfamily = IpFam}, + NewState = State#state{csock = {tcp, CSock}, ipfamily = IpFam}, activate_ctrl_connection(NewState), case Timeout - (millisec_time() - MsTime) of Timeout2 when (Timeout2 >= 0) -> @@ -1991,12 +2039,12 @@ setup_ctrl_connection(Host, Port, Timeout, State) -> setup_data_connection(#state{mode = active, caller = Caller, csock = CSock} = State) -> - case (catch inet:sockname(CSock)) of + case (catch sockname(CSock)) of {ok, {{_, _, _, _, _, _, _, _} = IP, _}} -> {ok, LSock} = gen_tcp:listen(0, [{ip, IP}, {active, false}, inet6, binary, {packet, 0}]), - {ok, Port} = inet:port(LSock), + {ok, {_, Port}} = sockname(LSock), IpAddress = inet_parse:ntoa(IP), Cmd = mk_cmd("EPRT |2|~s|~p|", [IpAddress, Port]), send_ctrl_message(State, Cmd), @@ -2029,20 +2077,6 @@ setup_data_connection(#state{mode = passive, ipfamily = inet, activate_ctrl_connection(State), {noreply, State#state{caller = {setup_data_connection, Caller}}}. - -%% setup_data_connection(#state{mode = passive, ip_v6_disabled = false, -%% caller = Caller} = State) -> -%% send_ctrl_message(State, mk_cmd("EPSV", [])), -%% activate_ctrl_connection(State), -%% {noreply, State#state{caller = {setup_data_connection, Caller}}}; - -%% setup_data_connection(#state{mode = passive, ip_v6_disabled = true, -%% caller = Caller} = State) -> -%% send_ctrl_message(State, mk_cmd("PASV", [])), -%% activate_ctrl_connection(State), -%% {noreply, State#state{caller = {setup_data_connection, Caller}}}. - - connect(Host, Port, Timeout, #state{ipfamily = inet = IpFam}) -> connect2(Host, Port, IpFam, Timeout); @@ -2088,39 +2122,40 @@ connect2(Host, Port, IpFam, Timeout) -> accept_data_connection(#state{mode = active, dtimeout = DTimeout, - dsock = {lsock, LSock}} = State) -> + dsock = {lsock, LSock}} = State0) -> case gen_tcp:accept(LSock, DTimeout) of {ok, Socket} -> gen_tcp:close(LSock), - {ok, State#state{dsock = Socket}}; + maybe_requests_tls_upgrade(State0#state{dsock = {tcp, Socket}}); {error, Reason} -> {error, {data_connect_failed, Reason}} end; accept_data_connection(#state{mode = passive} = State) -> - {ok, State}. + maybe_requests_tls_upgrade(State). send_ctrl_message(#state{csock = Socket, verbose = Verbose}, Message) -> - %% io:format("send control message: ~n~p~n", [lists:flatten(Message)]), verbose(lists:flatten(Message),Verbose,send), send_message(Socket, Message). send_data_message(#state{dsock = Socket}, Message) -> - send_message(Socket, Message). - -send_message(Socket, Message) -> - case gen_tcp:send(Socket, Message) of + case send_message(Socket, Message) of ok -> ok; {error, Reason} -> - Report = io_lib:format("gen_tcp:send/2 failed for " - "reason ~p~n", [Reason]), + Report = io_lib:format("send/2 for socket ~p failed with " + "reason ~p~n", [Socket, Reason]), error_logger:error_report(Report), - %% If tcp does not work the only option is to terminate, + %% If tcp/ssl does not work the only option is to terminate, %% this is the expected behavior under these circumstances. exit(normal) %% User will get error message from terminate/2 end. +send_message({tcp, Socket}, Message) -> + gen_tcp:send(Socket, Message); +send_message({ssl, Socket}, Message) -> + ssl:send(Socket, Message). + activate_ctrl_connection(#state{csock = Socket, ctrl_data = {<<>>, _, _}}) -> activate_connection(Socket); activate_ctrl_connection(#state{csock = Socket}) -> @@ -2128,35 +2163,39 @@ activate_ctrl_connection(#state{csock = Socket}) -> %% that has been saved in ctrl_data, process this first. self() ! {tcp, Socket, <<>>}. -activate_data_connection(#state{dsock = Socket}) -> - activate_connection(Socket). - -activate_connection(Socket) -> - inet:setopts(Socket, [{active, once}]). +activate_data_connection(#state{tls_upgrading_data_connection = {false, _, _}, + dsock = Socket} = State) -> + activate_connection(Socket), + State; +activate_data_connection(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State) -> + State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, activate_data_connection, undefined}}. +activate_connection({tcp, Socket}) -> + inet:setopts(Socket, [{active, once}]); +activate_connection({ssl, Socket}) -> + ssl:setopts(Socket, [{active, once}]). close_ctrl_connection(#state{csock = undefined}) -> ok; -close_ctrl_connection(#state{csock = Socket}) -> +close_ctrl_connection(#state{csock = {_, Socket}}) -> close_connection(Socket). close_data_connection(#state{dsock = undefined}) -> ok; -close_data_connection(#state{dsock = {lsock, Socket}}) -> - close_connection(Socket); -close_data_connection(#state{dsock = Socket}) -> +close_data_connection(#state{dsock = {_, Socket}}) -> close_connection(Socket). close_connection(Socket) -> gen_tcp:close(Socket). %% ------------ FILE HANDELING ---------------------------------------- - -send_file(Fd, State) -> +send_file(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State, Fd) -> + {noreply, State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, send_file, Fd}}}; +send_file(State, Fd) -> case file_read(Fd) of {ok, N, Bin} when N > 0-> send_data_message(State, Bin), progress_report({binary, Bin}, State), - send_file(Fd, State); + send_file(State, Fd); {ok, _, _} -> file_close(Fd), close_data_connection(State), @@ -2206,6 +2245,15 @@ call(GenServer, Msg, Format, Timeout) -> cast(GenServer, Msg) -> gen_server:cast(GenServer, {self(), Msg}). +send_bin(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State, Bin) -> + State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, send_bin, Bin}}; +send_bin(State, Bin) -> + send_data_message(State, Bin), + close_data_connection(State), + activate_ctrl_connection(State), + {noreply, State#state{caller = transfer_data_second_phase, + dsock = undefined}}. + mk_cmd(Fmt, Args) -> [io_lib:format(Fmt, Args)| [?CR, ?LF]]. % Deep list ok. @@ -2216,20 +2264,6 @@ pwd_result(Lines) -> lists:splitwith(fun(?DOUBLE_QUOTE) -> false; (_) -> true end, Rest), Dir. -%% is_verbose(Params) -> -%% check_param(verbose, Params). - -%% is_debug(Flags) -> -%% check_param(debug, Flags). - -%% is_trace(Flags) -> -%% check_param(trace, Flags). - -%% is_ipv6_disabled(Flags) -> -%% check_param(ip_v6_disabled, Flags). - -%% check_param(Param, Params) -> -%% lists:member(Param, Params). key_search(Key, List, Default) -> case lists:keysearch(Key, 1, List) of @@ -2239,14 +2273,6 @@ key_search(Key, List, Default) -> Default end. -%% check_option(Pred, Value, Default) -> -%% case Pred(Value) of -%% true -> -%% Value; -%% false -> -%% Default -%% end. - verbose(Lines, true, Direction) -> DirStr = case Direction of @@ -2276,3 +2302,37 @@ progress_report(Report, #state{progress = ProgressPid}) -> millisec_time() -> {A,B,C} = erlang:now(), A*1000000000+B*1000+(C div 1000). + +peername({tcp, Socket}) -> + inet:peername(Socket); +peername({ssl, Socket}) -> + ssl:peername(Socket). +sockname({tcp, Socket}) -> + inet:peername(Socket); +sockname({ssl, Socket}) -> + ssl:peername(Socket). + +maybe_tls_upgrade(Pid, undefined) -> + {ok, Pid}; +maybe_tls_upgrade(Pid, TLSOptions) -> + call(Pid, {open, tls_upgrade, TLSOptions}, plain). + +maybe_requests_tls_upgrade(#state{tls_options = [_|_]} = State) -> + send_ctrl_message(State, mk_cmd("PBSZ 0", [])), + activate_ctrl_connection(State), + {ok, State#state{tls_upgrading_data_connection = {true, pbsz, undefined}}}; +maybe_requests_tls_upgrade(#state{tls_options = undefined} = State) -> + {ok, State}. + +next_action_after_tls_upgrade({M, F, undefined}, State) -> + M:F(State); +next_action_after_tls_upgrade({M, F, A}, State) -> + M:F(State, A). + +start_chunk(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State) -> + State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, start_chunk, undefined}}; +start_chunk(#state{client = From} = State) -> + gen_server:reply(From, ok), + State#state{chunk = true, + client = undefined, + caller = undefined}. diff --git a/lib/inets/src/ftp/ftp_response.erl b/lib/inets/src/ftp/ftp_response.erl index 364f534737..d60e97d5ee 100644 --- a/lib/inets/src/ftp/ftp_response.erl +++ b/lib/inets/src/ftp/ftp_response.erl @@ -175,6 +175,7 @@ error_string(Reason) -> %% Positive Preleminary Reply interpret_status(?POS_PREL,_,_) -> pos_prel; +interpret_status(?POS_COMPL, ?AUTH_ACC, 3) -> tls_upgrade; %% Positive Completion Reply interpret_status(?POS_COMPL,_,_) -> pos_compl; %% Positive Intermediate Reply nedd account diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile index b1daf51e25..e51f32d8fa 100644 --- a/lib/inets/test/Makefile +++ b/lib/inets/test/Makefile @@ -150,6 +150,7 @@ INETS_ROOT = ../../inets MODULES = \ inets_test_lib \ erl_make_certs \ + old_ftp_SUITE \ ftp_SUITE \ ftp_format_SUITE \ ftp_solaris8_sparc_test \ diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl index 17e5f6777e..9eaeea4dd3 100644 --- a/lib/inets/test/ftp_SUITE.erl +++ b/lib/inets/test/ftp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. 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 @@ -18,110 +18,88 @@ %% %% +%% +%% ct:run("../inets_test", ftp_SUITE). +%% + -module(ftp_SUITE). +-include_lib("kernel/include/file.hrl"). -include_lib("common_test/include/ct.hrl"). --include("test_server_line.hrl"). - -%% Test server specific exports --export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]). -% -export([init_per_testcase/2, end_per_testcase/2]). --export([init_per_suite/1, end_per_suite/1]). - --define(FTP_USER, "anonymous"). --define(FTP_PASS, passwd()). --define(FTP_PORT, 21). - --define(BAD_HOST, "badhostname"). --define(BAD_USER, "baduser"). --define(BAD_DIR, "baddirectory"). - --ifdef(ftp_debug_client). --define(ftp_open(Host, Flags), do_ftp_open(Host, [debug] ++ Flags)). --else. --ifdef(ftp_trace_client). --define(ftp_open(Host, Flags), do_ftp_open(Host, [trace] ++ Flags)). --else. --define(ftp_open(Host, Flags), do_ftp_open(Host, [verbose] ++ Flags)). --endif. --endif. +-include("inets_test_lib.hrl"). +%% Note: This directive should only be used in test suites. +-compile(export_all). %%-------------------------------------------------------------------- -%% all(Arg) -> [Doc] | [Case] | {skip, Comment} -%% Arg - doc | suite -%% Doc - string() -%% Case - atom() -%% Name of a test case function. -%% Comment - string() -%% Description: Returns documentation/test cases in this test suite -%% or a skip tuple if the platform is not supported. +%% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- -suite() -> [{ct_hooks, [ts_install_cth]}]. +all() -> + [ + {group, ftp_passive}, + {group, ftp_active}, + {group, ftps_passive}, + {group, ftps_active} + ]. -all() -> +groups() -> [ - {group, solaris8_test}, - {group, solaris9_test}, - {group, solaris10_test}, - {group, linux_x86_test}, - {group, linux_ppc_test}, - {group, macosx_x86_test}, - {group, macosx_ppc_test}, - {group, openbsd_test}, - {group, freebsd_test}, - {group, netbsd_test}, - {group, windows_xp_test}, - {group, windows_2003_server_test}, - {group, ticket_tests} + {ftp_passive, [], ftp_tests()}, + {ftp_active, [], ftp_tests()}, + {ftps_passive, [], ftp_tests()}, + {ftps_active, [], ftp_tests()} ]. -groups() -> +ftp_tests()-> [ - {solaris8_test, [], [{ftp_solaris8_sparc_test, all}]}, - {solaris9_test, [], [{ftp_solaris9_sparc_test, all}]}, - {solaris10_test, [], [{ftp_solaris10_sparc_test, all}, - {ftp_solaris10_x86_test, all}]}, - {linux_x86_test, [], [{ftp_linux_x86_test, all}]}, - {linux_ppc_test, [], [{ftp_linux_ppc_test, all}]}, - {macosx_x86_test, [], [{ftp_macosx_x86_test, all}]}, - {macosx_ppc_test, [], [{ftp_macosx_ppc_test, all}]}, - {openbsd_test, [], [{ftp_openbsd_x86_test, all}]}, - {freebsd_test, [], [{ftp_freebsd_x86_test, all}]}, - {netbsd_test, [], [{ftp_netbsd_x86_test, all}]}, - {windows_xp_test, [], [{ftp_windows_xp_test, all}]}, - {windows_2003_server_test, [], [{ftp_windows_2003_server_test, all}]}, - {ticket_tests, [], [{ftp_ticket_test, all}]} + user, + pwd, + cd, + lcd, + ls, + nlist, + rename, + delete, + mkdir, + send, + send_bin, + send_chunk, + append, + append_bin, + append_chunk, + recv, + recv_bin, + recv_chunk, + type, + quote, + ip_v6_disabled ]. -init_per_group(_GroupName, Config) -> - Config. +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. -end_per_group(_GroupName, Config) -> - Config. +%%-------------------------------------------------------------------- +init_per_group(_Group, Config) -> + Config. +end_per_group(_, _Config) -> + ok. +%%-------------------------------------------------------------------- +init_per_testcase(_Case, Config) -> + Config. +end_per_testcase(_Case, _Config) -> + ok. %%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initiation before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. +%% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- -init_per_suite(Config) -> - inets:start(), - Config. %%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite +%% Internal functions ----------------------------------------------- %%-------------------------------------------------------------------- -end_per_suite(_Config) -> - inets:stop(), - ok. diff --git a/lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel b/lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel deleted file mode 100644 index 75096ce687..0000000000 --- a/lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel +++ /dev/null @@ -1,18 +0,0 @@ -%% Add a host name in the appropriate list -%% Each "platform" contains a list of hostnames (a string) that can -%% be used for testing the ftp client. -%% The definition below are an example!! -[{solaris8_sparc, ["solaris8_sparc_dummy1", "solaris8_sparc_dummy2"]}, - {solaris9_sparc, ["solaris9_sparc_dummy1"]}, - {solaris10_sparc, ["solaris10_sparc_dummy1"]}, - {solaris10_x86, ["solaris10_x86_dummy1", "solaris10_x86_dummy2"]}, - {linux_x86, ["linux_x86_dummy1", "linux_x86_dummy2"]}, - {linux_ppc, ["linux_ppc_dummy1"]}, - {macosx_ppc, ["macosx_ppc_dummy1"]}, - {macosx_x86, ["macosx_x86_dummy1", "macosx_x86_dummy2"]}, - {openbsd_x86, []}, - {freebsd_x86, ["freebsd_x86_dummy1"]}, - {netbsd_x86, []}, - {windows_xp, []}, - {windows_2003_server, ["win2003_dummy1"]}, - {ticket_test, ["solaris8_x86_dummy1", "linux_x86_dummy1"]}]. diff --git a/lib/inets/test/old_ftp_SUITE.erl b/lib/inets/test/old_ftp_SUITE.erl new file mode 100644 index 0000000000..91bcae3053 --- /dev/null +++ b/lib/inets/test/old_ftp_SUITE.erl @@ -0,0 +1,127 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2013. 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. +%% +%% %CopyrightEnd% +%% +%% + +-module(old_ftp_SUITE). + +-include_lib("common_test/include/ct.hrl"). +-include("test_server_line.hrl"). + +%% Test server specific exports +-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]). +% -export([init_per_testcase/2, end_per_testcase/2]). +-export([init_per_suite/1, end_per_suite/1]). + +-define(FTP_USER, "anonymous"). +-define(FTP_PASS, passwd()). +-define(FTP_PORT, 21). + +-define(BAD_HOST, "badhostname"). +-define(BAD_USER, "baduser"). +-define(BAD_DIR, "baddirectory"). + +-ifdef(ftp_debug_client). +-define(ftp_open(Host, Flags), do_ftp_open(Host, [debug] ++ Flags)). +-else. +-ifdef(ftp_trace_client). +-define(ftp_open(Host, Flags), do_ftp_open(Host, [trace] ++ Flags)). +-else. +-define(ftp_open(Host, Flags), do_ftp_open(Host, [verbose] ++ Flags)). +-endif. +-endif. + + +%%-------------------------------------------------------------------- +%% all(Arg) -> [Doc] | [Case] | {skip, Comment} +%% Arg - doc | suite +%% Doc - string() +%% Case - atom() +%% Name of a test case function. +%% Comment - string() +%% Description: Returns documentation/test cases in this test suite +%% or a skip tuple if the platform is not supported. +%%-------------------------------------------------------------------- +suite() -> [{ct_hooks, [ts_install_cth]}]. + +all() -> + [ + {group, solaris8_test}, + {group, solaris9_test}, + {group, solaris10_test}, + {group, linux_x86_test}, + {group, linux_ppc_test}, + {group, macosx_x86_test}, + {group, macosx_ppc_test}, + {group, openbsd_test}, + {group, freebsd_test}, + {group, netbsd_test}, + {group, windows_xp_test}, + {group, windows_2003_server_test}, + {group, ticket_tests} + ]. + +groups() -> + [ + {solaris8_test, [], [{ftp_solaris8_sparc_test, all}]}, + {solaris9_test, [], [{ftp_solaris9_sparc_test, all}]}, + {solaris10_test, [], [{ftp_solaris10_sparc_test, all}, + {ftp_solaris10_x86_test, all}]}, + {linux_x86_test, [], [{ftp_linux_x86_test, all}]}, + {linux_ppc_test, [], [{ftp_linux_ppc_test, all}]}, + {macosx_x86_test, [], [{ftp_macosx_x86_test, all}]}, + {macosx_ppc_test, [], [{ftp_macosx_ppc_test, all}]}, + {openbsd_test, [], [{ftp_openbsd_x86_test, all}]}, + {freebsd_test, [], [{ftp_freebsd_x86_test, all}]}, + {netbsd_test, [], [{ftp_netbsd_x86_test, all}]}, + {windows_xp_test, [], [{ftp_windows_xp_test, all}]}, + {windows_2003_server_test, [], [{ftp_windows_2003_server_test, all}]}, + {ticket_tests, [], [{ftp_ticket_test, all}]} + ]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + + + +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config) -> Config +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Initiation before the whole suite +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + inets:start(), + Config. + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config) -> _ +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after the whole suite +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + inets:stop(), + ok. diff --git a/lib/inets/test/old_ftp_SUITE_data/ftpd_hosts.skel b/lib/inets/test/old_ftp_SUITE_data/ftpd_hosts.skel new file mode 100644 index 0000000000..98dff81881 --- /dev/null +++ b/lib/inets/test/old_ftp_SUITE_data/ftpd_hosts.skel @@ -0,0 +1,18 @@ +%% Add a host name in the appropriate list +%% Each "platform" contains a list of hostnames (a string) that can +%% be used for testing the ftp client. +%% The definition below are an example!! +[{solaris8_sparc, ["solaris8_sparc_dummy1", "solaris8_sparc_dummy2"]}, + {solaris9_sparc, ["solaris9_sparc_dummy1"]}, + {solaris10_sparc, ["solaris10_sparc_dummy1"]}, + {solaris10_x86, ["solaris10_x86_dummy1", "solaris10_x86_dummy2"]}, + {linux_x86, ["linux_x86_dummy1", "linux_x86_dummy2"]}, + {linux_ppc, ["linux_ppc_dummy1"]}, + {macosx_ppc, ["macosx_ppc_dummy1"]}, + {macosx_x86, ["macosx_x86_dummy1", "macosx_x86_dummy2"]}, + {openbsd_x86, []}, + {freebsd_x86, ["freebsd_x86_dummy1"]}, + {netbsd_x86, []}, + {windows_xp, []}, + {windows_2003_server, ["win2003_dummy1"]}, + {ticket_test, ["solaris8_x86_dummy1", "linux_x86_dummy1"]}]. -- cgit v1.2.3 From 91d915b04c278ffb91c7aeaa298a1d4e96673b0f Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Fri, 27 Sep 2013 16:04:45 +0200 Subject: ftp: Implement ftps (upgrade to TLS) --- lib/inets/test/ftp_SUITE_data/vsftpd.conf | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 lib/inets/test/ftp_SUITE_data/vsftpd.conf diff --git a/lib/inets/test/ftp_SUITE_data/vsftpd.conf b/lib/inets/test/ftp_SUITE_data/vsftpd.conf new file mode 100644 index 0000000000..4763e439a4 --- /dev/null +++ b/lib/inets/test/ftp_SUITE_data/vsftpd.conf @@ -0,0 +1,9 @@ +listen=YES +listen_port=9999 +run_as_launching_user=YES +#ssl_enable=YES +#allow_anon_ssl=YES +background=YES +anonymous_enable=YES +#rsa_cert_file=cert.pem +#rsa_key_file=key.pem \ No newline at end of file -- cgit v1.2.3 From 83e9251f949d360dc4fb0eb04f24c4d5b85915da Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Fri, 8 Nov 2013 12:10:57 +0100 Subject: ftp: old ftp_SUITE re-written and works --- lib/inets/test/ftp_SUITE.erl | 724 +++++++++++++++++++++++++++++- lib/inets/test/ftp_SUITE_data/vsftpd.conf | 10 +- 2 files changed, 723 insertions(+), 11 deletions(-) diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl index 9eaeea4dd3..1b8f3af8ef 100644 --- a/lib/inets/test/ftp_SUITE.erl +++ b/lib/inets/test/ftp_SUITE.erl @@ -31,6 +31,16 @@ %% Note: This directive should only be used in test suites. -compile(export_all). +-define(FTP_USER, "anonymous"). +-define(FTP_PASS, (fun({ok,__H}) -> "ftp_SUITE@" ++ __H; + (_) -> "ftp_SUITE@localhost" + end)(inet:gethostname()) + ). + +-define(BAD_HOST, "badhostname"). +-define(BAD_USER, "baduser"). +-define(BAD_DIR, "baddirectory"). + %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- @@ -53,6 +63,7 @@ groups() -> ftp_tests()-> [ user, + bad_user, pwd, cd, lcd, @@ -61,13 +72,16 @@ ftp_tests()-> rename, delete, mkdir, + rmdir, send, + send_3, send_bin, send_chunk, append, append_bin, append_chunk, recv, + recv_3, recv_bin, recv_chunk, type, @@ -76,30 +90,720 @@ ftp_tests()-> ]. %%-------------------------------------------------------------------- + +%%% Config +%%% key meaning +%%% ................................................................ +%%% ftpservers list of servers to check if they are available +%%% The element is: +%%% {Name, % string(). The os command name +%%% StartCommand, % fun()->{ok,start_result()} | {error,string()}. +%%% % The command to start the daemon with. +%%% ChkUp, % fun(start_result()) -> string(). Os command to check +%%% % if the server is running. [] if not running. +%%% % The string in string() is suitable for logging. +%%% StopCommand, % fun(start_result()) -> void(). The command to stop the daemon with. +%%% Host, % string(). Mostly "localhost" +%%% Port % pos_integer() +%%% } +%%% + +-define(default_ftp_servers, + [{"vsftpd", + fun(__CONF__) -> +%% make_config_file(vsftpd, Conf), + ConfFile = filename:join(?config(data_dir,__CONF__), "vsftpd.conf"), + AnonRoot = ?config(priv_dir,__CONF__), + Cmd = ["vsftpd \"",ConfFile,"\"", + " -oftpd_banner=erlang_otp_testing", + " -oanon_root=\"",AnonRoot,"\"" + ], + ct:log("~s",[Cmd]), + case os:cmd(Cmd) of + [] -> {ok,'dont care'}; + [Msg] -> {error,Msg} + end + end, + fun(_StartResult) -> os:cmd("ps ax | grep erlang_otp_testing | grep -v grep") + end, + fun(_StartResult) -> os:cmd("kill `ps ax | grep erlang_otp_testing | awk '/vsftpd/{print $1}'`") + end, + fun(__CONF__) -> + AnonRoot = ?config(priv_dir,__CONF__), + [{id2ftp, fun(Id) -> filename:join(AnonRoot,Id) end}, + {id2ftp_result,fun(Id) -> filename:join(AnonRoot,Id) end} | __CONF__] + end, + "localhost", + 9999 + } + ] + ). + + init_per_suite(Config) -> - Config. + case find_executable(Config) of + false -> + {skip, "No ftp server found"}; + {ok,Data} -> + TstDir = filename:join(?config(priv_dir,Config), "test"), + file:make_dir(TstDir), + start_ftpd([{test_dir,TstDir}, + {ftpd_data,Data} + | Config]) + end. -end_per_suite(_Config) -> +end_per_suite(Config) -> + ps_ftpd(Config), +%% stop_ftpd(Config), + ps_ftpd(Config), ok. %%-------------------------------------------------------------------- -init_per_group(_Group, Config) -> - Config. +init_per_group(_Group, Config) -> Config. -end_per_group(_, _Config) -> - ok. +end_per_group(_Group, Config) -> Config. %%-------------------------------------------------------------------- -init_per_testcase(_Case, Config) -> - Config. +init_per_testcase(Case, Config0) -> + Group = proplists:get_value(name,?config(tc_group_properties,Config0)), + try ?MODULE:Case(doc) of + Msg -> ct:comment(Msg) + catch + _:_-> ok + end, + Config = + case Group of + ftp_active -> ftp__open(Config0, [{mode,active}]); + ftps_active -> ftp__open(Config0, [{mode,active}]); + ftp_passive -> ftp__open(Config0, [{mode,passive}]); + ftps_passive -> ftp__open(Config0, [{mode,passive}]) + end, + case Case of + user -> Config; + bad_user -> Config; + _ -> + Pid = ?config(ftp,Config), + ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), + ok = ftp:cd(Pid, ?config(priv_dir,Config)), + Config + end. -end_per_testcase(_Case, _Config) -> - ok. + +end_per_testcase(user, _Config) -> ok; +end_per_testcase(bad_user, _Config) -> ok; +end_per_testcase(_Case, Config) -> ftp__close(Config). %%-------------------------------------------------------------------- %% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- +user(doc) -> ["Open an ftp connection to a host, and logon as anonymous ftp, then logoff"]; +user(Config) -> + Pid = ?config(ftp, Config), + ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), % logon + ok = ftp:close(Pid), % logoff + {error,eclosed} = ftp:pwd(Pid), % check logoff result + ok. + +%%------------------------------------------------------------------------- +bad_user(doc) -> ["Open an ftp connection to a host, and logon with bad user."]; +bad_user(Config) -> + Pid = ?config(ftp, Config), + {error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS), + ok. + +%%------------------------------------------------------------------------- +pwd(doc) -> ["Test ftp:pwd/1 & ftp:lpwd/1"]; +pwd(Config0) -> + Config = set_state([reset], Config0), + Pid = ?config(ftp, Config), + {ok, PWD} = ftp:pwd(Pid), + {ok, PathLpwd} = ftp:lpwd(Pid), + ct:log("PWD=~p~nPathLpwd=~p",[PWD,PathLpwd]), + PWD = id2ftp_result("", Config), + PathLpwd = id2ftp_result("", Config). + +%%------------------------------------------------------------------------- +cd(doc) -> ["Open an ftp connection, log on as anonymous ftp, and cd to a" + "directory and to a non-existent directory."]; +cd(Config0) -> + Dir = "test", + Config = set_state([reset,{mkdir,Dir}], Config0), + Pid = ?config(ftp, Config), + ok = ftp:cd(Pid, id2ftp(Dir,Config)), + {ok, PWD} = ftp:pwd(Pid), + ExpectedPWD = id2ftp_result(Dir, Config), + ct:log("PWD=~p~nExpectedPWD=~p",[PWD,ExpectedPWD]), + PWD = ExpectedPWD, + {error, epath} = ftp:cd(Pid, ?BAD_DIR). + +%%------------------------------------------------------------------------- +lcd(doc) -> + ["Test api function ftp:lcd/2"]; +lcd(Config0) -> + Dir = "test", + Config = set_state([reset,{mkdir,Dir}], Config0), + Pid = ?config(ftp, Config), + ok = ftp:lcd(Pid, id2ftp(Dir,Config)), + {ok, PWD} = ftp:lpwd(Pid), + ExpectedPWD = id2ftp_result(Dir, Config), + ct:log("PWD=~p~nExpectedPWD=~p",[PWD,ExpectedPWD]), + PWD = ExpectedPWD, + {error, epath} = ftp:lcd(Pid, ?BAD_DIR). + +%%------------------------------------------------------------------------- +ls(doc) -> ["Open an ftp connection; ls the current directory, and the " + "\"test\" directory. We assume that ls never fails, since " + "it's output is meant to be read by humans. "]; +ls(Config0) -> + Config = set_state([reset,{mkdir,"test"}], Config0), + Pid = ?config(ftp, Config), + {ok, _R1} = ftp:ls(Pid), + {ok, _R2} = ftp:ls(Pid, id2ftp("test",Config)), + %% neither nlist nor ls operates on a directory + %% they operate on a pathname, which *can* be a + %% directory, but can also be a filename or a group + %% of files (including wildcards). + case ?config(wildcard_support, Config) of + true -> + {ok, _R3} = ftp:ls(Pid, id2ftp("te*",Config)); + _ -> + ok + end. + +%%------------------------------------------------------------------------- +nlist(doc) -> ["Open an ftp connection; nlist the current directory, and the " + "\"test\" directory. Nlist does not behave consistenly over " + "operating systems. On some it is an error to have an empty " + "directory."]; +nlist(Config0) -> + Config = set_state([reset,{mkdir,"test"}], Config0), + Pid = ?config(ftp, Config), + {ok, _R1} = ftp:nlist(Pid), + {ok, _R2} = ftp:nlist(Pid, id2ftp("test",Config)), + %% neither nlist nor ls operates on a directory + %% they operate on a pathname, which *can* be a + %% directory, but can also be a filename or a group + %% of files (including wildcards). + case ?config(wildcard_support, Config) of + true -> + {ok, _R3} = ftp:nlist(Pid, id2ftp("te*",Config)); + _ -> + ok + end. + +%%------------------------------------------------------------------------- +rename(doc) -> ["Rename a file."]; +rename(Config0) -> + Contents = <<"ftp_SUITE test ...">>, + OldFile = "old.txt", + NewFile = "new.txt", + Config = set_state([reset,{mkfile,OldFile,Contents}], Config0), + Pid = ?config(ftp, Config), + + ok = ftp:rename(Pid, + id2ftp(OldFile,Config), + id2ftp(NewFile,Config)), + + true = (chk_file(NewFile,Contents,Config) + and chk_no_file([OldFile],Config)). + + +%%------------------------------------------------------------------------- +send(doc) -> ["Transfer a file with ftp using send/2."]; +send(Config0) -> + Contents = <<"ftp_SUITE test ...">>, + SrcDir = "data", + File = "file.txt", + Config = set_state([reset,{mkfile,[SrcDir,File],Contents}], Config0), + Pid = ?config(ftp, Config), + +chk_no_file([File],Config), +chk_file([SrcDir,File],Contents,Config), + + ok = ftp:lcd(Pid, id2ftp(SrcDir,Config)), + ok = ftp:cd(Pid, id2ftp("",Config)), + ok = ftp:send(Pid, File), + + chk_file(File, Contents, Config). + +%%------------------------------------------------------------------------- +send_3(doc) -> ["Transfer a file with ftp using send/3."]; +send_3(Config0) -> + Contents = <<"ftp_SUITE test ...">>, + Dir = "incoming", + File = "file.txt", + RemoteFile = "remfile.txt", + Config = set_state([reset,{mkfile,File,Contents},{mkdir,Dir}], Config0), + Pid = ?config(ftp, Config), + + ok = ftp:cd(Pid, id2ftp(Dir,Config)), + ok = ftp:lcd(Pid, id2ftp("",Config)), + ok = ftp:send(Pid, File, RemoteFile), + + chk_file([Dir,RemoteFile], Contents, Config). + +%%------------------------------------------------------------------------- +send_bin(doc) -> ["Send a binary."]; +send_bin(Config0) -> + BinContents = <<"ftp_SUITE test ...">>, + File = "file.txt", + Config = set_state([reset], Config0), + Pid = ?config(ftp, Config), + {error, enotbinary} = ftp:send_bin(Pid, "some string", id2ftp(File,Config)), + ok = ftp:send_bin(Pid, BinContents, id2ftp(File,Config)), + chk_file(File, BinContents, Config). + +%%------------------------------------------------------------------------- +send_chunk(doc) -> ["Send a binary using chunks."]; +send_chunk(Config0) -> + Contents = <<"ftp_SUITE test ...">>, + File = "file.txt", + Config = set_state([reset,{mkdir,"incoming"}], Config0), + Pid = ?config(ftp, Config), + + ok = ftp:send_chunk_start(Pid, id2ftp(File,Config)), + {error, echunk} = ftp:cd(Pid, "incoming"), + {error, enotbinary} = ftp:send_chunk(Pid, "some string"), + ok = ftp:send_chunk(Pid, Contents), + ok = ftp:send_chunk(Pid, Contents), + ok = ftp:send_chunk_end(Pid), + chk_file(File, <>, Config). + +%%------------------------------------------------------------------------- +delete(doc) -> ["Delete a file."]; +delete(Config0) -> + Contents = <<"ftp_SUITE test ...">>, + File = "file.txt", + Config = set_state([reset,{mkfile,File,Contents}], Config0), + Pid = ?config(ftp, Config), + ok = ftp:delete(Pid, id2ftp(File,Config)), + chk_no_file([File], Config). + +%%------------------------------------------------------------------------- +mkdir(doc) -> ["Make a remote directory."]; +mkdir(Config0) -> + NewDir = "new_dir", + Config = set_state([reset], Config0), + Pid = ?config(ftp, Config), + ok = ftp:mkdir(Pid, id2ftp(NewDir,Config)), + chk_dir([NewDir], Config). + +%%------------------------------------------------------------------------- +rmdir(doc) -> ["Remove a directory."]; +rmdir(Config0) -> + Dir = "dir", + Config = set_state([reset,{mkdir,Dir}], Config0), + Pid = ?config(ftp, Config), + ok = ftp:rmdir(Pid, id2ftp(Dir,Config)), + chk_no_dir([Dir], Config). + +%%------------------------------------------------------------------------- +append(doc) -> ["Append a local file twice to a remote file"]; +append(Config0) -> + SrcFile = "f_src.txt", + DstFile = "f_dst.txt", + Contents = <<"ftp_SUITE test ...">>, + Config = set_state([reset,{mkfile,SrcFile,Contents}], Config0), + Pid = ?config(ftp, Config), + ok = ftp:append(Pid, id2ftp(SrcFile,Config), id2ftp(DstFile,Config)), + ok = ftp:append(Pid, id2ftp(SrcFile,Config), id2ftp(DstFile,Config)), + chk_file(DstFile, <>, Config). + +%%------------------------------------------------------------------------- +append_bin(doc) -> ["Append a local file twice to a remote file using append_bin"]; +append_bin(Config0) -> + DstFile = "f_dst.txt", + Contents = <<"ftp_SUITE test ...">>, + Config = set_state([reset], Config0), + Pid = ?config(ftp, Config), + ok = ftp:append_bin(Pid, Contents, id2ftp(DstFile,Config)), + ok = ftp:append_bin(Pid, Contents, id2ftp(DstFile,Config)), + chk_file(DstFile, <>, Config). + +%%------------------------------------------------------------------------- +append_chunk(doc) -> ["Append chunks."]; +append_chunk(Config0) -> + File = "f_dst.txt", + Contents = [<<"ER">>,<<"LE">>,<<"RL">>], + Config = set_state([reset], Config0), + Pid = ?config(ftp, Config), + ok = ftp:append_chunk_start(Pid, id2ftp(File,Config)), + {error, enotbinary} = ftp:append_chunk(Pid, binary_to_list(lists:nth(1,Contents))), + ok = ftp:append_chunk(Pid,lists:nth(1,Contents)), + ok = ftp:append_chunk(Pid,lists:nth(2,Contents)), + ok = ftp:append_chunk(Pid,lists:nth(3,Contents)), + ok = ftp:append_chunk_end(Pid), + chk_file(File, <<"ERLERL">>, Config). + +%%------------------------------------------------------------------------- +recv(doc) -> ["Receive a file using recv/2"]; +recv(Config0) -> + File = "f_dst.txt", + SrcDir = "a_dir", + Contents = <<"ftp_SUITE test ...">>, + Config = set_state([reset, {mkfile,[SrcDir,File],Contents}], Config0), + Pid = ?config(ftp, Config), + ok = ftp:cd(Pid, id2ftp(SrcDir,Config)), + ok = ftp:lcd(Pid, id2ftp("",Config)), + ok = ftp:recv(Pid, File), + chk_file(File, Contents, Config). + +%%------------------------------------------------------------------------- +recv_3(doc) -> ["Receive a file using recv/3"]; +recv_3(Config0) -> + DstFile = "f_src.txt", + SrcFile = "f_dst.txt", + Contents = <<"ftp_SUITE test ...">>, + Config = set_state([reset, {mkfile,SrcFile,Contents}], Config0), + Pid = ?config(ftp, Config), + ok = ftp:cd(Pid, id2ftp("",Config)), + ok = ftp:recv(Pid, SrcFile, id2abs(DstFile,Config)), + chk_file(DstFile, Contents, Config). + +%%------------------------------------------------------------------------- +recv_bin(doc) -> ["Receive a file as a binary."]; +recv_bin(Config0) -> + File = "f_dst.txt", + Contents = <<"ftp_SUITE test ...">>, + Config = set_state([reset, {mkfile,File,Contents}], Config0), + Pid = ?config(ftp, Config), + {ok,Received} = ftp:recv_bin(Pid, id2ftp(File,Config)), + find_diff(Received, Contents). + +%%------------------------------------------------------------------------- +recv_chunk(doc) -> ["Receive a file using chunk-wise."]; +recv_chunk(Config0) -> + File = "big_file.txt", + Contents = list_to_binary( lists:duplicate(1000, lists:seq(0,255)) ), + Config = set_state([reset, {mkfile,File,Contents}], Config0), + Pid = ?config(ftp, Config), + {{error, "ftp:recv_chunk_start/2 not called"},_} = recv_chunk(Pid, <<>>), + ok = ftp:recv_chunk_start(Pid, id2ftp(File,Config)), + {ok, ReceivedContents, Nchunks} = recv_chunk(Pid, <<>>), + ct:log("Received ~p chunks",[Nchunks]), + find_diff(ReceivedContents, Contents). + +recv_chunk(Pid, Acc) -> recv_chunk(Pid, Acc, 0). + +recv_chunk(Pid, Acc, N) -> + case ftp:recv_chunk(Pid) of + ok -> {ok, Acc, N}; + {ok, Bin} -> recv_chunk(Pid, <>, N+1); + Error -> {Error, N} + end. + +%%------------------------------------------------------------------------- +type(doc) -> ["Test that we can change btween ASCCI and binary transfer mode"]; +type(Config) -> + Pid = ?config(ftp, Config), + ok = ftp:type(Pid, ascii), + ok = ftp:type(Pid, binary), + ok = ftp:type(Pid, ascii), + {error, etype} = ftp:type(Pid, foobar). + +%%------------------------------------------------------------------------- +quote(doc) -> [""]; +quote(Config) -> + Pid = ?config(ftp, Config), + ["257 \""++_Rest] = ftp:quote(Pid, "pwd"), %% 257 + [_| _] = ftp:quote(Pid, "help"), + %% This negativ test causes some ftp servers to hang. This test + %% is not important for the client, so we skip it for now. + %%["425 Can't build data connection: Connection refused."] + %% = ftp:quote(Pid, "list"), + ok. + + +%%------------------------------------------------------------------------- +ip_v6_disabled(doc) -> ["Test ipv4 command PORT"]; +ip_v6_disabled(_Config) -> + %%% FIXME!!!! What is this??? + ok.%% send(Config). + +%%------------------------------------------------------------------------- +%% big_one(doc) -> +%% ["Create a local file and transfer it to the remote host into the " +%% "the \"incoming\" directory, remove " +%% "the local file. Then open a new connection; cd to \"incoming\", " +%% "lcd to the private directory; receive the file; delete the " +%% "remote file; close connection; check that received file is in " +%% "the correct directory; cleanup." ]; +%% big_one(Config) -> +%% Pid = ?config(ftp, Config), +%% do_recv(Pid, Config). + +%% do_recv(Pid, Config) -> +%% PrivDir = ?config(priv_dir, Config), +%% File = ?config(file, Config), +%% Newfile = ?config(new_file, Config), +%% AbsFile = filename:absname(File, PrivDir), +%% Contents = "ftp_SUITE:recv test ...", +%% ok = file:write_file(AbsFile, list_to_binary(Contents)), +%% ok = ftp:cd(Pid, "incoming"), +%% ftp:delete(Pid, File), % reset +%% ftp:lcd(Pid, PrivDir), +%% ok = ftp:send(Pid, File), +%% ok = file:delete(AbsFile), % cleanup +%% test_server:sleep(100), +%% ok = ftp:lcd(Pid, PrivDir), +%% ok = ftp:recv(Pid, File), +%% {ok, Files} = file:list_dir(PrivDir), +%% true = lists:member(File, Files), +%% ok = file:delete(AbsFile), % cleanup +%% ok = ftp:recv(Pid, File, Newfile), +%% ok = ftp:delete(Pid, File), % cleanup +%% ok. + %%-------------------------------------------------------------------- %% Internal functions ----------------------------------------------- %%-------------------------------------------------------------------- + +chk_file(Path=[C|_], ExpectedContents, Config) when 0 + chk_file([Path], ExpectedContents, Config); + +chk_file(PathList, ExpectedContents, Config) -> + Path = filename:join(PathList), + AbsPath = id2abs(Path,Config), +ct:log("chk_file/3 dbg: PathList=~p, Path=~p~nAbsPath=~p",[PathList,Path,AbsPath]), + case file:read_file(AbsPath) of + {ok,ExpectedContents} -> + true; + {ok,ReadContents} -> + {error,{diff,Pos,RC,LC}} = find_diff(ReadContents, ExpectedContents, 1), + ct:log("Bad contents of ~p.~nGot:~n~p~nExpected:~n~p~nDiff at pos ~p ~nRead: ~p~nExp : ~p", + [AbsPath,ReadContents,ExpectedContents,Pos,RC,LC]), + ct:fail("Bad contents of ~p", [Path]); + {error,Error} -> + try begin + {ok,CWD} = file:get_cwd(), + ct:log("file:get_cwd()=~p~nfiles:~n~p",[CWD,file:list_dir(CWD)]) + end + of _ -> ok + catch _:_ ->ok + end, + ct:fail("Error reading ~p: ~p",[Path,Error]) + end. + + +chk_no_file(Path=[C|_], Config) when 0 + chk_no_file([Path], Config); + +chk_no_file(PathList, Config) -> + Path = filename:join(PathList), + AbsPath = id2abs(Path,Config), + case file:read_file(AbsPath) of + {error,enoent} -> + true; + {ok,Contents} -> + ct:log("File ~p exists although it shouldn't. Contents:~n~p", + [AbsPath,Contents]), + ct:fail("File exists: ~p", [Path]); + {error,Error} -> + ct:fail("Unexpected error reading ~p: ~p",[Path,Error]) + end. + + +chk_dir(Path=[C|_], Config) when 0 + chk_dir([Path], Config); + +chk_dir(PathList, Config) -> + Path = filename:join(PathList), + AbsPath = id2abs(Path,Config), + case file:read_file_info(AbsPath) of + {ok, #file_info{type=directory}} -> + true; + {ok, #file_info{type=Type}} -> + ct:fail("Expected dir ~p is a ~p",[Path,Type]); + {error,Error} -> + ct:fail("Expected dir ~p: ~p",[Path,Error]) + end. + +chk_no_dir(PathList, Config) -> + Path = filename:join(PathList), + AbsPath = id2abs(Path,Config), + case file:read_file_info(AbsPath) of + {error,enoent} -> + true; + {ok, #file_info{type=directory}} -> + ct:fail("Dir ~p erroneously exists",[Path]); + {ok, #file_info{type=Type}} -> + ct:fail("~p ~p erroneously exists",[Type,Path]); + {error,Error} -> + ct:fail("Unexpected error for ~p: ~p",[Path,Error]) + end. + + +%%-------------------------------------------------------------------- +%%-------------------------------------------------------------------- +%% find a suitable ftpd +%% +find_executable(Config) -> + FTPservers = case ?config(ftpservers,Config) of + undefined -> ?default_ftp_servers; + L -> L + end, + case lists:dropwhile(fun not_available/1, FTPservers) of + [] -> false; + [FTPD_data|_] -> {ok, FTPD_data} + end. + +not_available({Name,_StartCmd,_ChkUp,_StopCommand,_ConfigUpd,_Host,_Port}) -> + os:find_executable(Name) == false. + +%%-------------------------------------------------------------------- +%% start/stop of ftpd +%% +start_ftpd(Config) -> + {Name,StartCmd,ChkUp,_StopCommand,ConfigRewrite,Host,Port} = ?config(ftpd_data, Config), + case StartCmd(Config) of + {ok,StartResult} -> + ct:log( ChkUp(StartResult) ), + [{ftpd_host,Host}, + {ftpd_port,Port}, + {ftpd_start_result,StartResult} | ConfigRewrite(Config)]; + {error,Msg} -> + {skip, [Name," not started: ",Msg]} + end. + +stop_ftpd(Config) -> + {_Name,_StartCmd,_ChkUp,StopCommand,_ConfigUpd,_Host,_Port} = ?config(ftpd_data, Config), + StopCommand(?config(ftpd_start_result,Config)). + +ps_ftpd(Config) -> + {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = ?config(ftpd_data, Config), + ct:log( ChkUp(?config(ftpd_start_result,Config)) ). + + +ftpd_running(Config) -> + {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = ?config(ftpd_data, Config), + ChkUp(?config(ftpd_start_result,Config)). + +%%-------------------------------------------------------------------- +%% start/stop of ftpc +%% +ftp__open(Config, Options) -> + Host = ?config(ftpd_host,Config), + Port = ?config(ftpd_port,Config), + ct:log("Host=~p, Port=~p",[Host,Port]), + {ok,Pid} = ftp:open(Host, [{port,Port} | Options]), + [{ftp,Pid}|Config]. + +ftp__close(Config) -> + ok = ftp:close(?config(ftp,Config)), + Config. + +%%-------------------------------------------------------------------- +%% +split(Cs) -> string:tokens(Cs, "\r\n"). + +%%-------------------------------------------------------------------- +%% +find_diff(Bin1, Bin2) -> + case find_diff(Bin1, Bin2, 1) of + {error, {diff,Pos,RC,LC}} -> + ct:log("Contents differ at position ~p.~nOp1: ~p~nOp2: ~p",[Pos,RC,LC]), + ct:fail("Contents differ at pos ~p",[Pos]); + Other -> + Other + end. + +find_diff(A, A, _) -> true; +find_diff(<>, <>, Pos) -> find_diff(T1, T2, Pos+1); +find_diff(RC, LC, Pos) -> {error, {diff, Pos, RC, LC}}. +%%-------------------------------------------------------------------- +%% +make_config_file(vsftpd, Config) -> + ct:log("Config=~p",[Config]), + DstFileName = filename:join(?config(data_dir,Config), "vsftpd.conf"), + ct:log("DstFileName=~p",[DstFileName]), + SrcFileName = DstFileName ++ ".src", + ct:log("SrcFileName=~p",[SrcFileName]), + Chroot = ?config(priv_dir,Config), + ct:log("Chroot=~p",[Chroot]), + {ok,SrcContents} = file:read_file(SrcFileName), + ct:log("SrcContents=~p",[SrcContents]), + SFN = list_to_binary(SrcFileName), + CHR = list_to_binary(Chroot), + DstContents = + <<"## Generated, do not edit!\n", + "## Source: ",SFN/binary,"\n", + "\n", + "chroot_local_user=YES\n", + "passwd_chroot_enable=",CHR/binary,"\n", + "\n", + SrcContents/binary + >>, + ct:log("DstContents=~p",[DstContents]), + file:write_file(DstFileName, DstContents). + + +%%-------------------------------------------------------------------- +%% +set_state(Ops, Config) when is_list(Ops) -> lists:foldl(fun set_state/2, Config, Ops); + +set_state(reset, Config) -> + rm('*', id2abs("",Config)), + PrivDir = ?config(priv_dir,Config), + file:set_cwd(PrivDir), + ftp:lcd(?config(ftp,Config),PrivDir), + set_state({mkdir,""},Config); +set_state({mkdir,Id}, Config) -> + Abs = id2abs(Id, Config), + mk_path(Abs), + file:make_dir(Abs), + Config; +set_state({mkfile,Id,Contents}, Config) -> + Abs = id2abs(Id, Config), + mk_path(Abs), + ok = file:write_file(Abs, Contents), + Config. + +mk_path(Abs) -> lists:foldl(fun mk_path/2, [], filename:split(filename:dirname(Abs))). + +mk_path(F, Pfx) -> +io:format('~p~n',[filename:join(Pfx,F)]), + case file:read_file_info(AbsName=filename:join(Pfx,F)) of + {ok,#file_info{type=directory}} -> + AbsName; + {error,eexist} -> + AbsName; + {error,enoent} -> + ok = file:make_dir(AbsName), + AbsName + end. + + +rm('*', Pfx) -> + {ok,Fs} = file:list_dir(Pfx), + ct:log("Deleting ~p with prefix=~p",[Fs,Pfx]), + lists:foreach(fun(F) -> rm(F, Pfx) end, Fs); +rm(F, Pfx) -> + ct:log("rm ~p in ~p",[F,Pfx]), + case file:read_file_info(AbsName=filename:join(Pfx,F)) of + {ok,#file_info{type=directory}} -> + {ok,Fs} = file:list_dir(AbsName), + lists:foreach(fun(F1) -> rm(F1,AbsName) end, Fs), + ok = file:del_dir(AbsName); + + {ok,#file_info{type=regular}} -> + ok = file:delete(AbsName); + + {error,enoent} -> + ok + end. + +%%-------------------------------------------------------------------- +%% + +id2abs(Id, Conf) -> filename:join(?config(priv_dir,Conf),ids(Id)). +id2ftp(Id, Conf) -> (?config(id2ftp,Conf))(ids(Id)). +id2ftp_result(Id, Conf) -> (?config(id2ftp_result,Conf))(ids(Id)). + +ids([[_|_]|_]=Ids) -> filename:join(Ids); +ids(Id) -> Id. + + +is_expected_absName(Id, File, Conf) -> File = (?config(id2abs,Conf))(Id). +is_expected_ftpInName(Id, File, Conf) -> File = (?config(id2ftp,Conf))(Id). +is_expected_ftpOutName(Id, File, Conf) -> File = (?config(id2ftp_result,Conf))(Id). diff --git a/lib/inets/test/ftp_SUITE_data/vsftpd.conf b/lib/inets/test/ftp_SUITE_data/vsftpd.conf index 4763e439a4..003d0115af 100644 --- a/lib/inets/test/ftp_SUITE_data/vsftpd.conf +++ b/lib/inets/test/ftp_SUITE_data/vsftpd.conf @@ -1,9 +1,17 @@ + listen=YES listen_port=9999 run_as_launching_user=YES #ssl_enable=YES #allow_anon_ssl=YES background=YES + +write_enable=YES anonymous_enable=YES +anon_upload_enable=YES +anon_mkdir_write_enable=YES +anon_other_write_enable=YES +anon_world_readable_only=NO + #rsa_cert_file=cert.pem -#rsa_key_file=key.pem \ No newline at end of file +#rsa_key_file=key.pem -- cgit v1.2.3 From ee15e17821721e727271bc6ea04034712e7ab697 Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Tue, 12 Nov 2013 15:35:37 +0100 Subject: ftp: towards a working ftps, chapter 1 --- lib/inets/src/ftp/ftp.erl | 63 ++++++++++++++++++++------------------ lib/inets/src/ftp/ftp_response.erl | 5 +-- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index b3cf1544f5..a45d99894a 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -54,7 +54,7 @@ -include("ftp_internal.hrl"). -%% Constante used in internal state definition +%% Constants used in internal state definition -define(CONNECTION_TIMEOUT, 60*1000). -define(DATA_ACCEPT_TIMEOUT, infinity). -define(DEFAULT_MODE, passive). @@ -92,7 +92,7 @@ ipfamily, % inet | inet6 | inet6fb4 progress = ignore, % ignore | pid() dtimeout = ?DATA_ACCEPT_TIMEOUT, % non_neg_integer() | infinity - tls_upgrading_data_connection + tls_upgrading_data_connection = {false,undefined} }). @@ -1104,9 +1104,10 @@ handle_call({_, {open, ip_comm, Host, Opts}}, From, State) -> handle_call({_, {open, tls_upgrade, TLSOptions}}, From, State) -> send_ctrl_message(State, mk_cmd("AUTH TLS", [])), + activate_ctrl_connection(State), {noreply, State#state{client = From, caller = open, tls_options = TLSOptions}}; -handle_call({_, {user, User, Password}}, From, +handle_call({_, {user, User, Password}}, From, #state{csock = CSock} = State) when (CSock =/= undefined) -> handle_user(User, Password, "", State#state{client = From}); @@ -1314,34 +1315,34 @@ handle_info(timeout, State) -> {noreply, State}; %%% Data socket messages %%% -handle_info({tcp, Socket, Data}, - #state{dsock = Socket, +handle_info({tcp, Socket, Data}, + #state{dsock = {tcp,Socket}, caller = {recv_file, Fd}} = State0) -> file_write(binary_to_list(Data), Fd), progress_report({binary, Data}, State0), State = activate_data_connection(State0), {noreply, State}; -handle_info({tcp, Socket, Data}, #state{dsock = Socket, client = From, - caller = recv_chunk} - = State) -> +handle_info({tcp, Socket, Data}, #state{dsock = {tcp,Socket}, client = From, + caller = recv_chunk} + = State) -> gen_server:reply(From, {ok, Data}), {noreply, State#state{client = undefined, data = <<>>}}; -handle_info({tcp, Socket, Data}, #state{dsock = Socket} = State0) -> +handle_info({tcp, Socket, Data}, #state{dsock = {tcp,Socket}} = State0) -> State = activate_data_connection(State0), {noreply, State#state{data = <<(State#state.data)/binary, Data/binary>>}}; -handle_info({tcp_closed, Socket}, #state{dsock = Socket, - caller = {recv_file, Fd}} +handle_info({tcp_closed, Socket}, #state{dsock = {tcp,Socket}, + caller = {recv_file, Fd}} = State) -> file_close(Fd), progress_report({transfer_size, 0}, State), activate_ctrl_connection(State), {noreply, State#state{dsock = undefined, data = <<>>}}; -handle_info({tcp_closed, Socket}, #state{dsock = Socket, client = From, +handle_info({tcp_closed, Socket}, #state{dsock = {tcp,Socket}, client = From, caller = recv_chunk} = State) -> gen_server:reply(From, ok), @@ -1349,13 +1350,13 @@ handle_info({tcp_closed, Socket}, #state{dsock = Socket, client = From, data = <<>>, caller = undefined, chunk = false}}; -handle_info({tcp_closed, Socket}, #state{dsock = Socket, caller = recv_bin, +handle_info({tcp_closed, Socket}, #state{dsock = {tcp,Socket}, caller = recv_bin, data = Data} = State) -> activate_ctrl_connection(State), {noreply, State#state{dsock = undefined, data = <<>>, caller = {recv_bin, Data}}}; -handle_info({tcp_closed, Socket}, #state{dsock = Socket, data = Data, +handle_info({tcp_closed, Socket}, #state{dsock = {tcp,Socket}, data = Data, caller = {handle_dir_result, Dir}} = State) -> activate_ctrl_connection(State), @@ -1444,8 +1445,8 @@ handle_info({'EXIT', Pid, Reason}, #state{progress = Pid} = State) -> %% so we do not want to crash, but we make a log entry as it is an %% unwanted behaviour.) handle_info(Info, State) -> - Report = io_lib:format("ftp : ~p : Unexpected message: ~p\n", - [self(), Info]), + Report = io_lib:format("ftp : ~p : Unexpected message: ~p~nState: ~p~n", + [self(), Info, State]), error_logger:info_report(Report), {noreply, State}. @@ -1590,7 +1591,7 @@ handle_user_account(Acc, State) -> handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, pbsz, _}} = State) -> send_ctrl_message(State, mk_cmd("PROT P", [])), activate_ctrl_connection(State), - {noreply, State#state{tls_upgrading_data_connection = {true, prot}}}; + {noreply, State#state{tls_upgrading_data_connection = {true, prot, undefined}}}; handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, prot, NextAction}, dsock = {tcp, Socket}, client = From, @@ -1681,7 +1682,7 @@ handle_ctrl_result({pos_compl, Lines}, Port = (P1 * 256) + P2, case connect(IP, Port, Timeout, State) of {ok, _, Socket} -> - handle_caller(State#state{caller = Caller, dsock = Socket}); + handle_caller(State#state{caller = Caller, dsock = {tcp,Socket}}); {error, _Reason} = Error -> gen_server:reply(From, Error), {noreply,State#state{client = undefined, caller = undefined}} @@ -2152,7 +2153,7 @@ send_data_message(#state{dsock = Socket}, Message) -> end. send_message({tcp, Socket}, Message) -> - gen_tcp:send(Socket, Message); + gen_tcp:send(Socket, Message); send_message({ssl, Socket}, Message) -> ssl:send(Socket, Message). @@ -2163,12 +2164,13 @@ activate_ctrl_connection(#state{csock = Socket}) -> %% that has been saved in ctrl_data, process this first. self() ! {tcp, Socket, <<>>}. -activate_data_connection(#state{tls_upgrading_data_connection = {false, _, _}, +activate_data_connection(#state{tls_upgrading_data_connection = {false, _}, dsock = Socket} = State) -> activate_connection(Socket), State; activate_data_connection(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State) -> - State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, activate_data_connection, undefined}}. + State#state{tls_upgrading_data_connection = {true, CTRL, {?MODULE,activate_data_connection,undefined}}}. + activate_connection({tcp, Socket}) -> inet:setopts(Socket, [{active, once}]); activate_connection({ssl, Socket}) -> @@ -2176,16 +2178,18 @@ activate_connection({ssl, Socket}) -> close_ctrl_connection(#state{csock = undefined}) -> ok; -close_ctrl_connection(#state{csock = {_, Socket}}) -> +close_ctrl_connection(#state{csock = Socket}) -> close_connection(Socket). close_data_connection(#state{dsock = undefined}) -> ok; -close_data_connection(#state{dsock = {_, Socket}}) -> +close_data_connection(#state{dsock = Socket}) -> close_connection(Socket). -close_connection(Socket) -> - gen_tcp:close(Socket). +close_connection({tcp, Socket}) -> + gen_tcp:close(Socket); +close_connection({ssl, Socket}) -> + ssl:close(Socket). %% ------------ FILE HANDELING ---------------------------------------- send_file(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State, Fd) -> @@ -2315,14 +2319,15 @@ sockname({ssl, Socket}) -> maybe_tls_upgrade(Pid, undefined) -> {ok, Pid}; maybe_tls_upgrade(Pid, TLSOptions) -> + catch ssl:start(), call(Pid, {open, tls_upgrade, TLSOptions}, plain). -maybe_requests_tls_upgrade(#state{tls_options = [_|_]} = State) -> +maybe_requests_tls_upgrade(#state{tls_options=undefined} = State) -> + {ok, State}; +maybe_requests_tls_upgrade(State) -> send_ctrl_message(State, mk_cmd("PBSZ 0", [])), activate_ctrl_connection(State), - {ok, State#state{tls_upgrading_data_connection = {true, pbsz, undefined}}}; -maybe_requests_tls_upgrade(#state{tls_options = undefined} = State) -> - {ok, State}. + {ok, State#state{tls_upgrading_data_connection = {true, pbsz, undefined}}}. next_action_after_tls_upgrade({M, F, undefined}, State) -> M:F(State); diff --git a/lib/inets/src/ftp/ftp_response.erl b/lib/inets/src/ftp/ftp_response.erl index d60e97d5ee..4131fb43c7 100644 --- a/lib/inets/src/ftp/ftp_response.erl +++ b/lib/inets/src/ftp/ftp_response.erl @@ -174,8 +174,9 @@ error_string(Reason) -> %%%======================================================================== %% Positive Preleminary Reply -interpret_status(?POS_PREL,_,_) -> pos_prel; -interpret_status(?POS_COMPL, ?AUTH_ACC, 3) -> tls_upgrade; +interpret_status(?POS_PREL,_,_) -> pos_prel; +%%FIXME ??? 3??? interpret_status(?POS_COMPL, ?AUTH_ACC, 3) -> tls_upgrade; +interpret_status(?POS_COMPL, ?AUTH_ACC, 4) -> tls_upgrade; %% Positive Completion Reply interpret_status(?POS_COMPL,_,_) -> pos_compl; %% Positive Intermediate Reply nedd account -- cgit v1.2.3 From ea05af7cdee9d9ab7260fcda20f118d4f250136e Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Tue, 12 Nov 2013 16:18:26 +0100 Subject: added some printouts --- lib/inets/src/ftp/ftp.erl | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index a45d99894a..b81ac16cd1 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -1318,6 +1318,7 @@ handle_info(timeout, State) -> handle_info({tcp, Socket, Data}, #state{dsock = {tcp,Socket}, caller = {recv_file, Fd}} = State0) -> + io:format('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State0]), file_write(binary_to_list(Data), Fd), progress_report({binary, Data}, State0), State = activate_data_connection(State0), @@ -1326,10 +1327,12 @@ handle_info({tcp, Socket, Data}, handle_info({tcp, Socket, Data}, #state{dsock = {tcp,Socket}, client = From, caller = recv_chunk} = State) -> + io:format('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State]), gen_server:reply(From, {ok, Data}), {noreply, State#state{client = undefined, data = <<>>}}; handle_info({tcp, Socket, Data}, #state{dsock = {tcp,Socket}} = State0) -> + io:format('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State0]), State = activate_data_connection(State0), {noreply, State#state{data = <<(State#state.data)/binary, Data/binary>>}}; @@ -1379,8 +1382,9 @@ handle_info({Transport, Socket, Data}, #state{csock = {Transport, Socket}, client = From, ctrl_data = {CtrlData, AccLines, LineStatus}} - = State) -> - case ftp_response:parse_lines(<>, + = State) -> + io:format('--ctrl ~p ----> ~s~p~n',[Socket,<>,State]), + case ftp_response:parse_lines(<>, AccLines, LineStatus) of {ok, Lines, NextMsgData} -> verbose(Lines, Verbose, 'receive'), @@ -1393,11 +1397,13 @@ handle_info({Transport, Socket, Data}, #state{csock = {Transport, Socket}, ctrl_data = {NextMsgData, [], start}}}; _ -> + io:format(' ...handle_ctrl_result(~p,...) ctrl_data=~p~n',[CtrlResult,{NextMsgData, [], start}]), handle_ctrl_result(CtrlResult, State#state{ctrl_data = {NextMsgData, [], start}}) end; {continue, NewCtrlData} -> + io:format(' ...Continue... ctrl_data=~p~n',[NewCtrlData]), activate_ctrl_connection(State), {noreply, State#state{ctrl_data = NewCtrlData}} end; @@ -1596,21 +1602,23 @@ handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, prot, NextAction}, dsock = {tcp, Socket}, client = From, tls_options = TLSOptions} = State0) -> - case ssl:connect(Socket, TLSOptions) of - {ok, TLSSocket} -> - State = State0#state{dsock = {ssl, TLSSocket}, - tls_upgrading_data_connection = {false, undefined}}, - next_action_after_tls_upgrade(NextAction, State); - {error, _} = Error -> - gen_server:reply(From, {Error, self()}), - {stop, normal, State0#state{client = undefined, + io:format('<--data ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State0]), + case ssl:connect(Socket, TLSOptions) of + {ok, TLSSocket} -> + State = State0#state{dsock = {ssl, TLSSocket}, + tls_upgrading_data_connection = {false, undefined}}, + next_action_after_tls_upgrade(NextAction, State); + {error, _} = Error -> + gen_server:reply(From, {Error, self()}), + {stop, normal, State0#state{client = undefined, caller = undefined}} - end; + end; handle_ctrl_result({tls_upgrade, _}, #state{csock = {tcp, Socket}, tls_options = TLSOptions, caller = open, client = From} = State) -> + io:format('<--ctrl ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State]), case ssl:connect(Socket, TLSOptions) of {ok, TLSSocket} -> gen_server:reply(From, {ok, self()}), @@ -2135,11 +2143,13 @@ accept_data_connection(#state{mode = active, accept_data_connection(#state{mode = passive} = State) -> maybe_requests_tls_upgrade(State). -send_ctrl_message(#state{csock = Socket, verbose = Verbose}, Message) -> +send_ctrl_message(S=#state{csock = Socket, verbose = Verbose}, Message) -> verbose(lists:flatten(Message),Verbose,send), + io:format('<--ctrl ~p ---- ~s~p~n',[Socket,Message,S]), send_message(Socket, Message). -send_data_message(#state{dsock = Socket}, Message) -> +send_data_message(S=#state{dsock = Socket}, Message) -> + io:format('<==data ~p ==== ~s~p~n',[Socket,Message,S]), case send_message(Socket, Message) of ok -> ok; -- cgit v1.2.3 From d211a8e51d5c578508a63fc8a5d0e8675f4f749c Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Tue, 12 Nov 2013 20:17:43 +0100 Subject: . --- lib/inets/src/ftp/ftp.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index b81ac16cd1..b6a38758b2 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -1688,6 +1688,7 @@ handle_ctrl_result({pos_compl, Lines}, string:tokens(NewPortAddr, [$,])), IP = {A1, A2, A3, A4}, Port = (P1 * 256) + P2, +io:format('tcp connect to ~p:~p, Caller=~p~n',[IP,Port,Caller]), case connect(IP, Port, Timeout, State) of {ok, _, Socket} -> handle_caller(State#state{caller = Caller, dsock = {tcp,Socket}}); -- cgit v1.2.3 From f73583ae73ec7b61bc1126f16390eb70d38257a6 Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Wed, 13 Nov 2013 10:39:39 +0100 Subject: ftps: working --- lib/inets/src/ftp/ftp.erl | 236 +++++++++++++++--------------- lib/inets/test/ftp_SUITE.erl | 49 +++++-- lib/inets/test/ftp_SUITE_data/vsftpd.conf | 11 +- 3 files changed, 156 insertions(+), 140 deletions(-) diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index b6a38758b2..15c0290ae1 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -18,7 +18,7 @@ %% %% %% Description: This module implements an ftp client, RFC 959. -%% It also supports ipv6 RFC 2428. +%% It also supports ipv6 RFC 2428 and starttls RFC 4217. -module(ftp). @@ -92,7 +92,7 @@ ipfamily, % inet | inet6 | inet6fb4 progress = ignore, % ignore | pid() dtimeout = ?DATA_ACCEPT_TIMEOUT, % non_neg_integer() | infinity - tls_upgrading_data_connection = {false,undefined} + tls_upgrading_data_connection = false }). @@ -101,6 +101,8 @@ -type common_reason() :: 'econn' | 'eclosed' | term(). -type file_write_error_reason() :: term(). % See file:write for more info +%%-define(DBG(F,A), 'n/a'). +-define(DBG(F,A), io:format(F,A)). %%%========================================================================= %%% API - CLIENT FUNCTIONS @@ -1315,61 +1317,63 @@ handle_info(timeout, State) -> {noreply, State}; %%% Data socket messages %%% -handle_info({tcp, Socket, Data}, - #state{dsock = {tcp,Socket}, - caller = {recv_file, Fd}} = State0) -> - io:format('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State0]), +handle_info({Trpt, Socket, Data}, + #state{dsock = {Trpt,Socket}, + caller = {recv_file, Fd}} = State0) when Trpt==tcp;Trpt==ssl -> + ?DBG('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State0]), file_write(binary_to_list(Data), Fd), progress_report({binary, Data}, State0), State = activate_data_connection(State0), {noreply, State}; -handle_info({tcp, Socket, Data}, #state{dsock = {tcp,Socket}, client = From, +handle_info({Trpt, Socket, Data}, #state{dsock = {Trpt,Socket}, client = From, caller = recv_chunk} - = State) -> - io:format('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State]), + = State) when Trpt==tcp;Trpt==ssl -> + ?DBG('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State]), gen_server:reply(From, {ok, Data}), {noreply, State#state{client = undefined, data = <<>>}}; -handle_info({tcp, Socket, Data}, #state{dsock = {tcp,Socket}} = State0) -> - io:format('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State0]), +handle_info({Trpt, Socket, Data}, #state{dsock = {Trpt,Socket}} = State0) when Trpt==tcp;Trpt==ssl -> + ?DBG('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State0]), State = activate_data_connection(State0), {noreply, State#state{data = <<(State#state.data)/binary, Data/binary>>}}; -handle_info({tcp_closed, Socket}, #state{dsock = {tcp,Socket}, +handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, caller = {recv_file, Fd}} - = State) -> + = State) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} -> file_close(Fd), progress_report({transfer_size, 0}, State), activate_ctrl_connection(State), {noreply, State#state{dsock = undefined, data = <<>>}}; -handle_info({tcp_closed, Socket}, #state{dsock = {tcp,Socket}, client = From, - caller = recv_chunk} - = State) -> +handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, client = From, + caller = recv_chunk} + = State) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} -> gen_server:reply(From, ok), {noreply, State#state{dsock = undefined, client = undefined, data = <<>>, caller = undefined, chunk = false}}; -handle_info({tcp_closed, Socket}, #state{dsock = {tcp,Socket}, caller = recv_bin, - data = Data} = State) -> +handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, caller = recv_bin, + data = Data} = State) + when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} -> activate_ctrl_connection(State), - {noreply, State#state{dsock = undefined, data = <<>>, + {noreply, State#state{dsock = undefined, data = <<>>, caller = {recv_bin, Data}}}; -handle_info({tcp_closed, Socket}, #state{dsock = {tcp,Socket}, data = Data, - caller = {handle_dir_result, Dir}} - = State) -> +handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, data = Data, + caller = {handle_dir_result, Dir}} + = State) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} -> activate_ctrl_connection(State), - {noreply, State#state{dsock = undefined, + {noreply, State#state{dsock = undefined, caller = {handle_dir_result, Dir, Data}, % data = <>}}; data = <<>>}}; - -handle_info({tcp_error, Socket, Reason}, #state{dsock = Socket, - client = From} = State) -> + +handle_info({Err, Socket, Reason}, #state{dsock = {Trpt,Socket}, + client = From} = State) + when {Err,Trpt}=={tcp_error,tcp} ; {Err,Trpt}=={ssl_error,ssl} -> gen_server:reply(From, {error, Reason}), close_data_connection(State), {noreply, State#state{dsock = undefined, client = undefined, @@ -1383,7 +1387,7 @@ handle_info({Transport, Socket, Data}, #state{csock = {Transport, Socket}, ctrl_data = {CtrlData, AccLines, LineStatus}} = State) -> - io:format('--ctrl ~p ----> ~s~p~n',[Socket,<>,State]), + ?DBG('--ctrl ~p ----> ~s~p~n',[Socket,<>,State]), case ftp_response:parse_lines(<>, AccLines, LineStatus) of {ok, Lines, NextMsgData} -> @@ -1394,37 +1398,34 @@ handle_info({Transport, Socket, Data}, #state{csock = {Transport, Socket}, gen_server:reply(From, string:tokens(Lines, [?CR, ?LF])), {noreply, State#state{client = undefined, caller = undefined, - ctrl_data = {NextMsgData, [], + ctrl_data = {NextMsgData, [], start}}}; _ -> - io:format(' ...handle_ctrl_result(~p,...) ctrl_data=~p~n',[CtrlResult,{NextMsgData, [], start}]), + ?DBG(' ...handle_ctrl_result(~p,...) ctrl_data=~p~n',[CtrlResult,{NextMsgData, [], start}]), handle_ctrl_result(CtrlResult, - State#state{ctrl_data = + State#state{ctrl_data = {NextMsgData, [], start}}) end; {continue, NewCtrlData} -> - io:format(' ...Continue... ctrl_data=~p~n',[NewCtrlData]), + ?DBG(' ...Continue... ctrl_data=~p~n',[NewCtrlData]), activate_ctrl_connection(State), {noreply, State#state{ctrl_data = NewCtrlData}} end; %% If the server closes the control channel it is %% the expected behavior that connection process terminates. -handle_info({tcp_closed, Socket}, #state{csock = {tcp, Socket}}) -> +handle_info({Cls, Socket}, #state{csock = {Trpt, Socket}}) + when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} -> exit(normal); %% User will get error message from terminate/2 -handle_info({ssl_closed, Socket}, #state{csock = {ssl, Socket}}) -> - exit(normal); -handle_info({tcp_error, Socket, Reason}, _) -> - Report = - io_lib:format("tcp_error on socket: ~p for reason: ~p~n", - [Socket, Reason]), +handle_info({Err, Socket, Reason}, _) when Err==tcp_error ; Err==ssl_error -> + Report = + io_lib:format("~p on socket: ~p for reason: ~p~n", + [Err, Socket, Reason]), error_logger:error_report(Report), %% If tcp does not work the only option is to terminate, %% this is the expected behavior under these circumstances. exit(normal); %% User will get error message from terminate/2 -handle_info({ssl_error, _Socket, _Reason}, _) -> - exit(normal); %% TODO?? %% Monitor messages - if the process owning the ftp connection goes %% down there is no point in continuing. @@ -1590,47 +1591,39 @@ handle_user_account(Acc, State) -> %%-------------------------------------------------------------------------- -%% handle_ctrl_result -%%-------------------------------------------------------------------------- +%% handle_ctrl_result %%-------------------------------------------------------------------------- -%% Handling of control connection setup -handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, pbsz, _}} = State) -> - send_ctrl_message(State, mk_cmd("PROT P", [])), - activate_ctrl_connection(State), - {noreply, State#state{tls_upgrading_data_connection = {true, prot, undefined}}}; - -handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, prot, NextAction}, - dsock = {tcp, Socket}, client = From, - tls_options = TLSOptions} = State0) -> - io:format('<--data ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State0]), - case ssl:connect(Socket, TLSOptions) of - {ok, TLSSocket} -> - State = State0#state{dsock = {ssl, TLSSocket}, - tls_upgrading_data_connection = {false, undefined}}, - next_action_after_tls_upgrade(NextAction, State); - {error, _} = Error -> - gen_server:reply(From, {Error, self()}), - {stop, normal, State0#state{client = undefined, - caller = undefined}} - end; - handle_ctrl_result({tls_upgrade, _}, #state{csock = {tcp, Socket}, tls_options = TLSOptions, caller = open, client = From} - = State) -> - io:format('<--ctrl ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State]), + = State0) -> + ?DBG('<--ctrl ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State0]), case ssl:connect(Socket, TLSOptions) of {ok, TLSSocket} -> - gen_server:reply(From, {ok, self()}), - {noreply, State#state{csock = {ssl, TLSSocket}, - client = undefined, - caller = undefined }}; + State = State0#state{csock = {ssl,TLSSocket}}, + send_ctrl_message(State, mk_cmd("PBSZ 0", [])), + activate_ctrl_connection(State), + {noreply, State#state{tls_upgrading_data_connection = {true, pbsz}} }; {error, _} = Error -> gen_server:reply(From, {Error, self()}), - {stop, normal, State#state{client = undefined, - caller = undefined}} + {stop, normal, State0#state{client = undefined, + caller = undefined, + tls_upgrading_data_connection = false}} end; -handle_ctrl_result({pos_compl, _}, #state{caller = open, client = From} + +handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, pbsz}} = State) -> + send_ctrl_message(State, mk_cmd("PROT P", [])), + activate_ctrl_connection(State), + {noreply, State#state{tls_upgrading_data_connection = {true, prot}}}; + +handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, prot}, + client = From} = State) -> + gen_server:reply(From, {ok, self()}), + {noreply, State#state{client = undefined, + caller = undefined, + tls_upgrading_data_connection = false}}; + +handle_ctrl_result({pos_compl, _}, #state{caller = open, client = From} = State) -> gen_server:reply(From, {ok, self()}), {noreply, State#state{client = undefined, @@ -1676,7 +1669,7 @@ handle_ctrl_result({pos_compl, Lines}, #state{mode = passive, ipfamily = inet, client = From, - caller = {setup_data_connection, Caller}, + caller = {setup_data_connection, Caller}, timeout = Timeout} = State) -> {_, [?LEFT_PAREN | Rest]} = @@ -1688,9 +1681,10 @@ handle_ctrl_result({pos_compl, Lines}, string:tokens(NewPortAddr, [$,])), IP = {A1, A2, A3, A4}, Port = (P1 * 256) + P2, -io:format('tcp connect to ~p:~p, Caller=~p~n',[IP,Port,Caller]), + + ?DBG('<--data tcp connect to ~p:~p, Caller=~p~n',[IP,Port,Caller]), case connect(IP, Port, Timeout, State) of - {ok, _, Socket} -> + {ok, _, Socket} -> handle_caller(State#state{caller = Caller, dsock = {tcp,Socket}}); {error, _Reason} = Error -> gen_server:reply(From, Error), @@ -2132,25 +2126,46 @@ connect2(Host, Port, IpFam, Timeout) -> accept_data_connection(#state{mode = active, dtimeout = DTimeout, + tls_options = TLSOptions, dsock = {lsock, LSock}} = State0) -> case gen_tcp:accept(LSock, DTimeout) of + {ok, Socket} when is_list(TLSOptions) -> + gen_tcp:close(LSock), + ?DBG('<--data ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State0]), + case ssl:connect(Socket, TLSOptions) of + {ok, TLSSocket} -> + {ok, State0#state{dsock={ssl,TLSSocket}}}; + {error, Reason} -> + {error, {ssl_connect_failed, Reason}} + end; {ok, Socket} -> gen_tcp:close(LSock), - maybe_requests_tls_upgrade(State0#state{dsock = {tcp, Socket}}); + {ok, State0#state{dsock={tcp,Socket}}}; {error, Reason} -> {error, {data_connect_failed, Reason}} end; +accept_data_connection(#state{mode = passive, + dsock = {tcp,Socket}, + tls_options = TLSOptions} = State) when is_list(TLSOptions) -> + ?DBG('<--data ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State]), + case ssl:connect(Socket, TLSOptions) of + {ok, TLSSocket} -> + {ok, State#state{dsock={ssl,TLSSocket}}}; + {error, Reason} -> + {error, {ssl_connect_failed, Reason}} + end; accept_data_connection(#state{mode = passive} = State) -> - maybe_requests_tls_upgrade(State). + {ok,State}. + send_ctrl_message(S=#state{csock = Socket, verbose = Verbose}, Message) -> verbose(lists:flatten(Message),Verbose,send), - io:format('<--ctrl ~p ---- ~s~p~n',[Socket,Message,S]), + ?DBG('<--ctrl ~p ---- ~s~p~n',[Socket,Message,S]), send_message(Socket, Message). send_data_message(S=#state{dsock = Socket}, Message) -> - io:format('<==data ~p ==== ~s~p~n',[Socket,Message,S]), + ?DBG('<==data ~p ==== ~s~p~n',[Socket,Message,S]), case send_message(Socket, Message) of ok -> ok; @@ -2173,34 +2188,28 @@ activate_ctrl_connection(#state{csock = Socket, ctrl_data = {<<>>, _, _}}) -> activate_ctrl_connection(#state{csock = Socket}) -> %% We have already received at least part of the next control message, %% that has been saved in ctrl_data, process this first. - self() ! {tcp, Socket, <<>>}. + self() ! {tcp, unwrap_socket(Socket), <<>>}. -activate_data_connection(#state{tls_upgrading_data_connection = {false, _}, - dsock = Socket} = State) -> +unwrap_socket({tcp,Socket}) -> Socket; +unwrap_socket({ssl,Socket}) -> Socket; +unwrap_socket(Socket) -> Socket. + + +activate_data_connection(#state{dsock = Socket} = State) -> activate_connection(Socket), - State; -activate_data_connection(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State) -> - State#state{tls_upgrading_data_connection = {true, CTRL, {?MODULE,activate_data_connection,undefined}}}. + State. -activate_connection({tcp, Socket}) -> - inet:setopts(Socket, [{active, once}]); -activate_connection({ssl, Socket}) -> - ssl:setopts(Socket, [{active, once}]). +activate_connection({tcp, Socket}) -> inet:setopts(Socket, [{active, once}]); +activate_connection({ssl, Socket}) -> ssl:setopts(Socket, [{active, once}]). -close_ctrl_connection(#state{csock = undefined}) -> - ok; -close_ctrl_connection(#state{csock = Socket}) -> - close_connection(Socket). +close_ctrl_connection(#state{csock = undefined}) -> ok; +close_ctrl_connection(#state{csock = Socket}) -> close_connection(Socket). -close_data_connection(#state{dsock = undefined}) -> - ok; -close_data_connection(#state{dsock = Socket}) -> - close_connection(Socket). +close_data_connection(#state{dsock = undefined}) -> ok; +close_data_connection(#state{dsock = Socket}) -> close_connection(Socket). -close_connection({tcp, Socket}) -> - gen_tcp:close(Socket); -close_connection({ssl, Socket}) -> - ssl:close(Socket). +close_connection({tcp, Socket}) -> gen_tcp:close(Socket); +close_connection({ssl, Socket}) -> ssl:close(Socket). %% ------------ FILE HANDELING ---------------------------------------- send_file(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State, Fd) -> @@ -2318,14 +2327,11 @@ millisec_time() -> {A,B,C} = erlang:now(), A*1000000000+B*1000+(C div 1000). -peername({tcp, Socket}) -> - inet:peername(Socket); -peername({ssl, Socket}) -> - ssl:peername(Socket). -sockname({tcp, Socket}) -> - inet:peername(Socket); -sockname({ssl, Socket}) -> - ssl:peername(Socket). +peername({tcp, Socket}) -> inet:peername(Socket); +peername({ssl, Socket}) -> ssl:peername(Socket). + +sockname({tcp, Socket}) -> inet:peername(Socket); +sockname({ssl, Socket}) -> ssl:peername(Socket). maybe_tls_upgrade(Pid, undefined) -> {ok, Pid}; @@ -2333,18 +2339,6 @@ maybe_tls_upgrade(Pid, TLSOptions) -> catch ssl:start(), call(Pid, {open, tls_upgrade, TLSOptions}, plain). -maybe_requests_tls_upgrade(#state{tls_options=undefined} = State) -> - {ok, State}; -maybe_requests_tls_upgrade(State) -> - send_ctrl_message(State, mk_cmd("PBSZ 0", [])), - activate_ctrl_connection(State), - {ok, State#state{tls_upgrading_data_connection = {true, pbsz, undefined}}}. - -next_action_after_tls_upgrade({M, F, undefined}, State) -> - M:F(State); -next_action_after_tls_upgrade({M, F, A}, State) -> - M:F(State, A). - start_chunk(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State) -> State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, start_chunk, undefined}}; start_chunk(#state{client = From} = State) -> diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl index 1b8f3af8ef..cf84f10a6f 100644 --- a/lib/inets/test/ftp_SUITE.erl +++ b/lib/inets/test/ftp_SUITE.erl @@ -32,15 +32,18 @@ -compile(export_all). -define(FTP_USER, "anonymous"). --define(FTP_PASS, (fun({ok,__H}) -> "ftp_SUITE@" ++ __H; - (_) -> "ftp_SUITE@localhost" - end)(inet:gethostname()) +-define(FTP_PASS(Cmnt), (fun({ok,__H}) -> "ftp_SUITE_"++Cmnt++"@" ++ __H; + (_) -> "ftp_SUITE_"++Cmnt++"@localhost" + end)(inet:gethostname()) ). -define(BAD_HOST, "badhostname"). -define(BAD_USER, "baduser"). -define(BAD_DIR, "baddirectory"). +go() -> ct:run_test([{suite,"ftp_SUITE"}, {logdir,"LOG"}]). +gos() -> ct:run_test([{suite,"ftp_SUITE"}, {group,ftps_passive}, {logdir,"LOG"}]). + %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- @@ -112,14 +115,24 @@ ftp_tests()-> [{"vsftpd", fun(__CONF__) -> %% make_config_file(vsftpd, Conf), - ConfFile = filename:join(?config(data_dir,__CONF__), "vsftpd.conf"), + DataDir = ?config(data_dir,__CONF__), + ConfFile = filename:join(DataDir, "vsftpd.conf"), AnonRoot = ?config(priv_dir,__CONF__), - Cmd = ["vsftpd \"",ConfFile,"\"", + Cmd = ["vsftpd "++filename:join(DataDir,"vsftpd.conf"), " -oftpd_banner=erlang_otp_testing", - " -oanon_root=\"",AnonRoot,"\"" + " -oanon_root=\"",AnonRoot,"\"", + " -orsa_cert_file=\"",filename:join(DataDir,"cert.pem"),"\"", + " -orsa_private_key_file=\"",filename:join(DataDir,"key.pem"),"\"" ], - ct:log("~s",[Cmd]), - case os:cmd(Cmd) of + Result = os:cmd(Cmd), + ct:log("Config file:~n~s~n~nServer start command:~n ~s~nResult:~n ~p", + [case file:read_file(ConfFile) of + {ok,X} -> X; + _ -> "" + end, + Cmd, Result + ]), + case Result of [] -> {ok,'dont care'}; [Msg] -> {error,Msg} end @@ -154,7 +167,7 @@ init_per_suite(Config) -> end_per_suite(Config) -> ps_ftpd(Config), -%% stop_ftpd(Config), + stop_ftpd(Config), ps_ftpd(Config), ok. @@ -171,19 +184,23 @@ init_per_testcase(Case, Config0) -> catch _:_-> ok end, + TLS = [{tls,[{reuse_sessions,true}]}], + ACTIVE = [{mode,active}], + PASSIVE = [{mode,passive}], + ExtraOpts = [verbose], Config = case Group of - ftp_active -> ftp__open(Config0, [{mode,active}]); - ftps_active -> ftp__open(Config0, [{mode,active}]); - ftp_passive -> ftp__open(Config0, [{mode,passive}]); - ftps_passive -> ftp__open(Config0, [{mode,passive}]) + ftp_active -> ftp__open(Config0, ACTIVE ++ExtraOpts); + ftps_active -> ftp__open(Config0, TLS++ ACTIVE ++ExtraOpts); + ftp_passive -> ftp__open(Config0, PASSIVE ++ExtraOpts); + ftps_passive -> ftp__open(Config0, TLS++PASSIVE ++ExtraOpts) end, case Case of user -> Config; bad_user -> Config; _ -> Pid = ?config(ftp,Config), - ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), + ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS(atom_to_list(Group)++"-"++atom_to_list(Case)) ), ok = ftp:cd(Pid, ?config(priv_dir,Config)), Config end. @@ -199,7 +216,7 @@ end_per_testcase(_Case, Config) -> ftp__close(Config). user(doc) -> ["Open an ftp connection to a host, and logon as anonymous ftp, then logoff"]; user(Config) -> Pid = ?config(ftp, Config), - ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), % logon + ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS("")),% logon ok = ftp:close(Pid), % logoff {error,eclosed} = ftp:pwd(Pid), % check logoff result ok. @@ -208,7 +225,7 @@ user(Config) -> bad_user(doc) -> ["Open an ftp connection to a host, and logon with bad user."]; bad_user(Config) -> Pid = ?config(ftp, Config), - {error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS), + {error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS("")), ok. %%------------------------------------------------------------------------- diff --git a/lib/inets/test/ftp_SUITE_data/vsftpd.conf b/lib/inets/test/ftp_SUITE_data/vsftpd.conf index 003d0115af..4133f8f3b1 100644 --- a/lib/inets/test/ftp_SUITE_data/vsftpd.conf +++ b/lib/inets/test/ftp_SUITE_data/vsftpd.conf @@ -2,8 +2,10 @@ listen=YES listen_port=9999 run_as_launching_user=YES -#ssl_enable=YES -#allow_anon_ssl=YES +ssl_enable=YES +allow_anon_ssl=YES + +#background=NO background=YES write_enable=YES @@ -14,4 +16,7 @@ anon_other_write_enable=YES anon_world_readable_only=NO #rsa_cert_file=cert.pem -#rsa_key_file=key.pem +#rsa_private_key_file exists, not rsa_key_file=key.pem + +### Shouldn't be necessary.... +require_ssl_reuse=NO -- cgit v1.2.3 From 1eddd2d967aa99c32ebd2d1b573113f5bc988bc1 Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Wed, 13 Nov 2013 14:24:00 +0100 Subject: ftp: Add timeouts to ssl:connect --- lib/inets/src/ftp/ftp.erl | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index 15c0290ae1..d53cca231f 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -101,8 +101,8 @@ -type common_reason() :: 'econn' | 'eclosed' | term(). -type file_write_error_reason() :: term(). % See file:write for more info -%%-define(DBG(F,A), 'n/a'). --define(DBG(F,A), io:format(F,A)). +-define(DBG(F,A), 'n/a'). +%%-define(DBG(F,A), io:format(F,A)). %%%========================================================================= %%% API - CLIENT FUNCTIONS @@ -1595,10 +1595,11 @@ handle_user_account(Acc, State) -> %%-------------------------------------------------------------------------- handle_ctrl_result({tls_upgrade, _}, #state{csock = {tcp, Socket}, tls_options = TLSOptions, + timeout = Timeout, caller = open, client = From} = State0) -> ?DBG('<--ctrl ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State0]), - case ssl:connect(Socket, TLSOptions) of + case ssl:connect(Socket, TLSOptions, Timeout) of {ok, TLSSocket} -> State = State0#state{csock = {ssl,TLSSocket}}, send_ctrl_message(State, mk_cmd("PBSZ 0", [])), @@ -2132,7 +2133,7 @@ accept_data_connection(#state{mode = active, {ok, Socket} when is_list(TLSOptions) -> gen_tcp:close(LSock), ?DBG('<--data ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State0]), - case ssl:connect(Socket, TLSOptions) of + case ssl:connect(Socket, TLSOptions, DTimeout) of {ok, TLSSocket} -> {ok, State0#state{dsock={ssl,TLSSocket}}}; {error, Reason} -> @@ -2146,10 +2147,11 @@ accept_data_connection(#state{mode = active, end; accept_data_connection(#state{mode = passive, + dtimeout = DTimeout, dsock = {tcp,Socket}, tls_options = TLSOptions} = State) when is_list(TLSOptions) -> ?DBG('<--data ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State]), - case ssl:connect(Socket, TLSOptions) of + case ssl:connect(Socket, TLSOptions, DTimeout) of {ok, TLSSocket} -> {ok, State#state{dsock={ssl,TLSSocket}}}; {error, Reason} -> @@ -2159,13 +2161,13 @@ accept_data_connection(#state{mode = passive} = State) -> {ok,State}. -send_ctrl_message(S=#state{csock = Socket, verbose = Verbose}, Message) -> +send_ctrl_message(_S=#state{csock = Socket, verbose = Verbose}, Message) -> verbose(lists:flatten(Message),Verbose,send), - ?DBG('<--ctrl ~p ---- ~s~p~n',[Socket,Message,S]), + ?DBG('<--ctrl ~p ---- ~s~p~n',[Socket,Message,_S]), send_message(Socket, Message). -send_data_message(S=#state{dsock = Socket}, Message) -> - ?DBG('<==data ~p ==== ~s~p~n',[Socket,Message,S]), +send_data_message(_S=#state{dsock = Socket}, Message) -> + ?DBG('<==data ~p ==== ~s~p~n',[Socket,Message,_S]), case send_message(Socket, Message) of ok -> ok; -- cgit v1.2.3 From 2fe876a18560bdbff1f578c3fe26522f21c2ec54 Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Wed, 13 Nov 2013 15:05:50 +0100 Subject: ftp: Add ftps documentation --- lib/inets/doc/src/ftp.xml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/inets/doc/src/ftp.xml b/lib/inets/doc/src/ftp.xml index f8f11ec705..ac14d3f274 100644 --- a/lib/inets/doc/src/ftp.xml +++ b/lib/inets/doc/src/ftp.xml @@ -547,15 +547,14 @@ Opts = options() options() = [option()] option() = start_option() | open_option() - start_option() = {verbose, verbose()} | {debug, debug()} verbose() = boolean() (defaults to false) debug() = disable | debug | trace (defaults to disable) - - open_option() = {ipfamily, ipfamily()} | {port, port()} | {mode, mode()} | {timeout, timeout()} | {dtimeout, dtimeout()} | {progress, progress()} + open_option() = {ipfamily, ipfamily()} | {port, port()} | {mode, mode()} | {tls, tls_options()} | {timeout, timeout()} | {dtimeout, dtimeout()} | {progress, progress()} ipfamily() = inet | inet6 | inet6fb4 (defaults to inet) port() = integer() > 0 (defaults to 21) mode() = active | passive (defaults to passive) + tls_options() = [ssl:ssloption()] timeout() = integer() > 0 (defaults to 60000 milliseconds) dtimeout() = integer() > 0 | infinity (defaults to infinity) pogress() = ignore | {module(), function(), initial_data()} (defaults to ignore) @@ -570,6 +569,9 @@ (without the inets service framework) and open a session with the FTP server at Host.

+

If the option {tls, tls_options()} is present, the ftp session will be transported over tls (ftps, see RFC 4217). The list tls_options() may be empty. The function ssl:connect/3 is used for establishing both the control connection and the data sessions. +

+

A session opened in this way, is closed using the close function.

-- cgit v1.2.3 From 8b236f3906d65d7958b1cba4f5e5624804209d9a Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Mon, 18 Nov 2013 16:37:54 +0100 Subject: ftp: small fixes --- lib/inets/src/ftp/ftp.erl | 29 +++++++++++++++++++---- lib/inets/test/ftp_SUITE.erl | 56 +++++++++++++------------------------------- 2 files changed, 40 insertions(+), 45 deletions(-) diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index d53cca231f..fe92001a6f 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -39,7 +39,8 @@ send_chunk_start/2, send_chunk/2, send_chunk_end/1, type/2, user/3, user/4, account/2, append/3, append/2, append_bin/3, - append_chunk/2, append_chunk_end/1, append_chunk_start/2, info/1]). + append_chunk/2, append_chunk_end/1, append_chunk_start/2, + info/1, latest_ctrl_response/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, @@ -84,6 +85,7 @@ %% and hence the end of the response is reached! ctrl_data = {<<>>, [], start}, % {binary(), [bytes()], LineStatus} %% pid() - Client pid (note not the same as "From") + latest_ctrl_response = "", owner = undefined, client = undefined, % "From" to be used in gen_server:reply/2 %% Function that activated a connection and maybe some @@ -753,6 +755,18 @@ info(Pid) -> call(Pid, info, list). +%%-------------------------------------------------------------------------- +%% latest_ctrl_response(Pid) -> string() +%% Pid = pid() +%% +%% Description: The latest received response from the server +%%-------------------------------------------------------------------------- + +-spec latest_ctrl_response(Pid :: pid()) -> string(). + +latest_ctrl_response(Pid) -> + call(Pid, latest_ctrl_response, string). + %%%======================================================================== %%% Behavior callbacks %%%======================================================================== @@ -1046,6 +1060,9 @@ handle_call({_, info}, _, #state{verbose = Verbose, {progress, Progress}], {reply, {ok, Options}, State}; +handle_call({_,latest_ctrl_response}, _, #state{latest_ctrl_response=Resp} = State) -> + {reply, {ok,Resp}, State}; + %% But everything else must come from the owner handle_call({Pid, _}, _, #state{owner = Owner} = State) when Owner =/= Pid -> {reply, {error, not_connection_owner}, State}; @@ -1396,15 +1413,17 @@ handle_info({Transport, Socket, Data}, #state{csock = {Transport, Socket}, case Caller of quote -> gen_server:reply(From, string:tokens(Lines, [?CR, ?LF])), - {noreply, State#state{client = undefined, + {noreply, State#state{client = undefined, caller = undefined, + latest_ctrl_response = Lines, ctrl_data = {NextMsgData, [], start}}}; _ -> ?DBG(' ...handle_ctrl_result(~p,...) ctrl_data=~p~n',[CtrlResult,{NextMsgData, [], start}]), handle_ctrl_result(CtrlResult, - State#state{ctrl_data = - {NextMsgData, [], start}}) + State#state{latest_ctrl_response = Lines, + ctrl_data = + {NextMsgData, [], start}}) end; {continue, NewCtrlData} -> ?DBG(' ...Continue... ctrl_data=~p~n',[NewCtrlData]), @@ -2167,7 +2186,7 @@ send_ctrl_message(_S=#state{csock = Socket, verbose = Verbose}, Message) -> send_message(Socket, Message). send_data_message(_S=#state{dsock = Socket}, Message) -> - ?DBG('<==data ~p ==== ~s~p~n',[Socket,Message,_S]), + ?DBG('<==data ~p ==== ~s~n~p~n',[Socket,Message,_S]), case send_message(Socket, Message) of ok -> ok; diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl index cf84f10a6f..a50d2bf05b 100644 --- a/lib/inets/test/ftp_SUITE.erl +++ b/lib/inets/test/ftp_SUITE.erl @@ -106,15 +106,16 @@ ftp_tests()-> %%% % if the server is running. [] if not running. %%% % The string in string() is suitable for logging. %%% StopCommand, % fun(start_result()) -> void(). The command to stop the daemon with. -%%% Host, % string(). Mostly "localhost" -%%% Port % pos_integer() +%%% AugmentFun, % fun(config()) -> config() Adds two funs for transforming names of files +%%% % and directories to the form they are returned from this server +%%% ServerHost, % string(). Mostly "localhost" +%%% ServerPort % pos_integer() %%% } %%% -define(default_ftp_servers, [{"vsftpd", fun(__CONF__) -> -%% make_config_file(vsftpd, Conf), DataDir = ?config(data_dir,__CONF__), ConfFile = filename:join(DataDir, "vsftpd.conf"), AnonRoot = ?config(priv_dir,__CONF__), @@ -208,7 +209,18 @@ init_per_testcase(Case, Config0) -> end_per_testcase(user, _Config) -> ok; end_per_testcase(bad_user, _Config) -> ok; -end_per_testcase(_Case, Config) -> ftp__close(Config). +end_per_testcase(_Case, Config) -> + case ?config(tc_status,Config) of + ok -> ok; + _ -> + try ftp:latest_ctrl_response(?config(ftp,Config)) + of + {ok,S} -> ct:log("***~n*** Latest ctrl channel response:~n*** ~p~n***",[S]) + catch + _:_ -> ok + end + end, + ftp__close(Config). %%-------------------------------------------------------------------- %% Test Cases -------------------------------------------------------- @@ -235,7 +247,6 @@ pwd(Config0) -> Pid = ?config(ftp, Config), {ok, PWD} = ftp:pwd(Pid), {ok, PathLpwd} = ftp:lpwd(Pid), - ct:log("PWD=~p~nPathLpwd=~p",[PWD,PathLpwd]), PWD = id2ftp_result("", Config), PathLpwd = id2ftp_result("", Config). @@ -249,7 +260,6 @@ cd(Config0) -> ok = ftp:cd(Pid, id2ftp(Dir,Config)), {ok, PWD} = ftp:pwd(Pid), ExpectedPWD = id2ftp_result(Dir, Config), - ct:log("PWD=~p~nExpectedPWD=~p",[PWD,ExpectedPWD]), PWD = ExpectedPWD, {error, epath} = ftp:cd(Pid, ?BAD_DIR). @@ -263,7 +273,6 @@ lcd(Config0) -> ok = ftp:lcd(Pid, id2ftp(Dir,Config)), {ok, PWD} = ftp:lpwd(Pid), ExpectedPWD = id2ftp_result(Dir, Config), - ct:log("PWD=~p~nExpectedPWD=~p",[PWD,ExpectedPWD]), PWD = ExpectedPWD, {error, epath} = ftp:lcd(Pid, ?BAD_DIR). @@ -497,7 +506,6 @@ recv_chunk(Config0) -> {{error, "ftp:recv_chunk_start/2 not called"},_} = recv_chunk(Pid, <<>>), ok = ftp:recv_chunk_start(Pid, id2ftp(File,Config)), {ok, ReceivedContents, Nchunks} = recv_chunk(Pid, <<>>), - ct:log("Received ~p chunks",[Nchunks]), find_diff(ReceivedContents, Contents). recv_chunk(Pid, Acc) -> recv_chunk(Pid, Acc, 0). @@ -582,7 +590,6 @@ chk_file(Path=[C|_], ExpectedContents, Config) when 0 chk_file(PathList, ExpectedContents, Config) -> Path = filename:join(PathList), AbsPath = id2abs(Path,Config), -ct:log("chk_file/3 dbg: PathList=~p, Path=~p~nAbsPath=~p",[PathList,Path,AbsPath]), case file:read_file(AbsPath) of {ok,ExpectedContents} -> true; @@ -675,7 +682,6 @@ start_ftpd(Config) -> {Name,StartCmd,ChkUp,_StopCommand,ConfigRewrite,Host,Port} = ?config(ftpd_data, Config), case StartCmd(Config) of {ok,StartResult} -> - ct:log( ChkUp(StartResult) ), [{ftpd_host,Host}, {ftpd_port,Port}, {ftpd_start_result,StartResult} | ConfigRewrite(Config)]; @@ -728,33 +734,6 @@ find_diff(Bin1, Bin2) -> find_diff(A, A, _) -> true; find_diff(<>, <>, Pos) -> find_diff(T1, T2, Pos+1); find_diff(RC, LC, Pos) -> {error, {diff, Pos, RC, LC}}. -%%-------------------------------------------------------------------- -%% -make_config_file(vsftpd, Config) -> - ct:log("Config=~p",[Config]), - DstFileName = filename:join(?config(data_dir,Config), "vsftpd.conf"), - ct:log("DstFileName=~p",[DstFileName]), - SrcFileName = DstFileName ++ ".src", - ct:log("SrcFileName=~p",[SrcFileName]), - Chroot = ?config(priv_dir,Config), - ct:log("Chroot=~p",[Chroot]), - {ok,SrcContents} = file:read_file(SrcFileName), - ct:log("SrcContents=~p",[SrcContents]), - SFN = list_to_binary(SrcFileName), - CHR = list_to_binary(Chroot), - DstContents = - <<"## Generated, do not edit!\n", - "## Source: ",SFN/binary,"\n", - "\n", - "chroot_local_user=YES\n", - "passwd_chroot_enable=",CHR/binary,"\n", - "\n", - SrcContents/binary - >>, - ct:log("DstContents=~p",[DstContents]), - file:write_file(DstFileName, DstContents). - - %%-------------------------------------------------------------------- %% set_state(Ops, Config) when is_list(Ops) -> lists:foldl(fun set_state/2, Config, Ops); @@ -779,7 +758,6 @@ set_state({mkfile,Id,Contents}, Config) -> mk_path(Abs) -> lists:foldl(fun mk_path/2, [], filename:split(filename:dirname(Abs))). mk_path(F, Pfx) -> -io:format('~p~n',[filename:join(Pfx,F)]), case file:read_file_info(AbsName=filename:join(Pfx,F)) of {ok,#file_info{type=directory}} -> AbsName; @@ -793,10 +771,8 @@ io:format('~p~n',[filename:join(Pfx,F)]), rm('*', Pfx) -> {ok,Fs} = file:list_dir(Pfx), - ct:log("Deleting ~p with prefix=~p",[Fs,Pfx]), lists:foreach(fun(F) -> rm(F, Pfx) end, Fs); rm(F, Pfx) -> - ct:log("rm ~p in ~p",[F,Pfx]), case file:read_file_info(AbsName=filename:join(Pfx,F)) of {ok,#file_info{type=directory}} -> {ok,Fs} = file:list_dir(AbsName), -- cgit v1.2.3 From 003a846f15f9861620b2c1b2bcd4055f5ec3064e Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Tue, 19 Nov 2013 09:56:40 +0100 Subject: ftp: delete old test files Conflicts: lib/inets/test/ftp_suite_lib.erl --- lib/inets/test/ftp_freebsd_x86_test.erl | 160 -- lib/inets/test/ftp_linux_ppc_test.erl | 158 -- lib/inets/test/ftp_linux_x86_test.erl | 160 -- lib/inets/test/ftp_macosx_ppc_test.erl | 159 -- lib/inets/test/ftp_macosx_x86_test.erl | 159 -- lib/inets/test/ftp_netbsd_x86_test.erl | 159 -- lib/inets/test/ftp_openbsd_x86_test.erl | 158 -- lib/inets/test/ftp_solaris10_sparc_test.erl | 161 -- lib/inets/test/ftp_solaris10_x86_test.erl | 162 -- lib/inets/test/ftp_solaris8_sparc_test.erl | 159 -- lib/inets/test/ftp_solaris9_sparc_test.erl | 158 -- lib/inets/test/ftp_suite_lib.erl | 1675 --------------------- lib/inets/test/ftp_ticket_test.erl | 61 - lib/inets/test/ftp_windows_2003_server_test.erl | 167 -- lib/inets/test/ftp_windows_xp_test.erl | 157 -- lib/inets/test/old_ftp_SUITE.erl | 127 -- lib/inets/test/old_ftp_SUITE_data/ftpd_hosts.skel | 18 - 17 files changed, 3958 deletions(-) delete mode 100644 lib/inets/test/ftp_freebsd_x86_test.erl delete mode 100644 lib/inets/test/ftp_linux_ppc_test.erl delete mode 100644 lib/inets/test/ftp_linux_x86_test.erl delete mode 100644 lib/inets/test/ftp_macosx_ppc_test.erl delete mode 100644 lib/inets/test/ftp_macosx_x86_test.erl delete mode 100644 lib/inets/test/ftp_netbsd_x86_test.erl delete mode 100644 lib/inets/test/ftp_openbsd_x86_test.erl delete mode 100644 lib/inets/test/ftp_solaris10_sparc_test.erl delete mode 100644 lib/inets/test/ftp_solaris10_x86_test.erl delete mode 100644 lib/inets/test/ftp_solaris8_sparc_test.erl delete mode 100644 lib/inets/test/ftp_solaris9_sparc_test.erl delete mode 100644 lib/inets/test/ftp_suite_lib.erl delete mode 100644 lib/inets/test/ftp_ticket_test.erl delete mode 100644 lib/inets/test/ftp_windows_2003_server_test.erl delete mode 100644 lib/inets/test/ftp_windows_xp_test.erl delete mode 100644 lib/inets/test/old_ftp_SUITE.erl delete mode 100644 lib/inets/test/old_ftp_SUITE_data/ftpd_hosts.skel diff --git a/lib/inets/test/ftp_freebsd_x86_test.erl b/lib/inets/test/ftp_freebsd_x86_test.erl deleted file mode 100644 index 1d66779882..0000000000 --- a/lib/inets/test/ftp_freebsd_x86_test.erl +++ /dev/null @@ -1,160 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2010. 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. -%% -%% %CopyrightEnd% -%% -%% - --module(ftp_freebsd_x86_test). - --compile(export_all). - --include_lib("common_test/include/ct.hrl"). - --define(LIB_MOD,ftp_suite_lib). --define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). --define(PLATFORM,"Freebsd x86 "). - -%% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initiation before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - {File, NewFile} = ?LIB_MOD:test_filenames(), - NewConfig = [{file, File}, {new_file, NewFile} | Config], - ?LIB_MOD:ftpd_init(freebsd_x86, NewConfig). - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(Config) -> - ?LIB_MOD:ftpd_fin(Config). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initiation before each test case -%%-------------------------------------------------------------------- -init_per_testcase(Case, Config) -> - ftp_suite_lib:init_per_testcase(Case, Config). -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(Case, Config) -> - ftp_suite_lib:end_per_testcase(Case, Config). - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -all() -> - [open, open_port, {group, passive}, {group, active}, - api_missuse, not_owner, {group, progress_report}]. - -groups() -> - [{passive, [], ftp_suite_lib:passive(suite)}, - {active, [], ftp_suite_lib:active(suite)}, - {progress_report, [], - ftp_suite_lib:progress_report(suite)}]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -%% Test cases starts here. -%%-------------------------------------------------------------------- - -open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). -open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). -not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). - -passive_user(X) -> ?LIB_MOD:passive_user(X). -passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). -passive_cd(X) -> ?LIB_MOD:passive_cd(X). -passive_lcd(X) -> ?LIB_MOD:passive_lcd(X). -passive_ls(X) -> ?LIB_MOD:passive_ls(X). -passive_nlist(X) -> ?LIB_MOD:passive_nlist(X). -passive_rename(X) -> ?LIB_MOD:passive_rename(X). -passive_delete(X) -> ?LIB_MOD:passive_delete(X). -passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X). -passive_send(X) -> ?LIB_MOD:passive_send(X). -passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X). -passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X). -passive_append(X) -> ?LIB_MOD:passive_append(X). -passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X). -passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X). -passive_recv(X) -> ?LIB_MOD:passive_recv(X). -passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X). -passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X). -passive_type(X) -> ?LIB_MOD:passive_type(X). -passive_quote(X) -> ?LIB_MOD:passive_quote(X). -passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X). -active_user(X) -> ?LIB_MOD:active_user(X). -active_pwd(X) -> ?LIB_MOD:active_pwd(X). -active_cd(X) -> ?LIB_MOD:active_cd(X). -active_lcd(X) -> ?LIB_MOD:active_lcd(X). -active_ls(X) -> ?LIB_MOD:active_ls(X). -active_nlist(X) -> ?LIB_MOD:active_nlist(X). -active_rename(X) -> ?LIB_MOD:active_rename(X). -active_delete(X) -> ?LIB_MOD:active_delete(X). -active_mkdir(X) -> ?LIB_MOD:active_mkdir(X). -active_send(X) -> ?LIB_MOD:active_send(X). -active_send_bin(X) -> ?LIB_MOD:active_send_bin(X). -active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X). -active_append(X) -> ?LIB_MOD:active_append(X). -active_append_bin(X) -> ?LIB_MOD:active_append_bin(X). -active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X). -active_recv(X) -> ?LIB_MOD:active_recv(X). -active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X). -active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X). -active_type(X) -> ?LIB_MOD:active_type(X). -active_quote(X) -> ?LIB_MOD:active_quote(X). -active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X). -progress_report_send(X) -> ?LIB_MOD:progress_report_send(X). -progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X). - -fin(Config) -> - ?LIB_MOD:ftpd_fin(Config). diff --git a/lib/inets/test/ftp_linux_ppc_test.erl b/lib/inets/test/ftp_linux_ppc_test.erl deleted file mode 100644 index bba97237f1..0000000000 --- a/lib/inets/test/ftp_linux_ppc_test.erl +++ /dev/null @@ -1,158 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2010. 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. -%% -%% %CopyrightEnd% -%% -%% - --module(ftp_linux_ppc_test). - -%% Note: This directive should only be used in test suites. --compile(export_all). - --include_lib("common_test/include/ct.hrl"). - --define(LIB_MOD,ftp_suite_lib). --define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). --define(PLATFORM,"Linux ppc "). - -%% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initiation before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - {File, NewFile} = ?LIB_MOD:test_filenames(), - NewConfig = [{file, File}, {new_file, NewFile} | Config], - ?LIB_MOD:ftpd_init(linux_ppc, NewConfig). - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(Config) -> - ?LIB_MOD:ftpd_fin(Config). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initiation before each test case -%%-------------------------------------------------------------------- -init_per_testcase(Case, Config) -> - ftp_suite_lib:init_per_testcase(Case, Config). -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(Case, Config) -> - ftp_suite_lib:end_per_testcase(Case, Config). - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -all() -> - [open, open_port, {group, passive}, {group, active}, - api_missuse, not_owner, {group, progress_report}]. - -groups() -> - [{passive, [], ftp_suite_lib:passive(suite)}, - {active, [], ftp_suite_lib:active(suite)}, - {progress_report, [], - ftp_suite_lib:progress_report(suite)}]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -%% Test cases starts here. -%%-------------------------------------------------------------------- - -open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). -open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). -not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). - -passive_user(X) -> ?LIB_MOD:passive_user(X). -passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). -passive_cd(X) -> ?LIB_MOD:passive_cd(X). -passive_lcd(X) -> ?LIB_MOD:passive_lcd(X). -passive_ls(X) -> ?LIB_MOD:passive_ls(X). -passive_nlist(X) -> ?LIB_MOD:passive_nlist(X). -passive_rename(X) -> ?LIB_MOD:passive_rename(X). -passive_delete(X) -> ?LIB_MOD:passive_delete(X). -passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X). -passive_send(X) -> ?LIB_MOD:passive_send(X). -passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X). -passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X). -passive_append(X) -> ?LIB_MOD:passive_append(X). -passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X). -passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X). -passive_recv(X) -> ?LIB_MOD:passive_recv(X). -passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X). -passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X). -passive_type(X) -> ?LIB_MOD:passive_type(X). -passive_quote(X) -> ?LIB_MOD:passive_quote(X). -passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X). -active_user(X) -> ?LIB_MOD:active_user(X). -active_pwd(X) -> ?LIB_MOD:active_pwd(X). -active_cd(X) -> ?LIB_MOD:active_cd(X). -active_lcd(X) -> ?LIB_MOD:active_lcd(X). -active_ls(X) -> ?LIB_MOD:active_ls(X). -active_nlist(X) -> ?LIB_MOD:active_nlist(X). -active_rename(X) -> ?LIB_MOD:active_rename(X). -active_delete(X) -> ?LIB_MOD:active_delete(X). -active_mkdir(X) -> ?LIB_MOD:active_mkdir(X). -active_send(X) -> ?LIB_MOD:active_send(X). -active_send_bin(X) -> ?LIB_MOD:active_send_bin(X). -active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X). -active_append(X) -> ?LIB_MOD:active_append(X). -active_append_bin(X) -> ?LIB_MOD:active_append_bin(X). -active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X). -active_recv(X) -> ?LIB_MOD:active_recv(X). -active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X). -active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X). -active_type(X) -> ?LIB_MOD:active_type(X). -active_quote(X) -> ?LIB_MOD:active_quote(X). -active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X). -progress_report_send(X) -> ?LIB_MOD:progress_report_send(X). -progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X). diff --git a/lib/inets/test/ftp_linux_x86_test.erl b/lib/inets/test/ftp_linux_x86_test.erl deleted file mode 100644 index bbefd8231e..0000000000 --- a/lib/inets/test/ftp_linux_x86_test.erl +++ /dev/null @@ -1,160 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2010. 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. -%% -%% %CopyrightEnd% -%% -%% - --module(ftp_linux_x86_test). - --compile(export_all). - --include_lib("common_test/include/ct.hrl"). - --define(LIB_MOD,ftp_suite_lib). --define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). --define(PLATFORM,"Linux x86 "). - -%% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initiation before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - {File, NewFile} = ?LIB_MOD:test_filenames(), - NewConfig = [{file, File}, {new_file, NewFile} | Config], - ?LIB_MOD:ftpd_init(linux_x86, NewConfig). - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(Config) -> - ?LIB_MOD:ftpd_fin(Config). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initiation before each test case -%%-------------------------------------------------------------------- -init_per_testcase(Case, Config) -> - ftp_suite_lib:init_per_testcase(Case, Config). -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(Case, Config) -> - ftp_suite_lib:end_per_testcase(Case, Config). - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -all() -> - [open, open_port, {group, passive}, {group, active}, - api_missuse, not_owner, {group, progress_report}]. - -groups() -> - [{passive, [], ftp_suite_lib:passive(suite)}, - {active, [], ftp_suite_lib:active(suite)}, - {progress_report, [], - ftp_suite_lib:progress_report(suite)}]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -%% Test cases starts here. -%%-------------------------------------------------------------------- - -open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). -open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). -not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). - -passive_user(X) -> ?LIB_MOD:passive_user(X). -passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). -passive_cd(X) -> ?LIB_MOD:passive_cd(X). -passive_lcd(X) -> ?LIB_MOD:passive_lcd(X). -passive_ls(X) -> ?LIB_MOD:passive_ls(X). -passive_nlist(X) -> ?LIB_MOD:passive_nlist(X). -passive_rename(X) -> ?LIB_MOD:passive_rename(X). -passive_delete(X) -> ?LIB_MOD:passive_delete(X). -passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X). -passive_send(X) -> ?LIB_MOD:passive_send(X). -passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X). -passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X). -passive_append(X) -> ?LIB_MOD:passive_append(X). -passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X). -passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X). -passive_recv(X) -> ?LIB_MOD:passive_recv(X). -passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X). -passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X). -passive_type(X) -> ?LIB_MOD:passive_type(X). -passive_quote(X) -> ?LIB_MOD:passive_quote(X). -passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X). -active_user(X) -> ?LIB_MOD:active_user(X). -active_pwd(X) -> ?LIB_MOD:active_pwd(X). -active_cd(X) -> ?LIB_MOD:active_cd(X). -active_lcd(X) -> ?LIB_MOD:active_lcd(X). -active_ls(X) -> ?LIB_MOD:active_ls(X). -active_nlist(X) -> ?LIB_MOD:active_nlist(X). -active_rename(X) -> ?LIB_MOD:active_rename(X). -active_delete(X) -> ?LIB_MOD:active_delete(X). -active_mkdir(X) -> ?LIB_MOD:active_mkdir(X). -active_send(X) -> ?LIB_MOD:active_send(X). -active_send_bin(X) -> ?LIB_MOD:active_send_bin(X). -active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X). -active_append(X) -> ?LIB_MOD:active_append(X). -active_append_bin(X) -> ?LIB_MOD:active_append_bin(X). -active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X). -active_recv(X) -> ?LIB_MOD:active_recv(X). -active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X). -active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X). -active_type(X) -> ?LIB_MOD:active_type(X). -active_quote(X) -> ?LIB_MOD:active_quote(X). -active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X). -progress_report_send(X) -> ?LIB_MOD:progress_report_send(X). -progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X). - -fin(Config) -> - ?LIB_MOD:ftpd_fin(Config). diff --git a/lib/inets/test/ftp_macosx_ppc_test.erl b/lib/inets/test/ftp_macosx_ppc_test.erl deleted file mode 100644 index c9f33b8beb..0000000000 --- a/lib/inets/test/ftp_macosx_ppc_test.erl +++ /dev/null @@ -1,159 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2010. 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. -%% -%% %CopyrightEnd% -%% -%% - --module(ftp_macosx_ppc_test). - --compile(export_all). - --include_lib("common_test/include/ct.hrl"). - --define(LIB_MOD,ftp_suite_lib). --define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). --define(PLATFORM,"Macosx ppc "). - - -%% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initiation before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - {File, NewFile} = ?LIB_MOD:test_filenames(), - NewConfig = [{file, File}, {new_file, NewFile} | Config], - ?LIB_MOD:ftpd_init(macosx_ppc, NewConfig). - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(Config) -> - ?LIB_MOD:ftpd_fin(Config). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initiation before each test case -%%-------------------------------------------------------------------- -init_per_testcase(Case, Config) -> - ftp_suite_lib:init_per_testcase(Case, Config). -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(Case, Config) -> - ftp_suite_lib:end_per_testcase(Case, Config). - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -all() -> -[open, open_port, {group, passive}, {group, active}, - api_missuse, not_owner, {group, progress_report}]. - -groups() -> - [{passive, [], ftp_suite_lib:passive(suite)}, - {active, [], ftp_suite_lib:active(suite)}, - {progress_report, [], - ftp_suite_lib:progress_report(suite)}]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - - -open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). -open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). -not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). - -passive_user(X) -> ?LIB_MOD:passive_user(X). -passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). -passive_cd(X) -> ?LIB_MOD:passive_cd(X). -passive_lcd(X) -> ?LIB_MOD:passive_lcd(X). -passive_ls(X) -> ?LIB_MOD:passive_ls(X). -passive_nlist(X) -> ?LIB_MOD:passive_nlist(X). -passive_rename(X) -> ?LIB_MOD:passive_rename(X). -passive_delete(X) -> ?LIB_MOD:passive_delete(X). -passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X). -passive_send(X) -> ?LIB_MOD:passive_send(X). -passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X). -passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X). -passive_append(X) -> ?LIB_MOD:passive_append(X). -passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X). -passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X). -passive_recv(X) -> ?LIB_MOD:passive_recv(X). -passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X). -passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X). -passive_type(X) -> ?LIB_MOD:passive_type(X). -passive_quote(X) -> ?LIB_MOD:passive_quote(X). -passive_ip_v6_disabled(_X) -> {skipped,"unknown error"}.%?LIB_MOD:passive_ip_v6_disabled(X). -active_user(X) -> ?LIB_MOD:active_user(X). -active_pwd(X) -> ?LIB_MOD:active_pwd(X). -active_cd(X) -> ?LIB_MOD:active_cd(X). -active_lcd(X) -> ?LIB_MOD:active_lcd(X). -active_ls(X) -> ?LIB_MOD:active_ls(X). -active_nlist(X) -> ?LIB_MOD:active_nlist(X). -active_rename(X) -> ?LIB_MOD:active_rename(X). -active_delete(X) -> ?LIB_MOD:active_delete(X). -active_mkdir(X) -> ?LIB_MOD:active_mkdir(X). -active_send(X) -> ?LIB_MOD:active_send(X). -active_send_bin(X) -> ?LIB_MOD:active_send_bin(X). -active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X). -active_append(X) -> ?LIB_MOD:active_append(X). -active_append_bin(X) -> ?LIB_MOD:active_append_bin(X). -active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X). -active_recv(X) -> ?LIB_MOD:active_recv(X). -active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X). -active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X). -active_type(X) -> ?LIB_MOD:active_type(X). -active_quote(X) -> ?LIB_MOD:active_quote(X). -active_ip_v6_disabled(_X) -> {skipped,"unknown error"}.%%?LIB_MOD:active_ip_v6_disabled(X). -progress_report_send(X) -> ?LIB_MOD:progress_report_send(X). -progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X). - -fin(Config) -> - ?LIB_MOD:ftpd_fin(Config). diff --git a/lib/inets/test/ftp_macosx_x86_test.erl b/lib/inets/test/ftp_macosx_x86_test.erl deleted file mode 100644 index 17b7160b95..0000000000 --- a/lib/inets/test/ftp_macosx_x86_test.erl +++ /dev/null @@ -1,159 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2010. 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. -%% -%% %CopyrightEnd% -%% -%% - --module(ftp_macosx_x86_test). - --compile(export_all). - --include_lib("common_test/include/ct.hrl"). - --define(LIB_MOD,ftp_suite_lib). --define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). --define(PLATFORM,"Macosx x86 "). - -%% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initiation before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - {File, NewFile} = ?LIB_MOD:test_filenames(), - NewConfig = [{file, File}, {new_file, NewFile} | Config], - ?LIB_MOD:ftpd_init(macosx_x86, NewConfig). - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(Config) -> - ?LIB_MOD:ftpd_fin(Config). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initiation before each test case -%%-------------------------------------------------------------------- -init_per_testcase(Case, Config) -> - ftp_suite_lib:init_per_testcase(Case, Config). -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(Case, Config) -> - ftp_suite_lib:end_per_testcase(Case, Config). - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -all() -> -[open, open_port, {group, passive}, {group, active}, - api_missuse, not_owner, {group, progress_report}]. - -groups() -> - [{passive, [], ftp_suite_lib:passive(suite)}, - {active, [], ftp_suite_lib:active(suite)}, - {progress_report, [], - ftp_suite_lib:progress_report(suite)}]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -%% Test cases starts here. -%%-------------------------------------------------------------------- -open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). -open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). -not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). - -passive_user(X) -> ?LIB_MOD:passive_user(X). -passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). -passive_cd(X) -> ?LIB_MOD:passive_cd(X). -passive_lcd(X) -> ?LIB_MOD:passive_lcd(X). -passive_ls(X) -> ?LIB_MOD:passive_ls(X). -passive_nlist(X) -> ?LIB_MOD:passive_nlist([{wildcard_support, false} | X]). -passive_rename(X) -> ?LIB_MOD:passive_rename(X). -passive_delete(X) -> ?LIB_MOD:passive_delete(X). -passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X). -passive_send(X) -> ?LIB_MOD:passive_send(X). -passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X). -passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X). -passive_append(X) -> ?LIB_MOD:passive_append(X). -passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X). -passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X). -passive_recv(X) -> ?LIB_MOD:passive_recv(X). -passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X). -passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X). -passive_type(X) -> ?LIB_MOD:passive_type(X). -passive_quote(X) -> ?LIB_MOD:passive_quote(X). -passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X). -active_user(X) -> ?LIB_MOD:active_user(X). -active_pwd(X) -> ?LIB_MOD:active_pwd(X). -active_cd(X) -> ?LIB_MOD:active_cd(X). -active_lcd(X) -> ?LIB_MOD:active_lcd(X). -active_ls(X) -> ?LIB_MOD:active_ls(X). -active_nlist(X) -> ?LIB_MOD:active_nlist([{wildcard_support, false} | X]). -active_rename(X) -> ?LIB_MOD:active_rename(X). -active_delete(X) -> ?LIB_MOD:active_delete(X). -active_mkdir(X) -> ?LIB_MOD:active_mkdir(X). -active_send(X) -> ?LIB_MOD:active_send(X). -active_send_bin(X) -> ?LIB_MOD:active_send_bin(X). -active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X). -active_append(X) -> ?LIB_MOD:active_append(X). -active_append_bin(X) -> ?LIB_MOD:active_append_bin(X). -active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X). -active_recv(X) -> ?LIB_MOD:active_recv(X). -active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X). -active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X). -active_type(X) -> ?LIB_MOD:active_type(X). -active_quote(X) -> ?LIB_MOD:active_quote(X). -active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X). -progress_report_send(X) -> ?LIB_MOD:progress_report_send(X). -progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X). - -fin(Config) -> - ?LIB_MOD:ftpd_fin(Config). diff --git a/lib/inets/test/ftp_netbsd_x86_test.erl b/lib/inets/test/ftp_netbsd_x86_test.erl deleted file mode 100644 index bb474852c5..0000000000 --- a/lib/inets/test/ftp_netbsd_x86_test.erl +++ /dev/null @@ -1,159 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2010. 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. -%% -%% %CopyrightEnd% -%% -%% - --module(ftp_netbsd_x86_test). - --compile(export_all). - --include_lib("common_test/include/ct.hrl"). - --define(LIB_MOD,ftp_suite_lib). --define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). --define(PLATFORM,"Netbsd x86 "). - -%% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initiation before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - {File, NewFile} = ?LIB_MOD:test_filenames(), - NewConfig = [{file, File}, {new_file, NewFile} | Config], - ?LIB_MOD:ftpd_init(netbsd_x86, NewConfig). - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(Config) -> - ?LIB_MOD:ftpd_fin(Config). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initiation before each test case -%%-------------------------------------------------------------------- -init_per_testcase(Case, Config) -> - ftp_suite_lib:init_per_testcase(Case, Config). -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(Case, Config) -> - ftp_suite_lib:end_per_testcase(Case, Config). - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -all() -> - [open, open_port, {group, passive}, {group, active}, - api_missuse, not_owner, {group, progress_report}]. - -groups() -> - [{passive, [], ftp_suite_lib:passive(suite)}, - {active, [], ftp_suite_lib:active(suite)}, - {progress_report, [], - ftp_suite_lib:progress_report(suite)}]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -%% Test cases starts here. -%%-------------------------------------------------------------------- -open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). -open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). -not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). - -passive_user(X) -> ?LIB_MOD:passive_user(X). -passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). -passive_cd(X) -> ?LIB_MOD:passive_cd(X). -passive_lcd(X) -> ?LIB_MOD:passive_lcd(X). -passive_ls(X) -> ?LIB_MOD:passive_ls(X). -passive_nlist(X) -> ?LIB_MOD:passive_nlist(X). -passive_rename(X) -> ?LIB_MOD:passive_rename(X). -passive_delete(X) -> ?LIB_MOD:passive_delete(X). -passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X). -passive_send(X) -> ?LIB_MOD:passive_send(X). -passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X). -passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X). -passive_append(X) -> ?LIB_MOD:passive_append(X). -passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X). -passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X). -passive_recv(X) -> ?LIB_MOD:passive_recv(X). -passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X). -passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X). -passive_type(X) -> ?LIB_MOD:passive_type(X). -passive_quote(X) -> ?LIB_MOD:passive_quote(X). -passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X). -active_user(X) -> ?LIB_MOD:active_user(X). -active_pwd(X) -> ?LIB_MOD:active_pwd(X). -active_cd(X) -> ?LIB_MOD:active_cd(X). -active_lcd(X) -> ?LIB_MOD:active_lcd(X). -active_ls(X) -> ?LIB_MOD:active_ls(X). -active_nlist(X) -> ?LIB_MOD:active_nlist(X). -active_rename(X) -> ?LIB_MOD:active_rename(X). -active_delete(X) -> ?LIB_MOD:active_delete(X). -active_mkdir(X) -> ?LIB_MOD:active_mkdir(X). -active_send(X) -> ?LIB_MOD:active_send(X). -active_send_bin(X) -> ?LIB_MOD:active_send_bin(X). -active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X). -active_append(X) -> ?LIB_MOD:active_append(X). -active_append_bin(X) -> ?LIB_MOD:active_append_bin(X). -active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X). -active_recv(X) -> ?LIB_MOD:active_recv(X). -active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X). -active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X). -active_type(X) -> ?LIB_MOD:active_type(X). -active_quote(X) -> ?LIB_MOD:active_quote(X). -active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X). -progress_report_send(X) -> ?LIB_MOD:progress_report_send(X). -progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X). - -fin(Config) -> - ?LIB_MOD:ftpd_fin(Config). diff --git a/lib/inets/test/ftp_openbsd_x86_test.erl b/lib/inets/test/ftp_openbsd_x86_test.erl deleted file mode 100644 index 54fce702a0..0000000000 --- a/lib/inets/test/ftp_openbsd_x86_test.erl +++ /dev/null @@ -1,158 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2010. 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. -%% -%% %CopyrightEnd% -%% -%% - --module(ftp_openbsd_x86_test). - -%% Note: This directive should only be used in test suites. --compile(export_all). - --include_lib("common_test/include/ct.hrl"). - --define(LIB_MOD,ftp_suite_lib). --define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). --define(PLATFORM,"Openbsd x86 "). - -%% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initiation before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - {File, NewFile} = ?LIB_MOD:test_filenames(), - NewConfig = [{file, File}, {new_file, NewFile} | Config], - ?LIB_MOD:ftpd_init(openbsd_x86, NewConfig). - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(Config) -> - ?LIB_MOD:ftpd_fin(Config). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initiation before each test case -%%-------------------------------------------------------------------- -init_per_testcase(Case, Config) -> - ftp_suite_lib:init_per_testcase(Case, Config). -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(Case, Config) -> - ftp_suite_lib:end_per_testcase(Case, Config). - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -all() -> - [open, open_port, {group, passive}, {group, active}, - api_missuse, not_owner, {group, progress_report}]. - -groups() -> - [{passive, [], ftp_suite_lib:passive(suite)}, - {active, [], ftp_suite_lib:active(suite)}, - {progress_report, [], - ftp_suite_lib:progress_report(suite)}]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -%% Test cases starts here. -%%-------------------------------------------------------------------- - -open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). -open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). -not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). - -passive_user(X) -> ?LIB_MOD:passive_user(X). -passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). -passive_cd(X) -> ?LIB_MOD:passive_cd(X). -passive_lcd(X) -> ?LIB_MOD:passive_lcd(X). -passive_ls(X) -> ?LIB_MOD:passive_ls(X). -passive_nlist(X) -> ?LIB_MOD:passive_nlist(X). -passive_rename(X) -> ?LIB_MOD:passive_rename(X). -passive_delete(X) -> ?LIB_MOD:passive_delete(X). -passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X). -passive_send(X) -> ?LIB_MOD:passive_send(X). -passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X). -passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X). -passive_append(X) -> ?LIB_MOD:passive_append(X). -passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X). -passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X). -passive_recv(X) -> ?LIB_MOD:passive_recv(X). -passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X). -passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X). -passive_type(X) -> ?LIB_MOD:passive_type(X). -passive_quote(X) -> ?LIB_MOD:passive_quote(X). -passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X). -active_user(X) -> ?LIB_MOD:active_user(X). -active_pwd(X) -> ?LIB_MOD:active_pwd(X). -active_cd(X) -> ?LIB_MOD:active_cd(X). -active_lcd(X) -> ?LIB_MOD:active_lcd(X). -active_ls(X) -> ?LIB_MOD:active_ls(X). -active_nlist(X) -> ?LIB_MOD:active_nlist(X). -active_rename(X) -> ?LIB_MOD:active_rename(X). -active_delete(X) -> ?LIB_MOD:active_delete(X). -active_mkdir(X) -> ?LIB_MOD:active_mkdir(X). -active_send(X) -> ?LIB_MOD:active_send(X). -active_send_bin(X) -> ?LIB_MOD:active_send_bin(X). -active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X). -active_append(X) -> ?LIB_MOD:active_append(X). -active_append_bin(X) -> ?LIB_MOD:active_append_bin(X). -active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X). -active_recv(X) -> ?LIB_MOD:active_recv(X). -active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X). -active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X). -active_type(X) -> ?LIB_MOD:active_type(X). -active_quote(X) -> ?LIB_MOD:active_quote(X). -active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X). -progress_report_send(X) -> ?LIB_MOD:progress_report_send(X). -progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X). diff --git a/lib/inets/test/ftp_solaris10_sparc_test.erl b/lib/inets/test/ftp_solaris10_sparc_test.erl deleted file mode 100644 index 0da50dc91b..0000000000 --- a/lib/inets/test/ftp_solaris10_sparc_test.erl +++ /dev/null @@ -1,161 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2010. 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. -%% -%% %CopyrightEnd% -%% -%% - --module(ftp_solaris10_sparc_test). - --compile(export_all). - --include_lib("common_test/include/ct.hrl"). - --define(LIB_MOD,ftp_suite_lib). --define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). --define(PLATFORM,"Solaris 10 sparc "). - - -%% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initiation before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - {File, NewFile} = ?LIB_MOD:test_filenames(), - NewConfig = [{file, File}, {new_file, NewFile} | Config], - ?LIB_MOD:ftpd_init(solaris10_sparc, NewConfig). - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(Config) -> - ?LIB_MOD:ftpd_fin(Config). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initiation before each test case -%%-------------------------------------------------------------------- -init_per_testcase(Case, Config) -> - ftp_suite_lib:init_per_testcase(Case, Config). -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(Case, Config) -> - ftp_suite_lib:end_per_testcase(Case, Config). - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -all() -> - [open, open_port, {group, passive}, {group, active}, - api_missuse, not_owner, {group, progress_report}]. - -groups() -> - [{passive, [], ftp_suite_lib:passive(suite)}, - {active, [], ftp_suite_lib:active(suite)}, - {progress_report, [], - ftp_suite_lib:progress_report(suite)}]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -%% Test cases starts here. -%%-------------------------------------------------------------------- - -open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). -open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). -not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). - -passive_user(X) -> ?LIB_MOD:passive_user(X). -passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). -passive_cd(X) -> ?LIB_MOD:passive_cd(X). -passive_lcd(X) -> ?LIB_MOD:passive_lcd(X). -passive_ls(X) -> ?LIB_MOD:passive_ls(X). -passive_nlist(X) -> ?LIB_MOD:passive_nlist(X). -passive_rename(X) -> ?LIB_MOD:passive_rename(X). -passive_delete(X) -> ?LIB_MOD:passive_delete(X). -passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X). -passive_send(X) -> ?LIB_MOD:passive_send(X). -passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X). -passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X). -passive_append(X) -> ?LIB_MOD:passive_append(X). -passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X). -passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X). -passive_recv(X) -> ?LIB_MOD:passive_recv(X). -passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X). -passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X). -passive_type(X) -> ?LIB_MOD:passive_type(X). -passive_quote(X) -> ?LIB_MOD:passive_quote(X). -passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X). -active_user(X) -> ?LIB_MOD:active_user(X). -active_pwd(X) -> ?LIB_MOD:active_pwd(X). -active_cd(X) -> ?LIB_MOD:active_cd(X). -active_lcd(X) -> ?LIB_MOD:active_lcd(X). -active_ls(X) -> ?LIB_MOD:active_ls(X). -active_nlist(X) -> ?LIB_MOD:active_nlist(X). -active_rename(X) -> ?LIB_MOD:active_rename(X). -active_delete(X) -> ?LIB_MOD:active_delete(X). -active_mkdir(X) -> ?LIB_MOD:active_mkdir(X). -active_send(X) -> ?LIB_MOD:active_send(X). -active_send_bin(X) -> ?LIB_MOD:active_send_bin(X). -active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X). -active_append(X) -> ?LIB_MOD:active_append(X). -active_append_bin(X) -> ?LIB_MOD:active_append_bin(X). -active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X). -active_recv(X) -> ?LIB_MOD:active_recv(X). -active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X). -active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X). -active_type(X) -> ?LIB_MOD:active_type(X). -active_quote(X) -> ?LIB_MOD:active_quote(X). -active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X). -progress_report_send(X) -> ?LIB_MOD:progress_report_send(X). -progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X). - -fin(Config) -> - ?LIB_MOD:ftpd_fin(Config). diff --git a/lib/inets/test/ftp_solaris10_x86_test.erl b/lib/inets/test/ftp_solaris10_x86_test.erl deleted file mode 100644 index 3e7045bb4d..0000000000 --- a/lib/inets/test/ftp_solaris10_x86_test.erl +++ /dev/null @@ -1,162 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2010. 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. -%% -%% %CopyrightEnd% -%% -%% - --module(ftp_solaris10_x86_test). - --compile(export_all). - --include_lib("common_test/include/ct.hrl"). - --define(LIB_MOD, ftp_suite_lib). --define(CASE_WRAPPER(_A_,_B_,_C_), ?LIB_MOD:wrapper(_A_,_B_,_C_)). --define(PLATFORM, "Solaris 10 x86 "). - - -%% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initiation before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - {File, NewFile} = ?LIB_MOD:test_filenames(), - NewConfig = [{file, File}, {new_file, NewFile} | Config], - ?LIB_MOD:ftpd_init(solaris10_x86, NewConfig). - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(Config) -> - ?LIB_MOD:ftpd_fin(Config). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initiation before each test case -%%-------------------------------------------------------------------- -init_per_testcase(Case, Config) -> - ftp_suite_lib:init_per_testcase(Case, Config). - -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(Case, Config) -> - ftp_suite_lib:end_per_testcase(Case, Config). - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -all() -> - [open, open_port, {group, passive}, {group, active}, - api_missuse, not_owner, {group, progress_report}]. - -groups() -> - [{passive, [], ftp_suite_lib:passive(suite)}, - {active, [], ftp_suite_lib:active(suite)}, - {progress_report, [], - ftp_suite_lib:progress_report(suite)}]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -%% Test cases starts here. -%%-------------------------------------------------------------------- - -open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). -open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). -not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). - -passive_user(X) -> ?LIB_MOD:passive_user(X). -passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). -passive_cd(X) -> ?LIB_MOD:passive_cd(X). -passive_lcd(X) -> ?LIB_MOD:passive_lcd(X). -passive_ls(X) -> ?LIB_MOD:passive_ls(X). -passive_nlist(X) -> ?LIB_MOD:passive_nlist(X). -passive_rename(X) -> ?LIB_MOD:passive_rename(X). -passive_delete(X) -> ?LIB_MOD:passive_delete(X). -passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X). -passive_send(X) -> ?LIB_MOD:passive_send(X). -passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X). -passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X). -passive_append(X) -> ?LIB_MOD:passive_append(X). -passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X). -passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X). -passive_recv(X) -> ?LIB_MOD:passive_recv(X). -passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X). -passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X). -passive_type(X) -> ?LIB_MOD:passive_type(X). -passive_quote(X) -> ?LIB_MOD:passive_quote(X). -passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X). -active_user(X) -> ?LIB_MOD:active_user(X). -active_pwd(X) -> ?LIB_MOD:active_pwd(X). -active_cd(X) -> ?LIB_MOD:active_cd(X). -active_lcd(X) -> ?LIB_MOD:active_lcd(X). -active_ls(X) -> ?LIB_MOD:active_ls(X). -active_nlist(X) -> ?LIB_MOD:active_nlist(X). -active_rename(X) -> ?LIB_MOD:active_rename(X). -active_delete(X) -> ?LIB_MOD:active_delete(X). -active_mkdir(X) -> ?LIB_MOD:active_mkdir(X). -active_send(X) -> ?LIB_MOD:active_send(X). -active_send_bin(X) -> ?LIB_MOD:active_send_bin(X). -active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X). -active_append(X) -> ?LIB_MOD:active_append(X). -active_append_bin(X) -> ?LIB_MOD:active_append_bin(X). -active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X). -active_recv(X) -> ?LIB_MOD:active_recv(X). -active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X). -active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X). -active_type(X) -> ?LIB_MOD:active_type(X). -active_quote(X) -> ?LIB_MOD:active_quote(X). -active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X). -progress_report_send(X) -> ?LIB_MOD:progress_report_send(X). -progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X). - -fin(Config) -> - ?LIB_MOD:ftpd_fin(Config). diff --git a/lib/inets/test/ftp_solaris8_sparc_test.erl b/lib/inets/test/ftp_solaris8_sparc_test.erl deleted file mode 100644 index 23dbfc8fe3..0000000000 --- a/lib/inets/test/ftp_solaris8_sparc_test.erl +++ /dev/null @@ -1,159 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2010. 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. -%% -%% %CopyrightEnd% -%% -%% - --module(ftp_solaris8_sparc_test). - --compile(export_all). - --include_lib("common_test/include/ct.hrl"). - --define(LIB_MOD,ftp_suite_lib). --define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). --define(PLATFORM,"Solaris 8 sparc "). - -%% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initiation before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - {File, NewFile} = ?LIB_MOD:test_filenames(), - NewConfig = [{file, File}, {new_file, NewFile} | Config], - ?LIB_MOD:ftpd_init(solaris8_sparc, NewConfig). - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(Config) -> - ?LIB_MOD:ftpd_fin(Config). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initiation before each test case -%%-------------------------------------------------------------------- -init_per_testcase(Case, Config) -> - ftp_suite_lib:init_per_testcase(Case, Config). -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(Case, Config) -> - ftp_suite_lib:end_per_testcase(Case, Config). - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -all() -> - [open, open_port, {group, passive}, {group, active}, - api_missuse, not_owner, {group, progress_report}]. - -groups() -> - [{passive, [], ftp_suite_lib:passive(suite)}, - {active, [], ftp_suite_lib:active(suite)}, - {progress_report, [], - ftp_suite_lib:progress_report(suite)}]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -%% Test cases starts here. - -open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). -open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). -not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). - -passive_user(X) -> ?LIB_MOD:passive_user(X). -passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). -passive_cd(X) -> ?LIB_MOD:passive_cd(X). -passive_lcd(X) -> ?LIB_MOD:passive_lcd(X). -passive_ls(X) -> ?LIB_MOD:passive_ls(X). -passive_nlist(X) -> ?LIB_MOD:passive_nlist(X). -passive_rename(X) -> ?LIB_MOD:passive_rename(X). -passive_delete(X) -> ?LIB_MOD:passive_delete(X). -passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X). -passive_send(X) -> ?LIB_MOD:passive_send(X). -passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X). -passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X). -passive_append(X) -> ?LIB_MOD:passive_append(X). -passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X). -passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X). -passive_recv(X) -> ?LIB_MOD:passive_recv(X). -passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X). -passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X). -passive_type(X) -> ?LIB_MOD:passive_type(X). -passive_quote(X) -> ?LIB_MOD:passive_quote(X). -passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X). -active_user(X) -> ?LIB_MOD:active_user(X). -active_pwd(X) -> ?LIB_MOD:active_pwd(X). -active_cd(X) -> ?LIB_MOD:active_cd(X). -active_lcd(X) -> ?LIB_MOD:active_lcd(X). -active_ls(X) -> ?LIB_MOD:active_ls(X). -active_nlist(X) -> ?LIB_MOD:active_nlist(X). -active_rename(X) -> ?LIB_MOD:active_rename(X). -active_delete(X) -> ?LIB_MOD:active_delete(X). -active_mkdir(X) -> ?LIB_MOD:active_mkdir(X). -active_send(X) -> ?LIB_MOD:active_send(X). -active_send_bin(X) -> ?LIB_MOD:active_send_bin(X). -active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X). -active_append(X) -> ?LIB_MOD:active_append(X). -active_append_bin(X) -> ?LIB_MOD:active_append_bin(X). -active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X). -active_recv(X) -> ?LIB_MOD:active_recv(X). -active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X). -active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X). -active_type(X) -> ?LIB_MOD:active_type(X). -active_quote(X) -> ?LIB_MOD:active_quote(X). -active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X). -progress_report_send(X) -> ?LIB_MOD:progress_report_send(X). -progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X). - -fin(Config) -> - ?LIB_MOD:ftpd_fin(Config). diff --git a/lib/inets/test/ftp_solaris9_sparc_test.erl b/lib/inets/test/ftp_solaris9_sparc_test.erl deleted file mode 100644 index 896e2f497f..0000000000 --- a/lib/inets/test/ftp_solaris9_sparc_test.erl +++ /dev/null @@ -1,158 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2010. 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. -%% -%% %CopyrightEnd% -%% -%% - --module(ftp_solaris9_sparc_test). - --compile(export_all). - --include_lib("common_test/include/ct.hrl"). - --define(LIB_MOD,ftp_suite_lib). --define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). --define(PLATFORM,"Solaris 9 sparc "). -%% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initiation before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - {File, NewFile} = ?LIB_MOD:test_filenames(), - NewConfig = [{file, File}, {new_file, NewFile} | Config], - ?LIB_MOD:ftpd_init(solaris9_sparc, NewConfig). - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(Config) -> - ?LIB_MOD:ftpd_fin(Config). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initiation before each test case -%%-------------------------------------------------------------------- -init_per_testcase(Case, Config) -> - ftp_suite_lib:init_per_testcase(Case, Config). -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(Case, Config) -> - ftp_suite_lib:end_per_testcase(Case, Config). - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -all() -> - [open, open_port, {group, passive}, {group, active}, - api_missuse, not_owner, {group, progress_report}]. - -groups() -> - [{passive, [], ftp_suite_lib:passive(suite)}, - {active, [], ftp_suite_lib:active(suite)}, - {progress_report, [], - ftp_suite_lib:progress_report(suite)}]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -%% Test cases starts here. - -open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). -open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). -not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). - -passive_user(X) -> ?LIB_MOD:passive_user(X). -passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). -passive_cd(X) -> ?LIB_MOD:passive_cd(X). -passive_lcd(X) -> ?LIB_MOD:passive_lcd(X). -passive_ls(X) -> ?LIB_MOD:passive_ls(X). -passive_nlist(X) -> ?LIB_MOD:passive_nlist(X). -passive_rename(X) -> ?LIB_MOD:passive_rename(X). -passive_delete(X) -> ?LIB_MOD:passive_delete(X). -passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X). -passive_send(X) -> ?LIB_MOD:passive_send(X). -passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X). -passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X). -passive_append(X) -> ?LIB_MOD:passive_append(X). -passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X). -passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X). -passive_recv(X) -> ?LIB_MOD:passive_recv(X). -passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X). -passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X). -passive_type(X) -> ?LIB_MOD:passive_type(X). -passive_quote(X) -> ?LIB_MOD:passive_quote(X). -passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X). -active_user(X) -> ?LIB_MOD:active_user(X). -active_pwd(X) -> ?LIB_MOD:active_pwd(X). -active_cd(X) -> ?LIB_MOD:active_cd(X). -active_lcd(X) -> ?LIB_MOD:active_lcd(X). -active_ls(X) -> ?LIB_MOD:active_ls(X). -active_nlist(X) -> ?LIB_MOD:active_nlist(X). -active_rename(X) -> ?LIB_MOD:active_rename(X). -active_delete(X) -> ?LIB_MOD:active_delete(X). -active_mkdir(X) -> ?LIB_MOD:active_mkdir(X). -active_send(X) -> ?LIB_MOD:active_send(X). -active_send_bin(X) -> ?LIB_MOD:active_send_bin(X). -active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X). -active_append(X) -> ?LIB_MOD:active_append(X). -active_append_bin(X) -> ?LIB_MOD:active_append_bin(X). -active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X). -active_recv(X) -> ?LIB_MOD:active_recv(X). -active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X). -active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X). -active_type(X) -> ?LIB_MOD:active_type(X). -active_quote(X) -> ?LIB_MOD:active_quote(X). -active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X). -progress_report_send(X) -> ?LIB_MOD:progress_report_send(X). -progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X). - -fin(Config) -> - ?LIB_MOD:ftpd_fin(Config). diff --git a/lib/inets/test/ftp_suite_lib.erl b/lib/inets/test/ftp_suite_lib.erl deleted file mode 100644 index 211c9b5bee..0000000000 --- a/lib/inets/test/ftp_suite_lib.erl +++ /dev/null @@ -1,1675 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2011. 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. -%% -%% %CopyrightEnd% -%% -%% - --module(ftp_suite_lib). - - --include_lib("test_server/include/test_server.hrl"). --include_lib("test_server/include/test_server_line.hrl"). --include("inets_test_lib.hrl"). - -%% Test server specific exports -% -export([init_per_testcase/2, end_per_testcase/2]). - --compile(export_all). - - --record(progress, { - current = 0, - total - }). - - - --define(FTP_USER, "anonymous"). --define(FTP_PASS, passwd()). --define(FTP_PORT, 21). - --define(BAD_HOST, "badhostname"). --define(BAD_USER, "baduser"). --define(BAD_DIR, "baddirectory"). - --ifdef(ftp_debug_client). --define(ftp_open(Host, Flags), - do_ftp_open(Host, [{debug, debug}, - {timeout, timer:seconds(15)} | Flags])). --else. --ifdef(ftp_trace_client). --define(ftp_open(Host, Flags), - do_ftp_open(Host, [{debug, trace}, - {timeout, timer:seconds(15)} | Flags])). --else. --define(ftp_open(Host, Flags), - do_ftp_open(Host, [{verbose, true}, - {timeout, timer:seconds(15)} | Flags])). --endif. --endif. - -%% -- Tickets -- - -tickets(doc) -> - "Test cases for reported bugs"; -tickets(suite) -> - [ticket_6035]. - -%% -- - -ftpd_init(FtpdTag, Config) -> - %% Get the host name(s) of FTP server - Hosts = - case ct:get_config(ftpd_hosts) of - undefined -> - ftpd_hosts(data_dir(Config)); - H -> - H - end, - p("ftpd_init -> " - "~n Hosts: ~p" - "~n Config: ~p" - "~n FtpdTag: ~p", [Hosts, Config, FtpdTag]), - %% Get the first host that actually have a running FTP server - case lists:keysearch(FtpdTag, 1, Hosts) of - {value, {_, TagHosts}} when is_list(TagHosts) -> - inets:start(), - case (catch get_ftpd_host(TagHosts)) of - {ok, Host} -> - inets:stop(), - [{ftp_remote_host, Host}|Config]; - _ -> - inets:stop(), - Reason = lists:flatten( - io_lib:format("Could not find a valid " - "FTP server for ~p (~p)", - [FtpdTag, TagHosts])), - {skip, Reason} - end; - _ -> - Reason = lists:flatten( - io_lib:format("No host(s) running FTPD server " - "for ~p", [FtpdTag])), - {skip, Reason} - end. - -ftpd_fin(Config) -> - lists:keydelete(ftp_remote_host, 1, Config). - -get_ftpd_host([]) -> - {error, no_host}; -get_ftpd_host([Host|Hosts]) -> - p("get_ftpd_host -> entry with" - "~n Host: ~p" - "~n", [Host]), - case (catch ftp:open(Host, [{port, ?FTP_PORT}, {timeout, 20000}])) of - {ok, Pid} -> - (catch ftp:close(Pid)), - {ok, Host}; - _ -> - get_ftpd_host(Hosts) - end. - - -%%-------------------------------------------------------------------- - -dirty_select_ftpd_host(Config) -> - Hosts = - case ct:get_config(ftpd_hosts) of - undefined -> - ftpd_hosts(data_dir(Config)); - H -> - H - end, - dirty_select_ftpd_host2(Hosts). - -dirty_select_ftpd_host2([]) -> - throw({error, not_found}); -dirty_select_ftpd_host2([{PlatformTag, Hosts} | PlatformHosts]) -> - case dirty_select_ftpd_host3(Hosts) of - none -> - dirty_select_ftpd_host2(PlatformHosts); - {ok, Host} -> - {PlatformTag, Host} - end. - -dirty_select_ftpd_host3([]) -> - none; -dirty_select_ftpd_host3([Host|Hosts]) when is_list(Host) -> - case dirty_select_ftpd_host4(Host) of - true -> - {ok, Host}; - false -> - dirty_select_ftpd_host3(Hosts) - end; -dirty_select_ftpd_host3([_|Hosts]) -> - dirty_select_ftpd_host3(Hosts). - -%% This is a very simple and dirty test that there is a -%% (FTP) deamon on the other end. -dirty_select_ftpd_host4(Host) -> - Port = 21, - IpFam = inet, - Opts = [IpFam, binary, {packet, 0}, {active, false}], - Timeout = ?SECS(5), - case gen_tcp:connect(Host, Port, Opts, Timeout) of - {ok, Sock} -> - gen_tcp:close(Sock), - true; - _Error -> - false - end. - - -%%-------------------------------------------------------------------- - -test_filenames() -> - {ok, Host} = inet:gethostname(), - File = Host ++ "_ftp_test.txt", - NewFile = "new_" ++ File, - {File, NewFile}. - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(Case, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_testcase(Case, Config) - when (Case =:= open) orelse - (Case =:= open_port) -> - put(ftp_testcase, Case), - io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]), - inets:start(), - NewConfig = data_dir(Config), - watch_dog(NewConfig); - -init_per_testcase(Case, Config) -> - put(ftp_testcase, Case), - do_init_per_testcase(Case, Config). - -do_init_per_testcase(Case, Config) - when (Case =:= passive_user) -> - io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE,Case]), - inets:start(), - NewConfig = close_connection(watch_dog(Config)), - Host = ftp_host(Config), - case (catch ?ftp_open(Host, [{mode, passive}])) of - {ok, Pid} -> - [{ftp, Pid} | data_dir(NewConfig)]; - {skip, _} = SKIP -> - SKIP - end; - -do_init_per_testcase(Case, Config) - when (Case =:= active_user) -> - io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]), - inets:start(), - NewConfig = close_connection(watch_dog(Config)), - Host = ftp_host(Config), - case (catch ?ftp_open(Host, [{mode, active}])) of - {ok, Pid} -> - [{ftp, Pid} | data_dir(NewConfig)]; - {skip, _} = SKIP -> - SKIP - end; - -do_init_per_testcase(Case, Config) - when (Case =:= progress_report_send) orelse - (Case =:= progress_report_recv) -> - inets:start(), - io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]), - NewConfig = close_connection(watch_dog(Config)), - Host = ftp_host(Config), - Opts = [{port, ?FTP_PORT}, - {verbose, true}, - {progress, {?MODULE, progress, #progress{}}}], - case ftp:open(Host, Opts) of - {ok, Pid} -> - ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), - [{ftp, Pid} | data_dir(NewConfig)]; - {skip, _} = SKIP -> - SKIP - end; - -do_init_per_testcase(Case, Config) -> - io:format(user,"~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]), - inets:start(), - NewConfig = close_connection(watch_dog(Config)), - Host = ftp_host(Config), - Opts1 = - if - ((Case =:= passive_ip_v6_disabled) orelse - (Case =:= active_ip_v6_disabled)) -> - [{ipfamily, inet}]; - true -> - [] - end, - Opts2 = - case string:tokens(atom_to_list(Case), [$_]) of - ["active" | _] -> - [{mode, active} | Opts1]; - _ -> - [{mode, passive} | Opts1] - end, - case (catch ?ftp_open(Host, Opts2)) of - {ok, Pid} -> - ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), - [{ftp, Pid} | data_dir(NewConfig)]; - {skip, _} = SKIP -> - SKIP - end. - - -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(Case, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(_, Config) -> - NewConfig = close_connection(Config), - Dog = ?config(watchdog, NewConfig), - inets:stop(), - test_server:timetrap_cancel(Dog), - ok. - - -%%------------------------------------------------------------------------- -%% Suites similar for all hosts. -%%------------------------------------------------------------------------- - -passive(suite) -> - [ - passive_user, - passive_pwd, - passive_cd, - passive_lcd, - passive_ls, - passive_nlist, - passive_rename, - passive_delete, - passive_mkdir, - passive_send, - passive_send_bin, - passive_send_chunk, - passive_append, - passive_append_bin, - passive_append_chunk, - passive_recv, - passive_recv_bin, - passive_recv_chunk, - passive_type, - passive_quote, - passive_ip_v6_disabled - ]. - -active(suite) -> - [ - active_user, - active_pwd, - active_cd, - active_lcd, - active_ls, - active_nlist, - active_rename, - active_delete, - active_mkdir, - active_send, - active_send_bin, - active_send_chunk, - active_append, - active_append_bin, - active_append_chunk, - active_recv, - active_recv_bin, - active_recv_chunk, - active_type, - active_quote, - active_ip_v6_disabled - ]. - - - -%%------------------------------------------------------------------------- -%% Test cases starts here. -%%------------------------------------------------------------------------- - -open(doc) -> - ["Open an ftp connection to a host and close the connection." - "Also check that !-messages does not disturbe the connection"]; -open(suite) -> - []; -open(Config) when is_list(Config) -> - Host = ftp_host(Config), - (catch tc_open(Host)). - - -tc_open(Host) -> - p("tc_open -> entry with" - "~n Host: ~p", [Host]), - {ok, Pid} = ?ftp_open(Host, []), - ok = ftp:close(Pid), - p("tc_open -> try (ok) open 1"), - {ok, Pid1} = - ftp:open({option_list, [{host,Host}, - {port, ?FTP_PORT}, - {flags, [verbose]}, - {timeout, 30000}]}), - ok = ftp:close(Pid1), - - p("tc_open -> try (fail) open 2"), - {error, ehost} = - ftp:open({option_list, [{port, ?FTP_PORT}, {flags, [verbose]}]}), - {ok, Pid2} = ftp:open(Host), - ok = ftp:close(Pid2), - - p("tc_open -> try (ok) open 3"), - {ok, NewHost} = inet:getaddr(Host, inet), - {ok, Pid3} = ftp:open(NewHost), - ftp:user(Pid3, ?FTP_USER, ?FTP_PASS), - Pid3 ! foobar, - test_server:sleep(5000), - {message_queue_len, 0} = process_info(self(), message_queue_len), - ["200" ++ _] = ftp:quote(Pid3, "NOOP"), - ok = ftp:close(Pid3), - - %% Bad input that has default values are ignored and the defult - %% is used. - p("tc_open -> try (ok) open 4"), - {ok, Pid4} = - ftp:open({option_list, [{host, Host}, - {port, badarg}, - {flags, [verbose]}, - {timeout, 30000}]}), - test_server:sleep(100), - ok = ftp:close(Pid4), - - p("tc_open -> try (ok) open 5"), - {ok, Pid5} = - ftp:open({option_list, [{host, Host}, - {port, ?FTP_PORT}, - {flags, [verbose]}, - {timeout, -42}]}), - test_server:sleep(100), - ok = ftp:close(Pid5), - - p("tc_open -> try (ok) open 6"), - {ok, Pid6} = - ftp:open({option_list, [{host, Host}, - {port, ?FTP_PORT}, - {flags, [verbose]}, - {mode, cool}]}), - test_server:sleep(100), - ok = ftp:close(Pid6), - - p("tc_open -> try (ok) open 7"), - {ok, Pid7} = - ftp:open(Host, [{port, ?FTP_PORT}, {verbose, true}, {timeout, 30000}]), - ok = ftp:close(Pid7), - - p("tc_open -> try (ok) open 8"), - {ok, Pid8} = - ftp:open(Host, ?FTP_PORT), - ok = ftp:close(Pid8), - - p("tc_open -> try (ok) open 9"), - {ok, Pid9} = - ftp:open(Host, [{port, ?FTP_PORT}, - {verbose, true}, - {timeout, 30000}, - {dtimeout, -99}]), - ok = ftp:close(Pid9), - - p("tc_open -> try (ok) open 10"), - {ok, Pid10} = - ftp:open(Host, [{port, ?FTP_PORT}, - {verbose, true}, - {timeout, 30000}, - {dtimeout, "foobar"}]), - ok = ftp:close(Pid10), - - p("tc_open -> try (ok) open 11"), - {ok, Pid11} = - ftp:open(Host, [{port, ?FTP_PORT}, - {verbose, true}, - {timeout, 30000}, - {dtimeout, 1}]), - ok = ftp:close(Pid11), - - p("tc_open -> done"), - ok. - - -%%------------------------------------------------------------------------- - -open_port(doc) -> - ["Open an ftp connection to a host with given port number " - "and close the connection."]; % See also OTP-3892 -open_port(suite) -> - []; -open_port(Config) when is_list(Config) -> - Host = ftp_host(Config), - {ok, Pid} = ftp:open(Host, [{port, ?FTP_PORT}]), - ok = ftp:close(Pid), - {error, ehost} = ftp:open(?BAD_HOST, []), - ok. - - -%%------------------------------------------------------------------------- - -passive_user(doc) -> - ["Open an ftp connection to a host, and logon as anonymous ftp."]; -passive_user(suite) -> - []; -passive_user(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - p("Pid: ~p",[Pid]), - do_user(Pid). - - -%%------------------------------------------------------------------------- - -passive_pwd(doc) -> - ["Test ftp:pwd/1 & ftp:lpwd/1"]; -passive_pwd(suite) -> - []; -passive_pwd(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_pwd(Pid). - - -%%------------------------------------------------------------------------- - -passive_cd(doc) -> - ["Open an ftp connection, log on as anonymous ftp, and cd to the" - "directory \"/pub\" and the to the non-existent directory."]; -passive_cd(suite) -> - []; -passive_cd(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_cd(Pid). - - -%%------------------------------------------------------------------------- - -passive_lcd(doc) -> - ["Test api function ftp:lcd/2"]; -passive_lcd(suite) -> - []; -passive_lcd(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - PrivDir = ?config(priv_dir, Config), - do_lcd(Pid, PrivDir). - - -%%------------------------------------------------------------------------- - -passive_ls(doc) -> - ["Open an ftp connection; ls the current directory, and the " - "\"incoming\" directory. We assume that ls never fails, since " - "it's output is meant to be read by humans. "]; -passive_ls(suite) -> - []; -passive_ls(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_ls(Pid). - - -%%------------------------------------------------------------------------- - -passive_nlist(doc) -> - ["Open an ftp connection; nlist the current directory, and the " - "\"incoming\" directory. Nlist does not behave consistenly over " - "operating systems. On some it is an error to have an empty " - "directory."]; -passive_nlist(suite) -> - []; -passive_nlist(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - WildcardSupport = ?config(wildcard_support, Config), - do_nlist(Pid, WildcardSupport). - - -%%------------------------------------------------------------------------- - -passive_rename(doc) -> - ["Transfer a file to the server, and rename it; then remove it."]; -passive_rename(suite) -> - []; -passive_rename(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_rename(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_delete(doc) -> - ["Transfer a file to the server, and then delete it"]; -passive_delete(suite) -> - []; -passive_delete(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_delete(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_mkdir(doc) -> - ["Make a remote directory, cd to it, go to parent directory, and " - "remove the directory."]; -passive_mkdir(suite) -> - []; -passive_mkdir(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_mkdir(Pid). - - -%%------------------------------------------------------------------------- - -passive_send(doc) -> - ["Create a local file in priv_dir; open an ftp connection to a host; " - "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to " - "priv_dir; send the file; get a directory listing and check that " - "the file is on the list;, delete the remote file; get another listing " - "and check that the file is not on the list; close the session; " - "delete the local file."]; -passive_send(suite) -> - []; -passive_send(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_send(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_append(doc) -> - ["Create a local file in priv_dir; open an ftp connection to a host; " - "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to " - "priv_dir; append the file to a file at the remote side that not exits" - "this will create the file at the remote side. Then it append the file " - "again. When this is done it recive the remote file and control that" - "the content is doubled in it.After that it will remove the files"]; -passive_append(suite) -> - []; -passive_append(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_append(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_send_bin(doc) -> - ["Open a connection to a host; cd to the directory \"incoming\"; " - "send a binary; remove file; close the connection."]; -passive_send_bin(suite) -> - []; -passive_send_bin(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_send_bin(Pid, Config). - -%%------------------------------------------------------------------------- - -passive_append_bin(doc) -> - ["Open a connection to a host; cd to the directory \"incoming\"; " - "append a binary twice; get the file and compare the content" - "remove file; close the connection."]; -passive_append_bin(suite) -> - []; -passive_append_bin(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_append_bin(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_send_chunk(doc) -> - ["Open a connection to a host; cd to the directory \"incoming\"; " - "send chunks; remove file; close the connection."]; -passive_send_chunk(suite) -> - []; -passive_send_chunk(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_send_chunk(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_append_chunk(doc) -> - ["Open a connection to a host; cd to the directory \"incoming\"; " - "append chunks;control content remove file; close the connection."]; -passive_append_chunk(suite) -> - []; -passive_append_chunk(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_append_chunk(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_recv(doc) -> - ["Create a local file and transfer it to the remote host into the " - "the \"incoming\" directory, remove " - "the local file. Then open a new connection; cd to \"incoming\", " - "lcd to the private directory; receive the file; delete the " - "remote file; close connection; check that received file is in " - "the correct directory; cleanup." ]; -passive_recv(suite) -> - []; -passive_recv(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_recv(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_recv_bin(doc) -> - ["Send a binary to the remote host; and retreive " - "the file; then remove the file."]; -passive_recv_bin(suite) -> - []; -passive_recv_bin(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_recv_bin(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_recv_chunk(doc) -> - ["Send a binary to the remote host; Connect again, and retreive " - "the file; then remove the file."]; -passive_recv_chunk(suite) -> - []; -passive_recv_chunk(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_recv_chunk(Pid, Config). - - -%%------------------------------------------------------------------------- - -passive_type(doc) -> - ["Test that we can change btween ASCCI and binary transfer mode"]; -passive_type(suite) -> - []; -passive_type(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_type(Pid). - - -%%------------------------------------------------------------------------- - -passive_quote(doc) -> - [""]; -passive_quote(suite) -> - []; -passive_quote(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_quote(Pid). - - -%%------------------------------------------------------------------------- - -passive_ip_v6_disabled(doc) -> - ["Test ipv4 command PASV"]; -passive_ip_v6_disabled(suite) -> - []; -passive_ip_v6_disabled(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_send(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_user(doc) -> - ["Open an ftp connection to a host, and logon as anonymous ftp."]; -active_user(suite) -> - []; -active_user(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_user(Pid). - - -%%------------------------------------------------------------------------- - -active_pwd(doc) -> - ["Test ftp:pwd/1 & ftp:lpwd/1"]; -active_pwd(suite) -> - []; -active_pwd(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_pwd(Pid). - - -%%------------------------------------------------------------------------- - -active_cd(doc) -> - ["Open an ftp connection, log on as anonymous ftp, and cd to the" - "directory \"/pub\" and to a non-existent directory."]; -active_cd(suite) -> - []; -active_cd(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_cd(Pid). - - -%%------------------------------------------------------------------------- - -active_lcd(doc) -> - ["Test api function ftp:lcd/2"]; -active_lcd(suite) -> - []; -active_lcd(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - PrivDir = ?config(priv_dir, Config), - do_lcd(Pid, PrivDir). - - -%%------------------------------------------------------------------------- - -active_ls(doc) -> - ["Open an ftp connection; ls the current directory, and the " - "\"incoming\" directory. We assume that ls never fails, since " - "it's output is meant to be read by humans. "]; -active_ls(suite) -> - []; -active_ls(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_ls(Pid). - - -%%------------------------------------------------------------------------- - -active_nlist(doc) -> - ["Open an ftp connection; nlist the current directory, and the " - "\"incoming\" directory. Nlist does not behave consistenly over " - "operating systems. On some it is an error to have an empty " - "directory."]; -active_nlist(suite) -> - []; -active_nlist(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - WildcardSupport = ?config(wildcard_support, Config), - do_nlist(Pid, WildcardSupport). - - -%%------------------------------------------------------------------------- - -active_rename(doc) -> - ["Transfer a file to the server, and rename it; then remove it."]; -active_rename(suite) -> - []; -active_rename(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_rename(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_delete(doc) -> - ["Transfer a file to the server, and then delete it"]; -active_delete(suite) -> - []; -active_delete(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_delete(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_mkdir(doc) -> - ["Make a remote directory, cd to it, go to parent directory, and " - "remove the directory."]; -active_mkdir(suite) -> - []; -active_mkdir(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_mkdir(Pid). - - -%%------------------------------------------------------------------------- - -active_send(doc) -> - ["Create a local file in priv_dir; open an ftp connection to a host; " - "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to " - "priv_dir; send the file; get a directory listing and check that " - "the file is on the list;, delete the remote file; get another listing " - "and check that the file is not on the list; close the session; " - "delete the local file."]; -active_send(suite) -> - []; -active_send(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_send(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_append(doc) -> - ["Create a local file in priv_dir; open an ftp connection to a host; " - "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to " - "priv_dir; append the file to a file at the remote side that not exits" - "this will create the file at the remote side. Then it append the file " - "again. When this is done it recive the remote file and control that" - "the content is doubled in it.After that it will remove the files"]; -active_append(suite) -> - []; -active_append(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_append(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_send_bin(doc) -> - ["Open a connection to a host; cd to the directory \"incoming\"; " - "send a binary; remove file; close the connection."]; -active_send_bin(suite) -> - []; -active_send_bin(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_send_bin(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_append_bin(doc) -> - ["Open a connection to a host; cd to the directory \"incoming\"; " - "append a binary twice; get the file and compare the content" - "remove file; close the connection."]; -active_append_bin(suite) -> - []; -active_append_bin(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_append_bin(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_send_chunk(doc) -> - ["Open a connection to a host; cd to the directory \"incoming\"; " - "send chunks; remove file; close the connection."]; -active_send_chunk(suite) -> - []; -active_send_chunk(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_send_chunk(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_append_chunk(doc) -> - ["Open a connection to a host; cd to the directory \"incoming\"; " - "append chunks;control content remove file; close the connection."]; -active_append_chunk(suite) -> - []; -active_append_chunk(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_append_chunk(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_recv(doc) -> - ["Create a local file and transfer it to the remote host into the " - "the \"incoming\" directory, remove " - "the local file. Then open a new connection; cd to \"incoming\", " - "lcd to the private directory; receive the file; delete the " - "remote file; close connection; check that received file is in " - "the correct directory; cleanup." ]; -active_recv(suite) -> - []; -active_recv(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_recv(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_recv_bin(doc) -> - ["Send a binary to the remote host; and retreive " - "the file; then remove the file."]; -active_recv_bin(suite) -> - []; -active_recv_bin(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_recv_bin(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_recv_chunk(doc) -> - ["Send a binary to the remote host; Connect again, and retreive " - "the file; then remove the file."]; -active_recv_chunk(suite) -> - []; -active_recv_chunk(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_recv_chunk(Pid, Config). - - -%%------------------------------------------------------------------------- - -active_type(doc) -> - ["Test that we can change btween ASCCI and binary transfer mode"]; -active_type(suite) -> - []; -active_type(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_type(Pid). - - -%%------------------------------------------------------------------------- - -active_quote(doc) -> - [""]; -active_quote(suite) -> - []; -active_quote(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_quote(Pid). - - -%%------------------------------------------------------------------------- - -active_ip_v6_disabled(doc) -> - ["Test ipv4 command PORT"]; -active_ip_v6_disabled(suite) -> - []; -active_ip_v6_disabled(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - do_send(Pid, Config). - - -%%------------------------------------------------------------------------- - -api_missuse(doc)-> - ["Test that behaviour of the ftp process if the api is abused"]; -api_missuse(suite) -> []; -api_missuse(Config) when is_list(Config) -> - p("api_missuse -> entry"), - Flag = process_flag(trap_exit, true), - Pid = ?config(ftp, Config), - Host = ftp_host(Config), - - %% Serious programming fault, connetion will be shut down - p("api_missuse -> verify bad call termination (~p)", [Pid]), - case (catch gen_server:call(Pid, {self(), foobar, 10}, infinity)) of - {error, {connection_terminated, 'API_violation'}} -> - ok; - Unexpected1 -> - exit({unexpected_result, Unexpected1}) - end, - test_server:sleep(500), - undefined = process_info(Pid, status), - - p("api_missuse -> start new client"), - {ok, Pid2} = ?ftp_open(Host, []), - %% Serious programming fault, connetion will be shut down - p("api_missuse -> verify bad cast termination"), - gen_server:cast(Pid2, {self(), foobar, 10}), - test_server:sleep(500), - undefined = process_info(Pid2, status), - - p("api_missuse -> start new client"), - {ok, Pid3} = ?ftp_open(Host, []), - %% Could be an innocent misstake the connection lives. - p("api_missuse -> verify bad bang"), - Pid3 ! foobar, - test_server:sleep(500), - {status, _} = process_info(Pid3, status), - process_flag(trap_exit, Flag), - p("api_missuse -> done"), - ok. - - -%%------------------------------------------------------------------------- - -not_owner(doc) -> - ["Test what happens if a process that not owns the connection tries " - "to use it"]; -not_owner(suite) -> - []; -not_owner(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - OtherPid = spawn_link(?MODULE, not_owner, [Pid, self()]), - - receive - {OtherPid, ok} -> - {ok, _} = ftp:pwd(Pid) - end, - ok. - -not_owner(FtpPid, Pid) -> - {error, not_connection_owner} = ftp:pwd(FtpPid), - ftp:close(FtpPid), - test_server:sleep(100), - Pid ! {self(), ok}. - - -%%------------------------------------------------------------------------- - - -progress_report(doc) -> - ["Solaris 8 sparc test the option progress."]; -progress_report(suite) -> - [progress_report_send, progress_report_recv]. - - -%% -- - -progress_report_send(doc) -> - ["Test the option progress for ftp:send/[2,3]"]; -progress_report_send(suite) -> - []; -progress_report_send(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - ReportPid = - spawn_link(?MODULE, progress_report_receiver_init, [self(), 1]), - do_send(Pid, Config), - receive - {ReportPid, ok} -> - ok - end. - - -%% -- - -progress_report_recv(doc) -> - ["Test the option progress for ftp:recv/[2,3]"]; -progress_report_recv(suite) -> - []; -progress_report_recv(Config) when is_list(Config) -> - Pid = ?config(ftp, Config), - ReportPid = - spawn_link(?MODULE, progress_report_receiver_init, [self(), 3]), - do_recv(Pid, Config), - receive - {ReportPid, ok} -> - ok - end, - ok. - -progress(#progress{} = Progress , _File, {file_size, Total}) -> - progress_report_receiver ! start, - Progress#progress{total = Total}; -progress(#progress{total = Total, current = Current} - = Progress, _File, {transfer_size, 0}) -> - progress_report_receiver ! finish, - case Total of - unknown -> - ok; - Current -> - ok; - _ -> - test_server:fail({error, {progress, {total, Total}, - {current, Current}}}) - end, - Progress; -progress(#progress{current = Current} = Progress, _File, - {transfer_size, Size}) -> - progress_report_receiver ! update, - Progress#progress{current = Current + Size}. - -progress_report_receiver_init(Pid, N) -> - register(progress_report_receiver, self()), - receive - start -> - ok - end, - progress_report_receiver_loop(Pid, N-1). - -progress_report_receiver_loop(Pid, N) -> - receive - update -> - progress_report_receiver_loop(Pid, N); - finish when N =:= 0 -> - Pid ! {self(), ok}; - finish -> - Pid ! {self(), ok}, - receive - start -> - ok - end, - progress_report_receiver_loop(Pid, N-1) - end. - - -%%------------------------------------------------------------------------- -%% Ticket test cases -%%------------------------------------------------------------------------- - -ticket_6035(doc) -> ["Test that owning process that exits with reason " - "'shutdown' does not cause an error message."]; -ticket_6035(suite) -> []; -ticket_6035(Config) -> - p("ticket_6035 -> entry with" - "~n Config: ~p", [Config]), - PrivDir = ?config(priv_dir, Config), - LogFile = filename:join([PrivDir,"ticket_6035.log"]), - try - begin - p("ticket_6035 -> select ftpd host"), - Host = dirty_select_ftpd_host(Config), - p("ticket_6035 -> ftpd host selected (~p) => now spawn ftp owner", [Host]), - Pid = spawn(?MODULE, open_wait_6035, [Host, self()]), - p("ticket_6035 -> waiter spawned: ~p => now open error logfile (~p)", - [Pid, LogFile]), - error_logger:logfile({open, LogFile}), - p("ticket_6035 -> error logfile open => now kill waiter process"), - true = kill_ftp_proc_6035(Pid, LogFile), - p("ticket_6035 -> waiter process killed => now close error logfile"), - error_logger:logfile(close), - p("ticket_6035 -> done", []), - ok - end - catch - throw:{error, not_found} -> - {skip, "No available FTP servers"} - end. - -kill_ftp_proc_6035(Pid, LogFile) -> - p("kill_ftp_proc_6035 -> entry"), - receive - open -> - p("kill_ftp_proc_6035 -> received open => now issue shutdown"), - exit(Pid, shutdown), - kill_ftp_proc_6035(Pid, LogFile); - {open_failed, Reason} -> - p("kill_ftp_proc_6035 -> received open_failed" - "~n Reason: ~p", [Reason]), - exit({skip, {failed_openening_server_connection, Reason}}) - after - 5000 -> - p("kill_ftp_proc_6035 -> timeout"), - is_error_report_6035(LogFile) - end. - -open_wait_6035({Tag, FtpServer}, From) -> - p("open_wait_6035 -> try connect to [~p] ~s for ~p", [Tag, FtpServer, From]), - case ftp:open(FtpServer, [{timeout, timer:seconds(15)}]) of - {ok, Pid} -> - p("open_wait_6035 -> connected (~p), now login", [Pid]), - LoginResult = ftp:user(Pid,"anonymous","kldjf"), - p("open_wait_6035 -> login result: ~p", [LoginResult]), - From ! open, - receive - dummy -> - p("open_wait_6035 -> received dummy"), - ok - after - 10000 -> - p("open_wait_6035 -> timeout"), - ok - end, - p("open_wait_6035 -> done(ok)"), - ok; - {error, Reason} -> - p("open_wait_6035 -> open failed" - "~n Reason: ~p", [Reason]), - From ! {open_failed, {Reason, FtpServer}}, - p("open_wait_6035 -> done(error)"), - ok - end. - -is_error_report_6035(LogFile) -> - p("is_error_report_6035 -> entry"), - Res = - case file:read_file(LogFile) of - {ok, Bin} -> - Txt = binary_to_list(Bin), - p("is_error_report_6035 -> logfile read: ~n~p", [Txt]), - read_log_6035(Txt); - _ -> - false - end, - p("is_error_report_6035 -> logfile read result: " - "~n ~p", [Res]), - %% file:delete(LogFile), - Res. - -read_log_6035("=ERROR REPORT===="++_Rest) -> - p("read_log_6035 -> ERROR REPORT detected"), - true; -read_log_6035([H|T]) -> - p("read_log_6035 -> OTHER: " - "~p", [H]), - read_log_6035(T); -read_log_6035([]) -> - p("read_log_6035 -> done"), - false. - - -%%-------------------------------------------------------------------- -%% Internal functions -%%-------------------------------------------------------------------- -do_user(Pid) -> - {error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS), - ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), - ok. - -do_pwd(Pid) -> - {ok, "/"} = ftp:pwd(Pid), - {ok, Path} = ftp:lpwd(Pid), - {ok, Path} = file:get_cwd(), - ok. - -do_cd(Pid) -> - ok = ftp:cd(Pid, "/pub"), - {error, epath} = ftp:cd(Pid, ?BAD_DIR), - ok. - -do_lcd(Pid, Dir) -> - ok = ftp:lcd(Pid, Dir), - {error, epath} = ftp:lcd(Pid, ?BAD_DIR), - ok. - - -do_ls(Pid) -> - {ok, _} = ftp:ls(Pid), - {ok, _} = ftp:ls(Pid, "incoming"), - %% neither nlist nor ls operates on a directory - %% they operate on a pathname, which *can* be a - %% directory, but can also be a filename or a group - %% of files (including wildcards). - {ok, _} = ftp:ls(Pid, "incom*"), - ok. - -do_nlist(Pid, WildcardSupport) -> - {ok, _} = ftp:nlist(Pid), - {ok, _} = ftp:nlist(Pid, "incoming"), - %% neither nlist nor ls operates on a directory - %% they operate on a pathname, which *can* be a - %% directory, but can also be a filename or a group - %% of files (including wildcards). - case WildcardSupport of - true -> - {ok, _} = ftp:nlist(Pid, "incom*"), - ok; - _ -> - ok - end. - -do_rename(Pid, Config) -> - PrivDir = ?config(priv_dir, Config), - LFile = ?config(file, Config), - NewLFile = ?config(new_file, Config), - AbsLFile = filename:absname(LFile, PrivDir), - Contents = "ftp_SUITE test ...", - ok = file:write_file(AbsLFile, list_to_binary(Contents)), - ok = ftp:cd(Pid, "incoming"), - ok = ftp:lcd(Pid, PrivDir), - ftp:delete(Pid, LFile), % reset - ftp:delete(Pid, NewLFile), % reset - ok = ftp:send(Pid, LFile), - {error, epath} = ftp:rename(Pid, NewLFile, LFile), - ok = ftp:rename(Pid, LFile, NewLFile), - ftp:delete(Pid, LFile), % cleanup - ftp:delete(Pid, NewLFile), % cleanup - ok. - -do_delete(Pid, Config) -> - PrivDir = ?config(priv_dir, Config), - LFile = ?config(file, Config), - AbsLFile = filename:absname(LFile, PrivDir), - Contents = "ftp_SUITE test ...", - ok = file:write_file(AbsLFile, list_to_binary(Contents)), - ok = ftp:cd(Pid, "incoming"), - ok = ftp:lcd(Pid, PrivDir), - ftp:delete(Pid,LFile), % reset - ok = ftp:send(Pid, LFile), - ok = ftp:delete(Pid,LFile), - ok. - -do_mkdir(Pid) -> - {A, B, C} = erlang:now(), - NewDir = "nisse_" ++ integer_to_list(A) ++ "_" ++ - integer_to_list(B) ++ "_" ++ integer_to_list(C), - ok = ftp:cd(Pid, "incoming"), - {ok, CurrDir} = ftp:pwd(Pid), - ok = ftp:mkdir(Pid, NewDir), - ok = ftp:cd(Pid, NewDir), - ok = ftp:cd(Pid, CurrDir), - ok = ftp:rmdir(Pid, NewDir), - ok. - -do_send(Pid, Config) -> - PrivDir = ?config(priv_dir, Config), - LFile = ?config(file, Config), - RFile = LFile ++ ".remote", - AbsLFile = filename:absname(LFile, PrivDir), - Contents = "ftp_SUITE test ...", - ok = file:write_file(AbsLFile, list_to_binary(Contents)), - ok = ftp:cd(Pid, "incoming"), - ok = ftp:lcd(Pid, PrivDir), - ok = ftp:send(Pid, LFile, RFile), - {ok, RFilesString} = ftp:nlist(Pid), - RFiles = split(RFilesString), - true = lists:member(RFile, RFiles), - ok = ftp:delete(Pid, RFile), - case ftp:nlist(Pid) of - {error, epath} -> - ok; % No files - {ok, RFilesString1} -> - RFiles1 = split(RFilesString1), - false = lists:member(RFile, RFiles1) - end, - ok = file:delete(AbsLFile). - -do_append(Pid, Config) -> - PrivDir = ?config(priv_dir, Config), - LFile = ?config(file, Config), - RFile = ?config(new_file, Config), - AbsLFile = filename:absname(LFile, PrivDir), - Contents = "ftp_SUITE test:appending\r\n", - - ok = file:write_file(AbsLFile, list_to_binary(Contents)), - ok = ftp:cd(Pid, "incoming"), - ok = ftp:lcd(Pid, PrivDir), - - %% remove files from earlier failed test case - ftp:delete(Pid, RFile), - ftp:delete(Pid, LFile), - - ok = ftp:append(Pid, LFile, RFile), - ok = ftp:append(Pid, LFile, RFile), - ok = ftp:append(Pid, LFile), - - %% Control the contents of the file - {ok, Bin1} = ftp:recv_bin(Pid, RFile), - ok = ftp:delete(Pid, RFile), - ok = file:delete(AbsLFile), - ok = check_content(binary_to_list(Bin1), Contents, double), - - {ok, Bin2} = ftp:recv_bin(Pid, LFile), - ok = ftp:delete(Pid, LFile), - ok = check_content(binary_to_list(Bin2), Contents, singel), - ok. - -do_send_bin(Pid, Config) -> - File = ?config(file, Config), - Contents = "ftp_SUITE test ...", - Bin = list_to_binary(Contents), - ok = ftp:cd(Pid, "incoming"), - {error, enotbinary} = ftp:send_bin(Pid, Contents, File), - ok = ftp:send_bin(Pid, Bin, File), - {ok, RFilesString} = ftp:nlist(Pid), - RFiles = split(RFilesString), - true = lists:member(File, RFiles), - ok = ftp:delete(Pid, File), - ok. - -do_append_bin(Pid, Config) -> - File = ?config(file, Config), - Contents = "ftp_SUITE test ...", - Bin = list_to_binary(Contents), - ok = ftp:cd(Pid, "incoming"), - {error, enotbinary} = ftp:append_bin(Pid, Contents, File), - ok = ftp:append_bin(Pid, Bin, File), - ok = ftp:append_bin(Pid, Bin, File), - %% Control the contents of the file - {ok, Bin2} = ftp:recv_bin(Pid, File), - ok = ftp:delete(Pid,File), - ok = check_content(binary_to_list(Bin2),binary_to_list(Bin), double). - -do_send_chunk(Pid, Config) -> - File = ?config(file, Config), - Contents = "ftp_SUITE test ...", - Bin = list_to_binary(Contents), - ok = ftp:cd(Pid, "incoming"), - ok = ftp:send_chunk_start(Pid, File), - {error, echunk} = ftp:cd(Pid, "incoming"), - {error, enotbinary} = ftp:send_chunk(Pid, Contents), - ok = ftp:send_chunk(Pid, Bin), - ok = ftp:send_chunk(Pid, Bin), - ok = ftp:send_chunk_end(Pid), - {ok, RFilesString} = ftp:nlist(Pid), - RFiles = split(RFilesString), - true = lists:member(File, RFiles), - ok = ftp:delete(Pid, File), - ok. - -do_append_chunk(Pid, Config) -> - File = ?config(file, Config), - Contents = ["ER","LE","RL"], - ok = ftp:cd(Pid, "incoming"), - ok = ftp:append_chunk_start(Pid, File), - {error, enotbinary} = ftp:append_chunk(Pid, lists:nth(1,Contents)), - ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(1,Contents))), - ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(2,Contents))), - ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(3,Contents))), - ok = ftp:append_chunk_end(Pid), - %%Control the contents of the file - {ok, Bin2} = ftp:recv_bin(Pid, File), - ok = check_content(binary_to_list(Bin2),"ERL", double), - ok = ftp:delete(Pid, File), - ok. - -do_recv(Pid, Config) -> - PrivDir = ?config(priv_dir, Config), - File = ?config(file, Config), - Newfile = ?config(new_file, Config), - AbsFile = filename:absname(File, PrivDir), - Contents = "ftp_SUITE:recv test ...", - ok = file:write_file(AbsFile, list_to_binary(Contents)), - ok = ftp:cd(Pid, "incoming"), - ftp:delete(Pid, File), % reset - ftp:lcd(Pid, PrivDir), - ok = ftp:send(Pid, File), - ok = file:delete(AbsFile), % cleanup - test_server:sleep(100), - ok = ftp:lcd(Pid, PrivDir), - ok = ftp:recv(Pid, File), - {ok, Files} = file:list_dir(PrivDir), - true = lists:member(File, Files), - ok = file:delete(AbsFile), % cleanup - ok = ftp:recv(Pid, File, Newfile), - ok = ftp:delete(Pid, File), % cleanup - ok. - -do_recv_bin(Pid, Config) -> - File = ?config(file, Config), - Contents1 = "ftp_SUITE test ...", - Bin1 = list_to_binary(Contents1), - ok = ftp:cd(Pid, "incoming"), - ok = ftp:send_bin(Pid, Bin1, File), - test_server:sleep(100), - {ok, Bin2} = ftp:recv_bin(Pid, File), - ok = ftp:delete(Pid, File), % cleanup - Contents2 = binary_to_list(Bin2), - Contents1 = Contents2, - ok. - -do_recv_chunk(Pid, Config) -> - File = ?config(file, Config), - Data = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" - "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" - "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" - "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG" - "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH" - "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", - - Contents1 = lists:flatten(lists:duplicate(10, Data)), - Bin1 = list_to_binary(Contents1), - ok = ftp:cd(Pid, "incoming"), - ok = ftp:type(Pid, binary), - ok = ftp:send_bin(Pid, Bin1, File), - test_server:sleep(100), - {error, "ftp:recv_chunk_start/2 not called"} = recv_chunk(Pid, <<>>), - ok = ftp:recv_chunk_start(Pid, File), - {ok, Contents2} = recv_chunk(Pid, <<>>), - ok = ftp:delete(Pid, File), % cleanup - ok = find_diff(Contents2, Contents1, 1), - ok. - -do_type(Pid) -> - ok = ftp:type(Pid, ascii), - ok = ftp:type(Pid, binary), - ok = ftp:type(Pid, ascii), - {error, etype} = ftp:type(Pid, foobar), - ok. - -do_quote(Pid) -> - ["257 \"/\""++_Rest] = ftp:quote(Pid, "pwd"), %% 257 - [_| _] = ftp:quote(Pid, "help"), - %% This negativ test causes some ftp servers to hang. This test - %% is not important for the client, so we skip it for now. - %%["425 Can't build data connection: Connection refused."] - %% = ftp:quote(Pid, "list"), - ok. - - watch_dog(Config) -> - Dog = test_server:timetrap(inets_test_lib:minutes(1)), - NewConfig = lists:keydelete(watchdog, 1, Config), - [{watchdog, Dog} | NewConfig]. - - close_connection(Config) -> - case ?config(ftp, Config) of - Pid when is_pid(Pid) -> - ok = ftp:close(Pid), - lists:delete({ftp, Pid}, Config); - _ -> - Config - end. - -ftp_host(Config) -> - case ?config(ftp_remote_host, Config) of - undefined -> - exit({skip, "No host specified"}); - Host -> - Host - end. - -check_content(RContent, LContent, Amount) -> - LContent2 = case Amount of - double -> - LContent ++ LContent; - singel -> - LContent - end, - case string:equal(RContent, LContent2) of - true -> - ok; - false -> - %% Find where the diff is - Where = find_diff(RContent, LContent2, 1), - Where - end. - -find_diff(A, A, _) -> - ok; -find_diff([H|T1], [H|T2], Pos) -> - find_diff(T1, T2, Pos+1); -find_diff(RC, LC, Pos) -> - {error, {diff, Pos, RC, LC}}. - -recv_chunk(Pid, Acc) -> - case ftp:recv_chunk(Pid) of - ok -> - {ok, binary_to_list(Acc)}; - {ok, Bin} -> - recv_chunk(Pid, <>); - Error -> - Error - end. - -split(Cs) -> - split(Cs, [], []). - -split([$\r, $\n| Cs], I, Is) -> - split(Cs, [], [lists:reverse(I)| Is]); -split([C| Cs], I, Is) -> - split(Cs, [C| I], Is); -split([], I, Is) -> - lists:reverse([lists:reverse(I)| Is]). - -do_ftp_open(Host, Opts) -> - p("do_ftp_open -> entry with" - "~n Host: ~p" - "~n Opts: ~p", [Host, Opts]), - case ftp:open(Host, Opts) of - {ok, _} = OK -> - OK; - {error, Reason} -> - Str = - lists:flatten( - io_lib:format("Unable to reach test FTP server ~p (~p)", - [Host, Reason])), - throw({skip, Str}) - end. - - -passwd() -> - Host = - case inet:gethostname() of - {ok, H} -> - H; - _ -> - "localhost" - end, - "ftp_SUITE@" ++ Host. - -ftpd_hosts(Config) -> - DataDir = ?config(data_dir, Config), - FileName = filename:join([DataDir, "../ftp_SUITE_data/", ftpd_hosts]), - p("FileName: ~p", [FileName]), - case file:consult(FileName) of - {ok, [Hosts]} when is_list(Hosts) -> - Hosts; - _ -> - [] - end. - -wrapper(Prefix,doc,Func) -> - Prefix++Func(doc); -wrapper(_,X,Func) -> - Func(X). - -data_dir(Config) -> - case ?config(data_dir, Config) of - List when (length(List) > 0) -> - PathList = filename:split(List), - {NewPathList,_} = lists:split((length(PathList)-1), PathList), - DataDir = filename:join(NewPathList ++ [ftp_SUITE_data]), - NewConfig = - lists:keyreplace(data_dir,1,Config, {data_dir,DataDir}), - NewConfig; - _ -> Config - end. - - - -p(F) -> - p(F, []). - -p(F, A) -> - case get(ftp_testcase) of - undefined -> - io:format("~w [~w] " ++ F ++ "~n", [?MODULE, self() | A]); - TC when is_atom(TC) -> - io:format("~w [~w] ~w:" ++ F ++ "~n", [?MODULE, self(), TC | A]) - end. diff --git a/lib/inets/test/ftp_ticket_test.erl b/lib/inets/test/ftp_ticket_test.erl deleted file mode 100644 index fe4ab35728..0000000000 --- a/lib/inets/test/ftp_ticket_test.erl +++ /dev/null @@ -1,61 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2006-2010. 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. -%% -%% %CopyrightEnd% -%% -%% - --module(ftp_ticket_test). - --compile(export_all). - --define(LIB_MOD,ftp_suite_lib). --define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). --define(PLATFORM,"Solaris 8 sparc "). - - -%% Test server callbacks -init_per_testcase(Case, Config) -> - ftp_suite_lib:init_per_testcase(Case, Config). - -end_per_testcase(Case, Config) -> - ftp_suite_lib:end_per_testcase(Case, Config). - - -all() -> - tickets(). - -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_suite(Config) -> - ?LIB_MOD:ftpd_init(ticket_test, Config). - -tickets() -> - [ticket_6035]. - - -end_per_suite(Config) -> - ?LIB_MOD:ftpd_fin(Config). - -ticket_6035(X) -> ?LIB_MOD:ticket_6035(X). diff --git a/lib/inets/test/ftp_windows_2003_server_test.erl b/lib/inets/test/ftp_windows_2003_server_test.erl deleted file mode 100644 index 32f25713f8..0000000000 --- a/lib/inets/test/ftp_windows_2003_server_test.erl +++ /dev/null @@ -1,167 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2011. 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. -%% -%% %CopyrightEnd% -%% -%% - --module(ftp_windows_2003_server_test). - --compile(export_all). - --include_lib("common_test/include/ct.hrl"). - --define(LIB_MOD,ftp_suite_lib). --define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). --define(PLATFORM,"Windows 2003 server "). - -%% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initiation before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - {File, NewFile} = ?LIB_MOD:test_filenames(), - NewConfig = [{file, File}, {new_file, NewFile} | Config], - ?LIB_MOD:ftpd_init(windows_2003_server, NewConfig). - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(Config) -> - ?LIB_MOD:ftpd_fin(Config). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initiation before each test case -%%-------------------------------------------------------------------- -init_per_testcase(Case, Config) -> - ftp_suite_lib:init_per_testcase(Case, Config). -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(Case, Config) -> - ftp_suite_lib:end_per_testcase(Case, Config). - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -all() -> - [ - open, - open_port, - {group, passive}, - {group, active}, - api_missuse, - not_owner, - {group, progress_report} - ]. - -groups() -> - [ - {passive, [], ftp_suite_lib:passive(suite)}, - {active, [], ftp_suite_lib:active(suite)}, - {progress_report, [], ftp_suite_lib:progress_report(suite)} - ]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -%% Test cases starts here. - -open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). -open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). -not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). - -passive_user(X) -> ?LIB_MOD:passive_user(X). -passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). -passive_cd(X) -> ?LIB_MOD:passive_cd(X). -passive_lcd(X) -> ?LIB_MOD:passive_lcd(X). -passive_ls(X) -> ?LIB_MOD:passive_ls(X). -passive_nlist(X) -> ?LIB_MOD:passive_nlist(X). -passive_rename(X) -> ?LIB_MOD:passive_rename(X). -passive_delete(X) -> ?LIB_MOD:passive_delete(X). -passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X). -passive_send(X) -> ?LIB_MOD:passive_send(X). -passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X). -passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X). -passive_append(X) -> ?LIB_MOD:passive_append(X). -passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X). -passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X). -passive_recv(X) -> ?LIB_MOD:passive_recv(X). -passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X). -passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X). -passive_type(X) -> ?LIB_MOD:passive_type(X). -passive_quote(X) -> ?LIB_MOD:passive_quote(X). -passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X). -active_user(X) -> ?LIB_MOD:active_user(X). -active_pwd(X) -> ?LIB_MOD:active_pwd(X). -active_cd(X) -> ?LIB_MOD:active_cd(X). -active_lcd(X) -> ?LIB_MOD:active_lcd(X). -active_ls(X) -> ?LIB_MOD:active_ls(X). -active_nlist(X) -> ?LIB_MOD:active_nlist(X). -active_rename(X) -> ?LIB_MOD:active_rename(X). -active_delete(X) -> ?LIB_MOD:active_delete(X). -active_mkdir(X) -> ?LIB_MOD:active_mkdir(X). -active_send(X) -> ?LIB_MOD:active_send(X). -active_send_bin(X) -> ?LIB_MOD:active_send_bin(X). -active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X). -active_append(X) -> ?LIB_MOD:active_append(X). -active_append_bin(X) -> ?LIB_MOD:active_append_bin(X). -active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X). -active_recv(X) -> ?LIB_MOD:active_recv(X). -active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X). -active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X). -active_type(X) -> ?LIB_MOD:active_type(X). -active_quote(X) -> ?LIB_MOD:active_quote(X). -active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X). -progress_report_send(X) -> ?LIB_MOD:progress_report_send(X). -progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X). - -fin(Config) -> - ?LIB_MOD:ftpd_fin(Config). diff --git a/lib/inets/test/ftp_windows_xp_test.erl b/lib/inets/test/ftp_windows_xp_test.erl deleted file mode 100644 index 06d919ba00..0000000000 --- a/lib/inets/test/ftp_windows_xp_test.erl +++ /dev/null @@ -1,157 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2010. 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. -%% -%% %CopyrightEnd% -%% -%% - --module(ftp_windows_xp_test). - --compile(export_all). - --include_lib("common_test/include/ct.hrl"). - --define(LIB_MOD,ftp_suite_lib). --define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). --define(PLATFORM,"Windows xp "). - -%% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initiation before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - {File, NewFile} = ?LIB_MOD:test_filenames(), - NewConfig = [{file, File}, {new_file, NewFile} | Config], - ?LIB_MOD:ftpd_init(windows_xp, NewConfig). - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(Config) -> - ?LIB_MOD:ftpd_fin(Config). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initiation before each test case -%%-------------------------------------------------------------------- -init_per_testcase(Case, Config) -> - ftp_suite_lib:init_per_testcase(Case, Config). -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(Case, Config) -> - ftp_suite_lib:end_per_testcase(Case, Config). - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -all() -> - [open, open_port, {group, passive}, {group, active}, - api_missuse, not_owner, {group, progress_report}]. - -groups() -> - [{passive, [], ftp_suite_lib:passive(suite)}, - {active, [], ftp_suite_lib:active(suite)}, - {progress_report, [], - ftp_suite_lib:progress_report(suite)}]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). -open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). -not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). - -passive_user(X) -> ?LIB_MOD:passive_user(X). -passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). -passive_cd(X) -> ?LIB_MOD:passive_cd(X). -passive_lcd(X) -> ?LIB_MOD:passive_lcd(X). -passive_ls(X) -> ?LIB_MOD:passive_ls(X). -passive_nlist(X) -> ?LIB_MOD:passive_nlist(X). -passive_rename(X) -> ?LIB_MOD:passive_rename(X). -passive_delete(X) -> ?LIB_MOD:passive_delete(X). -passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X). -passive_send(X) -> ?LIB_MOD:passive_send(X). -passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X). -passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X). -passive_append(X) -> ?LIB_MOD:passive_append(X). -passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X). -passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X). -passive_recv(X) -> ?LIB_MOD:passive_recv(X). -passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X). -passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X). -passive_type(X) -> ?LIB_MOD:passive_type(X). -passive_quote(X) -> ?LIB_MOD:passive_quote(X). -passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X). -active_user(X) -> ?LIB_MOD:active_user(X). -active_pwd(X) -> ?LIB_MOD:active_pwd(X). -active_cd(X) -> ?LIB_MOD:active_cd(X). -active_lcd(X) -> ?LIB_MOD:active_lcd(X). -active_ls(X) -> ?LIB_MOD:active_ls(X). -active_nlist(X) -> ?LIB_MOD:active_nlist(X). -active_rename(X) -> ?LIB_MOD:active_rename(X). -active_delete(X) -> ?LIB_MOD:active_delete(X). -active_mkdir(X) -> ?LIB_MOD:active_mkdir(X). -active_send(X) -> ?LIB_MOD:active_send(X). -active_send_bin(X) -> ?LIB_MOD:active_send_bin(X). -active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X). -active_append(X) -> ?LIB_MOD:active_append(X). -active_append_bin(X) -> ?LIB_MOD:active_append_bin(X). -active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X). -active_recv(X) -> ?LIB_MOD:active_recv(X). -active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X). -active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X). -active_type(X) -> ?LIB_MOD:active_type(X). -active_quote(X) -> ?LIB_MOD:active_quote(X). -active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X). -progress_report_send(X) -> ?LIB_MOD:progress_report_send(X). -progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X). - -fin(Config) -> - ?LIB_MOD:ftpd_fin(Config). diff --git a/lib/inets/test/old_ftp_SUITE.erl b/lib/inets/test/old_ftp_SUITE.erl deleted file mode 100644 index 91bcae3053..0000000000 --- a/lib/inets/test/old_ftp_SUITE.erl +++ /dev/null @@ -1,127 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2013. 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. -%% -%% %CopyrightEnd% -%% -%% - --module(old_ftp_SUITE). - --include_lib("common_test/include/ct.hrl"). --include("test_server_line.hrl"). - -%% Test server specific exports --export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]). -% -export([init_per_testcase/2, end_per_testcase/2]). --export([init_per_suite/1, end_per_suite/1]). - --define(FTP_USER, "anonymous"). --define(FTP_PASS, passwd()). --define(FTP_PORT, 21). - --define(BAD_HOST, "badhostname"). --define(BAD_USER, "baduser"). --define(BAD_DIR, "baddirectory"). - --ifdef(ftp_debug_client). --define(ftp_open(Host, Flags), do_ftp_open(Host, [debug] ++ Flags)). --else. --ifdef(ftp_trace_client). --define(ftp_open(Host, Flags), do_ftp_open(Host, [trace] ++ Flags)). --else. --define(ftp_open(Host, Flags), do_ftp_open(Host, [verbose] ++ Flags)). --endif. --endif. - - -%%-------------------------------------------------------------------- -%% all(Arg) -> [Doc] | [Case] | {skip, Comment} -%% Arg - doc | suite -%% Doc - string() -%% Case - atom() -%% Name of a test case function. -%% Comment - string() -%% Description: Returns documentation/test cases in this test suite -%% or a skip tuple if the platform is not supported. -%%-------------------------------------------------------------------- -suite() -> [{ct_hooks, [ts_install_cth]}]. - -all() -> - [ - {group, solaris8_test}, - {group, solaris9_test}, - {group, solaris10_test}, - {group, linux_x86_test}, - {group, linux_ppc_test}, - {group, macosx_x86_test}, - {group, macosx_ppc_test}, - {group, openbsd_test}, - {group, freebsd_test}, - {group, netbsd_test}, - {group, windows_xp_test}, - {group, windows_2003_server_test}, - {group, ticket_tests} - ]. - -groups() -> - [ - {solaris8_test, [], [{ftp_solaris8_sparc_test, all}]}, - {solaris9_test, [], [{ftp_solaris9_sparc_test, all}]}, - {solaris10_test, [], [{ftp_solaris10_sparc_test, all}, - {ftp_solaris10_x86_test, all}]}, - {linux_x86_test, [], [{ftp_linux_x86_test, all}]}, - {linux_ppc_test, [], [{ftp_linux_ppc_test, all}]}, - {macosx_x86_test, [], [{ftp_macosx_x86_test, all}]}, - {macosx_ppc_test, [], [{ftp_macosx_ppc_test, all}]}, - {openbsd_test, [], [{ftp_openbsd_x86_test, all}]}, - {freebsd_test, [], [{ftp_freebsd_x86_test, all}]}, - {netbsd_test, [], [{ftp_netbsd_x86_test, all}]}, - {windows_xp_test, [], [{ftp_windows_xp_test, all}]}, - {windows_2003_server_test, [], [{ftp_windows_2003_server_test, all}]}, - {ticket_tests, [], [{ftp_ticket_test, all}]} - ]. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - - - -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initiation before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - inets:start(), - Config. - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(_Config) -> - inets:stop(), - ok. diff --git a/lib/inets/test/old_ftp_SUITE_data/ftpd_hosts.skel b/lib/inets/test/old_ftp_SUITE_data/ftpd_hosts.skel deleted file mode 100644 index 98dff81881..0000000000 --- a/lib/inets/test/old_ftp_SUITE_data/ftpd_hosts.skel +++ /dev/null @@ -1,18 +0,0 @@ -%% Add a host name in the appropriate list -%% Each "platform" contains a list of hostnames (a string) that can -%% be used for testing the ftp client. -%% The definition below are an example!! -[{solaris8_sparc, ["solaris8_sparc_dummy1", "solaris8_sparc_dummy2"]}, - {solaris9_sparc, ["solaris9_sparc_dummy1"]}, - {solaris10_sparc, ["solaris10_sparc_dummy1"]}, - {solaris10_x86, ["solaris10_x86_dummy1", "solaris10_x86_dummy2"]}, - {linux_x86, ["linux_x86_dummy1", "linux_x86_dummy2"]}, - {linux_ppc, ["linux_ppc_dummy1"]}, - {macosx_ppc, ["macosx_ppc_dummy1"]}, - {macosx_x86, ["macosx_x86_dummy1", "macosx_x86_dummy2"]}, - {openbsd_x86, []}, - {freebsd_x86, ["freebsd_x86_dummy1"]}, - {netbsd_x86, []}, - {windows_xp, []}, - {windows_2003_server, ["win2003_dummy1"]}, - {ticket_test, ["solaris8_x86_dummy1", "linux_x86_dummy1"]}]. -- cgit v1.2.3 From 962184a8a808a71dc557ee79c5eb43319f5d9a40 Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Tue, 19 Nov 2013 11:40:56 +0100 Subject: ftp: Add documentation. --- lib/inets/doc/src/ftp.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/inets/doc/src/ftp.xml b/lib/inets/doc/src/ftp.xml index ac14d3f274..c6e37dc539 100644 --- a/lib/inets/doc/src/ftp.xml +++ b/lib/inets/doc/src/ftp.xml @@ -569,7 +569,7 @@ (without the inets service framework) and open a session with the FTP server at Host.

-

If the option {tls, tls_options()} is present, the ftp session will be transported over tls (ftps, see RFC 4217). The list tls_options() may be empty. The function ssl:connect/3 is used for establishing both the control connection and the data sessions. +

If the option {tls, tls_options()} is present, the ftp session will be transported over tls (ftps, see RFC 4217). The list tls_options() may be empty. The function ssl:connect/3 is used for securing both the control connection and the data sessions.

A session opened in this way, is closed using the -- cgit v1.2.3 From 08e0bce6d028e5315c7f6d3b857fd9246dd8c9f0 Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Tue, 19 Nov 2013 12:09:05 +0100 Subject: ftp: Linking rfc-refs. --- lib/inets/doc/src/ftp.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/inets/doc/src/ftp.xml b/lib/inets/doc/src/ftp.xml index c6e37dc539..b081fabc91 100644 --- a/lib/inets/doc/src/ftp.xml +++ b/lib/inets/doc/src/ftp.xml @@ -569,7 +569,8 @@ (without the inets service framework) and open a session with the FTP server at Host.

-

If the option {tls, tls_options()} is present, the ftp session will be transported over tls (ftps, see RFC 4217). The list tls_options() may be empty. The function ssl:connect/3 is used for securing both the control connection and the data sessions. +

If the option {tls, tls_options()} is present, the ftp session will be transported over tls (ftps, see +RFC 4217). The list tls_options() may be empty. The function ssl:connect/3 is used for securing both the control connection and the data sessions.

A session opened in this way, is closed using the @@ -817,8 +818,7 @@

Sets the file transfer type to ascii or binary. When an ftp session is opened, the default transfer type of the server is used, most often ascii, which is the default - according to RFC 959.

- + according to RFC 959.

@@ -945,7 +945,7 @@
SEE ALSO

file, filename, J. Postel and J. Reynolds: File Transfer Protocol - (RFC 959). + (RFC 959).

-- cgit v1.2.3 From 027dbfa4d9ed43f4de893803d9d7b45aecfd47f1 Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Tue, 19 Nov 2013 14:29:20 +0100 Subject: ftp: Clean Makefile and conf file --- lib/inets/test/Makefile | 16 ---------------- lib/inets/test/ftp_SUITE_data/vsftpd.conf | 12 ++++++++---- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile index e51f32d8fa..5c65fb3578 100644 --- a/lib/inets/test/Makefile +++ b/lib/inets/test/Makefile @@ -150,24 +150,8 @@ INETS_ROOT = ../../inets MODULES = \ inets_test_lib \ erl_make_certs \ - old_ftp_SUITE \ ftp_SUITE \ ftp_format_SUITE \ - ftp_solaris8_sparc_test \ - ftp_solaris9_sparc_test \ - ftp_solaris10_sparc_test \ - ftp_solaris10_x86_test \ - ftp_linux_x86_test \ - ftp_linux_ppc_test \ - ftp_macosx_x86_test \ - ftp_macosx_ppc_test \ - ftp_openbsd_x86_test \ - ftp_freebsd_x86_test \ - ftp_netbsd_x86_test \ - ftp_windows_xp_test \ - ftp_windows_2003_server_test \ - ftp_suite_lib \ - ftp_ticket_test \ http_format_SUITE \ httpc_SUITE \ httpc_cookie_SUITE \ diff --git a/lib/inets/test/ftp_SUITE_data/vsftpd.conf b/lib/inets/test/ftp_SUITE_data/vsftpd.conf index 4133f8f3b1..11c96ad016 100644 --- a/lib/inets/test/ftp_SUITE_data/vsftpd.conf +++ b/lib/inets/test/ftp_SUITE_data/vsftpd.conf @@ -1,11 +1,18 @@ +### +### Some parameters are given in the vsftpd start command. +### +### Typical command-line paramters are such that has a file path +### component like cert files. +### + + listen=YES listen_port=9999 run_as_launching_user=YES ssl_enable=YES allow_anon_ssl=YES -#background=NO background=YES write_enable=YES @@ -15,8 +22,5 @@ anon_mkdir_write_enable=YES anon_other_write_enable=YES anon_world_readable_only=NO -#rsa_cert_file=cert.pem -#rsa_private_key_file exists, not rsa_key_file=key.pem - ### Shouldn't be necessary.... require_ssl_reuse=NO -- cgit v1.2.3 From ddedcdff62a8710a7963b367564240b8e7bac68c Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Tue, 19 Nov 2013 14:30:12 +0100 Subject: ftp: Adds dynamic cert generation to tests. --- lib/inets/test/ftp_SUITE.erl | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl index a50d2bf05b..298e15a853 100644 --- a/lib/inets/test/ftp_SUITE.erl +++ b/lib/inets/test/ftp_SUITE.erl @@ -118,12 +118,13 @@ ftp_tests()-> fun(__CONF__) -> DataDir = ?config(data_dir,__CONF__), ConfFile = filename:join(DataDir, "vsftpd.conf"), - AnonRoot = ?config(priv_dir,__CONF__), + PrivDir = ?config(priv_dir,__CONF__), + AnonRoot = PrivDir, Cmd = ["vsftpd "++filename:join(DataDir,"vsftpd.conf"), " -oftpd_banner=erlang_otp_testing", " -oanon_root=\"",AnonRoot,"\"", - " -orsa_cert_file=\"",filename:join(DataDir,"cert.pem"),"\"", - " -orsa_private_key_file=\"",filename:join(DataDir,"key.pem"),"\"" + " -orsa_cert_file=\"",filename:join(DataDir,"server-cert.pem"),"\"", + " -orsa_private_key_file=\"",filename:join(DataDir,"server-key.pem"),"\"" ], Result = os:cmd(Cmd), ct:log("Config file:~n~s~n~nServer start command:~n ~s~nResult:~n ~p", @@ -161,6 +162,7 @@ init_per_suite(Config) -> {ok,Data} -> TstDir = filename:join(?config(priv_dir,Config), "test"), file:make_dir(TstDir), + make_cert_files(dsa, rsa, "server-", ?config(data_dir,Config)), start_ftpd([{test_dir,TstDir}, {ftpd_data,Data} | Config]) @@ -505,7 +507,7 @@ recv_chunk(Config0) -> Pid = ?config(ftp, Config), {{error, "ftp:recv_chunk_start/2 not called"},_} = recv_chunk(Pid, <<>>), ok = ftp:recv_chunk_start(Pid, id2ftp(File,Config)), - {ok, ReceivedContents, Nchunks} = recv_chunk(Pid, <<>>), + {ok, ReceivedContents, _Ncunks} = recv_chunk(Pid, <<>>), find_diff(ReceivedContents, Contents). recv_chunk(Pid, Acc) -> recv_chunk(Pid, Acc, 0). @@ -584,6 +586,22 @@ ip_v6_disabled(_Config) -> %% Internal functions ----------------------------------------------- %%-------------------------------------------------------------------- +make_cert_files(Alg1, Alg2, Prefix, Dir) -> + CaInfo = {CaCert,_} = erl_make_certs:make_cert([{key,Alg1}]), + {Cert,CertKey} = erl_make_certs:make_cert([{key,Alg2},{issuer,CaInfo}]), + CaCertFile = filename:join(Dir, Prefix++"cacerts.pem"), + CertFile = filename:join(Dir, Prefix++"cert.pem"), + KeyFile = filename:join(Dir, Prefix++"key.pem"), + der_to_pem(CaCertFile, [{'Certificate', CaCert, not_encrypted}]), + der_to_pem(CertFile, [{'Certificate', Cert, not_encrypted}]), + der_to_pem(KeyFile, [CertKey]), + ok. + +der_to_pem(File, Entries) -> + PemBin = public_key:pem_encode(Entries), + file:write_file(File, PemBin). + +%%-------------------------------------------------------------------- chk_file(Path=[C|_], ExpectedContents, Config) when 0 chk_file([Path], ExpectedContents, Config); @@ -679,7 +697,7 @@ not_available({Name,_StartCmd,_ChkUp,_StopCommand,_ConfigUpd,_Host,_Port}) -> %% start/stop of ftpd %% start_ftpd(Config) -> - {Name,StartCmd,ChkUp,_StopCommand,ConfigRewrite,Host,Port} = ?config(ftpd_data, Config), + {Name,StartCmd,_ChkUp,_StopCommand,ConfigRewrite,Host,Port} = ?config(ftpd_data, Config), case StartCmd(Config) of {ok,StartResult} -> [{ftpd_host,Host}, -- cgit v1.2.3 From 60e0f4cce2f9caeb60ae44cbe18b3eb3ca75a5a8 Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Tue, 19 Nov 2013 16:05:24 +0100 Subject: ftp,ssl: Fixes broken type link (ssloption). --- lib/inets/doc/src/ftp.xml | 2 +- lib/ssl/doc/src/ssl.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/inets/doc/src/ftp.xml b/lib/inets/doc/src/ftp.xml index b081fabc91..20698d8dbc 100644 --- a/lib/inets/doc/src/ftp.xml +++ b/lib/inets/doc/src/ftp.xml @@ -554,7 +554,7 @@ ipfamily() = inet | inet6 | inet6fb4 (defaults to inet) port() = integer() > 0 (defaults to 21) mode() = active | passive (defaults to passive) - tls_options() = [ssl:ssloption()] + tls_options() = [ssl:ssloption()] timeout() = integer() > 0 (defaults to 60000 milliseconds) dtimeout() = integer() > 0 | infinity (defaults to infinity) pogress() = ignore | {module(), function(), initial_data()} (defaults to ignore) diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 5098d26a3a..f2c1062102 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -70,7 +70,7 @@ gen_tcp(3).

-

ssloption() = {verify, verify_type()} | +

ssloption() = {verify, verify_type()} | {verify_fun, {fun(), term()}} | {fail_if_no_peer_cert, boolean()} {depth, integer()} | -- cgit v1.2.3 From 58889fea10db8188fff663a89cd7660bdbe855fe Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Mon, 2 Dec 2013 14:41:57 +0100 Subject: ftp: fix sockname dialyzer warning including ftp:sockname/1 bug --- lib/inets/src/ftp/ftp.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index fe92001a6f..6197838d92 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -2068,7 +2068,7 @@ setup_data_connection(#state{mode = active, {ok, LSock} = gen_tcp:listen(0, [{ip, IP}, {active, false}, inet6, binary, {packet, 0}]), - {ok, {_, Port}} = sockname(LSock), + {ok, {_, Port}} = sockname({tcp,LSock}), IpAddress = inet_parse:ntoa(IP), Cmd = mk_cmd("EPRT |2|~s|~p|", [IpAddress, Port]), send_ctrl_message(State, Cmd), @@ -2351,8 +2351,8 @@ millisec_time() -> peername({tcp, Socket}) -> inet:peername(Socket); peername({ssl, Socket}) -> ssl:peername(Socket). -sockname({tcp, Socket}) -> inet:peername(Socket); -sockname({ssl, Socket}) -> ssl:peername(Socket). +sockname({tcp, Socket}) -> inet:sockname(Socket); +sockname({ssl, Socket}) -> ssl:sockname(Socket). maybe_tls_upgrade(Pid, undefined) -> {ok, Pid}; -- cgit v1.2.3 From 91ec763a0a087516f4bd3de0baf07e326093b93a Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 3 Dec 2013 11:13:48 +0100 Subject: inets: Add crypto start check to ssl test cases --- lib/inets/test/httpd_SUITE.erl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index b84a806305..a1600fa97f 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -222,6 +222,15 @@ init_per_group(ipv6 = _GroupName, Config) -> _ -> {skip, "Host does not support IPv6"} end; +init_per_group(essl, Config) -> + catch crypto:stop(), + case (catch crypto:start()) of + ok -> + Config; + _ -> + {skip, "Crypto not startable"} + end; + init_per_group(_GroupName, Config) -> Config. -- cgit v1.2.3 From 56dcfd6610feea7a222c7373821ef7843905789b Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Mon, 2 Dec 2013 16:38:40 +0100 Subject: inets: Restore ftp test files for the inets_{,sup_}SUITE to not fail --- lib/inets/test/Makefile | 1 + lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel | 18 + lib/inets/test/ftp_suite_lib.erl | 1675 +++++++++++++++++++++++++ 3 files changed, 1694 insertions(+) create mode 100644 lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel create mode 100644 lib/inets/test/ftp_suite_lib.erl diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile index 5c65fb3578..b353687cff 100644 --- a/lib/inets/test/Makefile +++ b/lib/inets/test/Makefile @@ -151,6 +151,7 @@ MODULES = \ inets_test_lib \ erl_make_certs \ ftp_SUITE \ + ftp_suite_lib \ ftp_format_SUITE \ http_format_SUITE \ httpc_SUITE \ diff --git a/lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel b/lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel new file mode 100644 index 0000000000..98dff81881 --- /dev/null +++ b/lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel @@ -0,0 +1,18 @@ +%% Add a host name in the appropriate list +%% Each "platform" contains a list of hostnames (a string) that can +%% be used for testing the ftp client. +%% The definition below are an example!! +[{solaris8_sparc, ["solaris8_sparc_dummy1", "solaris8_sparc_dummy2"]}, + {solaris9_sparc, ["solaris9_sparc_dummy1"]}, + {solaris10_sparc, ["solaris10_sparc_dummy1"]}, + {solaris10_x86, ["solaris10_x86_dummy1", "solaris10_x86_dummy2"]}, + {linux_x86, ["linux_x86_dummy1", "linux_x86_dummy2"]}, + {linux_ppc, ["linux_ppc_dummy1"]}, + {macosx_ppc, ["macosx_ppc_dummy1"]}, + {macosx_x86, ["macosx_x86_dummy1", "macosx_x86_dummy2"]}, + {openbsd_x86, []}, + {freebsd_x86, ["freebsd_x86_dummy1"]}, + {netbsd_x86, []}, + {windows_xp, []}, + {windows_2003_server, ["win2003_dummy1"]}, + {ticket_test, ["solaris8_x86_dummy1", "linux_x86_dummy1"]}]. diff --git a/lib/inets/test/ftp_suite_lib.erl b/lib/inets/test/ftp_suite_lib.erl new file mode 100644 index 0000000000..172270e3f1 --- /dev/null +++ b/lib/inets/test/ftp_suite_lib.erl @@ -0,0 +1,1675 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2013. 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. +%% +%% %CopyrightEnd% +%% +%% + +-module(ftp_suite_lib). + + +-include_lib("test_server/include/test_server.hrl"). +-include_lib("test_server/include/test_server_line.hrl"). +-include("inets_test_lib.hrl"). + +%% Test server specific exports +% -export([init_per_testcase/2, end_per_testcase/2]). + +-compile(export_all). + + +-record(progress, { + current = 0, + total + }). + + + +-define(FTP_USER, "anonymous"). +-define(FTP_PASS, passwd()). +-define(FTP_PORT, 21). + +-define(BAD_HOST, "badhostname"). +-define(BAD_USER, "baduser"). +-define(BAD_DIR, "baddirectory"). + +-ifdef(ftp_debug_client). +-define(ftp_open(Host, Flags), + do_ftp_open(Host, [{debug, debug}, + {timeout, timer:seconds(15)} | Flags])). +-else. +-ifdef(ftp_trace_client). +-define(ftp_open(Host, Flags), + do_ftp_open(Host, [{debug, trace}, + {timeout, timer:seconds(15)} | Flags])). +-else. +-define(ftp_open(Host, Flags), + do_ftp_open(Host, [{verbose, true}, + {timeout, timer:seconds(15)} | Flags])). +-endif. +-endif. + +%% -- Tickets -- + +tickets(doc) -> + "Test cases for reported bugs"; +tickets(suite) -> + [ticket_6035]. + +%% -- + +ftpd_init(FtpdTag, Config) -> + %% Get the host name(s) of FTP server + Hosts = + case ct:get_config(ftpd_hosts) of + undefined -> + ftpd_hosts(data_dir(Config)); + H -> + H + end, + p("ftpd_init -> " + "~n Hosts: ~p" + "~n Config: ~p" + "~n FtpdTag: ~p", [Hosts, Config, FtpdTag]), + %% Get the first host that actually have a running FTP server + case lists:keysearch(FtpdTag, 1, Hosts) of + {value, {_, TagHosts}} when is_list(TagHosts) -> + inets:start(), + case (catch get_ftpd_host(TagHosts)) of + {ok, Host} -> + inets:stop(), + [{ftp_remote_host, Host}|Config]; + _ -> + inets:stop(), + Reason = lists:flatten( + io_lib:format("Could not find a valid " + "FTP server for ~p (~p)", + [FtpdTag, TagHosts])), + {skip, Reason} + end; + _ -> + Reason = lists:flatten( + io_lib:format("No host(s) running FTPD server " + "for ~p", [FtpdTag])), + {skip, Reason} + end. + +ftpd_fin(Config) -> + lists:keydelete(ftp_remote_host, 1, Config). + +get_ftpd_host([]) -> + {error, no_host}; +get_ftpd_host([Host|Hosts]) -> + p("get_ftpd_host -> entry with" + "~n Host: ~p" + "~n", [Host]), + case (catch ftp:open(Host, [{port, ?FTP_PORT}, {timeout, 20000}])) of + {ok, Pid} -> + (catch ftp:close(Pid)), + {ok, Host}; + _ -> + get_ftpd_host(Hosts) + end. + + +%%-------------------------------------------------------------------- + +dirty_select_ftpd_host(Config) -> + Hosts = + case ct:get_config(ftpd_hosts) of + undefined -> + ftpd_hosts(data_dir(Config)); + H -> + H + end, + dirty_select_ftpd_host2(Hosts). + +dirty_select_ftpd_host2([]) -> + throw({error, not_found}); +dirty_select_ftpd_host2([{PlatformTag, Hosts} | PlatformHosts]) -> + case dirty_select_ftpd_host3(Hosts) of + none -> + dirty_select_ftpd_host2(PlatformHosts); + {ok, Host} -> + {PlatformTag, Host} + end. + +dirty_select_ftpd_host3([]) -> + none; +dirty_select_ftpd_host3([Host|Hosts]) when is_list(Host) -> + case dirty_select_ftpd_host4(Host) of + true -> + {ok, Host}; + false -> + dirty_select_ftpd_host3(Hosts) + end; +dirty_select_ftpd_host3([_|Hosts]) -> + dirty_select_ftpd_host3(Hosts). + +%% This is a very simple and dirty test that there is a +%% (FTP) deamon on the other end. +dirty_select_ftpd_host4(Host) -> + Port = 21, + IpFam = inet, + Opts = [IpFam, binary, {packet, 0}, {active, false}], + Timeout = ?SECS(5), + case gen_tcp:connect(Host, Port, Opts, Timeout) of + {ok, Sock} -> + gen_tcp:close(Sock), + true; + _Error -> + false + end. + + +%%-------------------------------------------------------------------- + +test_filenames() -> + {ok, Host} = inet:gethostname(), + File = Host ++ "_ftp_test.txt", + NewFile = "new_" ++ File, + {File, NewFile}. + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(Case, Config) -> Config +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Initiation before each test case +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_testcase(Case, Config) + when (Case =:= open) orelse + (Case =:= open_port) -> + put(ftp_testcase, Case), + io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]), + inets:start(), + NewConfig = data_dir(Config), + watch_dog(NewConfig); + +init_per_testcase(Case, Config) -> + put(ftp_testcase, Case), + do_init_per_testcase(Case, Config). + +do_init_per_testcase(Case, Config) + when (Case =:= passive_user) -> + io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE,Case]), + inets:start(), + NewConfig = close_connection(watch_dog(Config)), + Host = ftp_host(Config), + case (catch ?ftp_open(Host, [{mode, passive}])) of + {ok, Pid} -> + [{ftp, Pid} | data_dir(NewConfig)]; + {skip, _} = SKIP -> + SKIP + end; + +do_init_per_testcase(Case, Config) + when (Case =:= active_user) -> + io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]), + inets:start(), + NewConfig = close_connection(watch_dog(Config)), + Host = ftp_host(Config), + case (catch ?ftp_open(Host, [{mode, active}])) of + {ok, Pid} -> + [{ftp, Pid} | data_dir(NewConfig)]; + {skip, _} = SKIP -> + SKIP + end; + +do_init_per_testcase(Case, Config) + when (Case =:= progress_report_send) orelse + (Case =:= progress_report_recv) -> + inets:start(), + io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]), + NewConfig = close_connection(watch_dog(Config)), + Host = ftp_host(Config), + Opts = [{port, ?FTP_PORT}, + {verbose, true}, + {progress, {?MODULE, progress, #progress{}}}], + case ftp:open(Host, Opts) of + {ok, Pid} -> + ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), + [{ftp, Pid} | data_dir(NewConfig)]; + {skip, _} = SKIP -> + SKIP + end; + +do_init_per_testcase(Case, Config) -> + io:format(user,"~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]), + inets:start(), + NewConfig = close_connection(watch_dog(Config)), + Host = ftp_host(Config), + Opts1 = + if + ((Case =:= passive_ip_v6_disabled) orelse + (Case =:= active_ip_v6_disabled)) -> + [{ipfamily, inet}]; + true -> + [] + end, + Opts2 = + case string:tokens(atom_to_list(Case), [$_]) of + ["active" | _] -> + [{mode, active} | Opts1]; + _ -> + [{mode, passive} | Opts1] + end, + case (catch ?ftp_open(Host, Opts2)) of + {ok, Pid} -> + ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), + [{ftp, Pid} | data_dir(NewConfig)]; + {skip, _} = SKIP -> + SKIP + end. + + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(Case, Config) -> _ +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after each test case +%%-------------------------------------------------------------------- +end_per_testcase(_, Config) -> + NewConfig = close_connection(Config), + Dog = ?config(watchdog, NewConfig), + inets:stop(), + test_server:timetrap_cancel(Dog), + ok. + + +%%------------------------------------------------------------------------- +%% Suites similar for all hosts. +%%------------------------------------------------------------------------- + +passive(suite) -> + [ + passive_user, + passive_pwd, + passive_cd, + passive_lcd, + passive_ls, + passive_nlist, + passive_rename, + passive_delete, + passive_mkdir, + passive_send, + passive_send_bin, + passive_send_chunk, + passive_append, + passive_append_bin, + passive_append_chunk, + passive_recv, + passive_recv_bin, + passive_recv_chunk, + passive_type, + passive_quote, + passive_ip_v6_disabled + ]. + +active(suite) -> + [ + active_user, + active_pwd, + active_cd, + active_lcd, + active_ls, + active_nlist, + active_rename, + active_delete, + active_mkdir, + active_send, + active_send_bin, + active_send_chunk, + active_append, + active_append_bin, + active_append_chunk, + active_recv, + active_recv_bin, + active_recv_chunk, + active_type, + active_quote, + active_ip_v6_disabled + ]. + + + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- + +open(doc) -> + ["Open an ftp connection to a host and close the connection." + "Also check that !-messages does not disturbe the connection"]; +open(suite) -> + []; +open(Config) when is_list(Config) -> + Host = ftp_host(Config), + (catch tc_open(Host)). + + +tc_open(Host) -> + p("tc_open -> entry with" + "~n Host: ~p", [Host]), + {ok, Pid} = ?ftp_open(Host, []), + ok = ftp:close(Pid), + p("tc_open -> try (ok) open 1"), + {ok, Pid1} = + ftp:open({option_list, [{host,Host}, + {port, ?FTP_PORT}, + {flags, [verbose]}, + {timeout, 30000}]}), + ok = ftp:close(Pid1), + + p("tc_open -> try (fail) open 2"), + {error, ehost} = + ftp:open({option_list, [{port, ?FTP_PORT}, {flags, [verbose]}]}), + {ok, Pid2} = ftp:open(Host), + ok = ftp:close(Pid2), + + p("tc_open -> try (ok) open 3"), + {ok, NewHost} = inet:getaddr(Host, inet), + {ok, Pid3} = ftp:open(NewHost), + ftp:user(Pid3, ?FTP_USER, ?FTP_PASS), + Pid3 ! foobar, + test_server:sleep(5000), + {message_queue_len, 0} = process_info(self(), message_queue_len), + ["200" ++ _] = ftp:quote(Pid3, "NOOP"), + ok = ftp:close(Pid3), + + %% Bad input that has default values are ignored and the defult + %% is used. + p("tc_open -> try (ok) open 4"), + {ok, Pid4} = + ftp:open({option_list, [{host, Host}, + {port, badarg}, + {flags, [verbose]}, + {timeout, 30000}]}), + test_server:sleep(100), + ok = ftp:close(Pid4), + + p("tc_open -> try (ok) open 5"), + {ok, Pid5} = + ftp:open({option_list, [{host, Host}, + {port, ?FTP_PORT}, + {flags, [verbose]}, + {timeout, -42}]}), + test_server:sleep(100), + ok = ftp:close(Pid5), + + p("tc_open -> try (ok) open 6"), + {ok, Pid6} = + ftp:open({option_list, [{host, Host}, + {port, ?FTP_PORT}, + {flags, [verbose]}, + {mode, cool}]}), + test_server:sleep(100), + ok = ftp:close(Pid6), + + p("tc_open -> try (ok) open 7"), + {ok, Pid7} = + ftp:open(Host, [{port, ?FTP_PORT}, {verbose, true}, {timeout, 30000}]), + ok = ftp:close(Pid7), + + p("tc_open -> try (ok) open 8"), + {ok, Pid8} = + ftp:open(Host, ?FTP_PORT), + ok = ftp:close(Pid8), + + p("tc_open -> try (ok) open 9"), + {ok, Pid9} = + ftp:open(Host, [{port, ?FTP_PORT}, + {verbose, true}, + {timeout, 30000}, + {dtimeout, -99}]), + ok = ftp:close(Pid9), + + p("tc_open -> try (ok) open 10"), + {ok, Pid10} = + ftp:open(Host, [{port, ?FTP_PORT}, + {verbose, true}, + {timeout, 30000}, + {dtimeout, "foobar"}]), + ok = ftp:close(Pid10), + + p("tc_open -> try (ok) open 11"), + {ok, Pid11} = + ftp:open(Host, [{port, ?FTP_PORT}, + {verbose, true}, + {timeout, 30000}, + {dtimeout, 1}]), + ok = ftp:close(Pid11), + + p("tc_open -> done"), + ok. + + +%%------------------------------------------------------------------------- + +open_port(doc) -> + ["Open an ftp connection to a host with given port number " + "and close the connection."]; % See also OTP-3892 +open_port(suite) -> + []; +open_port(Config) when is_list(Config) -> + Host = ftp_host(Config), + {ok, Pid} = ftp:open(Host, [{port, ?FTP_PORT}]), + ok = ftp:close(Pid), + {error, ehost} = ftp:open(?BAD_HOST, []), + ok. + + +%%------------------------------------------------------------------------- + +passive_user(doc) -> + ["Open an ftp connection to a host, and logon as anonymous ftp."]; +passive_user(suite) -> + []; +passive_user(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + p("Pid: ~p",[Pid]), + do_user(Pid). + + +%%------------------------------------------------------------------------- + +passive_pwd(doc) -> + ["Test ftp:pwd/1 & ftp:lpwd/1"]; +passive_pwd(suite) -> + []; +passive_pwd(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_pwd(Pid). + + +%%------------------------------------------------------------------------- + +passive_cd(doc) -> + ["Open an ftp connection, log on as anonymous ftp, and cd to the" + "directory \"/pub\" and the to the non-existent directory."]; +passive_cd(suite) -> + []; +passive_cd(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_cd(Pid). + + +%%------------------------------------------------------------------------- + +passive_lcd(doc) -> + ["Test api function ftp:lcd/2"]; +passive_lcd(suite) -> + []; +passive_lcd(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + PrivDir = ?config(priv_dir, Config), + do_lcd(Pid, PrivDir). + + +%%------------------------------------------------------------------------- + +passive_ls(doc) -> + ["Open an ftp connection; ls the current directory, and the " + "\"incoming\" directory. We assume that ls never fails, since " + "it's output is meant to be read by humans. "]; +passive_ls(suite) -> + []; +passive_ls(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_ls(Pid). + + +%%------------------------------------------------------------------------- + +passive_nlist(doc) -> + ["Open an ftp connection; nlist the current directory, and the " + "\"incoming\" directory. Nlist does not behave consistenly over " + "operating systems. On some it is an error to have an empty " + "directory."]; +passive_nlist(suite) -> + []; +passive_nlist(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + WildcardSupport = ?config(wildcard_support, Config), + do_nlist(Pid, WildcardSupport). + + +%%------------------------------------------------------------------------- + +passive_rename(doc) -> + ["Transfer a file to the server, and rename it; then remove it."]; +passive_rename(suite) -> + []; +passive_rename(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_rename(Pid, Config). + + +%%------------------------------------------------------------------------- + +passive_delete(doc) -> + ["Transfer a file to the server, and then delete it"]; +passive_delete(suite) -> + []; +passive_delete(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_delete(Pid, Config). + + +%%------------------------------------------------------------------------- + +passive_mkdir(doc) -> + ["Make a remote directory, cd to it, go to parent directory, and " + "remove the directory."]; +passive_mkdir(suite) -> + []; +passive_mkdir(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_mkdir(Pid). + + +%%------------------------------------------------------------------------- + +passive_send(doc) -> + ["Create a local file in priv_dir; open an ftp connection to a host; " + "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to " + "priv_dir; send the file; get a directory listing and check that " + "the file is on the list;, delete the remote file; get another listing " + "and check that the file is not on the list; close the session; " + "delete the local file."]; +passive_send(suite) -> + []; +passive_send(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_send(Pid, Config). + + +%%------------------------------------------------------------------------- + +passive_append(doc) -> + ["Create a local file in priv_dir; open an ftp connection to a host; " + "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to " + "priv_dir; append the file to a file at the remote side that not exits" + "this will create the file at the remote side. Then it append the file " + "again. When this is done it recive the remote file and control that" + "the content is doubled in it.After that it will remove the files"]; +passive_append(suite) -> + []; +passive_append(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_append(Pid, Config). + + +%%------------------------------------------------------------------------- + +passive_send_bin(doc) -> + ["Open a connection to a host; cd to the directory \"incoming\"; " + "send a binary; remove file; close the connection."]; +passive_send_bin(suite) -> + []; +passive_send_bin(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_send_bin(Pid, Config). + +%%------------------------------------------------------------------------- + +passive_append_bin(doc) -> + ["Open a connection to a host; cd to the directory \"incoming\"; " + "append a binary twice; get the file and compare the content" + "remove file; close the connection."]; +passive_append_bin(suite) -> + []; +passive_append_bin(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_append_bin(Pid, Config). + + +%%------------------------------------------------------------------------- + +passive_send_chunk(doc) -> + ["Open a connection to a host; cd to the directory \"incoming\"; " + "send chunks; remove file; close the connection."]; +passive_send_chunk(suite) -> + []; +passive_send_chunk(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_send_chunk(Pid, Config). + + +%%------------------------------------------------------------------------- + +passive_append_chunk(doc) -> + ["Open a connection to a host; cd to the directory \"incoming\"; " + "append chunks;control content remove file; close the connection."]; +passive_append_chunk(suite) -> + []; +passive_append_chunk(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_append_chunk(Pid, Config). + + +%%------------------------------------------------------------------------- + +passive_recv(doc) -> + ["Create a local file and transfer it to the remote host into the " + "the \"incoming\" directory, remove " + "the local file. Then open a new connection; cd to \"incoming\", " + "lcd to the private directory; receive the file; delete the " + "remote file; close connection; check that received file is in " + "the correct directory; cleanup." ]; +passive_recv(suite) -> + []; +passive_recv(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_recv(Pid, Config). + + +%%------------------------------------------------------------------------- + +passive_recv_bin(doc) -> + ["Send a binary to the remote host; and retreive " + "the file; then remove the file."]; +passive_recv_bin(suite) -> + []; +passive_recv_bin(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_recv_bin(Pid, Config). + + +%%------------------------------------------------------------------------- + +passive_recv_chunk(doc) -> + ["Send a binary to the remote host; Connect again, and retreive " + "the file; then remove the file."]; +passive_recv_chunk(suite) -> + []; +passive_recv_chunk(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_recv_chunk(Pid, Config). + + +%%------------------------------------------------------------------------- + +passive_type(doc) -> + ["Test that we can change btween ASCCI and binary transfer mode"]; +passive_type(suite) -> + []; +passive_type(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_type(Pid). + + +%%------------------------------------------------------------------------- + +passive_quote(doc) -> + [""]; +passive_quote(suite) -> + []; +passive_quote(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_quote(Pid). + + +%%------------------------------------------------------------------------- + +passive_ip_v6_disabled(doc) -> + ["Test ipv4 command PASV"]; +passive_ip_v6_disabled(suite) -> + []; +passive_ip_v6_disabled(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_send(Pid, Config). + + +%%------------------------------------------------------------------------- + +active_user(doc) -> + ["Open an ftp connection to a host, and logon as anonymous ftp."]; +active_user(suite) -> + []; +active_user(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_user(Pid). + + +%%------------------------------------------------------------------------- + +active_pwd(doc) -> + ["Test ftp:pwd/1 & ftp:lpwd/1"]; +active_pwd(suite) -> + []; +active_pwd(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_pwd(Pid). + + +%%------------------------------------------------------------------------- + +active_cd(doc) -> + ["Open an ftp connection, log on as anonymous ftp, and cd to the" + "directory \"/pub\" and to a non-existent directory."]; +active_cd(suite) -> + []; +active_cd(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_cd(Pid). + + +%%------------------------------------------------------------------------- + +active_lcd(doc) -> + ["Test api function ftp:lcd/2"]; +active_lcd(suite) -> + []; +active_lcd(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + PrivDir = ?config(priv_dir, Config), + do_lcd(Pid, PrivDir). + + +%%------------------------------------------------------------------------- + +active_ls(doc) -> + ["Open an ftp connection; ls the current directory, and the " + "\"incoming\" directory. We assume that ls never fails, since " + "it's output is meant to be read by humans. "]; +active_ls(suite) -> + []; +active_ls(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_ls(Pid). + + +%%------------------------------------------------------------------------- + +active_nlist(doc) -> + ["Open an ftp connection; nlist the current directory, and the " + "\"incoming\" directory. Nlist does not behave consistenly over " + "operating systems. On some it is an error to have an empty " + "directory."]; +active_nlist(suite) -> + []; +active_nlist(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + WildcardSupport = ?config(wildcard_support, Config), + do_nlist(Pid, WildcardSupport). + + +%%------------------------------------------------------------------------- + +active_rename(doc) -> + ["Transfer a file to the server, and rename it; then remove it."]; +active_rename(suite) -> + []; +active_rename(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_rename(Pid, Config). + + +%%------------------------------------------------------------------------- + +active_delete(doc) -> + ["Transfer a file to the server, and then delete it"]; +active_delete(suite) -> + []; +active_delete(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_delete(Pid, Config). + + +%%------------------------------------------------------------------------- + +active_mkdir(doc) -> + ["Make a remote directory, cd to it, go to parent directory, and " + "remove the directory."]; +active_mkdir(suite) -> + []; +active_mkdir(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_mkdir(Pid). + + +%%------------------------------------------------------------------------- + +active_send(doc) -> + ["Create a local file in priv_dir; open an ftp connection to a host; " + "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to " + "priv_dir; send the file; get a directory listing and check that " + "the file is on the list;, delete the remote file; get another listing " + "and check that the file is not on the list; close the session; " + "delete the local file."]; +active_send(suite) -> + []; +active_send(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_send(Pid, Config). + + +%%------------------------------------------------------------------------- + +active_append(doc) -> + ["Create a local file in priv_dir; open an ftp connection to a host; " + "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to " + "priv_dir; append the file to a file at the remote side that not exits" + "this will create the file at the remote side. Then it append the file " + "again. When this is done it recive the remote file and control that" + "the content is doubled in it.After that it will remove the files"]; +active_append(suite) -> + []; +active_append(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_append(Pid, Config). + + +%%------------------------------------------------------------------------- + +active_send_bin(doc) -> + ["Open a connection to a host; cd to the directory \"incoming\"; " + "send a binary; remove file; close the connection."]; +active_send_bin(suite) -> + []; +active_send_bin(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_send_bin(Pid, Config). + + +%%------------------------------------------------------------------------- + +active_append_bin(doc) -> + ["Open a connection to a host; cd to the directory \"incoming\"; " + "append a binary twice; get the file and compare the content" + "remove file; close the connection."]; +active_append_bin(suite) -> + []; +active_append_bin(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_append_bin(Pid, Config). + + +%%------------------------------------------------------------------------- + +active_send_chunk(doc) -> + ["Open a connection to a host; cd to the directory \"incoming\"; " + "send chunks; remove file; close the connection."]; +active_send_chunk(suite) -> + []; +active_send_chunk(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_send_chunk(Pid, Config). + + +%%------------------------------------------------------------------------- + +active_append_chunk(doc) -> + ["Open a connection to a host; cd to the directory \"incoming\"; " + "append chunks;control content remove file; close the connection."]; +active_append_chunk(suite) -> + []; +active_append_chunk(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_append_chunk(Pid, Config). + + +%%------------------------------------------------------------------------- + +active_recv(doc) -> + ["Create a local file and transfer it to the remote host into the " + "the \"incoming\" directory, remove " + "the local file. Then open a new connection; cd to \"incoming\", " + "lcd to the private directory; receive the file; delete the " + "remote file; close connection; check that received file is in " + "the correct directory; cleanup." ]; +active_recv(suite) -> + []; +active_recv(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_recv(Pid, Config). + + +%%------------------------------------------------------------------------- + +active_recv_bin(doc) -> + ["Send a binary to the remote host; and retreive " + "the file; then remove the file."]; +active_recv_bin(suite) -> + []; +active_recv_bin(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_recv_bin(Pid, Config). + + +%%------------------------------------------------------------------------- + +active_recv_chunk(doc) -> + ["Send a binary to the remote host; Connect again, and retreive " + "the file; then remove the file."]; +active_recv_chunk(suite) -> + []; +active_recv_chunk(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_recv_chunk(Pid, Config). + + +%%------------------------------------------------------------------------- + +active_type(doc) -> + ["Test that we can change btween ASCCI and binary transfer mode"]; +active_type(suite) -> + []; +active_type(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_type(Pid). + + +%%------------------------------------------------------------------------- + +active_quote(doc) -> + [""]; +active_quote(suite) -> + []; +active_quote(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_quote(Pid). + + +%%------------------------------------------------------------------------- + +active_ip_v6_disabled(doc) -> + ["Test ipv4 command PORT"]; +active_ip_v6_disabled(suite) -> + []; +active_ip_v6_disabled(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + do_send(Pid, Config). + + +%%------------------------------------------------------------------------- + +api_missuse(doc)-> + ["Test that behaviour of the ftp process if the api is abused"]; +api_missuse(suite) -> []; +api_missuse(Config) when is_list(Config) -> + p("api_missuse -> entry"), + Flag = process_flag(trap_exit, true), + Pid = ?config(ftp, Config), + Host = ftp_host(Config), + + %% Serious programming fault, connetion will be shut down + p("api_missuse -> verify bad call termination (~p)", [Pid]), + case (catch gen_server:call(Pid, {self(), foobar, 10}, infinity)) of + {error, {connection_terminated, 'API_violation'}} -> + ok; + Unexpected1 -> + exit({unexpected_result, Unexpected1}) + end, + test_server:sleep(500), + undefined = process_info(Pid, status), + + p("api_missuse -> start new client"), + {ok, Pid2} = ?ftp_open(Host, []), + %% Serious programming fault, connetion will be shut down + p("api_missuse -> verify bad cast termination"), + gen_server:cast(Pid2, {self(), foobar, 10}), + test_server:sleep(500), + undefined = process_info(Pid2, status), + + p("api_missuse -> start new client"), + {ok, Pid3} = ?ftp_open(Host, []), + %% Could be an innocent misstake the connection lives. + p("api_missuse -> verify bad bang"), + Pid3 ! foobar, + test_server:sleep(500), + {status, _} = process_info(Pid3, status), + process_flag(trap_exit, Flag), + p("api_missuse -> done"), + ok. + + +%%------------------------------------------------------------------------- + +not_owner(doc) -> + ["Test what happens if a process that not owns the connection tries " + "to use it"]; +not_owner(suite) -> + []; +not_owner(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + OtherPid = spawn_link(?MODULE, not_owner, [Pid, self()]), + + receive + {OtherPid, ok} -> + {ok, _} = ftp:pwd(Pid) + end, + ok. + +not_owner(FtpPid, Pid) -> + {error, not_connection_owner} = ftp:pwd(FtpPid), + ftp:close(FtpPid), + test_server:sleep(100), + Pid ! {self(), ok}. + + +%%------------------------------------------------------------------------- + + +progress_report(doc) -> + ["Solaris 8 sparc test the option progress."]; +progress_report(suite) -> + [progress_report_send, progress_report_recv]. + + +%% -- + +progress_report_send(doc) -> + ["Test the option progress for ftp:send/[2,3]"]; +progress_report_send(suite) -> + []; +progress_report_send(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + ReportPid = + spawn_link(?MODULE, progress_report_receiver_init, [self(), 1]), + do_send(Pid, Config), + receive + {ReportPid, ok} -> + ok + end. + + +%% -- + +progress_report_recv(doc) -> + ["Test the option progress for ftp:recv/[2,3]"]; +progress_report_recv(suite) -> + []; +progress_report_recv(Config) when is_list(Config) -> + Pid = ?config(ftp, Config), + ReportPid = + spawn_link(?MODULE, progress_report_receiver_init, [self(), 3]), + do_recv(Pid, Config), + receive + {ReportPid, ok} -> + ok + end, + ok. + +progress(#progress{} = Progress , _File, {file_size, Total}) -> + progress_report_receiver ! start, + Progress#progress{total = Total}; +progress(#progress{total = Total, current = Current} + = Progress, _File, {transfer_size, 0}) -> + progress_report_receiver ! finish, + case Total of + unknown -> + ok; + Current -> + ok; + _ -> + test_server:fail({error, {progress, {total, Total}, + {current, Current}}}) + end, + Progress; +progress(#progress{current = Current} = Progress, _File, + {transfer_size, Size}) -> + progress_report_receiver ! update, + Progress#progress{current = Current + Size}. + +progress_report_receiver_init(Pid, N) -> + register(progress_report_receiver, self()), + receive + start -> + ok + end, + progress_report_receiver_loop(Pid, N-1). + +progress_report_receiver_loop(Pid, N) -> + receive + update -> + progress_report_receiver_loop(Pid, N); + finish when N =:= 0 -> + Pid ! {self(), ok}; + finish -> + Pid ! {self(), ok}, + receive + start -> + ok + end, + progress_report_receiver_loop(Pid, N-1) + end. + + +%%------------------------------------------------------------------------- +%% Ticket test cases +%%------------------------------------------------------------------------- + +ticket_6035(doc) -> ["Test that owning process that exits with reason " + "'shutdown' does not cause an error message."]; +ticket_6035(suite) -> []; +ticket_6035(Config) -> + p("ticket_6035 -> entry with" + "~n Config: ~p", [Config]), + PrivDir = ?config(priv_dir, Config), + LogFile = filename:join([PrivDir,"ticket_6035.log"]), + try + begin + p("ticket_6035 -> select ftpd host"), + Host = dirty_select_ftpd_host(Config), + p("ticket_6035 -> ftpd host selected (~p) => now spawn ftp owner", [Host]), + Pid = spawn(?MODULE, open_wait_6035, [Host, self()]), + p("ticket_6035 -> waiter spawned: ~p => now open error logfile (~p)", + [Pid, LogFile]), + error_logger:logfile({open, LogFile}), + p("ticket_6035 -> error logfile open => now kill waiter process"), + true = kill_ftp_proc_6035(Pid, LogFile), + p("ticket_6035 -> waiter process killed => now close error logfile"), + error_logger:logfile(close), + p("ticket_6035 -> done", []), + ok + end + catch + throw:{error, not_found} -> + {skip, "No available FTP servers"} + end. + +kill_ftp_proc_6035(Pid, LogFile) -> + p("kill_ftp_proc_6035 -> entry"), + receive + open -> + p("kill_ftp_proc_6035 -> received open => now issue shutdown"), + exit(Pid, shutdown), + kill_ftp_proc_6035(Pid, LogFile); + {open_failed, Reason} -> + p("kill_ftp_proc_6035 -> received open_failed" + "~n Reason: ~p", [Reason]), + exit({skip, {failed_openening_server_connection, Reason}}) + after + 5000 -> + p("kill_ftp_proc_6035 -> timeout"), + is_error_report_6035(LogFile) + end. + +open_wait_6035({Tag, FtpServer}, From) -> + p("open_wait_6035 -> try connect to [~p] ~s for ~p", [Tag, FtpServer, From]), + case ftp:open(FtpServer, [{timeout, timer:seconds(15)}]) of + {ok, Pid} -> + p("open_wait_6035 -> connected (~p), now login", [Pid]), + LoginResult = ftp:user(Pid,"anonymous","kldjf"), + p("open_wait_6035 -> login result: ~p", [LoginResult]), + From ! open, + receive + dummy -> + p("open_wait_6035 -> received dummy"), + ok + after + 10000 -> + p("open_wait_6035 -> timeout"), + ok + end, + p("open_wait_6035 -> done(ok)"), + ok; + {error, Reason} -> + p("open_wait_6035 -> open failed" + "~n Reason: ~p", [Reason]), + From ! {open_failed, {Reason, FtpServer}}, + p("open_wait_6035 -> done(error)"), + ok + end. + +is_error_report_6035(LogFile) -> + p("is_error_report_6035 -> entry"), + Res = + case file:read_file(LogFile) of + {ok, Bin} -> + Txt = binary_to_list(Bin), + p("is_error_report_6035 -> logfile read: ~n~p", [Txt]), + read_log_6035(Txt); + _ -> + false + end, + p("is_error_report_6035 -> logfile read result: " + "~n ~p", [Res]), + %% file:delete(LogFile), + Res. + +read_log_6035("=ERROR REPORT===="++_Rest) -> + p("read_log_6035 -> ERROR REPORT detected"), + true; +read_log_6035([H|T]) -> + p("read_log_6035 -> OTHER: " + "~p", [H]), + read_log_6035(T); +read_log_6035([]) -> + p("read_log_6035 -> done"), + false. + + +%%-------------------------------------------------------------------- +%% Internal functions +%%-------------------------------------------------------------------- +do_user(Pid) -> + {error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS), + ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), + ok. + +do_pwd(Pid) -> + {ok, "/"} = ftp:pwd(Pid), + {ok, Path} = ftp:lpwd(Pid), + {ok, Path} = file:get_cwd(), + ok. + +do_cd(Pid) -> + ok = ftp:cd(Pid, "/pub"), + {error, epath} = ftp:cd(Pid, ?BAD_DIR), + ok. + +do_lcd(Pid, Dir) -> + ok = ftp:lcd(Pid, Dir), + {error, epath} = ftp:lcd(Pid, ?BAD_DIR), + ok. + + +do_ls(Pid) -> + {ok, _} = ftp:ls(Pid), + {ok, _} = ftp:ls(Pid, "incoming"), + %% neither nlist nor ls operates on a directory + %% they operate on a pathname, which *can* be a + %% directory, but can also be a filename or a group + %% of files (including wildcards). + {ok, _} = ftp:ls(Pid, "incom*"), + ok. + +do_nlist(Pid, WildcardSupport) -> + {ok, _} = ftp:nlist(Pid), + {ok, _} = ftp:nlist(Pid, "incoming"), + %% neither nlist nor ls operates on a directory + %% they operate on a pathname, which *can* be a + %% directory, but can also be a filename or a group + %% of files (including wildcards). + case WildcardSupport of + true -> + {ok, _} = ftp:nlist(Pid, "incom*"), + ok; + _ -> + ok + end. + +do_rename(Pid, Config) -> + PrivDir = ?config(priv_dir, Config), + LFile = ?config(file, Config), + NewLFile = ?config(new_file, Config), + AbsLFile = filename:absname(LFile, PrivDir), + Contents = "ftp_SUITE test ...", + ok = file:write_file(AbsLFile, list_to_binary(Contents)), + ok = ftp:cd(Pid, "incoming"), + ok = ftp:lcd(Pid, PrivDir), + ftp:delete(Pid, LFile), % reset + ftp:delete(Pid, NewLFile), % reset + ok = ftp:send(Pid, LFile), + {error, epath} = ftp:rename(Pid, NewLFile, LFile), + ok = ftp:rename(Pid, LFile, NewLFile), + ftp:delete(Pid, LFile), % cleanup + ftp:delete(Pid, NewLFile), % cleanup + ok. + +do_delete(Pid, Config) -> + PrivDir = ?config(priv_dir, Config), + LFile = ?config(file, Config), + AbsLFile = filename:absname(LFile, PrivDir), + Contents = "ftp_SUITE test ...", + ok = file:write_file(AbsLFile, list_to_binary(Contents)), + ok = ftp:cd(Pid, "incoming"), + ok = ftp:lcd(Pid, PrivDir), + ftp:delete(Pid,LFile), % reset + ok = ftp:send(Pid, LFile), + ok = ftp:delete(Pid,LFile), + ok. + +do_mkdir(Pid) -> + {A, B, C} = erlang:now(), + NewDir = "nisse_" ++ integer_to_list(A) ++ "_" ++ + integer_to_list(B) ++ "_" ++ integer_to_list(C), + ok = ftp:cd(Pid, "incoming"), + {ok, CurrDir} = ftp:pwd(Pid), + ok = ftp:mkdir(Pid, NewDir), + ok = ftp:cd(Pid, NewDir), + ok = ftp:cd(Pid, CurrDir), + ok = ftp:rmdir(Pid, NewDir), + ok. + +do_send(Pid, Config) -> + PrivDir = ?config(priv_dir, Config), + LFile = ?config(file, Config), + RFile = LFile ++ ".remote", + AbsLFile = filename:absname(LFile, PrivDir), + Contents = "ftp_SUITE test ...", + ok = file:write_file(AbsLFile, list_to_binary(Contents)), + ok = ftp:cd(Pid, "incoming"), + ok = ftp:lcd(Pid, PrivDir), + ok = ftp:send(Pid, LFile, RFile), + {ok, RFilesString} = ftp:nlist(Pid), + RFiles = split(RFilesString), + true = lists:member(RFile, RFiles), + ok = ftp:delete(Pid, RFile), + case ftp:nlist(Pid) of + {error, epath} -> + ok; % No files + {ok, RFilesString1} -> + RFiles1 = split(RFilesString1), + false = lists:member(RFile, RFiles1) + end, + ok = file:delete(AbsLFile). + +do_append(Pid, Config) -> + PrivDir = ?config(priv_dir, Config), + LFile = ?config(file, Config), + RFile = ?config(new_file, Config), + AbsLFile = filename:absname(LFile, PrivDir), + Contents = "ftp_SUITE test:appending\r\n", + + ok = file:write_file(AbsLFile, list_to_binary(Contents)), + ok = ftp:cd(Pid, "incoming"), + ok = ftp:lcd(Pid, PrivDir), + + %% remove files from earlier failed test case + ftp:delete(Pid, RFile), + ftp:delete(Pid, LFile), + + ok = ftp:append(Pid, LFile, RFile), + ok = ftp:append(Pid, LFile, RFile), + ok = ftp:append(Pid, LFile), + + %% Control the contents of the file + {ok, Bin1} = ftp:recv_bin(Pid, RFile), + ok = ftp:delete(Pid, RFile), + ok = file:delete(AbsLFile), + ok = check_content(binary_to_list(Bin1), Contents, double), + + {ok, Bin2} = ftp:recv_bin(Pid, LFile), + ok = ftp:delete(Pid, LFile), + ok = check_content(binary_to_list(Bin2), Contents, singel), + ok. + +do_send_bin(Pid, Config) -> + File = ?config(file, Config), + Contents = "ftp_SUITE test ...", + Bin = list_to_binary(Contents), + ok = ftp:cd(Pid, "incoming"), + {error, enotbinary} = ftp:send_bin(Pid, Contents, File), + ok = ftp:send_bin(Pid, Bin, File), + {ok, RFilesString} = ftp:nlist(Pid), + RFiles = split(RFilesString), + true = lists:member(File, RFiles), + ok = ftp:delete(Pid, File), + ok. + +do_append_bin(Pid, Config) -> + File = ?config(file, Config), + Contents = "ftp_SUITE test ...", + Bin = list_to_binary(Contents), + ok = ftp:cd(Pid, "incoming"), + {error, enotbinary} = ftp:append_bin(Pid, Contents, File), + ok = ftp:append_bin(Pid, Bin, File), + ok = ftp:append_bin(Pid, Bin, File), + %% Control the contents of the file + {ok, Bin2} = ftp:recv_bin(Pid, File), + ok = ftp:delete(Pid,File), + ok = check_content(binary_to_list(Bin2),binary_to_list(Bin), double). + +do_send_chunk(Pid, Config) -> + File = ?config(file, Config), + Contents = "ftp_SUITE test ...", + Bin = list_to_binary(Contents), + ok = ftp:cd(Pid, "incoming"), + ok = ftp:send_chunk_start(Pid, File), + {error, echunk} = ftp:cd(Pid, "incoming"), + {error, enotbinary} = ftp:send_chunk(Pid, Contents), + ok = ftp:send_chunk(Pid, Bin), + ok = ftp:send_chunk(Pid, Bin), + ok = ftp:send_chunk_end(Pid), + {ok, RFilesString} = ftp:nlist(Pid), + RFiles = split(RFilesString), + true = lists:member(File, RFiles), + ok = ftp:delete(Pid, File), + ok. + +do_append_chunk(Pid, Config) -> + File = ?config(file, Config), + Contents = ["ER","LE","RL"], + ok = ftp:cd(Pid, "incoming"), + ok = ftp:append_chunk_start(Pid, File), + {error, enotbinary} = ftp:append_chunk(Pid, lists:nth(1,Contents)), + ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(1,Contents))), + ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(2,Contents))), + ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(3,Contents))), + ok = ftp:append_chunk_end(Pid), + %%Control the contents of the file + {ok, Bin2} = ftp:recv_bin(Pid, File), + ok = check_content(binary_to_list(Bin2),"ERL", double), + ok = ftp:delete(Pid, File), + ok. + +do_recv(Pid, Config) -> + PrivDir = ?config(priv_dir, Config), + File = ?config(file, Config), + Newfile = ?config(new_file, Config), + AbsFile = filename:absname(File, PrivDir), + Contents = "ftp_SUITE:recv test ...", + ok = file:write_file(AbsFile, list_to_binary(Contents)), + ok = ftp:cd(Pid, "incoming"), + ftp:delete(Pid, File), % reset + ftp:lcd(Pid, PrivDir), + ok = ftp:send(Pid, File), + ok = file:delete(AbsFile), % cleanup + test_server:sleep(100), + ok = ftp:lcd(Pid, PrivDir), + ok = ftp:recv(Pid, File), + {ok, Files} = file:list_dir(PrivDir), + true = lists:member(File, Files), + ok = file:delete(AbsFile), % cleanup + ok = ftp:recv(Pid, File, Newfile), + ok = ftp:delete(Pid, File), % cleanup + ok. + +do_recv_bin(Pid, Config) -> + File = ?config(file, Config), + Contents1 = "ftp_SUITE test ...", + Bin1 = list_to_binary(Contents1), + ok = ftp:cd(Pid, "incoming"), + ok = ftp:send_bin(Pid, Bin1, File), + test_server:sleep(100), + {ok, Bin2} = ftp:recv_bin(Pid, File), + ok = ftp:delete(Pid, File), % cleanup + Contents2 = binary_to_list(Bin2), + Contents1 = Contents2, + ok. + +do_recv_chunk(Pid, Config) -> + File = ?config(file, Config), + Data = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" + "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG" + "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH" + "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII", + + Contents1 = lists:flatten(lists:duplicate(10, Data)), + Bin1 = list_to_binary(Contents1), + ok = ftp:cd(Pid, "incoming"), + ok = ftp:type(Pid, binary), + ok = ftp:send_bin(Pid, Bin1, File), + test_server:sleep(100), + {error, "ftp:recv_chunk_start/2 not called"} = recv_chunk(Pid, <<>>), + ok = ftp:recv_chunk_start(Pid, File), + {ok, Contents2} = recv_chunk(Pid, <<>>), + ok = ftp:delete(Pid, File), % cleanup + ok = find_diff(Contents2, Contents1, 1), + ok. + +do_type(Pid) -> + ok = ftp:type(Pid, ascii), + ok = ftp:type(Pid, binary), + ok = ftp:type(Pid, ascii), + {error, etype} = ftp:type(Pid, foobar), + ok. + +do_quote(Pid) -> + ["257 \"/\""++_Rest] = ftp:quote(Pid, "pwd"), %% 257 + [_| _] = ftp:quote(Pid, "help"), + %% This negativ test causes some ftp servers to hang. This test + %% is not important for the client, so we skip it for now. + %%["425 Can't build data connection: Connection refused."] + %% = ftp:quote(Pid, "list"), + ok. + + watch_dog(Config) -> + Dog = test_server:timetrap(inets_test_lib:minutes(1)), + NewConfig = lists:keydelete(watchdog, 1, Config), + [{watchdog, Dog} | NewConfig]. + + close_connection(Config) -> + case ?config(ftp, Config) of + Pid when is_pid(Pid) -> + ok = ftp:close(Pid), + lists:delete({ftp, Pid}, Config); + _ -> + Config + end. + +ftp_host(Config) -> + case ?config(ftp_remote_host, Config) of + undefined -> + exit({skip, "No host specified"}); + Host -> + Host + end. + +check_content(RContent, LContent, Amount) -> + LContent2 = case Amount of + double -> + LContent ++ LContent; + singel -> + LContent + end, + case string:equal(RContent, LContent2) of + true -> + ok; + false -> + %% Find where the diff is + Where = find_diff(RContent, LContent2, 1), + Where + end. + +find_diff(A, A, _) -> + ok; +find_diff([H|T1], [H|T2], Pos) -> + find_diff(T1, T2, Pos+1); +find_diff(RC, LC, Pos) -> + {error, {diff, Pos, RC, LC}}. + +recv_chunk(Pid, Acc) -> + case ftp:recv_chunk(Pid) of + ok -> + {ok, binary_to_list(Acc)}; + {ok, Bin} -> + recv_chunk(Pid, <>); + Error -> + Error + end. + +split(Cs) -> + split(Cs, [], []). + +split([$\r, $\n| Cs], I, Is) -> + split(Cs, [], [lists:reverse(I)| Is]); +split([C| Cs], I, Is) -> + split(Cs, [C| I], Is); +split([], I, Is) -> + lists:reverse([lists:reverse(I)| Is]). + +do_ftp_open(Host, Opts) -> + p("do_ftp_open -> entry with" + "~n Host: ~p" + "~n Opts: ~p", [Host, Opts]), + case ftp:open(Host, Opts) of + {ok, _} = OK -> + OK; + {error, Reason} -> + Str = + lists:flatten( + io_lib:format("Unable to reach test FTP server ~p (~p)", + [Host, Reason])), + throw({skip, Str}) + end. + + +passwd() -> + Host = + case inet:gethostname() of + {ok, H} -> + H; + _ -> + "localhost" + end, + "ftp_SUITE@" ++ Host. + +ftpd_hosts(Config) -> + DataDir = ?config(data_dir, Config), + FileName = filename:join([DataDir, "../ftp_SUITE_data/", ftpd_hosts]), + p("FileName: ~p", [FileName]), + case file:consult(FileName) of + {ok, [Hosts]} when is_list(Hosts) -> + Hosts; + _ -> + [] + end. + +wrapper(Prefix,doc,Func) -> + Prefix++Func(doc); +wrapper(_,X,Func) -> + Func(X). + +data_dir(Config) -> + case ?config(data_dir, Config) of + List when (length(List) > 0) -> + PathList = filename:split(List), + {NewPathList,_} = lists:split((length(PathList)-1), PathList), + DataDir = filename:join(NewPathList ++ [ftp_SUITE_data]), + NewConfig = + lists:keyreplace(data_dir,1,Config, {data_dir,DataDir}), + NewConfig; + _ -> Config + end. + + + +p(F) -> + p(F, []). + +p(F, A) -> + case get(ftp_testcase) of + undefined -> + io:format("~w [~w] " ++ F ++ "~n", [?MODULE, self() | A]); + TC when is_atom(TC) -> + io:format("~w [~w] ~w:" ++ F ++ "~n", [?MODULE, self(), TC | A]) + end. -- cgit v1.2.3 From d6577a26c9c2f2dd7bd26e585f3c9c22314ce1fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Wei=C3=9Fl?= Date: Thu, 18 Jul 2013 22:09:51 +0200 Subject: Fix httpd config option 'script_timeout' This fixes the usage of the httpd configuration option 'script_timeout', which got ignored before. The documentation states that the value is in seconds, which was true when using the Apache like configuration file, but not true when using the proplist style configuration. --- lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_cgi.erl | 2 +- lib/inets/src/http_server/mod_cgi.erl | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_cgi.erl b/lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_cgi.erl index d3f67eb77a..8c91b6f430 100644 --- a/lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_cgi.erl +++ b/lib/dialyzer/test/r9c_SUITE_data/src/inets/mod_cgi.erl @@ -338,7 +338,7 @@ exec_script(false,Info,Script,_AfterScript,_RequestURI) -> %% proxy(#mod{config_db = ConfigDb} = Info, Port) -> - Timeout = httpd_util:lookup(ConfigDb, cgi_timeout, ?DEFAULT_CGI_TIMEOUT), + Timeout = httpd_util:lookup(ConfigDb, script_timeout, ?DEFAULT_CGI_TIMEOUT), proxy(Info, Port, 0, undefined,[], Timeout). proxy(Info, Port, Size, StatusCode, AccResponse, Timeout) -> diff --git a/lib/inets/src/http_server/mod_cgi.erl b/lib/inets/src/http_server/mod_cgi.erl index f1b73810e6..0b7c7489f7 100644 --- a/lib/inets/src/http_server/mod_cgi.erl +++ b/lib/inets/src/http_server/mod_cgi.erl @@ -131,9 +131,9 @@ store({script_nocache, Value} = Conf, _) {ok, Conf}; store({script_nocache, Value}, _) -> {error, {wrong_type, {script_nocache, Value}}}; -store({script_timeout, Value} = Conf, _) +store({script_timeout, Value}, _) when is_integer(Value), Value >= 0 -> - {ok, Conf}; + {ok, {script_timeout, Value * 1000}}; store({script_timeout, Value}, _) -> {error, {wrong_type, {script_timeout, Value}}}. @@ -238,7 +238,7 @@ send_request_body_to_script(ModData, Port) -> end. deliver_webpage(#mod{config_db = Db} = ModData, Port) -> - Timeout = cgi_timeout(Db), + Timeout = script_timeout(Db), case receive_headers(Port, httpd_cgi, parse_headers, [<<>>, [], []], Timeout) of {Headers, Body} -> @@ -341,8 +341,8 @@ script_elements(#mod{method = "PUT", entity_body = Body}, _) -> script_elements(_, _) -> []. -cgi_timeout(Db) -> - httpd_util:lookup(Db, cgi_timeout, ?DEFAULT_CGI_TIMEOUT). +script_timeout(Db) -> + httpd_util:lookup(Db, script_timeout, ?DEFAULT_CGI_TIMEOUT). %% Convert error to printable string %% -- cgit v1.2.3 From 3ec3fcf098df6d0c2895eb11eaeb45d041cbf605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Wei=C3=9Fl?= Date: Thu, 18 Jul 2013 22:04:34 +0200 Subject: Fix httpd config option 'keep_alive_timeout' The documentation states that the value is in seconds, which was true when using the Apache like configuration file, but not true when using the proplist style configuration. --- lib/inets/src/http_server/httpd_conf.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl index b3ca13e2fe..27446ca7fe 100644 --- a/lib/inets/src/http_server/httpd_conf.erl +++ b/lib/inets/src/http_server/httpd_conf.erl @@ -798,6 +798,8 @@ store({log_format, LogFormat}, _ConfigList) store({server_tokens, ServerTokens} = Entry, _ConfigList) -> Server = server(ServerTokens), {ok, [Entry, {server, Server}]}; +store({keep_alive_timeout, KeepAliveTimeout}, _ConfigList) -> + {ok, {keep_alive_timeout, KeepAliveTimeout * 1000}}; store(ConfigListEntry, _ConfigList) -> {ok, ConfigListEntry}. -- cgit v1.2.3 From 5e7c3147b25b96cf08c3083cbb32f18f955f6f0f Mon Sep 17 00:00:00 2001 From: CA Meijer Date: Sat, 2 Mar 2013 06:48:04 +0200 Subject: Fix http_request:http_headers/1 to send content-length when length is zero In R16B01, the http_request:http_headers/1 function removes the content-length field from the HTTP headers if the content length is zero. This results in some (perhaps many) HTTP servers rejecting POSTs and PUTs without data with a 411 status word. From RFC2616, section 14.13: "Any Content-Length greater than or EQUAL to zero is a valid value". --- lib/inets/src/http_lib/http_request.erl | 9 ++------- lib/inets/test/http_format_SUITE.erl | 24 ++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/lib/inets/src/http_lib/http_request.erl b/lib/inets/src/http_lib/http_request.erl index c214aca4a4..b7f2379438 100644 --- a/lib/inets/src/http_lib/http_request.erl +++ b/lib/inets/src/http_lib/http_request.erl @@ -248,13 +248,8 @@ key_value_str(Key = 'content-language', Headers) -> key_value_str(atom_to_list(Key), Headers#http_request_h.'content-language'); key_value_str(Key = 'content-length', Headers) -> - case Headers#http_request_h.'content-length' of - "0" -> - undefined; - _ -> - key_value_str(atom_to_list(Key), - Headers#http_request_h.'content-length') - end; + key_value_str(atom_to_list(Key), + Headers#http_request_h.'content-length'); key_value_str(Key = 'content-location', Headers) -> key_value_str(atom_to_list(Key), Headers#http_request_h.'content-location'); diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl index 04c7358715..c913964094 100644 --- a/lib/inets/test/http_format_SUITE.erl +++ b/lib/inets/test/http_format_SUITE.erl @@ -35,14 +35,15 @@ 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]). + is_absolut_uri/1, convert_netscapecookie_date/1, + check_content_length_encoding/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [{group, chunk}, http_response, http_request, validate_request_line, {group, script}, is_absolut_uri, - convert_netscapecookie_date]. + convert_netscapecookie_date, check_content_length_encoding]. groups() -> [{script, [], [esi_parse_headers, cgi_parse_headers]}, @@ -456,6 +457,25 @@ validate_request_line(Config) when is_list(Config) -> httpd_request:validate("GET", NewForbiddenUri1, "HTTP/1.1"), ok. + +%%------------------------------------------------------------------------- +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(Config) when is_list(Config) -> + + %% Check that the content-length is preserved. + %% Sanity check. + Header1 = http_request:http_headers(#http_request_h{'content-length'="123"}), + 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. + %%------------------------------------------------------------------------- esi_parse_headers(doc) -> ["Test httpd_esi:*. All header values are received in the same" -- cgit v1.2.3 From 2c0afc1a1b4d2dd370024b7bd4884e41c3e3c148 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 3 Dec 2013 11:04:16 +0100 Subject: inets: Remove use of default gen_server timeout If you need a timeout between client and server use a server side timeout instead of the default gen_server:call/2 timeout that is quite useless. Also remove legacy code that has no further use. --- lib/inets/src/http_server/httpd.erl | 218 +--------------------------- lib/inets/src/http_server/httpd_manager.erl | 172 +++------------------- lib/inets/test/httpd_block.erl | 20 ++- 3 files changed, 31 insertions(+), 379 deletions(-) diff --git a/lib/inets/src/http_server/httpd.erl b/lib/inets/src/http_server/httpd.erl index 93608dbf96..6052ae9022 100644 --- a/lib/inets/src/http_server/httpd.erl +++ b/lib/inets/src/http_server/httpd.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2013. 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 @@ -36,13 +36,6 @@ %% API -export([parse_query/1, reload_config/2, info/1, info/2, info/3]). -%% Internal debugging and status info stuff... --export([ - get_status/1, get_status/2, get_status/3, - get_admin_state/0, get_admin_state/1, get_admin_state/2, - get_usage_state/0, get_usage_state/1, get_usage_state/2 - ]). - %%%======================================================================== %%% API %%%======================================================================== @@ -296,136 +289,6 @@ make_name(Addr, Port) -> httpd_util:make_name("httpd", Addr, Port). -%%%-------------------------------------------------------------- -%%% Internal debug functions - Do we want these functions here!? -%%%-------------------------------------------------------------------- - -%%% ========================================================= -%%% Function: get_admin_state/0, get_admin_state/1, get_admin_state/2 -%%% get_admin_state() -%%% get_admin_state(Port) -%%% get_admin_state(Addr,Port) -%%% -%%% Returns: {ok,State} | {error,Reason} -%%% -%%% Description: This function is used to retrieve the administrative -%%% state of the HTTP server. -%%% -%%% Types: Port -> integer() -%%% Addr -> {A,B,C,D} | string() | undefined -%%% State -> unblocked | shutting_down | blocked -%%% Reason -> term() -%%% -get_admin_state() -> get_admin_state(undefined,8888). -get_admin_state(Port) when is_integer(Port) -> get_admin_state(undefined,Port); - -get_admin_state(ConfigFile) when is_list(ConfigFile) -> - case get_addr_and_port(ConfigFile) of - {ok,Addr,Port} -> - unblock(Addr,Port); - Error -> - Error - end. - -get_admin_state(Addr,Port) when is_integer(Port) -> - Name = make_name(Addr,Port), - case whereis(Name) of - Pid when is_pid(Pid) -> - httpd_manager:get_admin_state(Pid); - _ -> - {error,not_started} - end. - - - -%%% ========================================================= -%%% Function: get_usage_state/0, get_usage_state/1, get_usage_state/2 -%%% get_usage_state() -%%% get_usage_state(Port) -%%% get_usage_state(Addr,Port) -%%% -%%% Returns: {ok,State} | {error,Reason} -%%% -%%% Description: This function is used to retrieve the usage -%%% state of the HTTP server. -%%% -%%% Types: Port -> integer() -%%% Addr -> {A,B,C,D} | string() | undefined -%%% State -> idle | active | busy -%%% Reason -> term() -%%% -get_usage_state() -> get_usage_state(undefined,8888). -get_usage_state(Port) when is_integer(Port) -> get_usage_state(undefined,Port); - -get_usage_state(ConfigFile) when is_list(ConfigFile) -> - case get_addr_and_port(ConfigFile) of - {ok,Addr,Port} -> - unblock(Addr,Port); - Error -> - Error - end. - -get_usage_state(Addr,Port) when is_integer(Port) -> - Name = make_name(Addr,Port), - case whereis(Name) of - Pid when is_pid(Pid) -> - httpd_manager:get_usage_state(Pid); - _ -> - {error,not_started} - end. - - - -%%% ========================================================= -%% Function: get_status(ConfigFile) -> Status -%% get_status(Port) -> Status -%% get_status(Addr,Port) -> Status -%% get_status(Port,Timeout) -> Status -%% get_status(Addr,Port,Timeout) -> Status -%% -%% Arguments: ConfigFile -> string() -%% Configuration file from which Port and -%% BindAddress will be extracted. -%% Addr -> {A,B,C,D} | string() -%% Bind Address of the http server -%% Port -> integer() -%% Port number of the http server -%% Timeout -> integer() -%% Timeout time for the call -%% -%% Returns: Status -> list() -%% -%% Description: This function is used when the caller runs in the -%% same node as the http server or if calling with a -%% program such as erl_call (see erl_interface). -%% - -get_status(ConfigFile) when is_list(ConfigFile) -> - case get_addr_and_port(ConfigFile) of - {ok,Addr,Port} -> - get_status(Addr,Port); - Error -> - Error - end; - -get_status(Port) when is_integer(Port) -> - get_status(undefined,Port,5000). - -get_status(Port,Timeout) when is_integer(Port) andalso is_integer(Timeout) -> - get_status(undefined,Port,Timeout); - -get_status(Addr,Port) -> - get_status(Addr,Port,5000). - -get_status(Addr,Port,Timeout) when is_integer(Port) -> - Name = make_name(Addr,Port), - case whereis(Name) of - Pid when is_pid(Pid) -> - httpd_manager:get_status(Pid,Timeout); - _ -> - not_started - end. - do_reload_config(ConfigList, Mode) -> case (catch httpd_conf:validate_properties(ConfigList)) of {ok, Config} -> @@ -438,85 +301,6 @@ do_reload_config(ConfigList, Mode) -> Error end. - %%%-------------------------------------------------------------- %%% Deprecated %%%-------------------------------------------------------------- - -%% start() -> -%% start("/var/tmp/server_root/conf/8888.conf"). - -%% start(ConfigFile) -> -%% {ok, Pid} = inets:start(httpd, ConfigFile, stand_alone), -%% unlink(Pid), -%% {ok, Pid}. - -%% start_link() -> -%% start("/var/tmp/server_root/conf/8888.conf"). - -%% start_link(ConfigFile) when is_list(ConfigFile) -> -%% inets:start(httpd, ConfigFile, stand_alone). - -%% stop() -> -%% stop(8888). - -%% stop(Port) when is_integer(Port) -> -%% stop(undefined, Port); -%% stop(Pid) when is_pid(Pid) -> -%% old_stop(Pid); -%% stop(ConfigFile) when is_list(ConfigFile) -> -%% old_stop(ConfigFile). - -%% stop(Addr, Port) when is_integer(Port) -> -%% old_stop(Addr, Port). - -%% start_child() -> -%% start_child("/var/tmp/server_root/conf/8888.conf"). - -%% start_child(ConfigFile) -> -%% httpd_sup:start_child(ConfigFile). - -%% stop_child() -> -%% stop_child(8888). - -%% stop_child(Port) -> -%% stop_child(undefined, Port). - -%% stop_child(Addr, Port) when is_integer(Port) -> -%% httpd_sup:stop_child(Addr, Port). - -%% restart() -> reload(undefined, 8888). - -%% restart(Port) when is_integer(Port) -> -%% reload(undefined, Port). -%% restart(Addr, Port) -> -%% reload(Addr, Port). - -%% old_stop(Pid) when is_pid(Pid) -> -%% do_stop(Pid); -%% old_stop(ConfigFile) when is_list(ConfigFile) -> -%% case get_addr_and_port(ConfigFile) of -%% {ok, Addr, Port} -> -%% old_stop(Addr, Port); - -%% Error -> -%% Error -%% end; -%% old_stop(_StartArgs) -> -%% ok. - -%% old_stop(Addr, Port) when is_integer(Port) -> -%% Name = old_make_name(Addr, Port), -%% case whereis(Name) of -%% Pid when is_pid(Pid) -> -%% do_stop(Pid), -%% ok; -%% _ -> -%% not_started -%% end. - -%% do_stop(Pid) -> -%% exit(Pid, shutdown). - -%% old_make_name(Addr,Port) -> -%% httpd_util:make_name("httpd_instance_sup",Addr,Port). diff --git a/lib/inets/src/http_server/httpd_manager.erl b/lib/inets/src/http_server/httpd_manager.erl index 3a486614b8..f6ae0d0933 100644 --- a/lib/inets/src/http_server/httpd_manager.erl +++ b/lib/inets/src/http_server/httpd_manager.erl @@ -27,9 +27,7 @@ %% Application internal API -export([start/2, start_link/2, start_link/3, start_link/4, stop/1, reload/2]). -export([new_connection/1, done_connection/1]). --export([config_lookup/2, config_lookup/3, - config_multi_lookup/2, config_multi_lookup/3, - config_match/2, config_match/3]). +-export([config_match/2, config_match/3]). %% gen_server exports -export([init/1, @@ -40,11 +38,6 @@ %% Management exports -export([block/2, block/3, unblock/1]). --export([get_admin_state/1, get_usage_state/1]). --export([is_busy/1,is_busy/2,is_busy_or_blocked/1,is_blocked/1]). %% ??????? --export([get_status/1, get_status/2]). - --export([c/1]). -record(state,{socket_type = ip_comm, config_file, @@ -58,9 +51,6 @@ %%TODO: Clean up this module! -c(Port) -> - Ref = httpd_util:make_name("httpd",undefined,Port), - call(Ref, fake_close). %% %% External API @@ -139,24 +129,7 @@ do_block(ServerRef,Method,Timeout) when Timeout > 0 -> unblock(ServerRef) -> call(ServerRef,unblock). -%% get admin/usage state - -get_admin_state(ServerRef) -> - call(ServerRef,get_admin_state). - -get_usage_state(ServerRef) -> - call(ServerRef,get_usage_state). - - -%% get_status - -get_status(ServerRef) -> - gen_server:call(ServerRef,get_status). -get_status(ServerRef,Timeout) -> - gen_server:call(ServerRef,get_status,Timeout). - -%% %% Internal API %% @@ -164,67 +137,19 @@ get_status(ServerRef,Timeout) -> %% new_connection new_connection(Manager) -> - gen_server:call(Manager, {new_connection, self()}, infinity). + call(Manager, {new_connection, self()}). %% done done_connection(Manager) -> - gen_server:cast(Manager, {done_connection, self()}). - - -%% is_busy(ServerRef) -> true | false -%% -%% Tests if the server is (in usage state) busy, -%% i.e. has rached the heavy load limit. -%% + cast(Manager, {done_connection, self()}). -is_busy(ServerRef) -> - gen_server:call(ServerRef,is_busy). - -is_busy(ServerRef,Timeout) -> - gen_server:call(ServerRef,is_busy,Timeout). - - -%% is_busy_or_blocked(ServerRef) -> busy | blocked | false -%% -%% Tests if the server is busy (usage state), i.e. has rached, -%% the heavy load limit, or blocked (admin state) . -%% - -is_busy_or_blocked(ServerRef) -> - gen_server:call(ServerRef,is_busy_or_blocked). - - -%% is_blocked(ServerRef) -> true | false -%% -%% Tests if the server is blocked (admin state) . -%% - -is_blocked(ServerRef) -> - gen_server:call(ServerRef,is_blocked). - - -%% -%% Module API. Theese functions are intended for use from modules only. -%% - -config_lookup(Port, Query) -> - config_lookup(undefined, Port, Query). -config_lookup(Addr, Port, Query) -> - Name = httpd_util:make_name("httpd",Addr,Port), - gen_server:call(whereis(Name), {config_lookup, Query}). - -config_multi_lookup(Port, Query) -> - config_multi_lookup(undefined,Port,Query). -config_multi_lookup(Addr,Port, Query) -> - Name = httpd_util:make_name("httpd",Addr,Port), - gen_server:call(whereis(Name), {config_multi_lookup, Query}). config_match(Port, Pattern) -> config_match(undefined,Port,Pattern). config_match(Addr, Port, Pattern) -> Name = httpd_util:make_name("httpd",Addr,Port), - gen_server:call(whereis(Name), {config_match, Pattern}). + call(whereis(Name), {config_match, Pattern}). %% @@ -320,66 +245,10 @@ do_initial_store(ConfigList) -> handle_call(stop, _From, State) -> {stop, normal, ok, State}; -handle_call({config_lookup, Query}, _From, State) -> - Res = httpd_util:lookup(State#state.config_db, Query), - {reply, Res, State}; - -handle_call({config_multi_lookup, Query}, _From, State) -> - Res = httpd_util:multi_lookup(State#state.config_db, Query), - {reply, Res, State}; - handle_call({config_match, Query}, _From, State) -> Res = ets:match_object(State#state.config_db, Query), {reply, Res, State}; -handle_call(get_status, _From, State) -> - ManagerStatus = manager_status(self()), - S1 = [{current_conn,length(State#state.connections)}|State#state.status]++ - [ManagerStatus], - {reply,S1,State}; - -handle_call(is_busy, _From, State) -> - Reply = case get_ustate(State) of - busy -> - true; - _ -> - false - end, - {reply,Reply,State}; - -handle_call(is_busy_or_blocked, _From, State) -> - Reply = - case get_astate(State) of - unblocked -> - case get_ustate(State) of - busy -> - busy; - _ -> - false - end; - _ -> - blocked - end, - {reply,Reply,State}; - -handle_call(is_blocked, _From, State) -> - Reply = - case get_astate(State) of - unblocked -> - false; - _ -> - true - end, - {reply,Reply,State}; - -handle_call(get_admin_state, _From, State) -> - Reply = get_astate(State), - {reply,Reply,State}; - -handle_call(get_usage_state, _From, State) -> - Reply = get_ustate(State), - {reply,Reply,State}; - handle_call({reload, Conf}, _From, State) when State#state.admin_state =:= blocked -> case handle_reload(Conf, State) of @@ -830,22 +699,6 @@ update_status_with_time(Status,Key) -> universal_time() -> calendar:universal_time(). -manager_status(P) -> - Items = [status, message_queue_len, reductions, - heap_size, stack_size], - {manager_status, process_status(P,Items,[])}. - - -process_status(P,[],L) -> - [{pid,P}|lists:reverse(L)]; -process_status(P,[H|T],L) -> - case (catch process_info(P,H)) of - {H, Value} -> - process_status(P,T,[{H,Value}|L]); - _ -> - process_status(P,T,[{H,undefined}|L]) - end. - make_name(Addr,Port) -> httpd_util:make_name("httpd",Addr,Port). @@ -857,9 +710,18 @@ report_error(State,String) -> mod_disk_log:report_error(Cdb,String). %% -call(ServerRef,Request) -> - gen_server:call(ServerRef,Request). +call(ServerRef, Request) -> + try gen_server:call(ServerRef, Request, infinity) + catch + exit:_ -> + {error, closed} + end. + +cast(ServerRef, Message) -> + try gen_server:cast(ServerRef, Message) + catch + exit:_ -> + {error, closed} + end. -cast(ServerRef,Message) -> - gen_server:cast(ServerRef,Message). diff --git a/lib/inets/test/httpd_block.erl b/lib/inets/test/httpd_block.erl index ac1bf43ff5..2f56096f75 100644 --- a/lib/inets/test/httpd_block.erl +++ b/lib/inets/test/httpd_block.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2013. 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 @@ -297,9 +297,12 @@ httpd_restart(Addr, Port) -> make_name(Addr, Port) -> httpd_util:make_name("httpd", Addr, Port). -get_admin_state(Node, _Host, Port) -> - Addr = undefined, - rpc:call(Node, httpd, get_admin_state, [Addr, Port]). +get_admin_state(_, _Host, Port) -> + Name = make_name(undefined, Port), + {status, _, _, StatusInfo} = sys:get_status(whereis(Name)), + [_, _,_, _, Prop] = StatusInfo, + State = state(Prop), + element(6, State). validate_admin_state(Node, Host, Port, Expect) -> io:format("try validating server admin state: ~p~n", [Expect]), @@ -363,6 +366,9 @@ do_long_poll(Type, Host, Port, Node, StatusCode, Timeout) -> end. - - - +state([{data,[{"State", State}]} | _]) -> + State; +state([{data,[{"StateData", State}]} | _]) -> + State; +state([_ | Rest]) -> + state(Rest). -- cgit v1.2.3 From 5b40274acb3355d23e65ef59def92e92f89c72e2 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Fri, 6 Dec 2013 15:12:49 +0100 Subject: inets: Start CT'ify httpd_SUITE Conflicts: lib/inets/test/httpd_SUITE.erl --- lib/inets/test/Makefile | 4 +- lib/inets/test/httpd_1_0.erl | 33 + lib/inets/test/httpd_1_1.erl | 12 +- lib/inets/test/httpd_SUITE.erl | 2991 ++++---------------- .../test/httpd_SUITE_data/server_root/Makefile | 209 -- .../httpd_SUITE_data/server_root/conf/8080.conf | 79 - .../httpd_SUITE_data/server_root/conf/8888.conf | 63 - .../httpd_SUITE_data/server_root/conf/httpd.conf | 268 -- .../httpd_SUITE_data/server_root/conf/mime.types | 465 --- .../httpd_SUITE_data/server_root/conf/ssl.conf | 66 - .../test/httpd_SUITE_data/server_root/icons/README | 161 -- .../server_root/logs/Dummy_File_Needed_By_WinZip | 1 - .../server_root/ssl/ssl_client.pem | 31 - .../server_root/ssl/ssl_server.pem | 31 - lib/inets/test/httpd_all.erl | 239 ++ lib/inets/test/httpd_mod_SUITE.erl | 76 + lib/inets/test/inets_test_lib.erl | 6 +- lib/inets/test/old_httpd_SUITE.erl | 2444 ++++++++++++++++ lib/inets/test/old_httpd_SUITE_data/Makefile.src | 14 + lib/inets/test/old_httpd_SUITE_data/cgi_echo.c | 97 + .../test/old_httpd_SUITE_data/server_root/Makefile | 208 ++ .../old_httpd_SUITE_data/server_root/auth/group | 3 + .../old_httpd_SUITE_data/server_root/auth/passwd | 4 + .../server_root/cgi-bin/printenv.bat | 7 + .../server_root/cgi-bin/printenv.sh | 6 + .../server_root/conf/8080.conf | 79 + .../server_root/conf/8888.conf | 63 + .../server_root/conf/httpd.conf | 268 ++ .../server_root/conf/mime.types | 462 +++ .../old_httpd_SUITE_data/server_root/conf/ssl.conf | 66 + .../server_root/htdocs/config.shtml | 60 + .../server_root/htdocs/dets_open/dummy.html | 10 + .../server_root/htdocs/dets_secret/dummy.html | 10 + .../htdocs/dets_secret/top_secret/index.html | 9 + .../server_root/htdocs/echo.shtml | 25 + .../server_root/htdocs/exec.shtml | 20 + .../server_root/htdocs/flastmod.shtml | 19 + .../server_root/htdocs/fsize.shtml | 19 + .../server_root/htdocs/include.shtml | 23 + .../server_root/htdocs/index.html | 25 + .../server_root/htdocs/last_modified.html | 12 + .../server_root/htdocs/misc/friedrich.html | 7 + .../server_root/htdocs/misc/oech.html | 4 + .../server_root/htdocs/misc/welcome.html | 1 + .../server_root/htdocs/mnesia_open/dummy.html | 10 + .../server_root/htdocs/mnesia_secret/dummy.html | 10 + .../htdocs/mnesia_secret/top_secret/index.html | 9 + .../server_root/htdocs/open/dummy.html | 10 + .../server_root/htdocs/secret/dummy.html | 10 + .../htdocs/secret/top_secret/index.html | 9 + .../old_httpd_SUITE_data/server_root/icons/README | 161 ++ .../old_httpd_SUITE_data/server_root/icons/a.gif | Bin 0 -> 246 bytes .../server_root/icons/alert.black.gif | Bin 0 -> 242 bytes .../server_root/icons/alert.red.gif | Bin 0 -> 247 bytes .../server_root/icons/apache_pb.gif | Bin 0 -> 2326 bytes .../server_root/icons/back.gif | Bin 0 -> 216 bytes .../server_root/icons/ball.gray.gif | Bin 0 -> 233 bytes .../server_root/icons/ball.red.gif | Bin 0 -> 205 bytes .../server_root/icons/binary.gif | Bin 0 -> 246 bytes .../server_root/icons/binhex.gif | Bin 0 -> 246 bytes .../server_root/icons/blank.gif | Bin 0 -> 148 bytes .../server_root/icons/bomb.gif | Bin 0 -> 308 bytes .../server_root/icons/box1.gif | Bin 0 -> 251 bytes .../server_root/icons/box2.gif | Bin 0 -> 268 bytes .../server_root/icons/broken.gif | Bin 0 -> 247 bytes .../server_root/icons/burst.gif | Bin 0 -> 235 bytes .../server_root/icons/button1.gif | Bin 0 -> 755 bytes .../server_root/icons/button10.gif | Bin 0 -> 781 bytes .../server_root/icons/button2.gif | Bin 0 -> 785 bytes .../server_root/icons/button3.gif | Bin 0 -> 745 bytes .../server_root/icons/button4.gif | Bin 0 -> 786 bytes .../server_root/icons/button5.gif | Bin 0 -> 780 bytes .../server_root/icons/button6.gif | Bin 0 -> 791 bytes .../server_root/icons/button7.gif | Bin 0 -> 796 bytes .../server_root/icons/button8.gif | Bin 0 -> 784 bytes .../server_root/icons/button9.gif | Bin 0 -> 784 bytes .../server_root/icons/buttonl.gif | Bin 0 -> 587 bytes .../server_root/icons/buttonr.gif | Bin 0 -> 576 bytes .../old_httpd_SUITE_data/server_root/icons/c.gif | Bin 0 -> 242 bytes .../server_root/icons/comp.blue.gif | Bin 0 -> 251 bytes .../server_root/icons/comp.gray.gif | Bin 0 -> 246 bytes .../server_root/icons/compressed.gif | Bin 0 -> 1038 bytes .../server_root/icons/continued.gif | Bin 0 -> 214 bytes .../old_httpd_SUITE_data/server_root/icons/dir.gif | Bin 0 -> 225 bytes .../server_root/icons/down.gif | Bin 0 -> 163 bytes .../old_httpd_SUITE_data/server_root/icons/dvi.gif | Bin 0 -> 238 bytes .../old_httpd_SUITE_data/server_root/icons/f.gif | Bin 0 -> 236 bytes .../server_root/icons/folder.gif | Bin 0 -> 225 bytes .../server_root/icons/folder.open.gif | Bin 0 -> 242 bytes .../server_root/icons/folder.sec.gif | Bin 0 -> 243 bytes .../server_root/icons/forward.gif | Bin 0 -> 219 bytes .../server_root/icons/generic.gif | Bin 0 -> 221 bytes .../server_root/icons/generic.red.gif | Bin 0 -> 220 bytes .../server_root/icons/generic.sec.gif | Bin 0 -> 249 bytes .../server_root/icons/hand.right.gif | Bin 0 -> 217 bytes .../server_root/icons/hand.up.gif | Bin 0 -> 223 bytes .../server_root/icons/htdig.gif | Bin 0 -> 1822 bytes .../server_root/icons/icon.sheet.gif | Bin 0 -> 11977 bytes .../server_root/icons/image1.gif | Bin 0 -> 274 bytes .../server_root/icons/image2.gif | Bin 0 -> 309 bytes .../server_root/icons/image3.gif | Bin 0 -> 286 bytes .../server_root/icons/index.gif | Bin 0 -> 268 bytes .../server_root/icons/layout.gif | Bin 0 -> 276 bytes .../server_root/icons/left.gif | Bin 0 -> 172 bytes .../server_root/icons/link.gif | Bin 0 -> 249 bytes .../server_root/icons/movie.gif | Bin 0 -> 243 bytes .../old_httpd_SUITE_data/server_root/icons/p.gif | Bin 0 -> 237 bytes .../server_root/icons/patch.gif | Bin 0 -> 251 bytes .../old_httpd_SUITE_data/server_root/icons/pdf.gif | Bin 0 -> 249 bytes .../server_root/icons/pie0.gif | Bin 0 -> 188 bytes .../server_root/icons/pie1.gif | Bin 0 -> 198 bytes .../server_root/icons/pie2.gif | Bin 0 -> 198 bytes .../server_root/icons/pie3.gif | Bin 0 -> 191 bytes .../server_root/icons/pie4.gif | Bin 0 -> 193 bytes .../server_root/icons/pie5.gif | Bin 0 -> 189 bytes .../server_root/icons/pie6.gif | Bin 0 -> 186 bytes .../server_root/icons/pie7.gif | Bin 0 -> 185 bytes .../server_root/icons/pie8.gif | Bin 0 -> 173 bytes .../server_root/icons/portal.gif | Bin 0 -> 254 bytes .../server_root/icons/poweredby.gif | Bin 0 -> 2748 bytes .../old_httpd_SUITE_data/server_root/icons/ps.gif | Bin 0 -> 244 bytes .../server_root/icons/quill.gif | Bin 0 -> 267 bytes .../server_root/icons/right.gif | Bin 0 -> 172 bytes .../server_root/icons/screw1.gif | Bin 0 -> 258 bytes .../server_root/icons/screw2.gif | Bin 0 -> 263 bytes .../server_root/icons/script.gif | Bin 0 -> 242 bytes .../server_root/icons/sound1.gif | Bin 0 -> 248 bytes .../server_root/icons/sound2.gif | Bin 0 -> 221 bytes .../server_root/icons/sphere1.gif | Bin 0 -> 285 bytes .../server_root/icons/sphere2.gif | Bin 0 -> 264 bytes .../server_root/icons/star.gif | Bin 0 -> 89 bytes .../server_root/icons/star_blank.gif | Bin 0 -> 53 bytes .../old_httpd_SUITE_data/server_root/icons/tar.gif | Bin 0 -> 243 bytes .../old_httpd_SUITE_data/server_root/icons/tex.gif | Bin 0 -> 251 bytes .../server_root/icons/text.gif | Bin 0 -> 229 bytes .../server_root/icons/transfer.gif | Bin 0 -> 242 bytes .../server_root/icons/unknown.gif | Bin 0 -> 245 bytes .../old_httpd_SUITE_data/server_root/icons/up.gif | Bin 0 -> 164 bytes .../old_httpd_SUITE_data/server_root/icons/uu.gif | Bin 0 -> 236 bytes .../server_root/icons/uuencoded.gif | Bin 0 -> 236 bytes .../server_root/icons/world1.gif | Bin 0 -> 228 bytes .../server_root/icons/world2.gif | Bin 0 -> 261 bytes .../server_root/logs/Dummy_File_Needed_By_WinZip | 0 .../server_root/ssl/ssl_client.pem | 31 + .../server_root/ssl/ssl_server.pem | 31 + 145 files changed, 5206 insertions(+), 3775 deletions(-) create mode 100644 lib/inets/test/httpd_1_0.erl delete mode 100644 lib/inets/test/httpd_SUITE_data/server_root/Makefile delete mode 100644 lib/inets/test/httpd_SUITE_data/server_root/conf/8080.conf delete mode 100644 lib/inets/test/httpd_SUITE_data/server_root/conf/8888.conf delete mode 100644 lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf delete mode 100644 lib/inets/test/httpd_SUITE_data/server_root/conf/mime.types delete mode 100644 lib/inets/test/httpd_SUITE_data/server_root/conf/ssl.conf delete mode 100644 lib/inets/test/httpd_SUITE_data/server_root/icons/README delete mode 100644 lib/inets/test/httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip delete mode 100644 lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_client.pem delete mode 100644 lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_server.pem create mode 100644 lib/inets/test/httpd_all.erl create mode 100644 lib/inets/test/httpd_mod_SUITE.erl create mode 100644 lib/inets/test/old_httpd_SUITE.erl create mode 100644 lib/inets/test/old_httpd_SUITE_data/Makefile.src create mode 100644 lib/inets/test/old_httpd_SUITE_data/cgi_echo.c create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/Makefile create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/auth/group create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/auth/passwd create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.bat create mode 100755 lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.sh create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/conf/8080.conf create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/conf/8888.conf create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/conf/httpd.conf create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/conf/mime.types create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/conf/ssl.conf create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/config.shtml create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_open/dummy.html create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/dummy.html create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/top_secret/index.html create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/echo.shtml create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/exec.shtml create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/flastmod.shtml create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/fsize.shtml create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/include.shtml create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/index.html create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/last_modified.html create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/friedrich.html create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/oech.html create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/welcome.html create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_open/dummy.html create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/dummy.html create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/top_secret/index.html create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/open/dummy.html create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/dummy.html create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/top_secret/index.html create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/README create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/a.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.black.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.red.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/apache_pb.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/back.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.gray.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.red.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/binary.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/binhex.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/blank.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/bomb.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/box1.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/box2.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/broken.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/burst.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/button1.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/button10.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/button2.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/button3.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/button4.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/button5.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/button6.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/button7.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/button8.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/button9.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonl.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonr.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/c.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.blue.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.gray.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/compressed.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/continued.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/dir.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/down.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/dvi.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/f.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.open.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.sec.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/forward.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.red.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.sec.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.right.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.up.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/htdig.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/icon.sheet.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/image1.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/image2.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/image3.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/index.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/layout.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/left.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/link.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/movie.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/p.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/patch.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/pdf.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie0.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie1.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie2.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie3.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie4.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie5.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie6.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie7.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie8.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/portal.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/poweredby.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/ps.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/quill.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/right.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw1.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw2.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/script.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound1.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound2.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere1.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere2.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/star.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/star_blank.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/tar.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/tex.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/text.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/transfer.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/unknown.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/up.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/uu.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/uuencoded.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/world1.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/icons/world2.gif create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_client.pem create mode 100644 lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_server.pem diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile index b353687cff..096d029865 100644 --- a/lib/inets/test/Makefile +++ b/lib/inets/test/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2012. All Rights Reserved. +# Copyright Ericsson AB 1997-2014. 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 @@ -158,12 +158,14 @@ MODULES = \ httpc_cookie_SUITE \ httpc_proxy_SUITE \ httpd_SUITE \ + old_httpd_SUITE \ httpd_basic_SUITE \ httpd_mod \ httpd_block \ httpd_load \ httpd_time_test \ httpd_1_1 \ + httpd_1_0 \ httpd_test_lib \ inets_sup_SUITE \ inets_SUITE \ diff --git a/lib/inets/test/httpd_1_0.erl b/lib/inets/test/httpd_1_0.erl new file mode 100644 index 0000000000..6059546057 --- /dev/null +++ b/lib/inets/test/httpd_1_0.erl @@ -0,0 +1,33 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013-2014. 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. +%% +%% %CopyrightEnd% +%% +%% + +-module(httpd_1_0). + +-export([host/4]). + +%%------------------------------------------------------------------------- +%% Test cases +%%------------------------------------------------------------------------- +host(Type, Port, Host, Node) -> + %% No host needed for HTTP/1.0 + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET / HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/1.0"}]). diff --git a/lib/inets/test/httpd_1_1.erl b/lib/inets/test/httpd_1_1.erl index 07d94ea97a..9d2b623772 100644 --- a/lib/inets/test/httpd_1_1.erl +++ b/lib/inets/test/httpd_1_1.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2014. 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 @@ -20,8 +20,6 @@ -module(httpd_1_1). --include("test_server.hrl"). --include("test_server_line.hrl"). -include_lib("kernel/include/file.hrl"). -export([host/4, chunked/4, expect/4, range/4, if_test/5, http_trace/4, @@ -40,14 +38,10 @@ %%------------------------------------------------------------------------- -%% Test cases starts here. +%% Test cases %%------------------------------------------------------------------------- host(Type, Port, Host, Node) -> - %% No host needed for HTTP/1.0 - ok = httpd_test_lib:verify_request(Type, Host, Port, Node, - "GET / HTTP/1.0\r\n\r\n", - [{statuscode, 200}, - {version, "HTTP/1.0"}]), + %% No host must generate an error ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET / HTTP/1.1\r\n\r\n", diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index a1600fa97f..021ea89e14 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2013. All Rights Reserved. +%% Copyright Ericsson AB 2013-2014. 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 @@ -18,2451 +18,664 @@ %% %% --module(httpd_SUITE). +%% +%% ct:run("../inets_test", httpd_SUITE). +%% --include_lib("test_server/include/test_server.hrl"). --include("test_server_line.hrl"). --include("inets_test_lib.hrl"). +-module(httpd_SUITE). -include_lib("kernel/include/file.hrl"). +-include_lib("common_test/include/ct.hrl"). +-include("inets_test_lib.hrl"). -%% Test server specific exports --export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]). --export([init_per_testcase/2, end_per_testcase/2, - init_per_suite/1, end_per_suite/1]). - -%% Core Server tests --export([ - ip_mod_alias/1, - ip_mod_actions/1, - ip_mod_security/1, - ip_mod_auth/1, - ip_mod_auth_api/1, - ip_mod_auth_mnesia_api/1, - ip_mod_htaccess/1, - ip_mod_cgi/1, - ip_mod_esi/1, - ip_mod_get/1, - ip_mod_head/1, - ip_mod_all/1, - ip_load_light/1, - ip_load_medium/1, - ip_load_heavy/1, - ip_dos_hostname/1, - ip_time_test/1, - ip_block_disturbing_idle/1, - ip_block_non_disturbing_idle/1, - ip_block_503/1, - ip_block_disturbing_active/1, - ip_block_non_disturbing_active/1, - ip_block_disturbing_active_timeout_not_released/1, - ip_block_disturbing_active_timeout_released/1, - ip_block_non_disturbing_active_timeout_not_released/1, - ip_block_non_disturbing_active_timeout_released/1, - ip_block_disturbing_blocker_dies/1, - ip_block_non_disturbing_blocker_dies/1, - ip_restart_no_block/1, - ip_restart_disturbing_block/1, - ip_restart_non_disturbing_block/1 - ]). - --export([ - essl_mod_alias/1, - essl_mod_actions/1, - essl_mod_security/1, - essl_mod_auth/1, - essl_mod_auth_api/1, - essl_mod_auth_mnesia_api/1, - essl_mod_htaccess/1, - essl_mod_cgi/1, - essl_mod_esi/1, - essl_mod_get/1, - essl_mod_head/1, - essl_mod_all/1, - essl_load_light/1, - essl_load_medium/1, - essl_load_heavy/1, - essl_dos_hostname/1, - essl_time_test/1, - essl_restart_no_block/1, - essl_restart_disturbing_block/1, - essl_restart_non_disturbing_block/1, - essl_block_disturbing_idle/1, - essl_block_non_disturbing_idle/1, - essl_block_503/1, - essl_block_disturbing_active/1, - essl_block_non_disturbing_active/1, - essl_block_disturbing_active_timeout_not_released/1, - essl_block_disturbing_active_timeout_released/1, - essl_block_non_disturbing_active_timeout_not_released/1, - essl_block_non_disturbing_active_timeout_released/1, - essl_block_disturbing_blocker_dies/1, - essl_block_non_disturbing_blocker_dies/1 - ]). - -%%% HTTP 1.1 tests --export([ip_host/1, ip_chunked/1, ip_expect/1, ip_range/1, - ip_if_test/1, ip_http_trace/1, ip_http1_1_head/1, - ip_mod_cgi_chunked_encoding_test/1]). - -%%% HTTP 1.0 tests --export([ip_head_1_0/1, ip_get_1_0/1, ip_post_1_0/1]). - -%%% HTTP 0.9 tests --export([ip_get_0_9/1]). - -%%% Ticket tests --export([ticket_5775/1,ticket_5865/1,ticket_5913/1,ticket_6003/1, - ticket_7304/1]). - -%%% IPv6 tests --export([ipv6_hostname_ipcomm/0, ipv6_hostname_ipcomm/1, - ipv6_address_ipcomm/0, ipv6_address_ipcomm/1, - ipv6_hostname_essl/0, ipv6_hostname_essl/1, - ipv6_address_essl/0, ipv6_address_essl/1]). - -%% Help functions --export([cleanup_mnesia/0, setup_mnesia/0, setup_mnesia/1]). - --define(IP_PORT, 8898). --define(SSL_PORT, 8899). --define(MAX_HEADER_SIZE, 256). --define(IPV6_LOCAL_HOST, "0:0:0:0:0:0:0:1"). - -%% Minutes before failed auths timeout. --define(FAIL_EXPIRE_TIME,1). - -%% Seconds before successful auths timeout. --define(AUTH_TIMEOUT,5). - --record(httpd_user, {user_name, password, user_data}). --record(httpd_group, {group_name, userlist}). - +%% Note: This directive should only be used in test suites. +-compile(export_all). %%-------------------------------------------------------------------- -%% all(Arg) -> [Doc] | [Case] | {skip, Comment} -%% Arg - doc | suite -%% Doc - string() -%% Case - atom() -%% Name of a test case function. -%% Comment - string() -%% Description: Returns documentation/test cases in this test suite -%% or a skip tuple if the platform is not supported. +%% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}]. -all() -> +all() -> [ - {group, ip}, - {group, ssl}, - {group, http_1_1_ip}, - {group, http_1_0_ip}, - {group, http_0_9_ip}, - {group, ipv6}, - {group, tickets} + {group, http} + %{group, https} ]. -groups() -> +groups() -> [ - {ip, [], - [ip_mod_alias, ip_mod_actions, ip_mod_security, - ip_mod_auth, ip_mod_auth_api, ip_mod_auth_mnesia_api, - ip_mod_htaccess, ip_mod_cgi, ip_mod_esi, ip_mod_get, - ip_mod_head, ip_mod_all, ip_load_light, ip_load_medium, - ip_load_heavy, ip_dos_hostname, ip_time_test, - ip_restart_no_block, ip_restart_disturbing_block, - ip_restart_non_disturbing_block, - ip_block_disturbing_idle, ip_block_non_disturbing_idle, - ip_block_503, ip_block_disturbing_active, - ip_block_non_disturbing_active, - ip_block_disturbing_active_timeout_not_released, - ip_block_disturbing_active_timeout_released, - ip_block_non_disturbing_active_timeout_not_released, - ip_block_non_disturbing_active_timeout_released, - ip_block_disturbing_blocker_dies, - ip_block_non_disturbing_blocker_dies]}, - {ssl, [], [{group, essl}]}, - {essl, [], - [essl_mod_alias, essl_mod_actions, essl_mod_security, - essl_mod_auth, essl_mod_auth_api, - essl_mod_auth_mnesia_api, essl_mod_htaccess, - essl_mod_cgi, essl_mod_esi, essl_mod_get, essl_mod_head, - essl_mod_all, essl_load_light, essl_load_medium, - essl_load_heavy, essl_dos_hostname, essl_time_test, - essl_restart_no_block, essl_restart_disturbing_block, - essl_restart_non_disturbing_block, - essl_block_disturbing_idle, - essl_block_non_disturbing_idle, essl_block_503, - essl_block_disturbing_active, - essl_block_non_disturbing_active, - essl_block_disturbing_active_timeout_not_released, - essl_block_disturbing_active_timeout_released, - essl_block_non_disturbing_active_timeout_not_released, - essl_block_non_disturbing_active_timeout_released, - essl_block_disturbing_blocker_dies, - essl_block_non_disturbing_blocker_dies]}, - {http_1_1_ip, [], - [ip_host, ip_chunked, ip_expect, ip_range, ip_if_test, - ip_http_trace, ip_http1_1_head, - ip_mod_cgi_chunked_encoding_test]}, - {http_1_0_ip, [], - [ip_head_1_0, ip_get_1_0, ip_post_1_0]}, - {http_0_9_ip, [], [ip_get_0_9]}, - {ipv6, [], [ipv6_hostname_ipcomm, ipv6_address_ipcomm, - ipv6_hostname_essl, ipv6_address_essl]}, - {tickets, [], - [ticket_5775, ticket_5865, ticket_5913, ticket_6003, - ticket_7304]}]. - - -init_per_group(ipv6 = _GroupName, Config) -> - case inets_test_lib:has_ipv6_support() of - {ok, _} -> - Config; - _ -> - {skip, "Host does not support IPv6"} - end; -init_per_group(essl, Config) -> - catch crypto:stop(), - case (catch crypto:start()) of - ok -> - Config; - _ -> - {skip, "Crypto not startable"} - end; - -init_per_group(_GroupName, Config) -> - Config. + {http, [], all_groups()}, + %{https, [], all_groups()}, + {http_1_1, [], [host, chunked, expect, cgi] ++ http_head() ++ http_get()}, + {http_1_0, [], [host, cgi] ++ http_head() ++ http_get()}, + {http_0_9, [], http_head() ++ http_get()} + ]. -end_per_group(_GroupName, Config) -> - Config. +all_groups ()-> + [{group, http_1_1}, + {group, http_1_0}, + {group, http_0_9} + ]. +http_head() -> + [head]. +http_get() -> + [alias, get, basic_auth, esi, ssi]. -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initiation before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- init_per_suite(Config) -> - io:format(user, "init_per_suite -> entry with" - "~n Config: ~p" - "~n", [Config]), - - ?PRINT_SYSTEM_INFO([]), - PrivDir = ?config(priv_dir, Config), - SuiteTopDir = filename:join(PrivDir, ?MODULE), - case file:make_dir(SuiteTopDir) of - ok -> - ok; - {error, eexist} -> - ok; - Error -> - throw({error, {failed_creating_suite_top_dir, Error}}) - end, - - [{has_ipv6_support, inets_test_lib:has_ipv6_support()}, - {suite_top_dir, SuiteTopDir}, + DataDir = ?config(data_dir, Config), + inets_test_lib:stop_apps([inets]), + inets_test_lib:start_apps([inets]), + ServerRoot = filename:join(PrivDir, "server_root"), + inets_test_lib:del_dirs(ServerRoot), + DocRoot = filename:join(ServerRoot, "htdocs"), + setup_server_dirs(ServerRoot, DocRoot, DataDir), + [{server_root, ServerRoot}, + {doc_root, DocRoot}, {node, node()}, - {host, inets_test_lib:hostname()}, - {address, getaddr()} | Config]. - - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- + {host, inets_test_lib:hostname()} | Config]. end_per_suite(_Config) -> - %% SuiteTopDir = ?config(suite_top_dir, Config), - %% inets_test_lib:del_dirs(SuiteTopDir), ok. - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(Case, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initiation before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_testcase(Case, Config) -> - NewConfig = init_per_testcase2(Case, Config), - init_per_testcase3(Case, NewConfig). - - -init_per_testcase2(Case, Config) -> - - %% tsp("init_per_testcase2 -> entry with" - %% "~n Config: ~p", [Config]), - - IpNormal = integer_to_list(?IP_PORT) ++ ".conf", - IpHtaccess = integer_to_list(?IP_PORT) ++ "htaccess.conf", - SslNormal = integer_to_list(?SSL_PORT) ++ ".conf", - SslHtaccess = integer_to_list(?SSL_PORT) ++ "htaccess.conf", - - DataDir = ?config(data_dir, Config), - SuiteTopDir = ?config(suite_top_dir, Config), - - %% tsp("init_per_testcase2 -> " - %% "~n SuiteDir: ~p" - %% "~n DataDir: ~p", [SuiteTopDir, DataDir]), - - TcTopDir = filename:join(SuiteTopDir, Case), - ?line ok = file:make_dir(TcTopDir), - - %% tsp("init_per_testcase2 -> " - %% "~n TcTopDir: ~p", [TcTopDir]), - - DataSrc = filename:join([DataDir, "server_root"]), - ServerRoot = filename:join([TcTopDir, "server_root"]), - - %% tsp("init_per_testcase2 -> " - %% "~n DataSrc: ~p" - %% "~n ServerRoot: ~p", [DataSrc, ServerRoot]), - - ok = file:make_dir(ServerRoot), - ok = file:make_dir(filename:join([TcTopDir, "logs"])), - - NewConfig = [{tc_top_dir, TcTopDir}, {server_root, ServerRoot} | Config], - - %% tsp("init_per_testcase2 -> copy DataSrc to ServerRoot"), - - inets_test_lib:copy_dirs(DataSrc, ServerRoot), - - %% tsp("init_per_testcase2 -> fix cgi"), - EnvCGI = filename:join([ServerRoot, "cgi-bin", "printenv.sh"]), - {ok, FileInfo} = file:read_file_info(EnvCGI), - ok = file:write_file_info(EnvCGI, - FileInfo#file_info{mode = 8#00755}), - - EchoCGI = case test_server:os_type() of - {win32, _} -> - "cgi_echo.exe"; - _ -> - "cgi_echo" - end, - CGIDir = filename:join([ServerRoot, "cgi-bin"]), - inets_test_lib:copy_file(EchoCGI, DataDir, CGIDir), - NewEchoCGI = filename:join([CGIDir, EchoCGI]), - {ok, FileInfo1} = file:read_file_info(NewEchoCGI), - ok = file:write_file_info(NewEchoCGI, - FileInfo1#file_info{mode = 8#00755}), - - %% To be used by IP test cases - %% tsp("init_per_testcase2 -> ip testcase setups"), - create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig], - normal_access, IpNormal), - create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig], - mod_htaccess, IpHtaccess), - - %% To be used by SSL test cases - %% tsp("init_per_testcase2 -> ssl testcase setups"), - SocketType = - case atom_to_list(Case) of - [X, $s, $s, $l | _] -> - case X of - $p -> ssl; - $e -> essl - end; - _ -> - ssl - end, - - create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig], - normal_access, SslNormal), - create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig], - mod_htaccess, SslHtaccess), - - %% To be used by IPv6 test cases. Case-clause is so that - %% you can do ts:run(inets, httpd_SUITE, ) - %% for all cases except the ipv6 cases as they depend - %% on 'test_host_ipv6_only' that will only be present - %% when you run the whole test suite due to shortcomings - %% of the test server. - - tsp("init_per_testcase2 -> maybe generate IPv6 config file(s)"), - NewConfig2 = - case atom_to_list(Case) of - "ipv6_" ++ _ -> - case (catch inets_test_lib:has_ipv6_support(NewConfig)) of - {ok, IPv6Address0} -> - {ok, Hostname} = inet:gethostname(), - IPv6Address = http_transport:ipv6_name(IPv6Address0), - create_ipv6_config([{port, ?IP_PORT}, - {sock_type, ip_comm}, - {ipv6_host, IPv6Address} | - NewConfig], - "ipv6_hostname_ipcomm.conf", - Hostname), - create_ipv6_config([{port, ?IP_PORT}, - {sock_type, ip_comm}, - {ipv6_host, IPv6Address} | - NewConfig], - "ipv6_address_ipcomm.conf", - IPv6Address), - create_ipv6_config([{port, ?SSL_PORT}, - {sock_type, essl}, - {ipv6_host, IPv6Address} | - NewConfig], - "ipv6_hostname_essl.conf", - Hostname), - create_ipv6_config([{port, ?SSL_PORT}, - {sock_type, essl}, - {ipv6_host, IPv6Address} | - NewConfig], - "ipv6_address_essl.conf", - IPv6Address), - [{ipv6_host, IPv6Address} | NewConfig]; - _ -> - NewConfig - end; - - _ -> - NewConfig - end, - - %% tsp("init_per_testcase2 -> done when" - %% "~n NewConfig2: ~p", [NewConfig2]), - - NewConfig2. - - -init_per_testcase3(Case, Config) -> - tsp("init_per_testcase3(~w) -> entry with" - "~n Config: ~p", [Case, Config]), - - -%% %% Create a new fresh node to be used by the server in this test-case - -%% NodeName = list_to_atom(atom_to_list(Case) ++ "_httpd"), -%% Node = inets_test_lib:start_node(NodeName), - - %% Clean up (we do not want this clean up in end_per_testcase - %% if init_per_testcase crashes for some testcase it will - %% have contaminated the environment and there will be no clean up.) - %% This init can take a few different paths so that one crashes - %% does not mean that all invocations will. - - application:unset_env(inets, services), - application:stop(inets), - application:stop(ssl), - cleanup_mnesia(), - - %% Start initialization - tsp("init_per_testcase3(~w) -> start init", [Case]), - - Dog = test_server:timetrap(inets_test_lib:minutes(10)), - NewConfig = lists:keydelete(watchdog, 1, Config), - TcTopDir = ?config(tc_top_dir, Config), - - CaseRest = - case atom_to_list(Case) of - "ip_mod_htaccess" -> - inets_test_lib:start_http_server( - filename:join(TcTopDir, - integer_to_list(?IP_PORT) ++ - "htaccess.conf")), - "mod_htaccess"; - "ip_" ++ Rest -> - inets_test_lib:start_http_server( - filename:join(TcTopDir, - integer_to_list(?IP_PORT) ++ ".conf")), - Rest; - "ticket_5913" -> - HttpdOptions = - [{file, - filename:join(TcTopDir, - integer_to_list(?IP_PORT) ++ ".conf")}, - {accept_timeout,30000}, - {debug,[{exported_functions, - [httpd_manager,httpd_request_handler]}]}], - inets_test_lib:start_http_server(HttpdOptions); - "ticket_"++Rest -> - %% OTP-5913 use the new syntax of inets.config - inets_test_lib:start_http_server([{file, - filename:join(TcTopDir, - integer_to_list(?IP_PORT) ++ ".conf")}]), - Rest; - - [X, $s, $s, $l, $_, $m, $o, $d, $_, $h, $t, $a, $c, $c, $e, $s, $s] -> - ?ENSURE_STARTED([crypto, public_key, ssl]), - SslTag = - case X of - $p -> ssl; % Plain - $e -> essl % Erlang based ssl - end, - case inets_test_lib:start_http_server_ssl( - filename:join(TcTopDir, - integer_to_list(?SSL_PORT) ++ - "htaccess.conf"), SslTag) of - ok -> - "mod_htaccess"; - Other -> - error_logger:info_msg("Other: ~p~n", [Other]), - {skip, "SSL does not seem to be supported"} - end; - [X, $s, $s, $l, $_ | Rest] -> - ?ENSURE_STARTED([crypto, public_key, ssl]), - SslTag = - case X of - $p -> ssl; - $e -> essl - end, - case inets_test_lib:start_http_server_ssl( - filename:join(TcTopDir, - integer_to_list(?SSL_PORT) ++ - ".conf"), SslTag) of - ok -> - Rest; - Other -> - error_logger:info_msg("Other: ~p~n", [Other]), - {skip, "SSL does not seem to be supported"} - end; - "ipv6_" ++ _ = TestCaseStr -> - case inets_test_lib:has_ipv6_support() of - {ok, _} -> - inets_test_lib:start_http_server( - filename:join(TcTopDir, - TestCaseStr ++ ".conf")); - - _ -> - {skip, "Host does not support IPv6"} - end - end, - - InitRes = - case CaseRest of - {skip, _} = Skip -> - Skip; - "mod_auth_" ++ _ -> - start_mnesia(?config(node, Config)), - [{watchdog, Dog} | NewConfig]; - "mod_htaccess" -> - ServerRoot = ?config(server_root, Config), - Path = filename:join([ServerRoot, "htdocs"]), - catch remove_htaccess(Path), - create_htaccess_data(Path, ?config(address, Config)), - [{watchdog, Dog} | NewConfig]; - "range" -> - ServerRoot = ?config(server_root, Config), - Path = filename:join([ServerRoot, "htdocs"]), - create_range_data(Path), - [{watchdog, Dog} | NewConfig]; - _ -> - [{watchdog, Dog} | NewConfig] - end, - - tsp("init_per_testcase3(~w) -> done when" - "~n InitRes: ~p", [Case, InitRes]), - - InitRes. - - -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(Case, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case %%-------------------------------------------------------------------- -end_per_testcase(Case, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - end_per_testcase2(Case, lists:keydelete(watchdog, 1, Config)), - ok. - -end_per_testcase2(Case, Config) -> - tsp("end_per_testcase2(~w) -> entry with" - "~n Config: ~p", [Case, Config]), - application:unset_env(inets, services), - application:stop(inets), - application:stop(ssl), - application:stop(crypto), % used by the new ssl (essl test cases) - cleanup_mnesia(), - tsp("end_per_testcase2(~w) -> done", [Case]), - ok. - - -%%------------------------------------------------------------------------- -%% Test cases starts here. -%%------------------------------------------------------------------------- - -%%------------------------------------------------------------------------- -ip_mod_alias(doc) -> - ["Module test: mod_alias"]; -ip_mod_alias(suite) -> - []; -ip_mod_alias(Config) when is_list(Config) -> - httpd_mod:alias(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), - ok. - -%%------------------------------------------------------------------------- -ip_mod_actions(doc) -> - ["Module test: mod_actions"]; -ip_mod_actions(suite) -> - []; -ip_mod_actions(Config) when is_list(Config) -> - httpd_mod:actions(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), - ok. - -%%------------------------------------------------------------------------- -ip_mod_security(doc) -> - ["Module test: mod_security"]; -ip_mod_security(suite) -> - []; -ip_mod_security(Config) when is_list(Config) -> - ServerRoot = ?config(server_root, Config), - httpd_mod:security(ServerRoot, ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), - ok. - -%%------------------------------------------------------------------------- -ip_mod_auth(doc) -> - ["Module test: mod_auth"]; -ip_mod_auth(suite) -> - []; -ip_mod_auth(Config) when is_list(Config) -> - httpd_mod:auth(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), - ok. - -%%------------------------------------------------------------------------- -ip_mod_auth_api(doc) -> - ["Module test: mod_auth_api"]; -ip_mod_auth_api(suite) -> - []; -ip_mod_auth_api(Config) when is_list(Config) -> - ServerRoot = ?config(server_root, Config), - Host = ?config(host, Config), - Node = ?config(node, Config), - httpd_mod:auth_api(ServerRoot, "", ip_comm, ?IP_PORT, Host, Node), - httpd_mod:auth_api(ServerRoot, "dets_", ip_comm, ?IP_PORT, Host, Node), - httpd_mod:auth_api(ServerRoot, "mnesia_", ip_comm, ?IP_PORT, Host, Node), - ok. -%%------------------------------------------------------------------------- -ip_mod_auth_mnesia_api(doc) -> - ["Module test: mod_auth_mnesia_api"]; -ip_mod_auth_mnesia_api(suite) -> - []; -ip_mod_auth_mnesia_api(Config) when is_list(Config) -> - httpd_mod:auth_mnesia_api(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), - ok. -%%------------------------------------------------------------------------- -ip_mod_htaccess(doc) -> - ["Module test: mod_htaccess"]; -ip_mod_htaccess(suite) -> - []; -ip_mod_htaccess(Config) when is_list(Config) -> - httpd_mod:htaccess(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), - ok. -%%------------------------------------------------------------------------- -ip_mod_cgi(doc) -> - ["Module test: mod_cgi"]; -ip_mod_cgi(suite) -> - []; -ip_mod_cgi(Config) when is_list(Config) -> - case test_server:os_type() of - vxworks -> - {skip, cgi_not_supported_on_vxwoks}; - _ -> - httpd_mod:cgi(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), - ok - end. -%%------------------------------------------------------------------------- -ip_mod_esi(doc) -> - ["Module test: mod_esi"]; -ip_mod_esi(suite) -> - []; -ip_mod_esi(Config) when is_list(Config) -> - httpd_mod:esi(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), - ok. - -%%------------------------------------------------------------------------- -ip_mod_get(doc) -> - ["Module test: mod_get"]; -ip_mod_get(suite) -> - []; -ip_mod_get(Config) when is_list(Config) -> - httpd_mod:get(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), - ok. - -%%------------------------------------------------------------------------- -ip_mod_head(doc) -> - ["Module test: mod_head"]; -ip_mod_head(suite) -> - []; -ip_mod_head(Config) when is_list(Config) -> - httpd_mod:head(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), - ok. -%%------------------------------------------------------------------------- -ip_mod_all(doc) -> - ["All modules test"]; -ip_mod_all(suite) -> - []; -ip_mod_all(Config) when is_list(Config) -> - httpd_mod:all(ip_comm, ?IP_PORT, - ?config(host, Config), ?config(node, Config)), - ok. -%%------------------------------------------------------------------------- -ip_load_light(doc) -> - ["Test light load"]; -ip_load_light(suite) -> - []; -ip_load_light(Config) when is_list(Config) -> - httpd_load:load_test(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config), - get_nof_clients(ip_comm, light)), - ok. -%%------------------------------------------------------------------------- -ip_load_medium(doc) -> - ["Test medium load"]; -ip_load_medium(suite) -> - []; -ip_load_medium(Config) when is_list(Config) -> - httpd_load:load_test(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config), - get_nof_clients(ip_comm, medium)), - ok. -%%------------------------------------------------------------------------- -ip_load_heavy(doc) -> - ["Test heavy load"]; -ip_load_heavy(suite) -> - []; -ip_load_heavy(Config) when is_list(Config) -> - httpd_load:load_test(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config), - get_nof_clients(ip_comm, heavy)), - ok. - - -%%------------------------------------------------------------------------- -ip_dos_hostname(doc) -> - ["Denial Of Service (DOS) attack test case"]; -ip_dos_hostname(suite) -> - []; -ip_dos_hostname(Config) when is_list(Config) -> - dos_hostname(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config), ?MAX_HEADER_SIZE), - ok. - - -%%------------------------------------------------------------------------- -ip_time_test(doc) -> - [""]; -ip_time_test(suite) -> - []; -ip_time_test(Config) when is_list(Config) -> - %% - Skippable = [win32], - Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% - - httpd_time_test:t(ip_comm, ?config(host, Config), ?IP_PORT), - ok. - -%%------------------------------------------------------------------------- -ip_block_503(doc) -> - ["Check that you will receive status code 503 when the server" - " is blocked and 200 when its not blocked."]; -ip_block_503(suite) -> - []; -ip_block_503(Config) when is_list(Config) -> - httpd_block:block_503(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config)), - ok. -%%------------------------------------------------------------------------- -ip_block_disturbing_idle(doc) -> - ["Check that you can block/unblock an idle server. The strategy " - "distribing does not really make a difference in this case."]; -ip_block_disturbing_idle(suite) -> - []; -ip_block_disturbing_idle(Config) when is_list(Config) -> - httpd_block:block_disturbing_idle(ip_comm, ?IP_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. -%%------------------------------------------------------------------------- -ip_block_non_disturbing_idle(doc) -> - ["Check that you can block/unblock an idle server. The strategy " - "non distribing does not really make a difference in this case."]; -ip_block_non_disturbing_idle(suite) -> - []; -ip_block_non_disturbing_idle(Config) when is_list(Config) -> - httpd_block:block_non_disturbing_idle(ip_comm, ?IP_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. -%%------------------------------------------------------------------------- -ip_block_disturbing_active(doc) -> - ["Check that you can block/unblock an active server. The strategy " - "distribing means ongoing requests should be terminated."]; -ip_block_disturbing_active(suite) -> - []; -ip_block_disturbing_active(Config) when is_list(Config) -> - httpd_block:block_disturbing_active(ip_comm, ?IP_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. -%%------------------------------------------------------------------------- -ip_block_non_disturbing_active(doc) -> - ["Check that you can block/unblock an idle server. The strategy " - "non distribing means the ongoing requests should be compleated."]; -ip_block_non_disturbing_active(suite) -> - []; -ip_block_non_disturbing_active(Config) when is_list(Config) -> - httpd_block:block_non_disturbing_idle(ip_comm, ?IP_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - -%%------------------------------------------------------------------------- -ip_block_disturbing_active_timeout_not_released(doc) -> - ["Check that you can block an active server. The strategy " - "distribing means ongoing requests should be compleated" - "if the timeout does not occur."]; -ip_block_disturbing_active_timeout_not_released(suite) -> - []; -ip_block_disturbing_active_timeout_not_released(Config) - when is_list(Config) -> - httpd_block:block_disturbing_active_timeout_not_released(ip_comm, - ?IP_PORT, - ?config(host, - Config), - ?config(node, - Config)), - ok. -%%------------------------------------------------------------------------- -ip_block_disturbing_active_timeout_released(doc) -> - ["Check that you can block an active server. The strategy " - "distribing means ongoing requests should be terminated when" - "the timeout occurs."]; -ip_block_disturbing_active_timeout_released(suite) -> - []; -ip_block_disturbing_active_timeout_released(Config) - when is_list(Config) -> - httpd_block:block_disturbing_active_timeout_released(ip_comm, - ?IP_PORT, - ?config(host, - Config), - ?config(node, - Config)), - ok. - -%%------------------------------------------------------------------------- -ip_block_non_disturbing_active_timeout_not_released(doc) -> - ["Check that you can block an active server. The strategy " - "non non distribing means ongoing requests should be completed."]; -ip_block_non_disturbing_active_timeout_not_released(suite) -> - []; -ip_block_non_disturbing_active_timeout_not_released(Config) - when is_list(Config) -> - httpd_block: - block_non_disturbing_active_timeout_not_released(ip_comm, - ?IP_PORT, - ?config(host, - Config), - ?config(node, - Config)), - ok. -%%------------------------------------------------------------------------- -ip_block_non_disturbing_active_timeout_released(doc) -> - ["Check that you can block an active server. The strategy " - "non non distribing means ongoing requests should be completed. " - "When the timeout occurs the block operation sohould be canceled." ]; -ip_block_non_disturbing_active_timeout_released(suite) -> - []; -ip_block_non_disturbing_active_timeout_released(Config) - when is_list(Config) -> - httpd_block: - block_non_disturbing_active_timeout_released(ip_comm, - ?IP_PORT, - ?config(host, - Config), - ?config(node, - Config)), - ok. -%%------------------------------------------------------------------------- -ip_block_disturbing_blocker_dies(doc) -> - []; -ip_block_disturbing_blocker_dies(suite) -> - []; -ip_block_disturbing_blocker_dies(Config) when is_list(Config) -> - httpd_block:disturbing_blocker_dies(ip_comm, ?IP_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. -%%------------------------------------------------------------------------- -ip_block_non_disturbing_blocker_dies(doc) -> - []; -ip_block_non_disturbing_blocker_dies(suite) -> - []; -ip_block_non_disturbing_blocker_dies(Config) when is_list(Config) -> - httpd_block:non_disturbing_blocker_dies(ip_comm, ?IP_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. -%%------------------------------------------------------------------------- -ip_restart_no_block(doc) -> - [""]; -ip_restart_no_block(suite) -> - []; -ip_restart_no_block(Config) when is_list(Config) -> - httpd_block:restart_no_block(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config)), - ok. -%%------------------------------------------------------------------------- -ip_restart_disturbing_block(doc) -> - [""]; -ip_restart_disturbing_block(suite) -> - []; -ip_restart_disturbing_block(Config) when is_list(Config) -> - %% - Condition = - fun() -> - case os:type() of - {unix, linux} -> - HW = string:strip(os:cmd("uname -m"), right, $\n), - case HW of - "ppc" -> - case inet:gethostname() of - {ok, "peach"} -> - true; - _ -> - false - end; - _ -> - false - end; - _ -> - false - end - end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% - - httpd_block:restart_disturbing_block(ip_comm, ?IP_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - -%%------------------------------------------------------------------------- -ip_restart_non_disturbing_block(doc) -> - [""]; -ip_restart_non_disturbing_block(suite) -> - []; -ip_restart_non_disturbing_block(Config) when is_list(Config) -> - %% - Condition = - fun() -> - case os:type() of - {unix, linux} -> - HW = string:strip(os:cmd("uname -m"), right, $\n), - case HW of - "ppc" -> - case inet:gethostname() of - {ok, "peach"} -> - true; - _ -> - false - end; - _ -> - false - end; - _ -> - false - end - end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% - - httpd_block:restart_non_disturbing_block(ip_comm, ?IP_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - -%%------------------------------------------------------------------------- - -essl_mod_alias(doc) -> - ["Module test: mod_alias - using new of configure new SSL"]; -essl_mod_alias(suite) -> - []; -essl_mod_alias(Config) when is_list(Config) -> - ssl_mod_alias(essl, Config). - - -ssl_mod_alias(Tag, Config) -> - httpd_mod:alias(Tag, ?SSL_PORT, - ?config(host, Config), ?config(node, Config)), - ok. - - -%%------------------------------------------------------------------------- - -essl_mod_actions(doc) -> - ["Module test: mod_actions - using new of configure new SSL"]; -essl_mod_actions(suite) -> - []; -essl_mod_actions(Config) when is_list(Config) -> - ssl_mod_actions(essl, Config). - - -ssl_mod_actions(Tag, Config) -> - httpd_mod:actions(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - - -%%------------------------------------------------------------------------- - -essl_mod_security(doc) -> - ["Module test: mod_security - using new of configure new SSL"]; -essl_mod_security(suite) -> - []; -essl_mod_security(Config) when is_list(Config) -> - ssl_mod_security(essl, Config). - -ssl_mod_security(Tag, Config) -> - ServerRoot = ?config(server_root, Config), - httpd_mod:security(ServerRoot, - Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - - -%%------------------------------------------------------------------------- - -essl_mod_auth(doc) -> - ["Module test: mod_auth - using new of configure new SSL"]; -essl_mod_auth(suite) -> - []; -essl_mod_auth(Config) when is_list(Config) -> - ssl_mod_auth(essl, Config). - -ssl_mod_auth(Tag, Config) -> - httpd_mod:auth(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - - -%%------------------------------------------------------------------------- - - -essl_mod_auth_api(doc) -> - ["Module test: mod_auth - using new of configure new SSL"]; -essl_mod_auth_api(suite) -> - []; -essl_mod_auth_api(Config) when is_list(Config) -> - ssl_mod_auth_api(essl, Config). - -ssl_mod_auth_api(Tag, Config) -> - ServerRoot = ?config(server_root, Config), - Host = ?config(host, Config), - Node = ?config(node, Config), - httpd_mod:auth_api(ServerRoot, "", Tag, ?SSL_PORT, Host, Node), - httpd_mod:auth_api(ServerRoot, "dets_", Tag, ?SSL_PORT, Host, Node), - httpd_mod:auth_api(ServerRoot, "mnesia_", Tag, ?SSL_PORT, Host, Node), - ok. - - -%%------------------------------------------------------------------------- - - -essl_mod_auth_mnesia_api(doc) -> - ["Module test: mod_auth_mnesia_api - using new of configure new SSL"]; -essl_mod_auth_mnesia_api(suite) -> - []; -essl_mod_auth_mnesia_api(Config) when is_list(Config) -> - ssl_mod_auth_mnesia_api(essl, Config). - -ssl_mod_auth_mnesia_api(Tag, Config) -> - httpd_mod:auth_mnesia_api(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - - -%%------------------------------------------------------------------------- - -essl_mod_htaccess(doc) -> - ["Module test: mod_htaccess - using new of configure new SSL"]; -essl_mod_htaccess(suite) -> - []; -essl_mod_htaccess(Config) when is_list(Config) -> - ssl_mod_htaccess(essl, Config). - -ssl_mod_htaccess(Tag, Config) -> - httpd_mod:htaccess(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - - -%%------------------------------------------------------------------------- - -essl_mod_cgi(doc) -> - ["Module test: mod_cgi - using new of configure new SSL"]; -essl_mod_cgi(suite) -> - []; -essl_mod_cgi(Config) when is_list(Config) -> - ssl_mod_cgi(essl, Config). - -ssl_mod_cgi(Tag, Config) -> - case test_server:os_type() of - vxworks -> - {skip, cgi_not_supported_on_vxwoks}; +init_per_group(https = Group, Config0) -> + case start_apps(Group) of + ok -> + init_httpd(Group, [{type, ssl} | Config0]); _ -> - httpd_mod:cgi(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok - end. - - -%%------------------------------------------------------------------------- - -essl_mod_esi(doc) -> - ["Module test: mod_esi - using new of configure new SSL"]; -essl_mod_esi(suite) -> - []; -essl_mod_esi(Config) when is_list(Config) -> - ssl_mod_esi(essl, Config). - -ssl_mod_esi(Tag, Config) -> - httpd_mod:esi(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - - -%%------------------------------------------------------------------------- - -essl_mod_get(doc) -> - ["Module test: mod_get - using new of configure new SSL"]; -essl_mod_get(suite) -> - []; -essl_mod_get(Config) when is_list(Config) -> - ssl_mod_get(essl, Config). - -ssl_mod_get(Tag, Config) -> - httpd_mod:get(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - - -%%------------------------------------------------------------------------- - -essl_mod_head(doc) -> - ["Module test: mod_head - using new of configure new SSL"]; -essl_mod_head(suite) -> - []; -essl_mod_head(Config) when is_list(Config) -> - ssl_mod_head(essl, Config). - -ssl_mod_head(Tag, Config) -> - httpd_mod:head(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - - -%%------------------------------------------------------------------------- - -essl_mod_all(doc) -> - ["All modules test - using new of configure new SSL"]; -essl_mod_all(suite) -> - []; -essl_mod_all(Config) when is_list(Config) -> - ssl_mod_all(essl, Config). - -ssl_mod_all(Tag, Config) -> - httpd_mod:all(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - - -%%------------------------------------------------------------------------- - -essl_load_light(doc) -> - ["Test light load - using new of configure new SSL"]; -essl_load_light(suite) -> - []; -essl_load_light(Config) when is_list(Config) -> - ssl_load_light(essl, Config). - -ssl_load_light(Tag, Config) -> - httpd_load:load_test(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config), - get_nof_clients(ssl, light)), - ok. - - -%%------------------------------------------------------------------------- - -essl_load_medium(doc) -> - ["Test medium load - using new of configure new SSL"]; -essl_load_medium(suite) -> - []; -essl_load_medium(Config) when is_list(Config) -> - ssl_load_medium(essl, Config). - -ssl_load_medium(Tag, Config) -> - %% - Skippable = [win32], - Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% - - httpd_load:load_test(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config), - get_nof_clients(ssl, medium)), - ok. - - -%%------------------------------------------------------------------------- - -essl_load_heavy(doc) -> - ["Test heavy load - using new of configure new SSL"]; -essl_load_heavy(suite) -> - []; -essl_load_heavy(Config) when is_list(Config) -> - ssl_load_heavy(essl, Config). - -ssl_load_heavy(Tag, Config) -> - %% - Skippable = [win32], - Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% - - httpd_load:load_test(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config), - get_nof_clients(ssl, heavy)), - ok. - - -%%------------------------------------------------------------------------- - - -essl_dos_hostname(doc) -> - ["Denial Of Service (DOS) attack test case - using new of configure new SSL"]; -essl_dos_hostname(suite) -> - []; -essl_dos_hostname(Config) when is_list(Config) -> - ssl_dos_hostname(essl, Config). - -ssl_dos_hostname(Tag, Config) -> - dos_hostname(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config), - ?MAX_HEADER_SIZE), - ok. - - -%%------------------------------------------------------------------------- - - -essl_time_test(doc) -> - ["using new of configure new SSL"]; -essl_time_test(suite) -> - []; -essl_time_test(Config) when is_list(Config) -> - ssl_time_test(essl, Config). - -ssl_time_test(Tag, Config) when is_list(Config) -> - %% - FreeBSDVersionVerify = - fun() -> - case os:version() of - {7, 1, _} -> % We only have one such machine, so... - true; - _ -> - false - end - end, - Skippable = [win32, {unix, [{freebsd, FreeBSDVersionVerify}]}], - Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% - - httpd_time_test:t(Tag, - ?config(host, Config), - ?SSL_PORT), - ok. - - -%%------------------------------------------------------------------------- - - -essl_block_503(doc) -> - ["Check that you will receive status code 503 when the server" - " is blocked and 200 when its not blocked - using new of configure new SSL."]; -essl_block_503(suite) -> - []; -essl_block_503(Config) when is_list(Config) -> - ssl_block_503(essl, Config). - -ssl_block_503(Tag, Config) -> - httpd_block:block_503(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - - -%%------------------------------------------------------------------------- - -essl_block_disturbing_idle(doc) -> - ["Check that you can block/unblock an idle server. The strategy " - "distribing does not really make a difference in this case." - "Using new of configure new SSL"]; -essl_block_disturbing_idle(suite) -> - []; -essl_block_disturbing_idle(Config) when is_list(Config) -> - ssl_block_disturbing_idle(essl, Config). - -ssl_block_disturbing_idle(Tag, Config) -> - httpd_block:block_disturbing_idle(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - - -%%------------------------------------------------------------------------- - -essl_block_non_disturbing_idle(doc) -> - ["Check that you can block/unblock an idle server. The strategy " - "non distribing does not really make a difference in this case." - "Using new of configure new SSL"]; -essl_block_non_disturbing_idle(suite) -> - []; -essl_block_non_disturbing_idle(Config) when is_list(Config) -> - ssl_block_non_disturbing_idle(essl, Config). - -ssl_block_non_disturbing_idle(Tag, Config) -> - httpd_block:block_non_disturbing_idle(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - - -%%------------------------------------------------------------------------- - -essl_block_disturbing_active(doc) -> - ["Check that you can block/unblock an active server. The strategy " - "distribing means ongoing requests should be terminated." - "Using new of configure new SSL"]; -essl_block_disturbing_active(suite) -> - []; -essl_block_disturbing_active(Config) when is_list(Config) -> - ssl_block_disturbing_active(essl, Config). - -ssl_block_disturbing_active(Tag, Config) -> - httpd_block:block_disturbing_active(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - - -%%------------------------------------------------------------------------- - -essl_block_non_disturbing_active(doc) -> - ["Check that you can block/unblock an idle server. The strategy " - "non distribing means the ongoing requests should be compleated." - "Using new of configure new SSL"]; -essl_block_non_disturbing_active(suite) -> - []; -essl_block_non_disturbing_active(Config) when is_list(Config) -> - ssl_block_non_disturbing_active(essl, Config). - -ssl_block_non_disturbing_active(Tag, Config) -> - httpd_block:block_non_disturbing_idle(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - - -%%------------------------------------------------------------------------- - -essl_block_disturbing_active_timeout_not_released(doc) -> - ["Check that you can block an active server. The strategy " - "distribing means ongoing requests should be compleated" - "if the timeout does not occur." - "Using new of configure new SSL"]; -essl_block_disturbing_active_timeout_not_released(suite) -> - []; -essl_block_disturbing_active_timeout_not_released(Config) - when is_list(Config) -> - ssl_block_disturbing_active_timeout_not_released(essl, Config). - -ssl_block_disturbing_active_timeout_not_released(Tag, Config) -> - Port = ?SSL_PORT, - Host = ?config(host, Config), - Node = ?config(node, Config), - httpd_block:block_disturbing_active_timeout_not_released(Tag, - Port, Host, Node), - ok. - - -%%------------------------------------------------------------------------- - -essl_block_disturbing_active_timeout_released(doc) -> - ["Check that you can block an active server. The strategy " - "distribing means ongoing requests should be terminated when" - "the timeout occurs." - "Using new of configure new SSL"]; -essl_block_disturbing_active_timeout_released(suite) -> - []; -essl_block_disturbing_active_timeout_released(Config) - when is_list(Config) -> - ssl_block_disturbing_active_timeout_released(essl, Config). - -ssl_block_disturbing_active_timeout_released(Tag, Config) -> - Port = ?SSL_PORT, - Host = ?config(host, Config), - Node = ?config(node, Config), - httpd_block:block_disturbing_active_timeout_released(Tag, - Port, - Host, - Node), - ok. - - -%%------------------------------------------------------------------------- - -essl_block_non_disturbing_active_timeout_not_released(doc) -> - ["Check that you can block an active server. The strategy " - "non non distribing means ongoing requests should be completed." - "Using new of configure new SSL"]; -essl_block_non_disturbing_active_timeout_not_released(suite) -> - []; -essl_block_non_disturbing_active_timeout_not_released(Config) - when is_list(Config) -> - ssl_block_non_disturbing_active_timeout_not_released(essl, Config). - -ssl_block_non_disturbing_active_timeout_not_released(Tag, Config) -> - Port = ?SSL_PORT, - Host = ?config(host, Config), - Node = ?config(node, Config), - httpd_block:block_non_disturbing_active_timeout_not_released(Tag, - Port, - Host, - Node), - ok. - - -%%------------------------------------------------------------------------- - - -essl_block_non_disturbing_active_timeout_released(doc) -> - ["Check that you can block an active server. The strategy " - "non distribing means ongoing requests should be completed. " - "When the timeout occurs the block operation sohould be canceled." - "Using new of configure new SSL"]; -essl_block_non_disturbing_active_timeout_released(suite) -> - []; -essl_block_non_disturbing_active_timeout_released(Config) - when is_list(Config) -> - ssl_block_non_disturbing_active_timeout_released(essl, Config). - -ssl_block_non_disturbing_active_timeout_released(Tag, Config) - when is_list(Config) -> - Port = ?SSL_PORT, - Host = ?config(host, Config), - Node = ?config(node, Config), - httpd_block:block_non_disturbing_active_timeout_released(Tag, - Port, - Host, - Node), - - ok. - - -%%------------------------------------------------------------------------- - - -essl_block_disturbing_blocker_dies(doc) -> - ["using new of configure new SSL"]; -essl_block_disturbing_blocker_dies(suite) -> - []; -essl_block_disturbing_blocker_dies(Config) when is_list(Config) -> - ssl_block_disturbing_blocker_dies(essl, Config). - -ssl_block_disturbing_blocker_dies(Tag, Config) -> - httpd_block:disturbing_blocker_dies(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - - -%%------------------------------------------------------------------------- + {skip, "Could not start https apps"} + end; -essl_block_non_disturbing_blocker_dies(doc) -> - ["using new of configure new SSL"]; -essl_block_non_disturbing_blocker_dies(suite) -> - []; -essl_block_non_disturbing_blocker_dies(Config) when is_list(Config) -> - ssl_block_non_disturbing_blocker_dies(essl, Config). - -ssl_block_non_disturbing_blocker_dies(Tag, Config) -> - httpd_block:non_disturbing_blocker_dies(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), +init_per_group(http = Group, Config0) -> + init_httpd(Group, [{type, ip_comm} | Config0]); +init_per_group(http_1_1, Config) -> + [{http_version, "HTTP/1.1"} | Config]; +init_per_group(http_1_0, Config) -> + [{http_version, "HTTP/1.0"} | Config]; +init_per_group(http_0_9, Config) -> + [{http_version, "HTTP/0.9"} | Config]; +init_per_group(_, Config) -> + Config. +end_per_group(_, _Config) -> ok. +init_httpd(Group, Config0) -> + Config = proplists:delete(port, Config0), + Port = server_start(Group, server_config(Group, Config)), + [{port, Port} | Config]. +%%-------------------------------------------------------------------- +init_per_testcase(host, Config) -> + Prop = ?config(tc_group_properties, Config), + Name = proplists:get_value(name, Prop), + Cb = case Name of + http_1_0 -> + httpd_1_0; + http_1_1 -> + httpd_1_1 + end, + [{version_cb, Cb} | proplists:delete(version_cb, Config)]; + +init_per_testcase(_Case, Config) -> + Config. - -%%------------------------------------------------------------------------- - - -essl_restart_no_block(doc) -> - ["using new of configure new SSL"]; -essl_restart_no_block(suite) -> - []; -essl_restart_no_block(Config) when is_list(Config) -> - ssl_restart_no_block(essl, Config). - -ssl_restart_no_block(Tag, Config) -> - httpd_block:restart_no_block(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), +end_per_testcase(_Case, _Config) -> ok. - %%------------------------------------------------------------------------- - - -essl_restart_disturbing_block(doc) -> - ["using new of configure new SSL"]; -essl_restart_disturbing_block(suite) -> - []; -essl_restart_disturbing_block(Config) when is_list(Config) -> - ssl_restart_disturbing_block(essl, Config). - -ssl_restart_disturbing_block(Tag, Config) -> - %% - Condition = - fun() -> - case os:type() of - {unix, linux} -> - case ?OSCMD("uname -m") of - "ppc" -> - case file:read_file_info("/etc/fedora-release") of - {ok, _} -> - case ?OSCMD("awk '{print $2}' /etc/fedora-release") of - "release" -> - %% Fedora 7 and later - case ?OSCMD("awk '{print $3}' /etc/fedora-release") of - "7" -> - true; - _ -> - false - end; - _ -> - false - end; - _ -> - false - end; - _ -> - false - end; - _ -> - false - end - end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% - - httpd_block:restart_disturbing_block(Tag, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. - - +%% Test cases starts here. %%------------------------------------------------------------------------- +head() -> + [{doc, "HTTP HEAD request for static page"}]. -essl_restart_non_disturbing_block(doc) -> - ["using new of configure new SSL"]; -essl_restart_non_disturbing_block(suite) -> - []; -essl_restart_non_disturbing_block(Config) when is_list(Config) -> - ssl_restart_non_disturbing_block(essl, Config). - -ssl_restart_non_disturbing_block(Tag, Config) -> - %% - Condition = - fun() -> - case os:type() of - {unix, linux} -> - HW = string:strip(os:cmd("uname -m"), right, $\n), - case HW of - "ppc" -> - case inet:gethostname() of - {ok, "peach"} -> - true; - _ -> - false - end; - _ -> - false - end; - _ -> - false - end - end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% +head(Config) when is_list(Config) -> + Version = ?config(http_version, Config), + Host = ?config(host, Config), + ok = httpd_test_lib:verify_request(?config(type, Config), Host, + ?config(port, Config), ?config(node, Config), + http_request("HEAD /index.html ", Version, Host), + [{statuscode, head_status(Version)}, + {version, Version}]). - httpd_block:restart_non_disturbing_block(Tag, - ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), - ok. +get() -> + [{doc, "HTTP GET request for static page"}]. +get(Config) when is_list(Config) -> + Version = ?config(http_version, Config), + Host = ?config(host, Config), + ok = httpd_test_lib:verify_request(?config(type, Config), Host, + ?config(port, Config), ?config(node, Config), + http_request("GET /index.html ", Version, Host), + [{statuscode, 200}, + {header, "Content-Type", "text/html"}, + {header, "Date"}, + {header, "Server"}, + {version, Version}]). -%%------------------------------------------------------------------------- -ip_host(doc) -> - ["Control that the server accepts/rejects requests with/ without host"]; -ip_host(suite)-> - []; -ip_host(Config) when is_list(Config) -> - httpd_1_1:host(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config)), - ok. -%%------------------------------------------------------------------------- -ip_chunked(doc) -> - ["Control that the server accepts chunked requests"]; -ip_chunked(suite) -> - []; -ip_chunked(Config) when is_list(Config) -> - httpd_1_1:chunked(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config)), - ok. -%%------------------------------------------------------------------------- -ip_expect(doc) -> - ["Control that the server handles request with the expect header " - "field appropiate"]; -ip_expect(suite)-> - []; -ip_expect(Config) when is_list(Config) -> - httpd_1_1:expect(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config)), - ok. -%%------------------------------------------------------------------------- -ip_range(doc) -> - ["Control that the server can handle range requests to plain files"]; -ip_range(suite)-> - []; -ip_range(Config) when is_list(Config) -> - httpd_1_1:range(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config)), - ok. -%%------------------------------------------------------------------------- -ip_if_test(doc) -> - ["Test that the if - request header fields is handled correclty"]; -ip_if_test(suite) -> - []; -ip_if_test(Config) when is_list(Config) -> - ServerRoot = ?config(server_root, Config), - DocRoot = filename:join([ServerRoot, "htdocs"]), - httpd_1_1:if_test(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config), DocRoot), - ok. -%%------------------------------------------------------------------------- -ip_http_trace(doc) -> - ["Test the trace module "]; -ip_http_trace(suite) -> - []; -ip_http_trace(Config) when is_list(Config) -> - httpd_1_1:http_trace(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config)), - ok. -%%------------------------------------------------------------------------- -ip_http1_1_head(doc) -> - ["Test the trace module "]; -ip_http1_1_head(suite)-> - []; -ip_http1_1_head(Config) when is_list(Config) -> - httpd_1_1:head(ip_comm, ?IP_PORT, ?config(host, Config), - ?config(node, Config)), - ok. +basic_auth() -> + [{doc, "Test Basic authentication with WWW-Authenticate header"}]. -%%------------------------------------------------------------------------- -ip_get_0_9(doc) -> - ["Test simple HTTP/0.9 GET"]; -ip_get_0_9(suite)-> - []; -ip_get_0_9(Config) when is_list(Config) -> - Host = ?config(host, Config), - Node = ?config(node, Config), - ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node, - "GET / \r\n\r\n", - [{statuscode, 200}, - {version, "HTTP/0.9"} ]), - %% Without space after uri - ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node, - "GET /\r\n\r\n", - [{statuscode, 200}, - {version, "HTTP/0.9"} ]), - ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node, - "GET / HTTP/0.9\r\n\r\n", - [{statuscode, 200}, - {version, "HTTP/0.9"}]), - - ok. -%%------------------------------------------------------------------------- -ip_head_1_0(doc) -> - ["Test HTTP/1.0 HEAD"]; -ip_head_1_0(suite)-> - []; -ip_head_1_0(Config) when is_list(Config) -> - Host = ?config(host, Config), - Node = ?config(node, Config), - ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node, - "HEAD / HTTP/1.0\r\n\r\n", [{statuscode, 200}, - {version, "HTTP/1.0"}]), - - ok. -%%------------------------------------------------------------------------- -ip_get_1_0(doc) -> - ["Test HTTP/1.0 GET"]; -ip_get_1_0(suite)-> - []; -ip_get_1_0(Config) when is_list(Config) -> - Host = ?config(host, Config), - Node = ?config(node, Config), - ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node, - "GET / HTTP/1.0\r\n\r\n", [{statuscode, 200}, - {version, "HTTP/1.0"}]), - - ok. -%%------------------------------------------------------------------------- -ip_post_1_0(doc) -> - ["Test HTTP/1.0 POST"]; -ip_post_1_0(suite)-> - []; -ip_post_1_0(Config) when is_list(Config) -> - Host = ?config(host, Config), - Node = ?config(node, Config), - %% Test the post message formatin 1.0! Real post are testes elsewhere - ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node, - "POST / HTTP/1.0\r\n\r\n " - "Content-Length:6 \r\n\r\nfoobar", - [{statuscode, 500}, {version, "HTTP/1.0"}]), - - ok. -%%------------------------------------------------------------------------- -ip_mod_cgi_chunked_encoding_test(doc) -> - ["Test the trace module "]; -ip_mod_cgi_chunked_encoding_test(suite)-> - []; -ip_mod_cgi_chunked_encoding_test(Config) when is_list(Config) -> +basic_auth(Config) -> + Version = ?config(http_version, Config), Host = ?config(host, Config), - Script = + basic_auth_requiered(Config), + %% Authentication OK! ["one:OnePassword" user first in user list] + ok = auth_status(auth_request("/open/dummy.html", "one", "onePassword", Version, Host), Config, + [{statuscode, 200}]), + %% Authentication OK and a directory listing is supplied! + %% ["Aladdin:open sesame" user second in user list] + ok = auth_status(auth_request("/open/", "Aladdin", "AladdinPassword", Version, Host), Config, + [{statuscode, 200}]), + %% User correct but wrong password! ["one:one" user first in user list] + ok = auth_status(auth_request("/open/dummy.html", "one", "one", Version, Host), Config, + [{statuscode, 401}, + {header, "WWW-Authenticate"}]), + %% Make sure Authenticate header is received even the second time + %% we try a incorrect password! Otherwise a browser client will hang! + ok = auth_status(auth_request("/open/dummy.html", "one", "one", Version, Host), Config, + [{statuscode, 401}, + {header, "WWW-Authenticate"}]), + %% Neither user or password correct! ["dummy:dummy"] + ok = auth_status(auth_request("/open/dummy.html", "dummy", "dummy", Version, Host), Config, + [{statuscode, 401}]), + %% Nested secret/top_secret OK! ["Aladdin:open sesame"] + ok = http_status(auth_request("/secret/top_secret/", "Aladdin", "AladdinPassword", Version, Host), + Config, [{statuscode, 200}]), + %% Authentication still required! + basic_auth_requiered(Config). + +ssi() -> + [{doc, "HTTP GET server side include test"}]. +ssi(Config) when is_list(Config) -> + Version = ?config(http_version, Config), + Host = ?config(host, Config), + ok = httpd_test_lib:verify_request(?config(type, Config), Host, ?config(port, Config), + ?config(node, Config), + http_request("GET /fsize.shtml ", Version, Host), + [{statuscode, 200}, + {header, "Content-Type", "text/html"}, + {header, "Date"}, + {header, "Server"}, + {version, Version}]). +host() -> + [{doc, "Test host header"}]. + +host(Config) when is_list(Config) -> + Cb = ?config(version_cb, Config), + Cb:host(?config(type, Config), ?config(port, Config), + ?config(host, Config), ?config(node, Config)). + +chunked() -> + [{doc, "Check that the server accepts chunked requests."}]. + +chunked(Config) when is_list(Config) -> + httpd_1_1:chunked(?config(type, Config), ?config(port, Config), + ?config(host, Config), ?config(node, Config)). + +expect() -> + ["Check that the server handles request with the expect header " + "field appropiate"]. +expect(Config) when is_list(Config) -> + httpd_1_1:expect(?config(type, Config), ?config(port, Config), + ?config(host, Config), ?config(node, Config)). + +esi() -> + [{doc, "Test mod_esi"}]. + +esi(Config) when is_list(Config) -> + ok = http_status("GET /eval?httpd_example:print(\"Hi!\") ", + Config, [{statuscode, 200}]), + ok = http_status("GET /eval?not_allowed:print(\"Hi!\") ", + Config, [{statuscode, 403}]), + ok = http_status("GET /eval?httpd_example:undef(\"Hi!\") ", + Config, [{statuscode, 500}]), + ok = http_status("GET /cgi-bin/erl/httpd_example ", + Config, [{statuscode, 400}]), + ok = http_status("GET /cgi-bin/erl/httpd_example:get ", + Config, [{statuscode, 200}]), + ok = http_status("GET /cgi-bin/erl/httpd_example:" + "get?input=4711 ", Config, + [{statuscode, 200}]), + ok = http_status("GET /cgi-bin/erl/httpd_example:post ", + Config, [{statuscode, 200}]), + ok = http_status("GET /cgi-bin/erl/not_allowed:post ", + Config, [{statuscode, 403}]), + ok = http_status("GET /cgi-bin/erl/httpd_example:undef ", + Config, [{statuscode, 404}]), + ok = http_status("GET /cgi-bin/erl/httpd_example/yahoo ", + Config, [{statuscode, 302}]), + %% Check "ErlScriptNoCache" directive (default: false) + ok = http_status("GET /cgi-bin/erl/httpd_example:get ", + Config, [{statuscode, 200}, + {no_header, "cache-control"}]). + +cgi() -> + [{doc, "Test mod_cgi"}]. + +cgi(Config) when is_list(Config) -> + {Script, Script2, Script3} = case test_server:os_type() of {win32, _} -> - "/cgi-bin/printenv.bat"; + {"printenv.bat", "printenv.sh", "cgi_echo.exe"}; _ -> - "/cgi-bin/printenv.sh" + {"printenv.sh", "printenv.bat", "cgi_echo"} end, - Requests = - ["GET " ++ Script ++ " HTTP/1.1\r\nHost:"++ Host ++"\r\n\r\n", - "GET /cgi-bin/erl/httpd_example/newformat HTTP/1.1\r\nHost:" - ++ Host ++"\r\n\r\n"], - httpd_1_1:mod_cgi_chunked_encoding_test(ip_comm, ?IP_PORT, - Host, - ?config(node, Config), - Requests), - ok. - -%------------------------------------------------------------------------- - -ipv6_hostname_ipcomm() -> - [{require, ipv6_hosts}]. -ipv6_hostname_ipcomm(X) -> - SocketType = ip_comm, - Port = ?IP_PORT, - ipv6_hostname(SocketType, Port, X). - -ipv6_hostname_essl() -> - [{require, ipv6_hosts}]. -ipv6_hostname_essl(X) -> - SocketType = essl, - Port = ?SSL_PORT, - ipv6_hostname(SocketType, Port, X). - -ipv6_hostname(_SocketType, _Port, doc) -> - ["Test standard ipv6 address"]; -ipv6_hostname(_SocketType, _Port, suite)-> - []; -ipv6_hostname(SocketType, Port, Config) when is_list(Config) -> - tsp("ipv6_hostname -> entry with" - "~n SocketType: ~p" - "~n Port: ~p" - "~n Config: ~p", [SocketType, Port, Config]), - Host = ?config(host, Config), - URI = "GET HTTP://" ++ - Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n", - tsp("ipv6_hostname -> Host: ~p", [Host]), - httpd_test_lib:verify_request(SocketType, Host, Port, [inet6], - node(), - URI, - [{statuscode, 200}, {version, "HTTP/1.1"}]), - ok. - -%%------------------------------------------------------------------------- - -ipv6_address_ipcomm() -> - [{require, ipv6_hosts}]. -ipv6_address_ipcomm(X) -> - SocketType = ip_comm, - Port = ?IP_PORT, - ipv6_address(SocketType, Port, X). - -ipv6_address_essl() -> - [{require, ipv6_hosts}]. -ipv6_address_essl(X) -> - SocketType = essl, - Port = ?SSL_PORT, - ipv6_address(SocketType, Port, X). - -ipv6_address(_SocketType, _Port, doc) -> - ["Test standard ipv6 address"]; -ipv6_address(_SocketType, _Port, suite)-> - []; -ipv6_address(SocketType, Port, Config) when is_list(Config) -> - tsp("ipv6_address -> entry with" - "~n SocketType: ~p" - "~n Port: ~p" - "~n Config: ~p", [SocketType, Port, Config]), - Host = ?config(host, Config), - tsp("ipv6_address -> Host: ~p", [Host]), - URI = "GET HTTP://" ++ - Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n", - httpd_test_lib:verify_request(SocketType, Host, Port, [inet6], - node(), - URI, - [{statuscode, 200}, {version, "HTTP/1.1"}]), - ok. - - -%%-------------------------------------------------------------------- -ticket_5775(doc) -> - ["Tests that content-length is correct"]; -ticket_5775(suite) -> - []; -ticket_5775(Config) -> - ok=httpd_test_lib:verify_request(ip_comm, ?config(host, Config), - ?IP_PORT, ?config(node, Config), - "GET /cgi-bin/erl/httpd_example:get_bin " - "HTTP/1.0\r\n\r\n", - [{statuscode, 200}, - {version, "HTTP/1.0"}]), - ok. -ticket_5865(doc) -> - ["Tests that a header without last-modified is handled"]; -ticket_5865(suite) -> - []; -ticket_5865(Config) -> - ?SKIP(as_of_r15_behaviour_of_calendar_has_changed), - Host = ?config(host,Config), - ServerRoot = ?config(server_root, Config), - DocRoot = filename:join([ServerRoot, "htdocs"]), - File = filename:join([DocRoot,"last_modified.html"]), - - Bad_mtime = case test_server:os_type() of - {win32, _} -> - {{1600,12,31},{23,59,59}}; - {unix, _} -> - {{1969,12,31},{23,59,59}} - end, - - {ok,FI}=file:read_file_info(File), - - case file:write_file_info(File,FI#file_info{mtime=Bad_mtime}) of - ok -> - ok = httpd_test_lib:verify_request(ip_comm, Host, - ?IP_PORT, ?config(node, Config), - "GET /last_modified.html" - " HTTP/1.1\r\nHost:" - ++Host++"\r\n\r\n", - [{statuscode, 200}, - {no_header, - "last-modified"}]), - ok; - {error, Reason} -> - Fault = - io_lib:format("Attempt to change the file info to set the" - " preconditions of the test case failed ~p~n", - [Reason]), - {skip, Fault} - end. - -ticket_5913(doc) -> - ["Tests that a header without last-modified is handled"]; -ticket_5913(suite) -> []; -ticket_5913(Config) -> - ok = httpd_test_lib:verify_request(ip_comm, ?config(host, Config), - ?IP_PORT, ?config(node, Config), - "GET /cgi-bin/erl/httpd_example:get_bin " - "HTTP/1.0\r\n\r\n", - [{statuscode, 200}, - {version, "HTTP/1.0"}]), - ok. -ticket_6003(doc) -> - ["Tests that a URI with a bad hexadecimal code is handled"]; -ticket_6003(suite) -> []; -ticket_6003(Config) -> - ok = httpd_test_lib:verify_request(ip_comm, ?config(host, Config), - ?IP_PORT, ?config(node, Config), - "GET http://www.erlang.org/%skalle " - "HTTP/1.0\r\n\r\n", - [{statuscode, 400}, - {version, "HTTP/1.0"}]), - ok. + %%The length (> 100) is intentional + ok = http_status("POST /cgi-bin/" ++ Script3 ++ " ", + {"Content-Length:100 \r\n", + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"}, + Config, + [{statuscode, 200}, + {header, "content-type", "text/plain"}]), + + ok = http_status("GET /cgi-bin/"++ Script ++ " ", Config, [{statuscode, 200}]), + + ok = http_status("GET /cgi-bin/not_there ", Config, + [{statuscode, 404}, {statuscode, 500}]), + + ok = http_status("GET /cgi-bin/"++ Script ++ "?Nisse:kkk?sss/lll ", + Config, + [{statuscode, 200}]), + + ok = http_status("POST /cgi-bin/"++ Script ++ " ", Config, + [{statuscode, 200}]), + + ok = http_status("GET /htbin/"++ Script ++ " ", Config, + [{statuscode, 200}]), + + ok = http_status("GET /htbin/not_there ", Config, + [{statuscode, 404},{statuscode, 500}]), + + ok = http_status("GET /htbin/"++ Script ++ "?Nisse:kkk?sss/lll ", Config, + [{statuscode, 200}]), + + ok = http_status("POST /htbin/"++ Script ++ " ", Config, + [{statuscode, 200}]), + + ok = http_status("POST /htbin/"++ Script ++ " ", Config, + [{statuscode, 200}]), + + %% Execute an existing, but bad CGI script.. + ok = http_status("POST /htbin/"++ Script2 ++ " ", Config, + [{statuscode, 404}]), + + ok = http_status("POST /cgi-bin/"++ Script2 ++ " ", Config, + [{statuscode, 404}]), + + %% Check "ScriptNoCache" directive (default: false) + ok = http_status("GET /cgi-bin/" ++ Script ++ " ", Config, + [{statuscode, 200}, + {no_header, "cache-control"}]). + +alias() -> + [{doc, "Test mod_alias"}]. + +alias(Config) when is_list(Config) -> + ok = http_status("GET /pics/icon.sheet.gif ", Config, + [{statuscode, 200}, + {header, "Content-Type","image/gif"}, + {header, "Server"}, + {header, "Date"}]), + + ok = http_status("GET / ", Config, + [{statuscode, 200}, + {header, "Content-Type","text/html"}, + {header, "Server"}, + {header, "Date"}]), + + ok = http_status("GET /misc/ ", Config, + [{statuscode, 200}, + {header, "Content-Type","text/html"}, + {header, "Server"}, + {header, "Date"}]), + + %% Check redirection if trailing slash is missing. + ok = http_status("GET /misc ", Config, + [{statuscode, 301}, + {header, "Location"}, + {header, "Content-Type","text/html"}]). + + +%% auth_api() -> +%% [{doc, "Test mod_auth API"}]. + +%% auth_api(Config) when is_list(Config) -> +%% Version = ?config(http_version, Config), +%% Host = ?config(host, Config), +%% ok = http_status("GET / ", Config, +%% [{statuscode, 200}]), +%% ok = auth_status(auth_request("/", "one", "WrongPassword", Version, Host), Config, +%% [{statuscode, 200}]), + +%% %% Make sure Authenticate header is received even the second time +%% %% we try a incorrect password! Otherwise a browser client will hang! +%% ok = auth_status(auth_request("/" ++ AuthStoreType ++ "open/", +%% "dummy", "WrongPassword", Host), Config, +%% [{statuscode, 401}, +%% {header, "WWW-Authenticate"}]), +%% ok = auth_status(auth_request("/" ++ AuthStoreType ++ "open/", "dummy", "WrongPassword", +%% Host), Config, [{statuscode, 401}, +%% {header, "WWW-Authenticate"}]), + +%% %% Change the password to DummyPassword then try to add a user +%% %% Get an error and set it to NoPassword +%% ok = update_password(Node, ServerRoot, Host, Port, AuthStoreType ++ +%% "open", "NoPassword", "DummyPassword"), +%% {error,bad_password} = +%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "open", "one", +%% "onePassword", []), +%% ok = update_password(Node, ServerRoot, Host, Port, AuthStoreType ++"open", +%% "DummyPassword", "NoPassword"), + +%% %% Test /*open, require user one Aladdin +%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType ++ "open"), + +%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/", +%% "one", "onePassword", [{statuscode, 401}]), + +%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/", +%% "two", "twoPassword", [{statuscode, 401}]), + +%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/", +%% "Aladdin", "onePassword", [{statuscode, 401}]), + +%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "open", "one", +%% "onePassword", []), +%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "open", "two", +%% "twoPassword", []), +%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "open", "Aladdin", +%% "AladdinPassword", []), + +%% {ok, [_|_]} = list_users(Node, ServerRoot, Host, Port, +%% AuthStoreType++"open"), +%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++ "open/", +%% "one", "WrongPassword", [{statuscode, 401}]), +%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++ "open/", +%% "one", "onePassword", [{statuscode, 200}]), +%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/", +%% "two", "twoPassword", [{statuscode, 401}]), +%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++ "open/", +%% "Aladdin", "WrongPassword", [{statuscode, 401}]), +%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/", +%% "Aladdin", "AladdinPassword", [{statuscode, 200}]), + +%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType++"open"), +%% {ok, []} = list_users(Node, ServerRoot, Host, Port, +%% AuthStoreType++"open"), + +%% %% Phase 2 +%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType++"secret"), +%% {ok, []} = list_users(Node, ServerRoot, Host, Port, AuthStoreType ++ +%% "secret"), +%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/", +%% "one", "onePassword", [{statuscode, 401}]), +%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/", +%% "two", "twoPassword", [{statuscode, 401}]), +%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++ "secret/", +%% "three", "threePassword", [{statuscode, 401}]), +%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "secret", "one", +%% "onePassword", +%% []), +%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "secret", +%% "two", "twoPassword", []), +%% add_user(Node, ServerRoot, Port, AuthStoreType++"secret", "Aladdin", +%% "AladdinPassword",[]), +%% add_group_member(Node, ServerRoot, Port, AuthStoreType ++ "secret", +%% "one", "group1"), +%% add_group_member(Node, ServerRoot, Port, AuthStoreType ++ "secret", +%% "two", "group1"), +%% add_group_member(Node, ServerRoot, Port, AuthStoreType ++ +%% "secret", "Aladdin", "group2"), +%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/", +%% "one", "onePassword", [{statuscode, 200}]), +%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/", +%% "two", "twoPassword", [{statuscode, 200}]), +%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/", +%% "Aladdin", "AladdinPassword", [{statuscode, 200}]), +%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "secret/", +%% "three", "threePassword", [{statuscode, 401}]), +%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType ++ "secret"), +%% {ok, []} = list_users(Node, ServerRoot, Host, Port, +%% AuthStoreType ++ "secret"), +%% remove_groups(Node, ServerRoot, Host, Port, AuthStoreType ++ "secret"), +%% Directory = filename:join([ServerRoot, "htdocs", AuthStoreType ++ +%% "secret"]), +%% {ok, []} = list_groups(Node, ServerRoot, Host, Port, Directory), + +%% %% Phase 3 +%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType ++ +%% "secret/top_secret"), +%% remove_groups(Node, ServerRoot, Host, Port, AuthStoreType ++ +%% "secret/top_secret"), +%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ +%% "secret/top_secret/", +%% "three", "threePassword", [{statuscode, 401}]), +%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ +%% "secret/top_secret/", "two", "twoPassword", +%% [{statuscode, 401}]), +%% add_user(Node, ServerRoot, Port, AuthStoreType ++ +%% "secret/top_secret","three", +%% "threePassword",[]), +%% add_user(Node, ServerRoot, Port, AuthStoreType ++ "secret/top_secret", +%% "two","twoPassword", []), +%% add_group_member(Node, ServerRoot, Port, AuthStoreType ++ +%% "secret/top_secret", +%% "three", "group3"), +%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ +%% "secret/top_secret/", "three", "threePassword", +%% [{statuscode, 200}]), +%% auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ +%% "secret/top_secret/", "two", "twoPassword", +%% [{statuscode, 401}]), +%% add_group_member(Node, ServerRoot, Port, AuthStoreType ++ +%% "secret/top_secret", +%% "two", "group3"), +%% auth_request(Type,Host,Port,Node,"/" ++ AuthStoreType ++ +%% "secret/top_secret/", +%% "two", "twoPassword", [{statuscode, 200}]), +%% remove_users(Node, ServerRoot, Host, Port, AuthStoreType ++ +%% "secret/top_secret"), +%% {ok, []} = list_users(Node, ServerRoot, Host, Port, +%% AuthStoreType ++ "secret/top_secret"), +%% remove_groups(Node, ServerRoot, Host, Port, AuthStoreType ++ +%% "secret/top_secret"), +%% Directory2 = filename:join([ServerRoot, "htdocs", +%% AuthStoreType ++ "secret/top_secret"]), +%% {ok, []} = list_groups(Node, ServerRoot, Host, Port, Directory2), +%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++ +%% "secret/top_secret/", "two", "twoPassword", +%% [{statuscode, 401}]), +%% auth_request(Type, Host, Port, Node, "/" ++ AuthStoreType ++ +%% "secret/top_secret/","three", "threePassword", +%% [{statuscode, 401}]). -ticket_7304(doc) -> - ["Tests missing CR in delimiter"]; -ticket_7304(suite) -> - []; -ticket_7304(Config) -> - ok = httpd_test_lib:verify_request(ip_comm, ?config(host, Config), - ?IP_PORT, ?config(node, Config), - "GET / HTTP/1.0\r\n\n", - [{statuscode, 200}, - {version, "HTTP/1.0"}]), - ok. %%-------------------------------------------------------------------- -%% Internal functions +%% Internal functions ----------------------------------- %%-------------------------------------------------------------------- -dos_hostname(Type, Port, Host, Node, Max) -> - H1 = {"", 200}, - H2 = {"dummy-host.ericsson.se", 200}, - TooLongHeader = lists:append(lists:duplicate(Max + 1, "a")), - H3 = {TooLongHeader, 403}, - Hosts = [H1,H2,H3], - dos_hostname_poll(Type, Host, Port, Node, Hosts). -%% make_ipv6(T) when is_tuple(T) andalso (size(T) =:= 8) -> -%% make_ipv6(tuple_to_list(T)); +setup_server_dirs(ServerRoot, DocRoot, DataDir) -> + CgiDir = filename:join(ServerRoot, "cgi-bin"), + AuthDir = filename:join(ServerRoot, "auth"), + PicsDir = filename:join(ServerRoot, "icons"), -%% make_ipv6([_, _, _, _, _, _, _, _] = IPV6) -> -%% lists:flatten(io_lib:format("~s:~s:~s:~s:~s:~s:~s:~s", IPV6)). + ok = file:make_dir(ServerRoot), + ok = file:make_dir(DocRoot), + ok = file:make_dir(CgiDir), + ok = file:make_dir(AuthDir), + ok = file:make_dir(PicsDir), + + DocSrc = filename:join(DataDir, "server_root/htdocs"), + AuthSrc = filename:join(DataDir, "server_root/auth"), + CgiSrc = filename:join(DataDir, "server_root/cgi-bin"), + PicsSrc = filename:join(DataDir, "server_root/icons"), + + inets_test_lib:copy_dirs(DocSrc, DocRoot), + inets_test_lib:copy_dirs(AuthSrc, AuthDir), + inets_test_lib:copy_dirs(CgiSrc, CgiDir), + inets_test_lib:copy_dirs(PicsSrc, PicsDir), + + Cgi = case test_server:os_type() of + {win32, _} -> + "cgi_echo.exe"; + _ -> + "cgi_echo" + end, + inets_test_lib:copy_file(Cgi, DataDir, CgiDir), + AbsCgi = filename:join([CgiDir, Cgi]), + {ok, FileInfo} = file:read_file_info(AbsCgi), + ok = file:write_file_info(AbsCgi, FileInfo#file_info{mode = 8#00755}), -%%-------------------------------------------------------------------- -%% Other help functions -create_config(Config, Access, FileName) -> - ServerRoot = ?config(server_root, Config), - TcTopDir = ?config(tc_top_dir, Config), - Port = ?config(port, Config), - Type = ?config(sock_type, Config), - Host = ?config(host, Config), - Mods = io_lib:format("~p", [httpd_mod]), - Funcs = io_lib:format("~p", [ssl_password_cb]), - MaxHdrSz = io_lib:format("~p", [256]), - MaxHdrAct = io_lib:format("~p", [close]), - - io:format(user, - "create_config -> " - "~n ServerRoot: ~p" - "~n TcTopDir: ~p" - "~n Type: ~p" - "~n Port: ~p" - "~n Host: ~p" - "~n", [ServerRoot, TcTopDir, Type, Port, Host]), - - SSL = - if - (Type =:= ssl) orelse - (Type =:= essl) -> - [cline(["SSLCertificateFile ", - filename:join(ServerRoot, "ssl/ssl_server.pem")]), - cline(["SSLCertificateKeyFile ", - filename:join(ServerRoot, "ssl/ssl_server.pem")]), - cline(["SSLCACertificateFile ", - filename:join(ServerRoot, "ssl/ssl_server.pem")]), - cline(["SSLPasswordCallbackModule ", Mods]), - cline(["SSLPasswordCallbackFunction ", Funcs]), - cline(["SSLVerifyClient 0"]), - cline(["SSLVerifyDepth 1"])]; - true -> - [] - end, - ModOrder = - case Access of - mod_htaccess -> - "Modules mod_alias mod_htaccess mod_auth " - "mod_security " - "mod_responsecontrol mod_trace mod_esi " - "mod_actions mod_cgi mod_include mod_dir " - "mod_range mod_get " - "mod_head mod_log mod_disk_log"; - _ -> - "Modules mod_alias mod_auth mod_security " - "mod_responsecontrol mod_trace mod_esi " - "mod_actions mod_cgi mod_include mod_dir " - "mod_range mod_get " - "mod_head mod_log mod_disk_log" - end, - - %% The test suite currently does not handle an explicit BindAddress. - %% They assume any has been used, that is Addr is always set to undefined! - - %% {ok, Hostname} = inet:gethostname(), - %% {ok, Addr} = inet:getaddr(Hostname, inet6), - %% AddrStr = make_ipv6(Addr), - %% BindAddress = lists:flatten(io_lib:format("~s|inet6", [AddrStr])), - - BindAddress = "*|inet", - %% BindAddress = "*", - - HttpConfig = [ - cline(["Port ", integer_to_list(Port)]), - cline(["ServerName ", Host]), - cline(["SocketType ", atom_to_list(Type)]), - cline([ModOrder]), - %% cline(["LogFormat ", "erlang"]), - cline(["ServerAdmin mattias@erix.ericsson.se"]), - cline(["BindAddress ", BindAddress]), - cline(["ServerRoot ", ServerRoot]), - cline(["ErrorLog ", TcTopDir, - "/logs/error_log_", integer_to_list(Port)]), - cline(["TransferLog ", TcTopDir, - "/logs/access_log_", integer_to_list(Port)]), - cline(["SecurityLog ", TcTopDir, - "/logs/security_log_", integer_to_list(Port)]), - cline(["ErrorDiskLog ", TcTopDir, - "/logs/error_disk_log_", integer_to_list(Port)]), - cline(["ErrorDiskLogSize ", "190000 ", "11"]), - cline(["TransferDiskLog ", TcTopDir, - "/logs/access_disk_log_", integer_to_list(Port)]), - cline(["TransferDiskLogSize ", "200000 ", "10"]), - cline(["SecurityDiskLog ", TcTopDir, - "/logs/security_disk_log_", integer_to_list(Port)]), - cline(["SecurityDiskLogSize ", "210000 ", "9"]), - cline(["MaxClients 10"]), - cline(["MaxHeaderSize ", MaxHdrSz]), - cline(["MaxHeaderAction ", MaxHdrAct]), - cline(["DocumentRoot ", - filename:join(ServerRoot, "htdocs")]), - cline(["DirectoryIndex ", "index.html ", "welcome.html"]), - cline(["DefaultType ", "text/plain"]), - SSL, - mod_alias_config(ServerRoot), - - config_directory(filename:join([ServerRoot,"htdocs", - "open"]), - "Open Area", - filename:join(ServerRoot, "auth/passwd"), - filename:join(ServerRoot, "auth/group"), - plain, - "user one Aladdin", - filename:join(ServerRoot, "security_data")), - config_directory(filename:join([ServerRoot,"htdocs", - "secret"]), - "Secret Area", - filename:join(ServerRoot, "auth/passwd"), - filename:join(ServerRoot, "auth/group"), - plain, - "group group1 group2", - filename:join(ServerRoot, "security_data")), - config_directory(filename:join([ServerRoot,"htdocs", - "secret", - "top_secret"]), - "Top Secret Area", - filename:join(ServerRoot, "auth/passwd"), - filename:join(ServerRoot, "auth/group"), - plain, - "group group3", - filename:join(ServerRoot, "security_data")), - - config_directory(filename:join([ServerRoot,"htdocs", - "dets_open"]), - "Dets Open Area", - filename:join(ServerRoot, "passwd"), - filename:join(ServerRoot, "group"), - dets, - "user one Aladdin", - filename:join(ServerRoot, "security_data")), - config_directory(filename:join([ServerRoot,"htdocs", - "dets_secret"]), - "Dets Secret Area", - filename:join(ServerRoot, "passwd"), - filename:join(ServerRoot, "group"), - dets, - "group group1 group2", - filename:join(ServerRoot, "security_data")), - config_directory(filename:join([ServerRoot,"htdocs", - "dets_secret", - "top_secret"]), - "Dets Top Secret Area", - filename:join(ServerRoot, "passwd"), - filename:join(ServerRoot, "group"), - dets, - "group group3", - filename:join(ServerRoot, "security_data")), - - config_directory(filename:join([ServerRoot,"htdocs", - "mnesia_open"]), - "Mnesia Open Area", - false, - false, - mnesia, - "user one Aladdin", - filename:join(ServerRoot, "security_data")), - config_directory(filename:join([ServerRoot,"htdocs", - "mnesia_secret"]), - "Mnesia Secret Area", - false, - false, - mnesia, - "group group1 group2", - filename:join(ServerRoot, "security_data")), - config_directory(filename:join( - [ServerRoot, "htdocs", "mnesia_secret", - "top_secret"]), - "Mnesia Top Secret Area", - false, - false, - mnesia, - "group group3", - filename:join(ServerRoot, "security_data")) - ], - ConfigFile = filename:join([TcTopDir, FileName]), - {ok, Fd} = file:open(ConfigFile, [write]), - ok = file:write(Fd, lists:flatten(HttpConfig)), - ok = file:close(Fd). - -config_directory(Dir, AuthName, AuthUserFile, AuthGroupFile, AuthDBType, - Require, SF) -> - file:delete(SF), - [ - cline([""]), - cline(["SecurityDataFile ", SF]), - cline(["SecurityMaxRetries 3"]), - cline(["SecurityFailExpireTime ", integer_to_list(?FAIL_EXPIRE_TIME)]), - cline(["SecurityBlockTime 1"]), - cline(["SecurityAuthTimeout ", integer_to_list(?AUTH_TIMEOUT)]), - cline(["SecurityCallbackModule ", "httpd_mod"]), - cline_if_set("AuthUserFile", AuthUserFile), - cline_if_set("AuthGroupFile", AuthGroupFile), - cline_if_set("AuthName", AuthName), - cline_if_set("AuthDBType", AuthDBType), - cline(["require ", Require]), - cline(["\r\n"]) - ]. + EnvCGI = filename:join([ServerRoot, "cgi-bin", "printenv.sh"]), + {ok, FileInfo1} = file:read_file_info(EnvCGI), + ok = file:write_file_info(EnvCGI, + FileInfo1#file_info{mode = 8#00755}). -mod_alias_config(Root) -> - [ - cline(["Alias /icons/ ", filename:join(Root,"icons"), "/"]), - cline(["Alias /pics/ ", filename:join(Root, "icons"), "/"]), - cline(["ScriptAlias /cgi-bin/ ", filename:join(Root, "cgi-bin"), "/"]), - cline(["ScriptAlias /htbin/ ", filename:join(Root, "cgi-bin"), "/"]), - cline(["ErlScriptAlias /cgi-bin/erl httpd_example io"]), - cline(["EvalScriptAlias /eval httpd_example io"]) - ]. +start_apps(https) -> + inets_test_lib:start_apps([crypto, public_key, ssl]); +start_apps(_) -> + ok. -cline(List) -> - lists:flatten([List, "\r\n"]). +server_start(_, HttpdConfig) -> + {ok, Pid} = inets:start(httpd, HttpdConfig), + Serv = inets:services_info(), + {value, {_, _, Info}} = lists:keysearch(Pid, 2, Serv), + proplists:get_value(port, Info). -cline_if_set(_, false) -> - []; -cline_if_set(Name, Var) when is_list(Var) -> - cline([Name, " ", Var]); -cline_if_set(Name, Var) when is_atom(Var) -> - cline([Name, " ", atom_to_list(Var)]). -getaddr() -> - {ok,HostName} = inet:gethostname(), - {ok,{A1,A2,A3,A4}} = inet:getaddr(HostName,inet), - lists:flatten(io_lib:format("~p.~p.~p.~p",[A1,A2,A3,A4])). +server_config(http, Config) -> + ServerRoot = ?config(server_root, Config), + [{port, 0}, + {server_name,"httpd_test"}, + {server_root, ServerRoot}, + {document_root, ?config(doc_root, Config)}, + {bind_address, any}, + {ipfamily, inet}, + {max_header_size, 256}, + {max_header_action, close}, + {mime_types, [{"html","text/html"},{"htm","text/html"}, {"shtml","text/html"}, + {"gif", "image/gif"}]}, + {alias, {"/icons/", filename:join(ServerRoot,"icons") ++ "/"}}, + {alias, {"/pics/", filename:join(ServerRoot,"icons") ++ "/"}}, + {script_alias, {"/cgi-bin/", filename:join(ServerRoot, "cgi-bin") ++ "/"}}, + {script_alias, {"/htbin/", filename:join(ServerRoot, "cgi-bin") ++ "/"}}, + {erl_script_alias, {"/cgi-bin/erl", [httpd_example, io]}}, + {eval_script_alias, {"/eval", [httpd_example, io]}} + ] ++ auth_conf(ServerRoot); +server_config(_, _) -> + []. + +http_request(Request, "HTTP/1.1" = Version, Host, {Headers, Body}) -> + Request ++ Version ++ "\r\nhost:" ++ Host ++ "\r\n" ++ Headers ++ "\r\n" ++ Body; +http_request(Request, Version, _, {Headers, Body}) -> + Request ++ Version ++ "\r\n" ++ Headers ++ "\r\n" ++ Body. + +http_request(Request, "HTTP/1.1" = Version, Host) -> + Request ++ Version ++ "\r\nhost:" ++ Host ++ "\r\n\r\n"; +http_request(Request, Version, _) -> + Request ++ Version ++ "\r\n\r\n". + +auth_request(Path, User, Passwd, "HTTP/1.1" = Version, Host) -> + "GET " ++ Path ++ " " ++ Version ++ "\r\nhost:" ++ Host ++ + "\r\nAuthorization: Basic " ++ + base64:encode_to_string(User++":"++Passwd) ++ + "\r\n\r\n"; +auth_request(Path, User, Passwd, Version, _Host) -> + "GET " ++ Path ++ " " ++ Version ++ + "\r\nAuthorization: Basic " ++ + base64:encode_to_string(User++":"++Passwd) ++ + "\r\n\r\n". + +head_status("HTTP/0.9") -> + 501; %% Not implemented in HTTP/0.9 +head_status(_) -> + 200. + +auth_conf(Root) -> + [{directory, {filename:join(Root, "htdocs/open"), + [{auth_type, plain}, + {auth_name, "Open Area"}, + {auth_user_file, filename:join(Root, "auth/passwd")}, + {auth_group_file, filename:join(Root, "auth/group")}, + {require_user, ["one", "Aladdin"]}]}}, + {directory, {filename:join(Root, "htdocs/secret"), + [{auth_type, plain}, + {auth_name, "Secret Area"}, + {auth_user_file, filename:join(Root, "auth/passwd")}, + {auth_group_file, filename:join(Root, "auth/group")}, + {require_group, ["group1", "group2"]}]}}, + {directory, {filename:join(Root, "htdocs/secret/top_secret"), + [{auth_type, plain}, + {auth_name, "Top Secret Area"}, + {auth_user_file, filename:join(Root, "auth/passwd")}, + {auth_group_file, filename:join(Root, "auth/group")}, + {require_group, ["group3"]}]}}, + {directory, {filename:join(Root, "htdocs/open"), + [{auth_type, mnesia}, + {auth_name, "Open Area"}, + {auth_user_file, filename:join(Root, "auth/passwd")}, + {auth_group_file, filename:join(Root, "auth/group")}, + {require_user, ["one", "Aladdin"]}]}}, + {directory, {filename:join(Root, "htdocs/secret"), + [{auth_type, mnesia}, + {auth_name, "Secret Area"}, + {auth_user_file, filename:join(Root, "auth/passwd")}, + {auth_group_file, filename:join(Root, "auth/group")}, + {require_group, ["group1", "group2"]}]}} + ]. -start_mnesia(Node) -> - case rpc:call(Node, ?MODULE, cleanup_mnesia, []) of - ok -> - ok; - Other -> - tsf({failed_to_cleanup_mnesia, Other}) - end, - case rpc:call(Node, ?MODULE, setup_mnesia, []) of - {atomic, ok} -> - ok; - Other2 -> - tsf({failed_to_setup_mnesia, Other2}) - end, - ok. -setup_mnesia() -> - setup_mnesia([node()]). - -setup_mnesia(Nodes) -> - ok = mnesia:create_schema(Nodes), - ok = mnesia:start(), - {atomic, ok} = mnesia:create_table(httpd_user, - [{attributes, - record_info(fields, httpd_user)}, - {disc_copies,Nodes}, {type, set}]), - {atomic, ok} = mnesia:create_table(httpd_group, - [{attributes, - record_info(fields, - httpd_group)}, - {disc_copies,Nodes}, {type,bag}]). - -cleanup_mnesia() -> - mnesia:start(), - mnesia:delete_table(httpd_user), - mnesia:delete_table(httpd_group), - stopped = mnesia:stop(), - mnesia:delete_schema([node()]), - ok. +http_status(Request, Config, Expected) -> + Version = ?config(http_version, Config), + Host = ?config(host, Config), + httpd_test_lib:verify_request(?config(type, Config), Host, + ?config(port, Config), ?config(node, Config), + http_request(Request, Version, Host), + Expected ++ [{version, Version}]). -create_htaccess_data(Path, IpAddress)-> - create_htaccess_dirs(Path), - - create_html_file(filename:join([Path,"ht/open/dummy.html"])), - create_html_file(filename:join([Path,"ht/blocknet/dummy.html"])), - create_html_file(filename:join([Path,"ht/secret/dummy.html"])), - create_html_file(filename:join([Path,"ht/secret/top_secret/dummy.html"])), - - create_htaccess_file(filename:join([Path,"ht/open/.htaccess"]), - Path, "user one Aladdin"), - create_htaccess_file(filename:join([Path,"ht/secret/.htaccess"]), - Path, "group group1 group2"), - create_htaccess_file(filename:join([Path, - "ht/secret/top_secret/.htaccess"]), - Path, "user four"), - create_htaccess_file(filename:join([Path,"ht/blocknet/.htaccess"]), - Path, nouser, IpAddress), - - create_user_group_file(filename:join([Path,"ht","users.file"]), - "one:OnePassword\ntwo:TwoPassword\nthree:" - "ThreePassword\nfour:FourPassword\nAladdin:" - "AladdinPassword"), - create_user_group_file(filename:join([Path,"ht","groups.file"]), - "group1: two one\ngroup2: two three"). - -create_html_file(PathAndFileName)-> - file:write_file(PathAndFileName,list_to_binary( - "test - testar")). - -create_htaccess_file(PathAndFileName, BaseDir, RequireData)-> - file:write_file(PathAndFileName, - list_to_binary( - "AuthUserFile "++ BaseDir ++ - "/ht/users.file\nAuthGroupFile "++ BaseDir - ++ "/ht/groups.file\nAuthName Test\nAuthType" - " Basic\n\nrequire " ++ RequireData ++ - "\n")). - -create_htaccess_file(PathAndFileName, BaseDir, nouser, IpAddress)-> - file:write_file(PathAndFileName,list_to_binary( - "AuthUserFile "++ BaseDir ++ - "/ht/users.file\nAuthGroupFile " ++ - BaseDir ++ "/ht/groups.file\nAuthName" - " Test\nAuthType" - " Basic\n\n\tallow from " ++ - format_ip(IpAddress, - string:rchr(IpAddress,$.)) ++ - "\n")). - -create_user_group_file(PathAndFileName, Data)-> - file:write_file(PathAndFileName, list_to_binary(Data)). - -create_htaccess_dirs(Path)-> - ok = file:make_dir(filename:join([Path,"ht"])), - ok = file:make_dir(filename:join([Path,"ht/open"])), - ok = file:make_dir(filename:join([Path,"ht/blocknet"])), - ok = file:make_dir(filename:join([Path,"ht/secret"])), - ok = file:make_dir(filename:join([Path,"ht/secret/top_secret"])). - -remove_htaccess_dirs(Path)-> - file:del_dir(filename:join([Path,"ht/secret/top_secret"])), - file:del_dir(filename:join([Path,"ht/secret"])), - file:del_dir(filename:join([Path,"ht/blocknet"])), - file:del_dir(filename:join([Path,"ht/open"])), - file:del_dir(filename:join([Path,"ht"])). - -format_ip(IpAddress,Pos)when Pos > 0-> - case lists:nth(Pos,IpAddress) of - $.-> - case lists:nth(Pos-2,IpAddress) of - $.-> - format_ip(IpAddress,Pos-3); - _-> - lists:sublist(IpAddress,Pos-2) ++ "." - end; - _ -> - format_ip(IpAddress,Pos-1) - end; +http_status(Request, HeadersAndBody, Config, Expected) -> + Version = ?config(http_version, Config), + Host = ?config(host, Config), + httpd_test_lib:verify_request(?config(type, Config), Host, + ?config(port, Config), ?config(node, Config), + http_request(Request, Version, Host, HeadersAndBody), + Expected ++ [{version, Version}]). -format_ip(IpAddress, _Pos)-> - "1" ++ IpAddress. - -remove_htaccess(Path)-> - file:delete(filename:join([Path,"ht/open/dummy.html"])), - file:delete(filename:join([Path,"ht/secret/dummy.html"])), - file:delete(filename:join([Path,"ht/secret/top_secret/dummy.html"])), - file:delete(filename:join([Path,"ht/blocknet/dummy.html"])), - file:delete(filename:join([Path,"ht/blocknet/.htaccess"])), - file:delete(filename:join([Path,"ht/open/.htaccess"])), - file:delete(filename:join([Path,"ht/secret/.htaccess"])), - file:delete(filename:join([Path,"ht/secret/top_secret/.htaccess"])), - file:delete(filename:join([Path,"ht","users.file"])), - file:delete(filename:join([Path,"ht","groups.file"])), - remove_htaccess_dirs(Path). - - -dos_hostname_poll(Type, Host, Port, Node, Hosts) -> - [dos_hostname_poll1(Type, Host, Port, Node, Host1, Code) - || {Host1,Code} <- Hosts]. - -dos_hostname_poll1(Type, Host, Port, Node, Host1, Code) -> - ok = httpd_test_lib:verify_request(Type, Host, Port, Node, - dos_hostname_request(Host1), - [{statuscode, Code}, - {version, "HTTP/1.0"}]). - -dos_hostname_request(Host) -> - "GET / HTTP/1.0\r\n" ++ Host ++ "\r\n\r\n". - -get_nof_clients(Mode, Load) -> - get_nof_clients(test_server:os_type(), Mode, Load). - -get_nof_clients(vxworks, _, light) -> 1; -get_nof_clients(vxworks, ip_comm, medium) -> 3; -get_nof_clients(vxworks, ssl, medium) -> 3; -get_nof_clients(vxworks, ip_comm, heavy) -> 5; -get_nof_clients(vxworks, ssl, heavy) -> 5; -get_nof_clients(_, ip_comm, light) -> 5; -get_nof_clients(_, ssl, light) -> 2; -get_nof_clients(_, ip_comm, medium) -> 10; -get_nof_clients(_, ssl, medium) -> 4; -get_nof_clients(_, ip_comm, heavy) -> 20; -get_nof_clients(_, ssl, heavy) -> 6. - -%% Make a file 100 bytes long containing 012...9*10 -create_range_data(Path) -> - PathAndFileName=filename:join([Path,"range.txt"]), - file:write_file(PathAndFileName,list_to_binary(["12345678901234567890", - "12345678901234567890", - "12345678901234567890", - "12345678901234567890", - "12345678901234567890"])). - -create_ipv6_config(Config, FileName, Ipv6Address) -> - ServerRoot = ?config(server_root, Config), - TcTopDir = ?config(tc_top_dir, Config), - Port = ?config(port, Config), - SockType = ?config(sock_type, Config), - Mods = io_lib:format("~p", [httpd_mod]), - Funcs = io_lib:format("~p", [ssl_password_cb]), - Host = ?config(ipv6_host, Config), - - MaxHdrSz = io_lib:format("~p", [256]), - MaxHdrAct = io_lib:format("~p", [close]), - - Mod_order = "Modules mod_alias mod_auth mod_esi mod_actions mod_cgi" - " mod_include mod_dir mod_get mod_head" - " mod_log mod_disk_log mod_trace", - - SSL = - if - (SockType =:= ssl) orelse - (SockType =:= essl) -> - [cline(["SSLCertificateFile ", - filename:join(ServerRoot, "ssl/ssl_server.pem")]), - cline(["SSLCertificateKeyFile ", - filename:join(ServerRoot, "ssl/ssl_server.pem")]), - cline(["SSLCACertificateFile ", - filename:join(ServerRoot, "ssl/ssl_server.pem")]), - cline(["SSLPasswordCallbackModule ", Mods]), - cline(["SSLPasswordCallbackFunction ", Funcs]), - cline(["SSLVerifyClient 0"]), - cline(["SSLVerifyDepth 1"])]; - true -> - [] - end, +auth_status(AuthRequest, Config, Expected) -> + Version = ?config(http_version, Config), + Host = ?config(host, Config), + httpd_test_lib:verify_request(?config(type, Config), Host, + ?config(port, Config), ?config(node, Config), + AuthRequest, + Expected ++ [{version, Version}]). + +basic_auth_requiered(Config) -> + ok = http_status("GET /secret/dummy.html ", Config, [{statuscode, 401}, + {header, "WWW-Authenticate"}]), + ok = http_status("GET /open/ ", Config, [{statuscode, 401}, + {header, "WWW-Authenticate"}]), + ok = http_status("GET /secret/ ", Config, [{statuscode, 401}, + {header, "WWW-Authenticate"}]), + ok = http_status("GET /secret/top_secret ", Config, [{statuscode, 401}, + {header, "WWW-Authenticate"}]). - BindAddress = "[" ++ Ipv6Address ++"]|inet6", - - HttpConfig = - [cline(["BindAddress ", BindAddress]), - cline(["Port ", integer_to_list(Port)]), - cline(["ServerName ", Host]), - cline(["SocketType ", atom_to_list(SockType)]), - cline([Mod_order]), - cline(["ServerRoot ", ServerRoot]), - cline(["DocumentRoot ", filename:join(ServerRoot, "htdocs")]), - cline(["MaxHeaderSize ",MaxHdrSz]), - cline(["MaxHeaderAction ",MaxHdrAct]), - cline(["DirectoryIndex ", "index.html "]), - cline(["DefaultType ", "text/plain"]), - SSL], - ConfigFile = filename:join([TcTopDir,FileName]), - {ok, Fd} = file:open(ConfigFile, [write]), - ok = file:write(Fd, lists:flatten(HttpConfig)), - ok = file:close(Fd). - - -tsp(F) -> - inets_test_lib:tsp("[~w]" ++ F, [?MODULE]). -tsp(F, A) -> - inets_test_lib:tsp("[~w]" ++ F, [?MODULE|A]). - -tsf(Reason) -> - inets_test_lib:tsf(Reason). diff --git a/lib/inets/test/httpd_SUITE_data/server_root/Makefile b/lib/inets/test/httpd_SUITE_data/server_root/Makefile deleted file mode 100644 index d7a3231068..0000000000 --- a/lib/inets/test/httpd_SUITE_data/server_root/Makefile +++ /dev/null @@ -1,209 +0,0 @@ -# -# %CopyrightBegin% -# -# Copyright Ericsson AB 1997-2010. 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. -# -# %CopyrightEnd% -# -# -include $(ERL_TOP)/make/target.mk -include $(ERL_TOP)/make/$(TARGET)/otp.mk - -# ---------------------------------------------------- -# Application version -# ---------------------------------------------------- -include ../../vsn.mk -VSN=$(INETS_VSN) - -# ---------------------------------------------------- -# Release directory specification -# ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN) - -# ---------------------------------------------------- -# Target Specs -# ---------------------------------------------------- -MODULE= - -AUTH_FILES = auth/group \ - auth/passwd -CGI_FILES = cgi-bin/printenv.sh -CONF_FILES = conf/8080.conf \ - conf/8888.conf \ - conf/httpd.conf \ - conf/ssl.conf \ - conf/mime.types -OPEN_FILES = htdocs/open/dummy.html -MNESIA_OPEN_FILES = htdocs/mnesia_open/dummy.html -MISC_FILES = htdocs/misc/friedrich.html \ - htdocs/misc/oech.html -SECRET_FILES = htdocs/secret/dummy.html -MNESIA_SECRET_FILES = htdocs/mnesia_secret/dummy.html -HTDOCS_FILES = htdocs/index.html \ - htdocs/config.shtml \ - htdocs/echo.shtml \ - htdocs/exec.shtml \ - htdocs/flastmod.shtml \ - htdocs/fsize.shtml \ - htdocs/include.shtml -ICON_FILES = icons/README \ - icons/a.gif \ - icons/alert.black.gif \ - icons/alert.red.gif \ - icons/apache_pb.gif \ - icons/back.gif \ - icons/ball.gray.gif \ - icons/ball.red.gif \ - icons/binary.gif \ - icons/binhex.gif \ - icons/blank.gif \ - icons/bomb.gif \ - icons/box1.gif \ - icons/box2.gif \ - icons/broken.gif \ - icons/burst.gif \ - icons/button1.gif \ - icons/button10.gif \ - icons/button2.gif \ - icons/button3.gif \ - icons/button4.gif \ - icons/button5.gif \ - icons/button6.gif \ - icons/button7.gif \ - icons/button8.gif \ - icons/button9.gif \ - icons/buttonl.gif \ - icons/buttonr.gif \ - icons/c.gif \ - icons/comp.blue.gif \ - icons/comp.gray.gif \ - icons/compressed.gif \ - icons/continued.gif \ - icons/dir.gif \ - icons/down.gif \ - icons/dvi.gif \ - icons/f.gif \ - icons/folder.gif \ - icons/folder.open.gif \ - icons/folder.sec.gif \ - icons/forward.gif \ - icons/generic.gif \ - icons/generic.red.gif \ - icons/generic.sec.gif \ - icons/hand.right.gif \ - icons/hand.up.gif \ - icons/htdig.gif \ - icons/icon.sheet.gif \ - icons/image1.gif \ - icons/image2.gif \ - icons/image3.gif \ - icons/index.gif \ - icons/layout.gif \ - icons/left.gif \ - icons/link.gif \ - icons/movie.gif \ - icons/p.gif \ - icons/patch.gif \ - icons/pdf.gif \ - icons/pie0.gif \ - icons/pie1.gif \ - icons/pie2.gif \ - icons/pie3.gif \ - icons/pie4.gif \ - icons/pie5.gif \ - icons/pie6.gif \ - icons/pie7.gif \ - icons/pie8.gif \ - icons/portal.gif \ - icons/poweredby.gif \ - icons/ps.gif \ - icons/quill.gif \ - icons/right.gif \ - icons/screw1.gif \ - icons/screw2.gif \ - icons/script.gif \ - icons/sound1.gif \ - icons/sound2.gif \ - icons/sphere1.gif \ - icons/sphere2.gif \ - icons/star.gif \ - icons/star_blank.gif \ - icons/tar.gif \ - icons/tex.gif \ - icons/text.gif \ - icons/transfer.gif \ - icons/unknown.gif \ - icons/up.gif \ - icons/uu.gif \ - icons/uuencoded.gif \ - icons/world1.gif \ - icons/world2.gif - -SSL_FILES = ssl/ssl_client.pem \ - ssl/ssl_server.pem - -# ---------------------------------------------------- -# FLAGS -# ---------------------------------------------------- -ERL_COMPILE_FLAGS += - -# ---------------------------------------------------- -# Targets -# ---------------------------------------------------- - -debug opt: - -clean: - -docs: - -# ---------------------------------------------------- -# Release Target -# ---------------------------------------------------- -include $(ERL_TOP)/make/otp_release_targets.mk - -release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/auth - $(INSTALL_DATA) $(AUTH_FILES) $(RELSYSDIR)/examples/server_root/auth - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/cgi-bin - $(INSTALL_SCRIPT) $(CGI_FILES) $(RELSYSDIR)/examples/server_root/cgi-bin - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/conf - $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/examples/server_root/conf - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/open - $(INSTALL_DATA) $(OPEN_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/open - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open - $(INSTALL_DATA) $(MNESIA_OPEN_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/misc - $(INSTALL_DATA) $(MISC_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/misc - $(INSTALL_DIR) \ - $(RELSYSDIR)/examples/server_root/htdocs/secret/top_secret - $(INSTALL_DIR) \ - $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret/top_secret - $(INSTALL_DATA) $(SECRET_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/secret - $(INSTALL_DATA) $(MNESIA_SECRET_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs - $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/examples/server_root/htdocs - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/icons - $(INSTALL_DATA) $(ICON_FILES) $(RELSYSDIR)/examples/server_root/icons - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/ssl - $(INSTALL_DATA) $(SSL_FILES) $(RELSYSDIR)/examples/server_root/ssl - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/logs - -release_docs_spec: - diff --git a/lib/inets/test/httpd_SUITE_data/server_root/conf/8080.conf b/lib/inets/test/httpd_SUITE_data/server_root/conf/8080.conf deleted file mode 100644 index 48e66f0114..0000000000 --- a/lib/inets/test/httpd_SUITE_data/server_root/conf/8080.conf +++ /dev/null @@ -1,79 +0,0 @@ -Port 8080 -#ServerName your.server.net -SocketType ip_comm -Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_include mod_dir mod_get mod_head mod_log mod_disk_log -ServerAdmin jocke@erix.ericsson.se -ServerRoot /var/tmp/server_root -ErrorLog logs/error_log_8080 -TransferLog logs/access_log_8080 -SecurityLog logs/security_log_8080 -ErrorDiskLog logs/error_disk_log_8080 -ErrorDiskLogSize 200000 10 -TransferDiskLog logs/access_disk_log_8080 -TransferDiskLogSize 200000 10 -SecurityDiskLog logs/security_disk_log -SecurityDiskLogSize 200000 10 -MaxClients 50 -#KeepAlive 5 -#KeepAliveTimeout 10 -DocumentRoot /var/tmp/server_root/htdocs -DirectoryIndex index.html welcome.html -DefaultType text/plain -Alias /icons/ /var/tmp/server_root/icons/ -Alias /pics/ /var/tmp/server_root/icons/ -ScriptAlias /cgi-bin/ /var/tmp/server_root/cgi-bin/ -ScriptAlias /htbin/ /var/tmp/server_root/cgi-bin/ -ErlScriptAlias /cgi-bin/erl httpd_example io -EvalScriptAlias /eval httpd_example io -#Script HEAD /cgi-bin/printenv.sh -#Action image/gif /cgi-bin/printenv.sh - - -AuthDBType plain -AuthName Open Area -AuthUserFile /var/tmp/server_root/auth/passwd -AuthGroupFile /var/tmp/server_root/auth/group -require user one Aladdin - - - -AuthDBType plain -AuthName Secret Area -AuthUserFile /var/tmp/server_root/auth/passwd -AuthGroupFile /var/tmp/server_root/auth/group -require group group1 group2 - - - -AuthDBType plain -AuthName Top Secret Area -AuthUserFile /var/tmp/server_root/auth/passwd -AuthGroupFile /var/tmp/server_root/auth/group -require group group3 - - - -AuthDBType mnesia -AuthName Open Area -require user one Aladdin - - - -AuthDBType mnesia -AuthName Secret Area -require group group1 group2 - - - -AuthDBType mnesia -AuthName Top Secret Area -require group group3 -allow from 130.100.34 130.100.35 -deny from 100.234.22.12 194.100.34.1 130.100.34.25 -SecurityDataFile logs/security_data -SecurityMaxRetries 3 -SecurityBlockTime 10 -SecurityFailExpireTime 1 -SecurityAuthTimeout 1 -SecurityCallbackModule security_callback - diff --git a/lib/inets/test/httpd_SUITE_data/server_root/conf/8888.conf b/lib/inets/test/httpd_SUITE_data/server_root/conf/8888.conf deleted file mode 100644 index 79bb7fcca4..0000000000 --- a/lib/inets/test/httpd_SUITE_data/server_root/conf/8888.conf +++ /dev/null @@ -1,63 +0,0 @@ -Port 8888 -#ServerName your.server.net -SocketType ip_comm -Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_include mod_dir mod_get mod_head mod_log mod_disk_log -ServerAdmin jocke@erix.ericsson.se -ServerRoot /var/tmp/server_root -ErrorLog logs/error_log_8888 -TransferLog logs/access_log_8888 -ErrorDiskLog logs/error_disk_log_8888 -ErrorDiskLogSize 200000 10 -TransferDiskLog logs/access_disk_log_8888 -TransferDiskLogSize 200000 10 -MaxClients 150 -DocumentRoot /var/tmp/server_root/htdocs -DirectoryIndex index.html welcome.html -DefaultType text/plain -Alias /icons/ /var/tmp/server_root/icons/ -Alias /pics/ /var/tmp/server_root/icons/ -ScriptAlias /cgi-bin/ /var/tmp/server_root/cgi-bin/ -ScriptAlias /htbin/ /var/tmp/server_root/cgi-bin/ -ErlScriptAlias /cgi-bin/erl httpd_example io -EvalScriptAlias /eval httpd_example io -#Script HEAD /cgi-bin/printenv.sh -#Action image/gif /cgi-bin/printenv.sh - - -AuthName Open Area -AuthUserFile /var/tmp/server_root/auth/passwd -AuthGroupFile /var/tmp/server_root/auth/group -require user one Aladdin - - - -AuthName Secret Area -AuthUserFile /var/tmp/server_root/auth/passwd -AuthGroupFile /var/tmp/server_root/auth/group -require group group1 group2 - - - -AuthName Top Secret Area -AuthUserFile /var/tmp/server_root/auth/passwd -AuthGroupFile /var/tmp/server_root/auth/group -require group group3 - - - -AuthName Open Area -AuthMnesiaDB On -require user one Aladdin - - - -AuthName Secret Area -AuthMnesiaDB On -require group group1 group2 - - - -AuthName Top Secret Area -AuthMnesiaDB On -require group group3 - diff --git a/lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf b/lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf deleted file mode 100644 index ceb94237d2..0000000000 --- a/lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf +++ /dev/null @@ -1,268 +0,0 @@ -# -# %CopyrightBegin% -# -# Copyright Ericsson AB 1997-2011. 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. -# -# %CopyrightEnd% -# -# - -# Port: The port the standalone listens to. For ports < 1023, you will -# need httpd to be run as root initially. - -Port 8888 - -# BindAddress: This directive is used to tell the server which IP address -# to listen to. It can either contain "*", an IP address, or a fully -# qualified Internet domain name. -# -# It is also possible to specify the ip-family with the directive. -# There ar three possible value: inet, inet6 and inet6fb4 -# inet: Use IpFamily inet when retreiving the address and -# fail if that does not work. -# inet6: Use IpFamily inet6 when retreiving the address and -# fail if that does not work. -# inet6fb4: First IpFamily inet6 is tried and if that does not work, -# inet is used as fallback. -# Default value for ip-family is inet6fb4 -# -# The syntax is:

[|] -# -#BindAddress * -#BindAddress *|inet - - -# ServerName allows you to set a host name which is sent back to clients for -# your server if it's different than the one the program would get (i.e. use -# "www" instead of the host's real name). -# -# Note: You cannot just invent host names and hope they work. The name you -# define here must be a valid DNS name for your host. If you don't understand -# this, ask your network administrator. - -#ServerName your.server.net - -# SocketType is either ip_comm, sockets or ssl. - -SocketType ip_comm - -# Modules: Server run-time plug-in modules written using the Erlang -# Web Server API (EWSAPI). The server API make it easy to add functionality -# to the server. Read more about EWSAPI in the Reference Manual. -# WARNING! Do not tamper with this directive unless you are familiar with -# EWSAPI. - -Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_responsecontrol mod_trace mod_range mod_head mod_include mod_dir mod_get mod_log mod_disk_log - -# ServerAdmin: Your address, where problems with the server should be -# e-mailed. - -ServerAdmin jocke@erix.ericsson.se - -# ServerRoot: The directory the server's config, error, and log files -# are kept in - -ServerRoot /var/tmp/server_root - -# ErrorLog: The location of the error log file. If this does not start -# with /, ServerRoot is prepended to it. - -ErrorLog logs/error_log - -# TransferLog: The location of the transfer log file. If this does not -# start with /, ServerRoot is prepended to it. - -TransferLog logs/access_log - -# SecurityLog: The location of the security log file (mod_security required) -# -SecurityLog logs/security_log - -# ErrorDiskLog: The location of the error log file. If this does not -# start with /, ServerRoot is prepended to it. This log file is managed -# with the disk_log module [See disk_log(3)]. The ErrorDiskLogSize directive -# takes two argument, i.e. MaxBytes and MaxFiles. The wrap log writes at most -# MaxBytes bytes on each file, and it uses MaxFiles files before it wraps, and -# truncates the first file. - -ErrorDiskLog logs/error_disk_log -ErrorDiskLogSize 200000 10 - -# TransferDiskLog: The location of the transfer log file. If this does not -# start with /, ServerRoot is prepended to it. This log file is managed -# with the disk_log module [See disk_log(3)]. The TransferDiskLogSize directive -# takes two argument, i.e. MaxBytes and MaxFiles. The wrap log writes at most -# MaxBytes bytes on each file, and it uses MaxFiles files before it wraps, and -# truncates the first file. - -TransferDiskLog logs/access_disk_log -TransferDiskLogSize 200000 10 - -# SecurityDiskLog: The location of the security log file. If this does not -# start with /, ServerRoot is prepended to it. This log file is managed -# with the disk_log module [See disk_log(3)]. The SecurityDiskLogSize directive -# takes two argument, i.e. MaxBytes and MaxFiles. The wrap log writes at most -# MaxBytes bytes on each file, and it uses MaxFiles files before it wraps, and -# truncates the first file. - -SecurityDiskLog logs/security_disk_log -SecurityDiskLogSize 200000 10 - -# Limit on total number of servers running, i.e., limit on the number -# of clients who can simultaneously connect --- if this limit is ever -# reached, clients will be LOCKED OUT, so it should NOT BE SET TOO LOW. -# It is intended mainly as a brake to keep a runaway server from taking -# the server with it as it spirals down... - -MaxClients 50 - -# KeepAlive set the flag for persistent connections. For peristent connections -# set KeepAlive to on. To use One request per connection set the flag to off -# Note: The value has changed since previous version of INETS. -KeepAlive on - -# KeepAliveTimeout sets the number of seconds before a persistent connection -# times out and closes. -KeepAliveTimeout 10 - -# MaxKeepAliveRequests sets the number of seconds before a persistent connection -# times out and closes. -MaxKeepAliveRequests 10 - - - -# DocumentRoot: The directory out of which you will serve your -# documents. By default, all requests are taken from this directory, but -# symbolic links and aliases may be used to point to other locations. - -DocumentRoot /var/tmp/server_root/htdocs - -# DirectoryIndex: Name of the file or files to use as a pre-written HTML -# directory index. Separate multiple entries with spaces. - -DirectoryIndex index.html welcome.html - -# DefaultType is the default MIME type for documents which the server -# cannot find the type of from filename extensions. - -DefaultType text/plain - -# Aliases: Add here as many aliases as you need (with no limit). The format is -# Alias fakename realname - -Alias /icons/ /var/tmp/server_root/icons/ -Alias /pics/ /var/tmp/server_root/icons/ - -# ScriptAlias: This controls which directories contain server scripts. -# Format: ScriptAlias fakename realname - -ScriptAlias /cgi-bin/ /var/tmp/server_root/cgi-bin/ -ScriptAlias /htbin/ /var/tmp/server_root/cgi-bin/ - -# This directive adds an action, which will activate cgi-script when a -# file is requested using the method of method, which can be one of -# GET, POST and HEAD. It sends the URL and file path of the requested -# document using the standard CGI PATH_INFO and PATH_TRANSLATED -# environment variables. - -#Script HEAD /cgi-bin/printenv.sh - -# This directive adds an action, which will activate cgi-script when a -# file of content type mime-type is requested. It sends the URL and -# file path of the requested document using the standard CGI PATH_INFO -# and PATH_TRANSLATED environment variables. - -#Action image/gif /cgi-bin/printenv.sh - -# ErlScriptAlias: This specifies how "Erl" server scripts are called. -# Format: ErlScriptAlias fakename realname allowed_modules - -ErlScriptAlias /down/erl httpd_example io - -# EvalScriptAlias: This specifies how "Eval" server scripts are called. -# Format: EvalScriptAlias fakename realname allowed_modules - -EvalScriptAlias /eval httpd_example io - -# Point SSLCertificateFile at a PEM encoded certificate. - -SSLCertificateFile /var/tmp/server_root/ssl/ssl_server.pem - -# If the key is not combined with the certificate, use this directive to -# point at the key file. - -SSLCertificateKeyFile /var/tmp/server_root/ssl/ssl_server.pem - -# Set SSLVerifyClient to: -# 0 if no certicate is required -# 1 if the client may present a valid certificate -# 2 if the client must present a valid certificate -# 3 if the client may present a valid certificate but it is not required to -# have a valid CA - -SSLVerifyClient 0 - -# Each directory to which INETS has access, can be configured with respect -# to which services and features are allowed and/or disabled in that -# directory (and its subdirectories). - - -AuthDBType plain -AuthName Open Area -AuthUserFile /var/tmp/server_root/auth/passwd -AuthGroupFile /var/tmp/server_root/auth/group -require user one Aladdin - - - -AuthDBType plain -AuthName Secret Area -AuthUserFile /var/tmp/server_root/auth/passwd -AuthGroupFile /var/tmp/server_root/auth/group -require group group1 group2 - - - -AuthDBType plain -AuthName Top Secret Area -AuthUserFile /var/tmp/server_root/auth/passwd -AuthGroupFile /var/tmp/server_root/auth/group -require group group3 - - - -AuthDBType mnesia -AuthName Open Area -require user one Aladdin - - - -AuthDBType mnesia -AuthName Secret Area -require group group1 group2 - - - -AuthDBType mnesia -AuthName Top Secret Area -require group group3 -allow from 130.100.34 130.100.35 -deny from 100.234.22.12 194.100.34.1 130.100.34.25 -SecurityDataFile logs/security_data -SecurityMaxRetries 3 -SecurityBlockTime 10 -SecurityFailExpireTime 1 -SecurityAuthTimeout 1 -SecurityCallbackModule security_callback - diff --git a/lib/inets/test/httpd_SUITE_data/server_root/conf/mime.types b/lib/inets/test/httpd_SUITE_data/server_root/conf/mime.types deleted file mode 100644 index d2f81e4e5e..0000000000 --- a/lib/inets/test/httpd_SUITE_data/server_root/conf/mime.types +++ /dev/null @@ -1,465 +0,0 @@ -# This is a comment. I love comments. - -# MIME type Extension -application/EDI-Consent -application/EDI-X12 -application/EDIFACT -application/activemessage -application/andrew-inset ez -application/applefile -application/atomicmail -application/batch-SMTP -application/beep+xml -application/cals-1840 -application/commonground -application/cybercash -application/dca-rft -application/dec-dx -application/dvcs -application/eshop -application/http -application/hyperstudio -application/iges -application/index -application/index.cmd -application/index.obj -application/index.response -application/index.vnd -application/iotp -application/ipp -application/isup -application/font-tdpfr -application/mac-binhex40 hqx -application/mac-compactpro cpt -application/macwriteii -application/marc -application/mathematica -application/mathematica-old -application/msword doc -application/news-message-id -application/news-transmission -application/ocsp-request -application/ocsp-response -application/octet-stream bin dms lha lzh exe class so dll -application/oda oda -application/parityfec -application/pdf pdf -application/pgp-encrypted -application/pgp-keys -application/pgp-signature -application/pkcs10 -application/pkcs7-mime -application/pkcs7-signature -application/pkix-cert -application/pkix-crl -application/pkixcmp -application/postscript ai eps ps -application/prs.alvestrand.titrax-sheet -application/prs.cww -application/prs.nprend -application/qsig -application/remote-printing -application/riscos -application/rtf -application/sdp -application/set-payment -application/set-payment-initiation -application/set-registration -application/set-registration-initiation -application/sgml -application/sgml-open-catalog -application/sieve -application/slate -application/smil smi smil -application/timestamp-query -application/timestamp-reply -application/vemmi -application/vnd.3M.Post-it-Notes -application/vnd.FloGraphIt -application/vnd.accpac.simply.aso -application/vnd.accpac.simply.imp -application/vnd.acucobol -application/vnd.aether.imp -application/vnd.anser-web-certificate-issue-initiation -application/vnd.anser-web-funds-transfer-initiation -application/vnd.audiograph -application/vnd.businessobjects -application/vnd.bmi -application/vnd.canon-cpdl -application/vnd.canon-lips -application/vnd.claymore -application/vnd.commerce-battelle -application/vnd.commonspace -application/vnd.comsocaller -application/vnd.contact.cmsg -application/vnd.cosmocaller -application/vnd.cups-postscript -application/vnd.cups-raster -application/vnd.cups-raw -application/vnd.ctc-posml -application/vnd.cybank -application/vnd.dna -application/vnd.dpgraph -application/vnd.dxr -application/vnd.ecdis-update -application/vnd.ecowin.chart -application/vnd.ecowin.filerequest -application/vnd.ecowin.fileupdate -application/vnd.ecowin.series -application/vnd.ecowin.seriesrequest -application/vnd.ecowin.seriesupdate -application/vnd.enliven -application/vnd.epson.esf -application/vnd.epson.msf -application/vnd.epson.quickanime -application/vnd.epson.salt -application/vnd.epson.ssf -application/vnd.ericsson.quickcall -application/vnd.eudora.data -application/vnd.fdf -application/vnd.ffsns -application/vnd.framemaker -application/vnd.fsc.weblaunch -application/vnd.fujitsu.oasys -application/vnd.fujitsu.oasys2 -application/vnd.fujitsu.oasys3 -application/vnd.fujitsu.oasysgp -application/vnd.fujitsu.oasysprs -application/vnd.fujixerox.ddd -application/vnd.fujixerox.docuworks -application/vnd.fujixerox.docuworks.binder -application/vnd.fut-misnet -application/vnd.grafeq -application/vnd.groove-account -application/vnd.groove-identity-message -application/vnd.groove-injector -application/vnd.groove-tool-message -application/vnd.groove-tool-template -application/vnd.groove-vcard -application/vnd.hhe.lesson-player -application/vnd.hp-HPGL -application/vnd.hp-PCL -application/vnd.hp-PCLXL -application/vnd.hp-hpid -application/vnd.hp-hps -application/vnd.httphone -application/vnd.hzn-3d-crossword -application/vnd.ibm.afplinedata -application/vnd.ibm.MiniPay -application/vnd.ibm.modcap -application/vnd.informix-visionary -application/vnd.intercon.formnet -application/vnd.intertrust.digibox -application/vnd.intertrust.nncp -application/vnd.intu.qbo -application/vnd.intu.qfx -application/vnd.irepository.package+xml -application/vnd.is-xpr -application/vnd.japannet-directory-service -application/vnd.japannet-jpnstore-wakeup -application/vnd.japannet-payment-wakeup -application/vnd.japannet-registration -application/vnd.japannet-registration-wakeup -application/vnd.japannet-setstore-wakeup -application/vnd.japannet-verification -application/vnd.japannet-verification-wakeup -application/vnd.koan -application/vnd.lotus-1-2-3 -application/vnd.lotus-approach -application/vnd.lotus-freelance -application/vnd.lotus-notes -application/vnd.lotus-organizer -application/vnd.lotus-screencam -application/vnd.lotus-wordpro -application/vnd.mcd -application/vnd.mediastation.cdkey -application/vnd.meridian-slingshot -application/vnd.mif mif -application/vnd.minisoft-hp3000-save -application/vnd.mitsubishi.misty-guard.trustweb -application/vnd.mobius.daf -application/vnd.mobius.dis -application/vnd.mobius.msl -application/vnd.mobius.plc -application/vnd.mobius.txf -application/vnd.motorola.flexsuite -application/vnd.motorola.flexsuite.adsi -application/vnd.motorola.flexsuite.fis -application/vnd.motorola.flexsuite.gotap -application/vnd.motorola.flexsuite.kmr -application/vnd.motorola.flexsuite.ttc -application/vnd.motorola.flexsuite.wem -application/vnd.mozilla.xul+xml -application/vnd.ms-artgalry -application/vnd.ms-asf -application/vnd.ms-excel xls -application/vnd.ms-lrm -application/vnd.ms-powerpoint ppt -application/vnd.ms-project -application/vnd.ms-tnef -application/vnd.ms-works -application/vnd.mseq -application/vnd.msign -application/vnd.music-niff -application/vnd.musician -application/vnd.netfpx -application/vnd.noblenet-directory -application/vnd.noblenet-sealer -application/vnd.noblenet-web -application/vnd.novadigm.EDM -application/vnd.novadigm.EDX -application/vnd.novadigm.EXT -application/vnd.osa.netdeploy -application/vnd.palm -application/vnd.pg.format -application/vnd.pg.osasli -application/vnd.powerbuilder6 -application/vnd.powerbuilder6-s -application/vnd.powerbuilder7 -application/vnd.powerbuilder7-s -application/vnd.powerbuilder75 -application/vnd.powerbuilder75-s -application/vnd.previewsystems.box -application/vnd.publishare-delta-tree -application/vnd.pvi.ptid1 -application/vnd.pwg-xhtml-print+xml -application/vnd.rapid -application/vnd.s3sms -application/vnd.seemail -application/vnd.shana.informed.formdata -application/vnd.shana.informed.formtemplate -application/vnd.shana.informed.interchange -application/vnd.shana.informed.package -application/vnd.sss-cod -application/vnd.sss-dtf -application/vnd.sss-ntf -application/vnd.street-stream -application/vnd.svd -application/vnd.swiftview-ics -application/vnd.triscape.mxs -application/vnd.trueapp -application/vnd.truedoc -application/vnd.tve-trigger -application/vnd.ufdl -application/vnd.uplanet.alert -application/vnd.uplanet.alert-wbxml -application/vnd.uplanet.bearer-choice-wbxml -application/vnd.uplanet.bearer-choice -application/vnd.uplanet.cacheop -application/vnd.uplanet.cacheop-wbxml -application/vnd.uplanet.channel -application/vnd.uplanet.channel-wbxml -application/vnd.uplanet.list -application/vnd.uplanet.list-wbxml -application/vnd.uplanet.listcmd -application/vnd.uplanet.listcmd-wbxml -application/vnd.uplanet.signal -application/vnd.vcx -application/vnd.vectorworks -application/vnd.vidsoft.vidconference -application/vnd.visio -application/vnd.vividence.scriptfile -application/vnd.wap.sic -application/vnd.wap.slc -application/vnd.wap.wbxml wbxml -application/vnd.wap.wmlc wmlc -application/vnd.wap.wmlscriptc wmlsc -application/vnd.webturbo -application/vnd.wrq-hp3000-labelled -application/vnd.wt.stf -application/vnd.xara -application/vnd.xfdl -application/vnd.yellowriver-custom-menu -application/whoispp-query -application/whoispp-response -application/wita -application/wordperfect5.1 -application/x-bcpio bcpio -application/x-cdlink vcd -application/x-chess-pgn pgn -application/x-compress -application/x-cpio cpio -application/x-csh csh -application/x-director dcr dir dxr -application/x-dvi dvi -application/x-futuresplash spl -application/x-gtar gtar -application/x-gzip -application/x-hdf hdf -application/x-javascript js -application/x-koan skp skd skt skm -application/x-latex latex -application/x-netcdf nc cdf -application/x-sh sh -application/x-shar shar -application/x-shockwave-flash swf -application/x-stuffit sit -application/x-sv4cpio sv4cpio -application/x-sv4crc sv4crc -application/x-tar tar -application/x-tcl tcl -application/x-tex tex -application/x-texinfo texinfo texi -application/x-troff t tr roff -application/x-troff-man man -application/x-troff-me me -application/x-troff-ms ms -application/x-ustar ustar -application/x-wais-source src -application/x400-bp -application/xml -application/xml-dtd -application/xml-external-parsed-entity -application/zip zip -audio/32kadpcm -audio/basic au snd -audio/g.722.1 -audio/l16 -audio/midi mid midi kar -audio/mp4a-latm -audio/mpa-robust -audio/mpeg mpga mp2 mp3 -audio/parityfec -audio/prs.sid -audio/telephone-event -audio/tone -audio/vnd.cisco.nse -audio/vnd.cns.anp1 -audio/vnd.cns.inf1 -audio/vnd.digital-winds -audio/vnd.everad.plj -audio/vnd.lucent.voice -audio/vnd.nortel.vbk -audio/vnd.nuera.ecelp4800 -audio/vnd.nuera.ecelp7470 -audio/vnd.nuera.ecelp9600 -audio/vnd.octel.sbc -audio/vnd.qcelp -audio/vnd.rhetorex.32kadpcm -audio/vnd.vmx.cvsd -audio/x-aiff aif aiff aifc -audio/x-mpegurl m3u -audio/x-pn-realaudio ram rm -audio/x-pn-realaudio-plugin rpm -audio/x-realaudio ra -audio/x-wav wav -chemical/x-pdb pdb -chemical/x-xyz xyz -image/bmp bmp -image/cgm -image/g3fax -image/gif gif -image/ief ief -image/jpeg jpeg jpg jpe -image/naplps -image/png png -image/prs.btif -image/prs.pti -image/tiff tiff tif -image/vnd.cns.inf2 -image/vnd.dwg -image/vnd.dxf -image/vnd.fastbidsheet -image/vnd.fpx -image/vnd.fst -image/vnd.fujixerox.edmics-mmr -image/vnd.fujixerox.edmics-rlc -image/vnd.mix -image/vnd.net-fpx -image/vnd.svf -image/vnd.wap.wbmp wbmp -image/vnd.xiff -image/x-cmu-raster ras -image/x-portable-anymap pnm -image/x-portable-bitmap pbm -image/x-portable-graymap pgm -image/x-portable-pixmap ppm -image/x-rgb rgb -image/x-xbitmap xbm -image/x-xpixmap xpm -image/x-xwindowdump xwd -message/delivery-status -message/disposition-notification -message/external-body -message/http -message/news -message/partial -message/rfc822 -message/s-http -model/iges igs iges -model/mesh msh mesh silo -model/vnd.dwf -model/vnd.flatland.3dml -model/vnd.gdl -model/vnd.gs-gdl -model/vnd.gtw -model/vnd.mts -model/vnd.vtu -model/vrml wrl vrml -multipart/alternative -multipart/appledouble -multipart/byteranges -multipart/digest -multipart/encrypted -multipart/form-data -multipart/header-set -multipart/mixed -multipart/parallel -multipart/related -multipart/report -multipart/signed -multipart/voice-message -text/calendar -text/css css -text/directory -text/enriched -text/html html htm -text/parityfec -text/plain asc txt -text/prs.lines.tag -text/rfc822-headers -text/richtext rtx -text/rtf rtf -text/sgml sgml sgm -text/tab-separated-values tsv -text/t140 -text/uri-list -text/vnd.DMClientScript -text/vnd.IPTC.NITF -text/vnd.IPTC.NewsML -text/vnd.abc -text/vnd.curl -text/vnd.flatland.3dml -text/vnd.fly -text/vnd.fmi.flexstor -text/vnd.in3d.3dml -text/vnd.in3d.spot -text/vnd.latex-z -text/vnd.motorola.reflex -text/vnd.ms-mediapackage -text/vnd.wap.si -text/vnd.wap.sl -text/vnd.wap.wml wml -text/vnd.wap.wmlscript wmls -text/x-setext etx -text/x-server-parsed-html shtml -text/xml xml xsl -text/xml-external-parsed-entity -video/mp4v-es -video/mpeg mpeg mpg mpe -video/parityfec -video/pointer -video/quicktime qt mov -video/vnd.fvt -video/vnd.motorola.video -video/vnd.motorola.videop -video/vnd.mpegurl mxu -video/vnd.mts -video/vnd.nokia.interleaved-multimedia -video/vnd.vivo -video/x-msvideo avi -video/x-sgi-movie movie -x-conference/x-cooltalk ice - - - diff --git a/lib/inets/test/httpd_SUITE_data/server_root/conf/ssl.conf b/lib/inets/test/httpd_SUITE_data/server_root/conf/ssl.conf deleted file mode 100644 index 8b8c57a98b..0000000000 --- a/lib/inets/test/httpd_SUITE_data/server_root/conf/ssl.conf +++ /dev/null @@ -1,66 +0,0 @@ -Port 8088 -#ServerName your.server.net -SocketType ssl -Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_include mod_dir mod_get mod_head mod_log mod_disk_log -ServerAdmin jocke@erix.ericsson.se -ServerRoot /var/tmp/server_root -ErrorLog logs/error_log_8088 -TransferLog logs/access_log_8088 -ErrorDiskLog logs/error_disk_log_8088 -ErrorDiskLogSize 200000 10 -TransferDiskLog logs/access_disk_log_8088 -TransferDiskLogSize 200000 10 -MaxClients 150 -DocumentRoot /var/tmp/server_root/htdocs -DirectoryIndex index.html welcome.html -DefaultType text/plain -Alias /icons/ /var/tmp/server_root/icons/ -Alias /pics/ /var/tmp/server_root/icons/ -ScriptAlias /cgi-bin/ /var/tmp/server_root/cgi-bin/ -ScriptAlias /htbin/ /var/tmp/server_root/cgi-bin/ -ErlScriptAlias /cgi-bin/erl httpd_example io -EvalScriptAlias /eval httpd_example io -SSLCertificateFile /var/tmp/server_root/ssl/ssl_server.pem -SSLCertificateKeyFile /var/tmp/server_root/ssl/ssl_server.pem -SSLVerifyClient 0 -#Script HEAD /cgi-bin/printenv.sh -#Action image/gif /cgi-bin/printenv.sh - - -AuthName Open Area -AuthUserFile /var/tmp/server_root/auth/passwd -AuthGroupFile /var/tmp/server_root/auth/group -require user one Aladdin - - - -AuthName Secret Area -AuthUserFile /var/tmp/server_root/auth/passwd -AuthGroupFile /var/tmp/server_root/auth/group -require group group1 group2 - - - -AuthName Top Secret Area -AuthUserFile /var/tmp/server_root/auth/passwd -AuthGroupFile /var/tmp/server_root/auth/group -require group group3 - - - -AuthName Open Area -AuthMnesiaDB On -require user one Aladdin - - - -AuthName Secret Area -AuthMnesiaDB On -require group group1 group2 - - - -AuthName Top Secret Area -AuthMnesiaDB On -require group group3 - diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/README b/lib/inets/test/httpd_SUITE_data/server_root/icons/README deleted file mode 100644 index a1fc5a5a9c..0000000000 --- a/lib/inets/test/httpd_SUITE_data/server_root/icons/README +++ /dev/null @@ -1,161 +0,0 @@ -Public Domain Icons - - These icons were originally made for Mosaic for X and have been - included in the NCSA httpd and Apache server distributions in the - past. They are in the public domain and may be freely included in any - application. The originals were done by Kevin Hughes (kevinh@eit.com). - - Many thanks to Andy Polyakov for tuning the icon colors and adding a - few new images. If you'd like to contribute additions or ideas to - this set, please let me know. - - The distribution site for these icons is at: - - http://www.eit.com/goodies/www.icons/ - - Kevin Hughes - September 11, 1995 - - -Suggested Uses - -The following are a few suggestions, to serve as a starting point for ideas. -Please feel free to tweak and rename the icons as you like. - - a.gif - This might be used to represent PostScript or text layout - languages. - - alert.black.gif, alert.red.gif - These can be used to highlight any important items, such as a - README file in a directory. - - back.gif, forward.gif - These can be used as links to go to previous and next areas. - - ball.gray.gif, ball.red.gif - These might be used as bullets. - - binary.gif - This can be used to represent binary files. - - binhex.gif - This can represent BinHex-encoded data. - - blank.gif - This can be used as a placeholder or a spacing element. - - bomb.gif - This can be used to repreesnt core files. - - box1.gif, box2.gif - These icons can be used to represent generic 3D applications and - related files. - - broken.gif - This can represent corrupted data. - - burst.gif - This can call attention to new and important items. - - c.gif - This might represent C source code. - - comp.blue.gif, comp.red.gif - These little computer icons can stand for telnet or FTP - sessions. - - compressed.gif - This may represent compressed data. - - continued.gif - This can be a link to a continued listing of a directory. - - down.gif, up.gif, left.gif, right.gif - These can be used to scroll up, down, left and right in a - listing or may be used to denote items in an outline. - - dvi.gif - This can represent DVI files. - - f.gif - This might represent FORTRAN or Forth source code. - - folder.gif, folder.open.gif, folder.sec.gif - The folder can represent directories. There is also a version - that can represent secure directories or directories that cannot - be viewed. - - generic.gif, generic.sec.gif, generic.red.gif - These can represent generic files, secure files, and important - files, respectively. - - hand.right.gif, hand.up.gif - These can point out important items (pun intended). - - image1.gif, image2.gif, image3.gif - These can represent image formats of various types. - - index.gif - This might represent a WAIS index or search facility. - - layout.gif - This might represent files and formats that contain graphics as - well as text layout, such as HTML and PDF files. - - link.gif - This might represent files that are symbolic links. - - movie.gif - This can represent various movie formats. - - p.gif - This may stand for Perl or Python source code. - - pie0.gif ... pie8.gif - These icons can be used in applications where a list of - documents is returned from a search. The little pie chart images - can denote how relevant the documents may be to your search - query. - - patch.gif - This may stand for patches and diff files. - - portal.gif - This might be a link to an online service or a 3D world. - - ps.gif, quill.gif - These may represent PostScript files. - - screw1.gif, screw2.gif - These may represent CAD or engineering data and formats. - - script.gif - This can represent any of various interpreted languages, such as - Perl, python, TCL, and shell scripts, as well as server - configuration files. - - sound1.gif, sound2.gif - These can represent sound files. - - sphere1.gif, sphere2.gif - These can represent 3D worlds or rendering applications and - formats. - - tex.gif - This can represent TeX files. - - text.gif - This can represent generic (plain) text files. - - transfer.gif - This can represent FTP transfers or uploads/downloads. - - unknown.gif - This may represent a file of an unknown type. - - uuencoded.gif - This can stand for uuencoded data. - - world1.gif, world2.gif - These can represent 3D worlds or other 3D formats. diff --git a/lib/inets/test/httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip b/lib/inets/test/httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip deleted file mode 100644 index 8d1c8b69c3..0000000000 --- a/lib/inets/test/httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_client.pem b/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_client.pem deleted file mode 100644 index 427447958d..0000000000 --- a/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_client.pem +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXQIBAAKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSVwC+n -0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53h2Zr -3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwIDAQAB -AoGACdIVYe/LTeydUihtInC8lZ2QuPgJmoBNocRjqJFipEihoL4scHAx25n1bBvB -I0HZphffzBkGp28oBAtl2LRPWXqu527unc/RWRfLMqSK1xNSq1DxD1a30zkrZPna -QiV65vEJuNSJTtlDy/Zqc/BVZXCpxWlzYQedZgkmf0Qse8ECQQCmaz02Yur8zC9f -eSQKU5OSzGw3bSIumEzziCfHdTheK6MEoccf5TCAyLXhZwA7QlKja4tFXfeyVxws -/LlnUJN9AkEA4j+xnOeYUyGKXL5i+BAbnqpI4MzPiq+IoCYkaRlD/wAws24r5HNI -ZQmEHWqD/NNzOf/A2XuyLtMiTGJPW/DftwJBAKKpJP6Ytuh6xz8BUCnLwO12Y7vV -LtjuQiCzD3aUa5EYA9HOMqxJPxxRkf0LyR0i2VUkE8+sZiPpov+R0cJa7p0CQQCj -40GUiArGRSiF7/+e84QeVfl+pb29F1QftiFv5DZmFEwy3Z572KpbTh5edJbxYHY6 -UDHxGHJFCvnwXNJhpkVXAkBJqfEfiMJ3Q/E5Gpf3sQizacouW92iiN8ojlF1oB80 -t34RysJH7SgI3gdMhTribCo2UUaV0StjR6yodPN+TB2J ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIIChzCCAfCgAwIBAgIGAIsapa8BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT -BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE -BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD -VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw -MDAwWjB7MQ8wDQYDVQQDEwZjbGllbnQxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl -cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD -VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB -AQUAA4GNADCBiQKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSV -wC+n0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53 -h2Zr3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwID -AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAG8t6f1A -PF7xayGxtUpG2r6W5ETylC3ZIKPS2kfJk9aYi7AZNTp7/xTU6SgqvFBN8aBPzxCD -4jHrSNC8DSb4X1x9uimarb6qdZDHEdij+DRAd2eygJHZxEf7+8B4Fx34thQeU9hZ -S1Izke5AlsyFMkvB7h0anE4k9BfuU70vl6v5 ------END CERTIFICATE----- diff --git a/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_server.pem b/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_server.pem deleted file mode 100644 index 4aac86db49..0000000000 --- a/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_server.pem +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXQIBAAKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9Adq6 -7k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ4UAt -NHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQIDAQAB -AoGAQIlma0r6W6bcRj4+Wd4fXCFvHuq5Psu1fYEeC5Yvz8761xVjjSfbrDHJZ9pm -FjOEgedK+s5lbDXqYVyjbdyZSugStBRocSmbG8SQHcAsxR2ZIkNzX2hYzB+lslWo -T3YJojDyB134O7XJznCu+ZFXP86jyJ1JT6k6a+OIHcwnJ+ECQQDYn57dY4Px3mEd -VBLStN3YkRF5oFyT+xk7IaKeLLB6n4gCnoVbBoHut7PFbPYPzoNzEwPk3MQKDIHb -Kig3S5CpAkEAvPA1VmoJWAlN6kUi+F2L8HXEArzE8x7vwdsslrwMKUe4dFS+ZC/7 -5iDOaxcZ7TYkCgwzBt341++DCgP6j3fY1QJBALB6AcOcwi52m6l4B8mu3ZkEPjdX -BHTuONTqhv/TqoaLlxODL2NDvvDKqeMp7KBd/srt79swW2lQXS4+fvrlTdkCQQCm -zxj4O1QWkthkfje6ubSkTwUIOatUzrp1F9GNH2dJRtX2dx9FCwxGCC7WY6XzRXqa -GF0wsedSllbGD+82nWQlAkAicMGqCqRq4hKR/cVmFatOqKVWCVkx6OFF2FhuiI5Z -h5eIOPGCt8dVRs1P9DNSld/D98Sfm65m85z8BtXovvYV ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIIChzCCAfCgAwIBAgIGANUxXM9BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT -BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE -BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD -VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw -MDAwWjB7MQ8wDQYDVQQDEwZzZXJ2ZXIxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl -cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD -VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB -AQUAA4GNADCBiQKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9 -Adq67k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ -4UAtNHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQID -AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAGF5Pfwk -QDdwJup/mVITPxbBls4Yl7anDooUQsq8066lA1g54H/PRfXscGkyCFGh1ifXvf1L -psMRoBAdDHL/wSJplk3rRavkC94eBgnTFZmfKL6844g1j53yameiYL8IEVExYMBg -/XGyc0qwq57WT8B/K4aElrvlBlQ0wF3wN54M ------END CERTIFICATE----- diff --git a/lib/inets/test/httpd_all.erl b/lib/inets/test/httpd_all.erl new file mode 100644 index 0000000000..7ce1129bf5 --- /dev/null +++ b/lib/inets/test/httpd_all.erl @@ -0,0 +1,239 @@ +alias(Version, Type, Port, Host, Node) -> + Opts = [], + ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node, + "GET /pics/icon.sheet.gif " + ++ Version ++ "\r\n\r\n", + [{statuscode, 200}, + {header, "Content-Type","image/gif"}, + {header, "Server"}, + {header, "Date"}, + {version, Version}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node, + "GET / " ++ Version ++ "\r\n\r\n", + [{statuscode, 200}, + {header, "Content-Type","text/html"}, + {header, "Server"}, + {header, "Date"}, + {version, Version}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node, + "GET /misc/ " ++ Version ++ "\r\n\r\n", + [{statuscode, 200}, + {header, "Content-Type","text/html"}, + {header, "Server"}, + {header, "Date"}, + {version, Version}]), + + %% Check redirection if trailing slash is missing. + ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node, + "GET /misc "++ Version ++ "\r\n\r\n", + [{statuscode, 301}, + {header, "Location"}, + {header, "Content-Type","text/html"}, + {version, Version}]). + + +head(Version, Type, Port, Host, Node) -> + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "HEAD /index.html " ++ Version ++ "\r\n\r\n", + [{statuscode, 200}, + {version, Version}]). + + +get(Version, Type, Port, Host, Node) -> + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /index.html " ++ Version ++ "\r\n\r\n", + [{statuscode, 200}, + {header, "Content-Type", "text/html"}, + {header, "Date"}, + {header, "Server"}, + {version, Version}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /fsize.shtml " ++ Version ++ "\r\nHost:" + ++ Host ++ "\r\n\r\n", + [{statuscode, 200}, + {header, "Content-Type", "text/html"}, + {header, "Date"}, + {header, "Server"}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /secret/dummy.html " + ++ Version ++ "\r\n\r\n", + [{statuscode, 401}, + {header, "WWW-Authenticate"}, + {version, Version}]). + +esi(Version, Type, Port, Host, Node) -> + %% Check "ErlScriptAlias" and "EvalScriptAlias" directives + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /eval?httpd_example:print(\"Hi!\") " + ++ Version ++ "\r\n\r\n", + [{statuscode, 200}, + {version, Version}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /eval?not_allowed:print(\"Hi!\") " + ++ Version ++ "\r\n\r\n", + [{statuscode, 403}, + {version, Version}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /eval?httpd_example:undef(\"Hi!\") " + ++ Version ++ "\r\n\r\n", + [{statuscode, 500}, + {version, Version}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/erl/httpd_example " + ++ Version ++ "\r\n\r\n", + [{statuscode, 400}, + {version, Version}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/erl/httpd_example:get " + ++ Version ++ "\r\n\r\n", + [{statuscode, 200}, + {version, Version}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/erl/httpd_example:" + "get?input=4711" + " HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/1.0"}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/erl/httpd_example:" + "post " ++ Version ++ "\r\n\r\n", + [{statuscode, 200}, + {version, Version}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/erl/not_allowed:post " + ++ Version ++ "\r\n\r\n", + [{statuscode, 403}, + {version, Version}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/erl/httpd_example:undef " + ++ Version ++ "\r\n\r\n", + [{statuscode, 404}, + {version, Version}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/erl/httpd_example/yahoo " + ++ Version ++ "\r\n\r\n", + [{statuscode, 302}, + {version, Version}]), + %% Check "ErlScriptNoCache" directive (default: false) + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/erl/httpd_example:get " + ++ Version ++ "\r\n\r\n", + [{statuscode, 200}, + {no_header, "cache-control"}, + {version, "HTTP/1.0"}]). + +cgi(Version, Type, Port, Host, Node) -> + {Script, Script2, Script3} = + case test_server:os_type() of + {win32, _} -> + {"printenv.bat", "printenv.sh", "cgi_echo.exe"}; + _ -> + {"printenv.sh", "printenv.bat", "cgi_echo"} + end, + + %% The length (> 100) is intentional + ok = httpd_test_lib: + verify_request(Type, Host, Port, Node, + "POST /cgi-bin/" ++ Script3 ++ + Version ++ " \r\n" + "Content-Length:100 \r\n\r\n " + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + " \r\n\r\n", + [{statuscode, 200}, + {version, Version}, + {header, "content-type", "text/plain"}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/"++ Script ++ + " " ++ Version ++ "\r\n\r\n", + [{statuscode, 200}, + {version, Version}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/not_there " ++ + Version ++ "\r\n\r\n", + [{statuscode, 404},{statuscode, 500}, + {version, Version}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/"++ Script ++ + "?Nisse:kkk?sss/lll " ++ Version ++ "\r\n\r\n", + [{statuscode, 200}, + {version, Version}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "POST /cgi-bin/"++ Script ++ + " HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/1.0"}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /htbin/"++ Script ++ + " HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/1.0"}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /htbin/not_there " + "HTTP/1.0\r\n\r\n", + [{statuscode, 404},{statuscode, 500}, + {version, "HTTP/1.0"}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /htbin/"++ Script ++ + "?Nisse:kkk?sss/lll HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/1.0"}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "POST /htbin/"++ Script ++ + " HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/1.0"}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "POST /htbin/"++ Script ++ + " HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/1.0"}]), + + %% Execute an existing, but bad CGI script.. + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "POST /htbin/"++ Script2 ++ + " HTTP/1.0\r\n\r\n", + [{statuscode, 404}, + {version, "HTTP/1.0"}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "POST /cgi-bin/"++ Script2 ++ + " HTTP/1.0\r\n\r\n", + [{statuscode, 404}, + {version, "HTTP/1.0"}]), + + %% Check "ScriptNoCache" directive (default: false) + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/" ++ Script ++ + " HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {no_header, "cache-control"}, + {version, "HTTP/1.0"}]). diff --git a/lib/inets/test/httpd_mod_SUITE.erl b/lib/inets/test/httpd_mod_SUITE.erl new file mode 100644 index 0000000000..07e2c2418a --- /dev/null +++ b/lib/inets/test/httpd_mod_SUITE.erl @@ -0,0 +1,76 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013-2014. 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. +%% +%% %CopyrightEnd% +%% +%% + +%% +%% ct:run("../inets_test", httpd_mod_SUITE). +-module(httpd_mod_SUITE). + +-include_lib("kernel/include/file.hrl"). +-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). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- +suite() -> + [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ + {group, http}, + {group, https} + ]. + +groups() -> + [ + {http, [], all_version_groups()}, + {https, [], all_version_groups()} + {http_1_1, [], []}, + {http_1_0, [], []}, + {http_0_9, [], []}, + {mod_alias, [], []}, + {mod_actions, [], []}, + {mod_security, [], []}, + {mod_auth, [], []}, + {mod_htaccess, [], []}, + {mod_cgi, [], []}, + {mod_esi, [], []}, + {mod_head, [], []}, + {configure, [], []} + ]. + +all_version_groups ()-> + [ + {group, mod_alias}, + {group, mod_actions}, + {group, mod_security}, + {group, mod_auth}, + {group, mod_htaccess}, + {group, mod_cgi}, + {group, mod_esi}, + {group, mod_head} + ]. + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl index 3494269d70..4755776ee8 100644 --- a/lib/inets/test/inets_test_lib.erl +++ b/lib/inets/test/inets_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2013. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. 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 @@ -287,7 +287,9 @@ print(F, A, Mod, Line) -> print("", F, A, Mod, Line). hostname() -> - from($@, atom_to_list(node())). + {ok, Name} = inet:gethostname(), + Name. + from(H, [H | T]) -> T; from(H, [_ | T]) -> from(H, T); from(_, []) -> []. diff --git a/lib/inets/test/old_httpd_SUITE.erl b/lib/inets/test/old_httpd_SUITE.erl new file mode 100644 index 0000000000..b2ad593629 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE.erl @@ -0,0 +1,2444 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2014. 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. +%% +%% %CopyrightEnd% +%% +%% + +-module(old_httpd_SUITE). + +-include_lib("test_server/include/test_server.hrl"). +-include("test_server_line.hrl"). +-include("inets_test_lib.hrl"). + +-include_lib("kernel/include/file.hrl"). + +%% Test server specific exports +-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]). +-export([init_per_testcase/2, end_per_testcase/2, + init_per_suite/1, end_per_suite/1]). + +%% Core Server tests +-export([ + ip_mod_alias/1, + ip_mod_actions/1, + ip_mod_security/1, + ip_mod_auth/1, + ip_mod_auth_api/1, + ip_mod_auth_mnesia_api/1, + ip_mod_htaccess/1, + ip_mod_cgi/1, + ip_mod_esi/1, + ip_mod_get/1, + ip_mod_head/1, + ip_mod_all/1, + ip_load_light/1, + ip_load_medium/1, + ip_load_heavy/1, + ip_dos_hostname/1, + ip_time_test/1, + ip_block_disturbing_idle/1, + ip_block_non_disturbing_idle/1, + ip_block_503/1, + ip_block_disturbing_active/1, + ip_block_non_disturbing_active/1, + ip_block_disturbing_active_timeout_not_released/1, + ip_block_disturbing_active_timeout_released/1, + ip_block_non_disturbing_active_timeout_not_released/1, + ip_block_non_disturbing_active_timeout_released/1, + ip_block_disturbing_blocker_dies/1, + ip_block_non_disturbing_blocker_dies/1, + ip_restart_no_block/1, + ip_restart_disturbing_block/1, + ip_restart_non_disturbing_block/1 + ]). + +-export([ + essl_mod_alias/1, + essl_mod_actions/1, + essl_mod_security/1, + essl_mod_auth/1, + essl_mod_auth_api/1, + essl_mod_auth_mnesia_api/1, + essl_mod_htaccess/1, + essl_mod_cgi/1, + essl_mod_esi/1, + essl_mod_get/1, + essl_mod_head/1, + essl_mod_all/1, + essl_load_light/1, + essl_load_medium/1, + essl_load_heavy/1, + essl_dos_hostname/1, + essl_time_test/1, + essl_restart_no_block/1, + essl_restart_disturbing_block/1, + essl_restart_non_disturbing_block/1, + essl_block_disturbing_idle/1, + essl_block_non_disturbing_idle/1, + essl_block_503/1, + essl_block_disturbing_active/1, + essl_block_non_disturbing_active/1, + essl_block_disturbing_active_timeout_not_released/1, + essl_block_disturbing_active_timeout_released/1, + essl_block_non_disturbing_active_timeout_not_released/1, + essl_block_non_disturbing_active_timeout_released/1, + essl_block_disturbing_blocker_dies/1, + essl_block_non_disturbing_blocker_dies/1 + ]). + +%%% HTTP 1.1 tests +-export([ip_host/1, ip_chunked/1, ip_expect/1, ip_range/1, + ip_if_test/1, ip_http_trace/1, ip_http1_1_head/1, + ip_mod_cgi_chunked_encoding_test/1]). + +%%% HTTP 1.0 tests +-export([ip_head_1_0/1, ip_get_1_0/1, ip_post_1_0/1]). + +%%% HTTP 0.9 tests +-export([ip_get_0_9/1]). + +%%% Ticket tests +-export([ticket_5775/1,ticket_5865/1,ticket_5913/1,ticket_6003/1, + ticket_7304/1]). + +%%% IPv6 tests +-export([ipv6_hostname_ipcomm/0, ipv6_hostname_ipcomm/1, + ipv6_address_ipcomm/0, ipv6_address_ipcomm/1, + ipv6_hostname_essl/0, ipv6_hostname_essl/1, + ipv6_address_essl/0, ipv6_address_essl/1]). + +%% Help functions +-export([cleanup_mnesia/0, setup_mnesia/0, setup_mnesia/1]). + +-define(IP_PORT, 8898). +-define(SSL_PORT, 8899). +-define(MAX_HEADER_SIZE, 256). +-define(IPV6_LOCAL_HOST, "0:0:0:0:0:0:0:1"). + +%% Minutes before failed auths timeout. +-define(FAIL_EXPIRE_TIME,1). + +%% Seconds before successful auths timeout. +-define(AUTH_TIMEOUT,5). + +-record(httpd_user, {user_name, password, user_data}). +-record(httpd_group, {group_name, userlist}). + + +%%-------------------------------------------------------------------- +%% all(Arg) -> [Doc] | [Case] | {skip, Comment} +%% Arg - doc | suite +%% Doc - string() +%% Case - atom() +%% Name of a test case function. +%% Comment - string() +%% Description: Returns documentation/test cases in this test suite +%% or a skip tuple if the platform is not supported. +%%-------------------------------------------------------------------- +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ + {group, ip}, + {group, ssl}, + {group, http_1_1_ip}, + {group, http_1_0_ip}, + {group, http_0_9_ip}, + {group, ipv6}, + {group, tickets} + ]. + +groups() -> + [ + {ip, [], + [ip_mod_alias, ip_mod_actions, ip_mod_security, + ip_mod_auth, ip_mod_auth_api, ip_mod_auth_mnesia_api, + ip_mod_htaccess, ip_mod_cgi, ip_mod_esi, ip_mod_get, + ip_mod_head, ip_mod_all, ip_load_light, ip_load_medium, + ip_load_heavy, ip_dos_hostname, ip_time_test, + ip_restart_no_block, ip_restart_disturbing_block, + ip_restart_non_disturbing_block, + ip_block_disturbing_idle, ip_block_non_disturbing_idle, + ip_block_503, ip_block_disturbing_active, + ip_block_non_disturbing_active, + ip_block_disturbing_active_timeout_not_released, + ip_block_disturbing_active_timeout_released, + ip_block_non_disturbing_active_timeout_not_released, + ip_block_non_disturbing_active_timeout_released, + ip_block_disturbing_blocker_dies, + ip_block_non_disturbing_blocker_dies]}, + {ssl, [], [{group, essl}]}, + {essl, [], + [essl_mod_alias, essl_mod_actions, essl_mod_security, + essl_mod_auth, essl_mod_auth_api, + essl_mod_auth_mnesia_api, essl_mod_htaccess, + essl_mod_cgi, essl_mod_esi, essl_mod_get, essl_mod_head, + essl_mod_all, essl_load_light, essl_load_medium, + essl_load_heavy, essl_dos_hostname, essl_time_test, + essl_restart_no_block, essl_restart_disturbing_block, + essl_restart_non_disturbing_block, + essl_block_disturbing_idle, + essl_block_non_disturbing_idle, essl_block_503, + essl_block_disturbing_active, + essl_block_non_disturbing_active, + essl_block_disturbing_active_timeout_not_released, + essl_block_disturbing_active_timeout_released, + essl_block_non_disturbing_active_timeout_not_released, + essl_block_non_disturbing_active_timeout_released, + essl_block_disturbing_blocker_dies, + essl_block_non_disturbing_blocker_dies]}, + {http_1_1_ip, [], + [ip_host, ip_chunked, ip_expect, ip_range, ip_if_test, + ip_http_trace, ip_http1_1_head, + ip_mod_cgi_chunked_encoding_test]}, + {http_1_0_ip, [], + [ip_head_1_0, ip_get_1_0, ip_post_1_0]}, + {http_0_9_ip, [], [ip_get_0_9]}, + {ipv6, [], [ipv6_hostname_ipcomm, ipv6_address_ipcomm, + ipv6_hostname_essl, ipv6_address_essl]}, + {tickets, [], + [ticket_5775, ticket_5865, ticket_5913, ticket_6003, + ticket_7304]}]. + + +init_per_group(ipv6 = _GroupName, Config) -> + case inets_test_lib:has_ipv6_support() of + {ok, _} -> + Config; + _ -> + {skip, "Host does not support IPv6"} + end; +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config) -> Config +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Initiation before the whole suite +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + io:format(user, "init_per_suite -> entry with" + "~n Config: ~p" + "~n", [Config]), + + ?PRINT_SYSTEM_INFO([]), + + PrivDir = ?config(priv_dir, Config), + SuiteTopDir = filename:join(PrivDir, ?MODULE), + case file:make_dir(SuiteTopDir) of + ok -> + ok; + {error, eexist} -> + ok; + Error -> + throw({error, {failed_creating_suite_top_dir, Error}}) + end, + + [{has_ipv6_support, inets_test_lib:has_ipv6_support()}, + {suite_top_dir, SuiteTopDir}, + {node, node()}, + {host, inets_test_lib:hostname()}, + {address, getaddr()} | Config]. + + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config) -> _ +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after the whole suite +%%-------------------------------------------------------------------- + +end_per_suite(_Config) -> + %% SuiteTopDir = ?config(suite_top_dir, Config), + %% inets_test_lib:del_dirs(SuiteTopDir), + ok. + + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(Case, Config) -> Config +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Initiation before each test case +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_testcase(Case, Config) -> + NewConfig = init_per_testcase2(Case, Config), + init_per_testcase3(Case, NewConfig). + + +init_per_testcase2(Case, Config) -> + + %% tsp("init_per_testcase2 -> entry with" + %% "~n Config: ~p", [Config]), + + IpNormal = integer_to_list(?IP_PORT) ++ ".conf", + IpHtaccess = integer_to_list(?IP_PORT) ++ "htaccess.conf", + SslNormal = integer_to_list(?SSL_PORT) ++ ".conf", + SslHtaccess = integer_to_list(?SSL_PORT) ++ "htaccess.conf", + + DataDir = ?config(data_dir, Config), + SuiteTopDir = ?config(suite_top_dir, Config), + + %% tsp("init_per_testcase2 -> " + %% "~n SuiteDir: ~p" + %% "~n DataDir: ~p", [SuiteTopDir, DataDir]), + + TcTopDir = filename:join(SuiteTopDir, Case), + ?line ok = file:make_dir(TcTopDir), + + %% tsp("init_per_testcase2 -> " + %% "~n TcTopDir: ~p", [TcTopDir]), + + DataSrc = filename:join([DataDir, "server_root"]), + ServerRoot = filename:join([TcTopDir, "server_root"]), + + %% tsp("init_per_testcase2 -> " + %% "~n DataSrc: ~p" + %% "~n ServerRoot: ~p", [DataSrc, ServerRoot]), + + ok = file:make_dir(ServerRoot), + ok = file:make_dir(filename:join([TcTopDir, "logs"])), + + NewConfig = [{tc_top_dir, TcTopDir}, {server_root, ServerRoot} | Config], + + %% tsp("init_per_testcase2 -> copy DataSrc to ServerRoot"), + + inets_test_lib:copy_dirs(DataSrc, ServerRoot), + + %% tsp("init_per_testcase2 -> fix cgi"), + EnvCGI = filename:join([ServerRoot, "cgi-bin", "printenv.sh"]), + {ok, FileInfo} = file:read_file_info(EnvCGI), + ok = file:write_file_info(EnvCGI, + FileInfo#file_info{mode = 8#00755}), + + EchoCGI = case test_server:os_type() of + {win32, _} -> + "cgi_echo.exe"; + _ -> + "cgi_echo" + end, + CGIDir = filename:join([ServerRoot, "cgi-bin"]), + inets_test_lib:copy_file(EchoCGI, DataDir, CGIDir), + NewEchoCGI = filename:join([CGIDir, EchoCGI]), + {ok, FileInfo1} = file:read_file_info(NewEchoCGI), + ok = file:write_file_info(NewEchoCGI, + FileInfo1#file_info{mode = 8#00755}), + + %% To be used by IP test cases + %% tsp("init_per_testcase2 -> ip testcase setups"), + create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig], + normal_access, IpNormal), + create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig], + mod_htaccess, IpHtaccess), + + %% To be used by SSL test cases + %% tsp("init_per_testcase2 -> ssl testcase setups"), + SocketType = + case atom_to_list(Case) of + [X, $s, $s, $l | _] -> + case X of + $p -> ssl; + $e -> essl + end; + _ -> + ssl + end, + + create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig], + normal_access, SslNormal), + create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig], + mod_htaccess, SslHtaccess), + + %% To be used by IPv6 test cases. Case-clause is so that + %% you can do ts:run(inets, httpd_SUITE, ) + %% for all cases except the ipv6 cases as they depend + %% on 'test_host_ipv6_only' that will only be present + %% when you run the whole test suite due to shortcomings + %% of the test server. + + tsp("init_per_testcase2 -> maybe generate IPv6 config file(s)"), + NewConfig2 = + case atom_to_list(Case) of + "ipv6_" ++ _ -> + case (catch inets_test_lib:has_ipv6_support(NewConfig)) of + {ok, IPv6Address0} -> + {ok, Hostname} = inet:gethostname(), + IPv6Address = http_transport:ipv6_name(IPv6Address0), + create_ipv6_config([{port, ?IP_PORT}, + {sock_type, ip_comm}, + {ipv6_host, IPv6Address} | + NewConfig], + "ipv6_hostname_ipcomm.conf", + Hostname), + create_ipv6_config([{port, ?IP_PORT}, + {sock_type, ip_comm}, + {ipv6_host, IPv6Address} | + NewConfig], + "ipv6_address_ipcomm.conf", + IPv6Address), + create_ipv6_config([{port, ?SSL_PORT}, + {sock_type, essl}, + {ipv6_host, IPv6Address} | + NewConfig], + "ipv6_hostname_essl.conf", + Hostname), + create_ipv6_config([{port, ?SSL_PORT}, + {sock_type, essl}, + {ipv6_host, IPv6Address} | + NewConfig], + "ipv6_address_essl.conf", + IPv6Address), + [{ipv6_host, IPv6Address} | NewConfig]; + _ -> + NewConfig + end; + + _ -> + NewConfig + end, + + %% tsp("init_per_testcase2 -> done when" + %% "~n NewConfig2: ~p", [NewConfig2]), + + NewConfig2. + + +init_per_testcase3(Case, Config) -> + tsp("init_per_testcase3(~w) -> entry with" + "~n Config: ~p", [Case, Config]), + + +%% %% Create a new fresh node to be used by the server in this test-case + +%% NodeName = list_to_atom(atom_to_list(Case) ++ "_httpd"), +%% Node = inets_test_lib:start_node(NodeName), + + %% Clean up (we do not want this clean up in end_per_testcase + %% if init_per_testcase crashes for some testcase it will + %% have contaminated the environment and there will be no clean up.) + %% This init can take a few different paths so that one crashes + %% does not mean that all invocations will. + + application:unset_env(inets, services), + application:stop(inets), + application:stop(ssl), + cleanup_mnesia(), + + %% Start initialization + tsp("init_per_testcase3(~w) -> start init", [Case]), + + Dog = test_server:timetrap(inets_test_lib:minutes(10)), + NewConfig = lists:keydelete(watchdog, 1, Config), + TcTopDir = ?config(tc_top_dir, Config), + + CaseRest = + case atom_to_list(Case) of + "ip_mod_htaccess" -> + inets_test_lib:start_http_server( + filename:join(TcTopDir, + integer_to_list(?IP_PORT) ++ + "htaccess.conf")), + "mod_htaccess"; + "ip_" ++ Rest -> + inets_test_lib:start_http_server( + filename:join(TcTopDir, + integer_to_list(?IP_PORT) ++ ".conf")), + Rest; + "ticket_5913" -> + HttpdOptions = + [{file, + filename:join(TcTopDir, + integer_to_list(?IP_PORT) ++ ".conf")}, + {accept_timeout,30000}, + {debug,[{exported_functions, + [httpd_manager,httpd_request_handler]}]}], + inets_test_lib:start_http_server(HttpdOptions); + "ticket_"++Rest -> + %% OTP-5913 use the new syntax of inets.config + inets_test_lib:start_http_server([{file, + filename:join(TcTopDir, + integer_to_list(?IP_PORT) ++ ".conf")}]), + Rest; + + [X, $s, $s, $l, $_, $m, $o, $d, $_, $h, $t, $a, $c, $c, $e, $s, $s] -> + ?ENSURE_STARTED([crypto, public_key, ssl]), + SslTag = + case X of + $p -> ssl; % Plain + $e -> essl % Erlang based ssl + end, + case inets_test_lib:start_http_server_ssl( + filename:join(TcTopDir, + integer_to_list(?SSL_PORT) ++ + "htaccess.conf"), SslTag) of + ok -> + "mod_htaccess"; + Other -> + error_logger:info_msg("Other: ~p~n", [Other]), + {skip, "SSL does not seem to be supported"} + end; + [X, $s, $s, $l, $_ | Rest] -> + ?ENSURE_STARTED([crypto, public_key, ssl]), + SslTag = + case X of + $p -> ssl; + $e -> essl + end, + case inets_test_lib:start_http_server_ssl( + filename:join(TcTopDir, + integer_to_list(?SSL_PORT) ++ + ".conf"), SslTag) of + ok -> + Rest; + Other -> + error_logger:info_msg("Other: ~p~n", [Other]), + {skip, "SSL does not seem to be supported"} + end; + "ipv6_" ++ _ = TestCaseStr -> + case inets_test_lib:has_ipv6_support() of + {ok, _} -> + inets_test_lib:start_http_server( + filename:join(TcTopDir, + TestCaseStr ++ ".conf")); + + _ -> + {skip, "Host does not support IPv6"} + end + end, + + InitRes = + case CaseRest of + {skip, _} = Skip -> + Skip; + "mod_auth_" ++ _ -> + start_mnesia(?config(node, Config)), + [{watchdog, Dog} | NewConfig]; + "mod_htaccess" -> + ServerRoot = ?config(server_root, Config), + Path = filename:join([ServerRoot, "htdocs"]), + catch remove_htaccess(Path), + create_htaccess_data(Path, ?config(address, Config)), + [{watchdog, Dog} | NewConfig]; + "range" -> + ServerRoot = ?config(server_root, Config), + Path = filename:join([ServerRoot, "htdocs"]), + create_range_data(Path), + [{watchdog, Dog} | NewConfig]; + _ -> + [{watchdog, Dog} | NewConfig] + end, + + tsp("init_per_testcase3(~w) -> done when" + "~n InitRes: ~p", [Case, InitRes]), + + InitRes. + + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(Case, Config) -> _ +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after each test case +%%-------------------------------------------------------------------- +end_per_testcase(Case, Config) -> + Dog = ?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + end_per_testcase2(Case, lists:keydelete(watchdog, 1, Config)), + ok. + +end_per_testcase2(Case, Config) -> + tsp("end_per_testcase2(~w) -> entry with" + "~n Config: ~p", [Case, Config]), + application:unset_env(inets, services), + application:stop(inets), + application:stop(ssl), + application:stop(crypto), % used by the new ssl (essl test cases) + cleanup_mnesia(), + tsp("end_per_testcase2(~w) -> done", [Case]), + ok. + + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- + +%%------------------------------------------------------------------------- +ip_mod_alias(doc) -> + ["Module test: mod_alias"]; +ip_mod_alias(suite) -> + []; +ip_mod_alias(Config) when is_list(Config) -> + httpd_mod:alias(ip_comm, ?IP_PORT, + ?config(host, Config), ?config(node, Config)), + ok. + +%%------------------------------------------------------------------------- +ip_mod_actions(doc) -> + ["Module test: mod_actions"]; +ip_mod_actions(suite) -> + []; +ip_mod_actions(Config) when is_list(Config) -> + httpd_mod:actions(ip_comm, ?IP_PORT, + ?config(host, Config), ?config(node, Config)), + ok. + +%%------------------------------------------------------------------------- +ip_mod_security(doc) -> + ["Module test: mod_security"]; +ip_mod_security(suite) -> + []; +ip_mod_security(Config) when is_list(Config) -> + ServerRoot = ?config(server_root, Config), + httpd_mod:security(ServerRoot, ip_comm, ?IP_PORT, + ?config(host, Config), ?config(node, Config)), + ok. + +%%------------------------------------------------------------------------- +ip_mod_auth(doc) -> + ["Module test: mod_auth"]; +ip_mod_auth(suite) -> + []; +ip_mod_auth(Config) when is_list(Config) -> + httpd_mod:auth(ip_comm, ?IP_PORT, + ?config(host, Config), ?config(node, Config)), + ok. + +%%------------------------------------------------------------------------- +ip_mod_auth_api(doc) -> + ["Module test: mod_auth_api"]; +ip_mod_auth_api(suite) -> + []; +ip_mod_auth_api(Config) when is_list(Config) -> + ServerRoot = ?config(server_root, Config), + Host = ?config(host, Config), + Node = ?config(node, Config), + httpd_mod:auth_api(ServerRoot, "", ip_comm, ?IP_PORT, Host, Node), + httpd_mod:auth_api(ServerRoot, "dets_", ip_comm, ?IP_PORT, Host, Node), + httpd_mod:auth_api(ServerRoot, "mnesia_", ip_comm, ?IP_PORT, Host, Node), + ok. +%%------------------------------------------------------------------------- +ip_mod_auth_mnesia_api(doc) -> + ["Module test: mod_auth_mnesia_api"]; +ip_mod_auth_mnesia_api(suite) -> + []; +ip_mod_auth_mnesia_api(Config) when is_list(Config) -> + httpd_mod:auth_mnesia_api(ip_comm, ?IP_PORT, + ?config(host, Config), ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ip_mod_htaccess(doc) -> + ["Module test: mod_htaccess"]; +ip_mod_htaccess(suite) -> + []; +ip_mod_htaccess(Config) when is_list(Config) -> + httpd_mod:htaccess(ip_comm, ?IP_PORT, + ?config(host, Config), ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ip_mod_cgi(doc) -> + ["Module test: mod_cgi"]; +ip_mod_cgi(suite) -> + []; +ip_mod_cgi(Config) when is_list(Config) -> + httpd_mod:cgi(ip_comm, ?IP_PORT, + ?config(host, Config), ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ip_mod_esi(doc) -> + ["Module test: mod_esi"]; +ip_mod_esi(suite) -> + []; +ip_mod_esi(Config) when is_list(Config) -> + httpd_mod:esi(ip_comm, ?IP_PORT, + ?config(host, Config), ?config(node, Config)), + ok. + +%%------------------------------------------------------------------------- +ip_mod_get(doc) -> + ["Module test: mod_get"]; +ip_mod_get(suite) -> + []; +ip_mod_get(Config) when is_list(Config) -> + httpd_mod:get(ip_comm, ?IP_PORT, + ?config(host, Config), ?config(node, Config)), + ok. + +%%------------------------------------------------------------------------- +ip_mod_head(doc) -> + ["Module test: mod_head"]; +ip_mod_head(suite) -> + []; +ip_mod_head(Config) when is_list(Config) -> + httpd_mod:head(ip_comm, ?IP_PORT, + ?config(host, Config), ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ip_mod_all(doc) -> + ["All modules test"]; +ip_mod_all(suite) -> + []; +ip_mod_all(Config) when is_list(Config) -> + httpd_mod:all(ip_comm, ?IP_PORT, + ?config(host, Config), ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ip_load_light(doc) -> + ["Test light load"]; +ip_load_light(suite) -> + []; +ip_load_light(Config) when is_list(Config) -> + httpd_load:load_test(ip_comm, ?IP_PORT, ?config(host, Config), + ?config(node, Config), + get_nof_clients(ip_comm, light)), + ok. +%%------------------------------------------------------------------------- +ip_load_medium(doc) -> + ["Test medium load"]; +ip_load_medium(suite) -> + []; +ip_load_medium(Config) when is_list(Config) -> + httpd_load:load_test(ip_comm, ?IP_PORT, ?config(host, Config), + ?config(node, Config), + get_nof_clients(ip_comm, medium)), + ok. +%%------------------------------------------------------------------------- +ip_load_heavy(doc) -> + ["Test heavy load"]; +ip_load_heavy(suite) -> + []; +ip_load_heavy(Config) when is_list(Config) -> + httpd_load:load_test(ip_comm, ?IP_PORT, ?config(host, Config), + ?config(node, Config), + get_nof_clients(ip_comm, heavy)), + ok. + + +%%------------------------------------------------------------------------- +ip_dos_hostname(doc) -> + ["Denial Of Service (DOS) attack test case"]; +ip_dos_hostname(suite) -> + []; +ip_dos_hostname(Config) when is_list(Config) -> + dos_hostname(ip_comm, ?IP_PORT, ?config(host, Config), + ?config(node, Config), ?MAX_HEADER_SIZE), + ok. + + +%%------------------------------------------------------------------------- +ip_time_test(doc) -> + [""]; +ip_time_test(suite) -> + []; +ip_time_test(Config) when is_list(Config) -> + %% + Skippable = [win32], + Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, + ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% + + httpd_time_test:t(ip_comm, ?config(host, Config), ?IP_PORT), + ok. + +%%------------------------------------------------------------------------- +ip_block_503(doc) -> + ["Check that you will receive status code 503 when the server" + " is blocked and 200 when its not blocked."]; +ip_block_503(suite) -> + []; +ip_block_503(Config) when is_list(Config) -> + httpd_block:block_503(ip_comm, ?IP_PORT, ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ip_block_disturbing_idle(doc) -> + ["Check that you can block/unblock an idle server. The strategy " + "distribing does not really make a difference in this case."]; +ip_block_disturbing_idle(suite) -> + []; +ip_block_disturbing_idle(Config) when is_list(Config) -> + httpd_block:block_disturbing_idle(ip_comm, ?IP_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ip_block_non_disturbing_idle(doc) -> + ["Check that you can block/unblock an idle server. The strategy " + "non distribing does not really make a difference in this case."]; +ip_block_non_disturbing_idle(suite) -> + []; +ip_block_non_disturbing_idle(Config) when is_list(Config) -> + httpd_block:block_non_disturbing_idle(ip_comm, ?IP_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ip_block_disturbing_active(doc) -> + ["Check that you can block/unblock an active server. The strategy " + "distribing means ongoing requests should be terminated."]; +ip_block_disturbing_active(suite) -> + []; +ip_block_disturbing_active(Config) when is_list(Config) -> + httpd_block:block_disturbing_active(ip_comm, ?IP_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ip_block_non_disturbing_active(doc) -> + ["Check that you can block/unblock an idle server. The strategy " + "non distribing means the ongoing requests should be compleated."]; +ip_block_non_disturbing_active(suite) -> + []; +ip_block_non_disturbing_active(Config) when is_list(Config) -> + httpd_block:block_non_disturbing_idle(ip_comm, ?IP_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + +%%------------------------------------------------------------------------- +ip_block_disturbing_active_timeout_not_released(doc) -> + ["Check that you can block an active server. The strategy " + "distribing means ongoing requests should be compleated" + "if the timeout does not occur."]; +ip_block_disturbing_active_timeout_not_released(suite) -> + []; +ip_block_disturbing_active_timeout_not_released(Config) + when is_list(Config) -> + httpd_block:block_disturbing_active_timeout_not_released(ip_comm, + ?IP_PORT, + ?config(host, + Config), + ?config(node, + Config)), + ok. +%%------------------------------------------------------------------------- +ip_block_disturbing_active_timeout_released(doc) -> + ["Check that you can block an active server. The strategy " + "distribing means ongoing requests should be terminated when" + "the timeout occurs."]; +ip_block_disturbing_active_timeout_released(suite) -> + []; +ip_block_disturbing_active_timeout_released(Config) + when is_list(Config) -> + httpd_block:block_disturbing_active_timeout_released(ip_comm, + ?IP_PORT, + ?config(host, + Config), + ?config(node, + Config)), + ok. + +%%------------------------------------------------------------------------- +ip_block_non_disturbing_active_timeout_not_released(doc) -> + ["Check that you can block an active server. The strategy " + "non non distribing means ongoing requests should be completed."]; +ip_block_non_disturbing_active_timeout_not_released(suite) -> + []; +ip_block_non_disturbing_active_timeout_not_released(Config) + when is_list(Config) -> + httpd_block: + block_non_disturbing_active_timeout_not_released(ip_comm, + ?IP_PORT, + ?config(host, + Config), + ?config(node, + Config)), + ok. +%%------------------------------------------------------------------------- +ip_block_non_disturbing_active_timeout_released(doc) -> + ["Check that you can block an active server. The strategy " + "non non distribing means ongoing requests should be completed. " + "When the timeout occurs the block operation sohould be canceled." ]; +ip_block_non_disturbing_active_timeout_released(suite) -> + []; +ip_block_non_disturbing_active_timeout_released(Config) + when is_list(Config) -> + httpd_block: + block_non_disturbing_active_timeout_released(ip_comm, + ?IP_PORT, + ?config(host, + Config), + ?config(node, + Config)), + ok. +%%------------------------------------------------------------------------- +ip_block_disturbing_blocker_dies(doc) -> + []; +ip_block_disturbing_blocker_dies(suite) -> + []; +ip_block_disturbing_blocker_dies(Config) when is_list(Config) -> + httpd_block:disturbing_blocker_dies(ip_comm, ?IP_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ip_block_non_disturbing_blocker_dies(doc) -> + []; +ip_block_non_disturbing_blocker_dies(suite) -> + []; +ip_block_non_disturbing_blocker_dies(Config) when is_list(Config) -> + httpd_block:non_disturbing_blocker_dies(ip_comm, ?IP_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ip_restart_no_block(doc) -> + [""]; +ip_restart_no_block(suite) -> + []; +ip_restart_no_block(Config) when is_list(Config) -> + httpd_block:restart_no_block(ip_comm, ?IP_PORT, ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ip_restart_disturbing_block(doc) -> + [""]; +ip_restart_disturbing_block(suite) -> + []; +ip_restart_disturbing_block(Config) when is_list(Config) -> + %% + Condition = + fun() -> + case os:type() of + {unix, linux} -> + HW = string:strip(os:cmd("uname -m"), right, $\n), + case HW of + "ppc" -> + case inet:gethostname() of + {ok, "peach"} -> + true; + _ -> + false + end; + _ -> + false + end; + _ -> + false + end + end, + ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% + + httpd_block:restart_disturbing_block(ip_comm, ?IP_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + +%%------------------------------------------------------------------------- +ip_restart_non_disturbing_block(doc) -> + [""]; +ip_restart_non_disturbing_block(suite) -> + []; +ip_restart_non_disturbing_block(Config) when is_list(Config) -> + %% + Condition = + fun() -> + case os:type() of + {unix, linux} -> + HW = string:strip(os:cmd("uname -m"), right, $\n), + case HW of + "ppc" -> + case inet:gethostname() of + {ok, "peach"} -> + true; + _ -> + false + end; + _ -> + false + end; + _ -> + false + end + end, + ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% + + httpd_block:restart_non_disturbing_block(ip_comm, ?IP_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + +%%------------------------------------------------------------------------- + +essl_mod_alias(doc) -> + ["Module test: mod_alias - using new of configure new SSL"]; +essl_mod_alias(suite) -> + []; +essl_mod_alias(Config) when is_list(Config) -> + ssl_mod_alias(essl, Config). + + +ssl_mod_alias(Tag, Config) -> + httpd_mod:alias(Tag, ?SSL_PORT, + ?config(host, Config), ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + +essl_mod_actions(doc) -> + ["Module test: mod_actions - using new of configure new SSL"]; +essl_mod_actions(suite) -> + []; +essl_mod_actions(Config) when is_list(Config) -> + ssl_mod_actions(essl, Config). + + +ssl_mod_actions(Tag, Config) -> + httpd_mod:actions(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + +essl_mod_security(doc) -> + ["Module test: mod_security - using new of configure new SSL"]; +essl_mod_security(suite) -> + []; +essl_mod_security(Config) when is_list(Config) -> + ssl_mod_security(essl, Config). + +ssl_mod_security(Tag, Config) -> + ServerRoot = ?config(server_root, Config), + httpd_mod:security(ServerRoot, + Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + +essl_mod_auth(doc) -> + ["Module test: mod_auth - using new of configure new SSL"]; +essl_mod_auth(suite) -> + []; +essl_mod_auth(Config) when is_list(Config) -> + ssl_mod_auth(essl, Config). + +ssl_mod_auth(Tag, Config) -> + httpd_mod:auth(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + + +essl_mod_auth_api(doc) -> + ["Module test: mod_auth - using new of configure new SSL"]; +essl_mod_auth_api(suite) -> + []; +essl_mod_auth_api(Config) when is_list(Config) -> + ssl_mod_auth_api(essl, Config). + +ssl_mod_auth_api(Tag, Config) -> + ServerRoot = ?config(server_root, Config), + Host = ?config(host, Config), + Node = ?config(node, Config), + httpd_mod:auth_api(ServerRoot, "", Tag, ?SSL_PORT, Host, Node), + httpd_mod:auth_api(ServerRoot, "dets_", Tag, ?SSL_PORT, Host, Node), + httpd_mod:auth_api(ServerRoot, "mnesia_", Tag, ?SSL_PORT, Host, Node), + ok. + + +%%------------------------------------------------------------------------- + + +essl_mod_auth_mnesia_api(doc) -> + ["Module test: mod_auth_mnesia_api - using new of configure new SSL"]; +essl_mod_auth_mnesia_api(suite) -> + []; +essl_mod_auth_mnesia_api(Config) when is_list(Config) -> + ssl_mod_auth_mnesia_api(essl, Config). + +ssl_mod_auth_mnesia_api(Tag, Config) -> + httpd_mod:auth_mnesia_api(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + +essl_mod_htaccess(doc) -> + ["Module test: mod_htaccess - using new of configure new SSL"]; +essl_mod_htaccess(suite) -> + []; +essl_mod_htaccess(Config) when is_list(Config) -> + ssl_mod_htaccess(essl, Config). + +ssl_mod_htaccess(Tag, Config) -> + httpd_mod:htaccess(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + +essl_mod_cgi(doc) -> + ["Module test: mod_cgi - using new of configure new SSL"]; +essl_mod_cgi(suite) -> + []; +essl_mod_cgi(Config) when is_list(Config) -> + ssl_mod_cgi(essl, Config). + +ssl_mod_cgi(Tag, Config) -> + httpd_mod:cgi(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + +essl_mod_esi(doc) -> + ["Module test: mod_esi - using new of configure new SSL"]; +essl_mod_esi(suite) -> + []; +essl_mod_esi(Config) when is_list(Config) -> + ssl_mod_esi(essl, Config). + +ssl_mod_esi(Tag, Config) -> + httpd_mod:esi(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + +essl_mod_get(doc) -> + ["Module test: mod_get - using new of configure new SSL"]; +essl_mod_get(suite) -> + []; +essl_mod_get(Config) when is_list(Config) -> + ssl_mod_get(essl, Config). + +ssl_mod_get(Tag, Config) -> + httpd_mod:get(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + +essl_mod_head(doc) -> + ["Module test: mod_head - using new of configure new SSL"]; +essl_mod_head(suite) -> + []; +essl_mod_head(Config) when is_list(Config) -> + ssl_mod_head(essl, Config). + +ssl_mod_head(Tag, Config) -> + httpd_mod:head(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + +essl_mod_all(doc) -> + ["All modules test - using new of configure new SSL"]; +essl_mod_all(suite) -> + []; +essl_mod_all(Config) when is_list(Config) -> + ssl_mod_all(essl, Config). + +ssl_mod_all(Tag, Config) -> + httpd_mod:all(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + +essl_load_light(doc) -> + ["Test light load - using new of configure new SSL"]; +essl_load_light(suite) -> + []; +essl_load_light(Config) when is_list(Config) -> + ssl_load_light(essl, Config). + +ssl_load_light(Tag, Config) -> + httpd_load:load_test(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config), + get_nof_clients(ssl, light)), + ok. + + +%%------------------------------------------------------------------------- + +essl_load_medium(doc) -> + ["Test medium load - using new of configure new SSL"]; +essl_load_medium(suite) -> + []; +essl_load_medium(Config) when is_list(Config) -> + ssl_load_medium(essl, Config). + +ssl_load_medium(Tag, Config) -> + %% + Skippable = [win32], + Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, + ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% + + httpd_load:load_test(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config), + get_nof_clients(ssl, medium)), + ok. + + +%%------------------------------------------------------------------------- + +essl_load_heavy(doc) -> + ["Test heavy load - using new of configure new SSL"]; +essl_load_heavy(suite) -> + []; +essl_load_heavy(Config) when is_list(Config) -> + ssl_load_heavy(essl, Config). + +ssl_load_heavy(Tag, Config) -> + %% + Skippable = [win32], + Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, + ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% + + httpd_load:load_test(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config), + get_nof_clients(ssl, heavy)), + ok. + + +%%------------------------------------------------------------------------- + + +essl_dos_hostname(doc) -> + ["Denial Of Service (DOS) attack test case - using new of configure new SSL"]; +essl_dos_hostname(suite) -> + []; +essl_dos_hostname(Config) when is_list(Config) -> + ssl_dos_hostname(essl, Config). + +ssl_dos_hostname(Tag, Config) -> + dos_hostname(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config), + ?MAX_HEADER_SIZE), + ok. + + +%%------------------------------------------------------------------------- + + +essl_time_test(doc) -> + ["using new of configure new SSL"]; +essl_time_test(suite) -> + []; +essl_time_test(Config) when is_list(Config) -> + ssl_time_test(essl, Config). + +ssl_time_test(Tag, Config) when is_list(Config) -> + %% + FreeBSDVersionVerify = + fun() -> + case os:version() of + {7, 1, _} -> % We only have one such machine, so... + true; + _ -> + false + end + end, + Skippable = [win32, {unix, [{freebsd, FreeBSDVersionVerify}]}], + Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, + ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% + + httpd_time_test:t(Tag, + ?config(host, Config), + ?SSL_PORT), + ok. + + +%%------------------------------------------------------------------------- + + +essl_block_503(doc) -> + ["Check that you will receive status code 503 when the server" + " is blocked and 200 when its not blocked - using new of configure new SSL."]; +essl_block_503(suite) -> + []; +essl_block_503(Config) when is_list(Config) -> + ssl_block_503(essl, Config). + +ssl_block_503(Tag, Config) -> + httpd_block:block_503(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + +essl_block_disturbing_idle(doc) -> + ["Check that you can block/unblock an idle server. The strategy " + "distribing does not really make a difference in this case." + "Using new of configure new SSL"]; +essl_block_disturbing_idle(suite) -> + []; +essl_block_disturbing_idle(Config) when is_list(Config) -> + ssl_block_disturbing_idle(essl, Config). + +ssl_block_disturbing_idle(Tag, Config) -> + httpd_block:block_disturbing_idle(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + +essl_block_non_disturbing_idle(doc) -> + ["Check that you can block/unblock an idle server. The strategy " + "non distribing does not really make a difference in this case." + "Using new of configure new SSL"]; +essl_block_non_disturbing_idle(suite) -> + []; +essl_block_non_disturbing_idle(Config) when is_list(Config) -> + ssl_block_non_disturbing_idle(essl, Config). + +ssl_block_non_disturbing_idle(Tag, Config) -> + httpd_block:block_non_disturbing_idle(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + +essl_block_disturbing_active(doc) -> + ["Check that you can block/unblock an active server. The strategy " + "distribing means ongoing requests should be terminated." + "Using new of configure new SSL"]; +essl_block_disturbing_active(suite) -> + []; +essl_block_disturbing_active(Config) when is_list(Config) -> + ssl_block_disturbing_active(essl, Config). + +ssl_block_disturbing_active(Tag, Config) -> + httpd_block:block_disturbing_active(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + +essl_block_non_disturbing_active(doc) -> + ["Check that you can block/unblock an idle server. The strategy " + "non distribing means the ongoing requests should be compleated." + "Using new of configure new SSL"]; +essl_block_non_disturbing_active(suite) -> + []; +essl_block_non_disturbing_active(Config) when is_list(Config) -> + ssl_block_non_disturbing_active(essl, Config). + +ssl_block_non_disturbing_active(Tag, Config) -> + httpd_block:block_non_disturbing_idle(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + +essl_block_disturbing_active_timeout_not_released(doc) -> + ["Check that you can block an active server. The strategy " + "distribing means ongoing requests should be compleated" + "if the timeout does not occur." + "Using new of configure new SSL"]; +essl_block_disturbing_active_timeout_not_released(suite) -> + []; +essl_block_disturbing_active_timeout_not_released(Config) + when is_list(Config) -> + ssl_block_disturbing_active_timeout_not_released(essl, Config). + +ssl_block_disturbing_active_timeout_not_released(Tag, Config) -> + Port = ?SSL_PORT, + Host = ?config(host, Config), + Node = ?config(node, Config), + httpd_block:block_disturbing_active_timeout_not_released(Tag, + Port, Host, Node), + ok. + + +%%------------------------------------------------------------------------- + +essl_block_disturbing_active_timeout_released(doc) -> + ["Check that you can block an active server. The strategy " + "distribing means ongoing requests should be terminated when" + "the timeout occurs." + "Using new of configure new SSL"]; +essl_block_disturbing_active_timeout_released(suite) -> + []; +essl_block_disturbing_active_timeout_released(Config) + when is_list(Config) -> + ssl_block_disturbing_active_timeout_released(essl, Config). + +ssl_block_disturbing_active_timeout_released(Tag, Config) -> + Port = ?SSL_PORT, + Host = ?config(host, Config), + Node = ?config(node, Config), + httpd_block:block_disturbing_active_timeout_released(Tag, + Port, + Host, + Node), + ok. + + +%%------------------------------------------------------------------------- + +essl_block_non_disturbing_active_timeout_not_released(doc) -> + ["Check that you can block an active server. The strategy " + "non non distribing means ongoing requests should be completed." + "Using new of configure new SSL"]; +essl_block_non_disturbing_active_timeout_not_released(suite) -> + []; +essl_block_non_disturbing_active_timeout_not_released(Config) + when is_list(Config) -> + ssl_block_non_disturbing_active_timeout_not_released(essl, Config). + +ssl_block_non_disturbing_active_timeout_not_released(Tag, Config) -> + Port = ?SSL_PORT, + Host = ?config(host, Config), + Node = ?config(node, Config), + httpd_block:block_non_disturbing_active_timeout_not_released(Tag, + Port, + Host, + Node), + ok. + + +%%------------------------------------------------------------------------- + + +essl_block_non_disturbing_active_timeout_released(doc) -> + ["Check that you can block an active server. The strategy " + "non distribing means ongoing requests should be completed. " + "When the timeout occurs the block operation sohould be canceled." + "Using new of configure new SSL"]; +essl_block_non_disturbing_active_timeout_released(suite) -> + []; +essl_block_non_disturbing_active_timeout_released(Config) + when is_list(Config) -> + ssl_block_non_disturbing_active_timeout_released(essl, Config). + +ssl_block_non_disturbing_active_timeout_released(Tag, Config) + when is_list(Config) -> + Port = ?SSL_PORT, + Host = ?config(host, Config), + Node = ?config(node, Config), + httpd_block:block_non_disturbing_active_timeout_released(Tag, + Port, + Host, + Node), + + ok. + + +%%------------------------------------------------------------------------- + + +essl_block_disturbing_blocker_dies(doc) -> + ["using new of configure new SSL"]; +essl_block_disturbing_blocker_dies(suite) -> + []; +essl_block_disturbing_blocker_dies(Config) when is_list(Config) -> + ssl_block_disturbing_blocker_dies(essl, Config). + +ssl_block_disturbing_blocker_dies(Tag, Config) -> + httpd_block:disturbing_blocker_dies(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + +essl_block_non_disturbing_blocker_dies(doc) -> + ["using new of configure new SSL"]; +essl_block_non_disturbing_blocker_dies(suite) -> + []; +essl_block_non_disturbing_blocker_dies(Config) when is_list(Config) -> + ssl_block_non_disturbing_blocker_dies(essl, Config). + +ssl_block_non_disturbing_blocker_dies(Tag, Config) -> + httpd_block:non_disturbing_blocker_dies(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + + +essl_restart_no_block(doc) -> + ["using new of configure new SSL"]; +essl_restart_no_block(suite) -> + []; +essl_restart_no_block(Config) when is_list(Config) -> + ssl_restart_no_block(essl, Config). + +ssl_restart_no_block(Tag, Config) -> + httpd_block:restart_no_block(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + + +essl_restart_disturbing_block(doc) -> + ["using new of configure new SSL"]; +essl_restart_disturbing_block(suite) -> + []; +essl_restart_disturbing_block(Config) when is_list(Config) -> + ssl_restart_disturbing_block(essl, Config). + +ssl_restart_disturbing_block(Tag, Config) -> + %% + Condition = + fun() -> + case os:type() of + {unix, linux} -> + case ?OSCMD("uname -m") of + "ppc" -> + case file:read_file_info("/etc/fedora-release") of + {ok, _} -> + case ?OSCMD("awk '{print $2}' /etc/fedora-release") of + "release" -> + %% Fedora 7 and later + case ?OSCMD("awk '{print $3}' /etc/fedora-release") of + "7" -> + true; + _ -> + false + end; + _ -> + false + end; + _ -> + false + end; + _ -> + false + end; + _ -> + false + end + end, + ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% + + httpd_block:restart_disturbing_block(Tag, ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- + + +essl_restart_non_disturbing_block(doc) -> + ["using new of configure new SSL"]; +essl_restart_non_disturbing_block(suite) -> + []; +essl_restart_non_disturbing_block(Config) when is_list(Config) -> + ssl_restart_non_disturbing_block(essl, Config). + +ssl_restart_non_disturbing_block(Tag, Config) -> + %% + Condition = + fun() -> + case os:type() of + {unix, linux} -> + HW = string:strip(os:cmd("uname -m"), right, $\n), + case HW of + "ppc" -> + case inet:gethostname() of + {ok, "peach"} -> + true; + _ -> + false + end; + _ -> + false + end; + _ -> + false + end + end, + ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% + + httpd_block:restart_non_disturbing_block(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + + +%%------------------------------------------------------------------------- +ip_host(doc) -> + ["Control that the server accepts/rejects requests with/ without host"]; +ip_host(suite)-> + []; +ip_host(Config) when is_list(Config) -> + httpd_1_1:host(ip_comm, ?IP_PORT, ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ip_chunked(doc) -> + ["Control that the server accepts chunked requests"]; +ip_chunked(suite) -> + []; +ip_chunked(Config) when is_list(Config) -> + httpd_1_1:chunked(ip_comm, ?IP_PORT, ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ip_expect(doc) -> + ["Control that the server handles request with the expect header " + "field appropiate"]; +ip_expect(suite)-> + []; +ip_expect(Config) when is_list(Config) -> + httpd_1_1:expect(ip_comm, ?IP_PORT, ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ip_range(doc) -> + ["Control that the server can handle range requests to plain files"]; +ip_range(suite)-> + []; +ip_range(Config) when is_list(Config) -> + httpd_1_1:range(ip_comm, ?IP_PORT, ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ip_if_test(doc) -> + ["Test that the if - request header fields is handled correclty"]; +ip_if_test(suite) -> + []; +ip_if_test(Config) when is_list(Config) -> + ServerRoot = ?config(server_root, Config), + DocRoot = filename:join([ServerRoot, "htdocs"]), + httpd_1_1:if_test(ip_comm, ?IP_PORT, ?config(host, Config), + ?config(node, Config), DocRoot), + ok. +%%------------------------------------------------------------------------- +ip_http_trace(doc) -> + ["Test the trace module "]; +ip_http_trace(suite) -> + []; +ip_http_trace(Config) when is_list(Config) -> + httpd_1_1:http_trace(ip_comm, ?IP_PORT, ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ip_http1_1_head(doc) -> + ["Test the trace module "]; +ip_http1_1_head(suite)-> + []; +ip_http1_1_head(Config) when is_list(Config) -> + httpd_1_1:head(ip_comm, ?IP_PORT, ?config(host, Config), + ?config(node, Config)), + ok. + +%%------------------------------------------------------------------------- +ip_get_0_9(doc) -> + ["Test simple HTTP/0.9 GET"]; +ip_get_0_9(suite)-> + []; +ip_get_0_9(Config) when is_list(Config) -> + Host = ?config(host, Config), + Node = ?config(node, Config), + ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node, + "GET / \r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/0.9"} ]), + %% Without space after uri + ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node, + "GET /\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/0.9"} ]), + ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node, + "GET / HTTP/0.9\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/0.9"}]), + + ok. +%%------------------------------------------------------------------------- +ip_head_1_0(doc) -> + ["Test HTTP/1.0 HEAD"]; +ip_head_1_0(suite)-> + []; +ip_head_1_0(Config) when is_list(Config) -> + Host = ?config(host, Config), + Node = ?config(node, Config), + ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node, + "HEAD / HTTP/1.0\r\n\r\n", [{statuscode, 200}, + {version, "HTTP/1.0"}]), + + ok. +%%------------------------------------------------------------------------- +ip_get_1_0(doc) -> + ["Test HTTP/1.0 GET"]; +ip_get_1_0(suite)-> + []; +ip_get_1_0(Config) when is_list(Config) -> + Host = ?config(host, Config), + Node = ?config(node, Config), + ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node, + "GET / HTTP/1.0\r\n\r\n", [{statuscode, 200}, + {version, "HTTP/1.0"}]), + + ok. +%%------------------------------------------------------------------------- +ip_post_1_0(doc) -> + ["Test HTTP/1.0 POST"]; +ip_post_1_0(suite)-> + []; +ip_post_1_0(Config) when is_list(Config) -> + Host = ?config(host, Config), + Node = ?config(node, Config), + %% Test the post message formatin 1.0! Real post are testes elsewhere + ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node, + "POST / HTTP/1.0\r\n\r\n " + "Content-Length:6 \r\n\r\nfoobar", + [{statuscode, 500}, {version, "HTTP/1.0"}]), + + ok. +%%------------------------------------------------------------------------- +ip_mod_cgi_chunked_encoding_test(doc) -> + ["Test the trace module "]; +ip_mod_cgi_chunked_encoding_test(suite)-> + []; +ip_mod_cgi_chunked_encoding_test(Config) when is_list(Config) -> + Host = ?config(host, Config), + Script = + case test_server:os_type() of + {win32, _} -> + "/cgi-bin/printenv.bat"; + _ -> + "/cgi-bin/printenv.sh" + end, + Requests = + ["GET " ++ Script ++ " HTTP/1.1\r\nHost:"++ Host ++"\r\n\r\n", + "GET /cgi-bin/erl/httpd_example/newformat HTTP/1.1\r\nHost:" + ++ Host ++"\r\n\r\n"], + httpd_1_1:mod_cgi_chunked_encoding_test(ip_comm, ?IP_PORT, + Host, + ?config(node, Config), + Requests), + ok. + +%------------------------------------------------------------------------- + +ipv6_hostname_ipcomm() -> + [{require, ipv6_hosts}]. +ipv6_hostname_ipcomm(X) -> + SocketType = ip_comm, + Port = ?IP_PORT, + ipv6_hostname(SocketType, Port, X). + +ipv6_hostname_essl() -> + [{require, ipv6_hosts}]. +ipv6_hostname_essl(X) -> + SocketType = essl, + Port = ?SSL_PORT, + ipv6_hostname(SocketType, Port, X). + +ipv6_hostname(_SocketType, _Port, doc) -> + ["Test standard ipv6 address"]; +ipv6_hostname(_SocketType, _Port, suite)-> + []; +ipv6_hostname(SocketType, Port, Config) when is_list(Config) -> + tsp("ipv6_hostname -> entry with" + "~n SocketType: ~p" + "~n Port: ~p" + "~n Config: ~p", [SocketType, Port, Config]), + Host = ?config(host, Config), + URI = "GET HTTP://" ++ + Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n", + tsp("ipv6_hostname -> Host: ~p", [Host]), + httpd_test_lib:verify_request(SocketType, Host, Port, [inet6], + node(), + URI, + [{statuscode, 200}, {version, "HTTP/1.1"}]), + ok. + +%%------------------------------------------------------------------------- + +ipv6_address_ipcomm() -> + [{require, ipv6_hosts}]. +ipv6_address_ipcomm(X) -> + SocketType = ip_comm, + Port = ?IP_PORT, + ipv6_address(SocketType, Port, X). + +ipv6_address_essl() -> + [{require, ipv6_hosts}]. +ipv6_address_essl(X) -> + SocketType = essl, + Port = ?SSL_PORT, + ipv6_address(SocketType, Port, X). + +ipv6_address(_SocketType, _Port, doc) -> + ["Test standard ipv6 address"]; +ipv6_address(_SocketType, _Port, suite)-> + []; +ipv6_address(SocketType, Port, Config) when is_list(Config) -> + tsp("ipv6_address -> entry with" + "~n SocketType: ~p" + "~n Port: ~p" + "~n Config: ~p", [SocketType, Port, Config]), + Host = ?config(host, Config), + tsp("ipv6_address -> Host: ~p", [Host]), + URI = "GET HTTP://" ++ + Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n", + httpd_test_lib:verify_request(SocketType, Host, Port, [inet6], + node(), + URI, + [{statuscode, 200}, {version, "HTTP/1.1"}]), + ok. + + +%%-------------------------------------------------------------------- +ticket_5775(doc) -> + ["Tests that content-length is correct"]; +ticket_5775(suite) -> + []; +ticket_5775(Config) -> + ok=httpd_test_lib:verify_request(ip_comm, ?config(host, Config), + ?IP_PORT, ?config(node, Config), + "GET /cgi-bin/erl/httpd_example:get_bin " + "HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/1.0"}]), + ok. +ticket_5865(doc) -> + ["Tests that a header without last-modified is handled"]; +ticket_5865(suite) -> + []; +ticket_5865(Config) -> + ?SKIP(as_of_r15_behaviour_of_calendar_has_changed), + Host = ?config(host,Config), + ServerRoot = ?config(server_root, Config), + DocRoot = filename:join([ServerRoot, "htdocs"]), + File = filename:join([DocRoot,"last_modified.html"]), + + Bad_mtime = case test_server:os_type() of + {win32, _} -> + {{1600,12,31},{23,59,59}}; + {unix, _} -> + {{1969,12,31},{23,59,59}} + end, + + {ok,FI}=file:read_file_info(File), + + case file:write_file_info(File,FI#file_info{mtime=Bad_mtime}) of + ok -> + ok = httpd_test_lib:verify_request(ip_comm, Host, + ?IP_PORT, ?config(node, Config), + "GET /last_modified.html" + " HTTP/1.1\r\nHost:" + ++Host++"\r\n\r\n", + [{statuscode, 200}, + {no_header, + "last-modified"}]), + ok; + {error, Reason} -> + Fault = + io_lib:format("Attempt to change the file info to set the" + " preconditions of the test case failed ~p~n", + [Reason]), + {skip, Fault} + end. + +ticket_5913(doc) -> + ["Tests that a header without last-modified is handled"]; +ticket_5913(suite) -> []; +ticket_5913(Config) -> + ok = httpd_test_lib:verify_request(ip_comm, ?config(host, Config), + ?IP_PORT, ?config(node, Config), + "GET /cgi-bin/erl/httpd_example:get_bin " + "HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/1.0"}]), + ok. + +ticket_6003(doc) -> + ["Tests that a URI with a bad hexadecimal code is handled"]; +ticket_6003(suite) -> []; +ticket_6003(Config) -> + ok = httpd_test_lib:verify_request(ip_comm, ?config(host, Config), + ?IP_PORT, ?config(node, Config), + "GET http://www.erlang.org/%skalle " + "HTTP/1.0\r\n\r\n", + [{statuscode, 400}, + {version, "HTTP/1.0"}]), + ok. + +ticket_7304(doc) -> + ["Tests missing CR in delimiter"]; +ticket_7304(suite) -> + []; +ticket_7304(Config) -> + ok = httpd_test_lib:verify_request(ip_comm, ?config(host, Config), + ?IP_PORT, ?config(node, Config), + "GET / HTTP/1.0\r\n\n", + [{statuscode, 200}, + {version, "HTTP/1.0"}]), + ok. + +%%-------------------------------------------------------------------- +%% Internal functions +%%-------------------------------------------------------------------- +dos_hostname(Type, Port, Host, Node, Max) -> + H1 = {"", 200}, + H2 = {"dummy-host.ericsson.se", 200}, + TooLongHeader = lists:append(lists:duplicate(Max + 1, "a")), + H3 = {TooLongHeader, 403}, + Hosts = [H1,H2,H3], + dos_hostname_poll(Type, Host, Port, Node, Hosts). + +%% make_ipv6(T) when is_tuple(T) andalso (size(T) =:= 8) -> +%% make_ipv6(tuple_to_list(T)); + +%% make_ipv6([_, _, _, _, _, _, _, _] = IPV6) -> +%% lists:flatten(io_lib:format("~s:~s:~s:~s:~s:~s:~s:~s", IPV6)). + + +%%-------------------------------------------------------------------- +%% Other help functions +create_config(Config, Access, FileName) -> + ServerRoot = ?config(server_root, Config), + TcTopDir = ?config(tc_top_dir, Config), + Port = ?config(port, Config), + Type = ?config(sock_type, Config), + Host = ?config(host, Config), + Mods = io_lib:format("~p", [httpd_mod]), + Funcs = io_lib:format("~p", [ssl_password_cb]), + MaxHdrSz = io_lib:format("~p", [256]), + MaxHdrAct = io_lib:format("~p", [close]), + + io:format(user, + "create_config -> " + "~n ServerRoot: ~p" + "~n TcTopDir: ~p" + "~n Type: ~p" + "~n Port: ~p" + "~n Host: ~p" + "~n", [ServerRoot, TcTopDir, Type, Port, Host]), + + SSL = + if + (Type =:= ssl) orelse + (Type =:= essl) -> + [cline(["SSLCertificateFile ", + filename:join(ServerRoot, "ssl/ssl_server.pem")]), + cline(["SSLCertificateKeyFile ", + filename:join(ServerRoot, "ssl/ssl_server.pem")]), + cline(["SSLCACertificateFile ", + filename:join(ServerRoot, "ssl/ssl_server.pem")]), + cline(["SSLPasswordCallbackModule ", Mods]), + cline(["SSLPasswordCallbackFunction ", Funcs]), + cline(["SSLVerifyClient 0"]), + cline(["SSLVerifyDepth 1"])]; + true -> + [] + end, + ModOrder = + case Access of + mod_htaccess -> + "Modules mod_alias mod_htaccess mod_auth " + "mod_security " + "mod_responsecontrol mod_trace mod_esi " + "mod_actions mod_cgi mod_include mod_dir " + "mod_range mod_get " + "mod_head mod_log mod_disk_log"; + _ -> + "Modules mod_alias mod_auth mod_security " + "mod_responsecontrol mod_trace mod_esi " + "mod_actions mod_cgi mod_include mod_dir " + "mod_range mod_get " + "mod_head mod_log mod_disk_log" + end, + + %% The test suite currently does not handle an explicit BindAddress. + %% They assume any has been used, that is Addr is always set to undefined! + + %% {ok, Hostname} = inet:gethostname(), + %% {ok, Addr} = inet:getaddr(Hostname, inet6), + %% AddrStr = make_ipv6(Addr), + %% BindAddress = lists:flatten(io_lib:format("~s|inet6", [AddrStr])), + + BindAddress = "*|inet", + %% BindAddress = "*", + + HttpConfig = [ + cline(["Port ", integer_to_list(Port)]), + cline(["ServerName ", Host]), + cline(["SocketType ", atom_to_list(Type)]), + cline([ModOrder]), + %% cline(["LogFormat ", "erlang"]), + cline(["ServerAdmin mattias@erix.ericsson.se"]), + cline(["BindAddress ", BindAddress]), + cline(["ServerRoot ", ServerRoot]), + cline(["ErrorLog ", TcTopDir, + "/logs/error_log_", integer_to_list(Port)]), + cline(["TransferLog ", TcTopDir, + "/logs/access_log_", integer_to_list(Port)]), + cline(["SecurityLog ", TcTopDir, + "/logs/security_log_", integer_to_list(Port)]), + cline(["ErrorDiskLog ", TcTopDir, + "/logs/error_disk_log_", integer_to_list(Port)]), + cline(["ErrorDiskLogSize ", "190000 ", "11"]), + cline(["TransferDiskLog ", TcTopDir, + "/logs/access_disk_log_", integer_to_list(Port)]), + cline(["TransferDiskLogSize ", "200000 ", "10"]), + cline(["SecurityDiskLog ", TcTopDir, + "/logs/security_disk_log_", integer_to_list(Port)]), + cline(["SecurityDiskLogSize ", "210000 ", "9"]), + cline(["MaxClients 10"]), + cline(["MaxHeaderSize ", MaxHdrSz]), + cline(["MaxHeaderAction ", MaxHdrAct]), + cline(["DocumentRoot ", + filename:join(ServerRoot, "htdocs")]), + cline(["DirectoryIndex ", "index.html ", "welcome.html"]), + cline(["DefaultType ", "text/plain"]), + SSL, + mod_alias_config(ServerRoot), + + config_directory(filename:join([ServerRoot,"htdocs", + "open"]), + "Open Area", + filename:join(ServerRoot, "auth/passwd"), + filename:join(ServerRoot, "auth/group"), + plain, + "user one Aladdin", + filename:join(ServerRoot, "security_data")), + config_directory(filename:join([ServerRoot,"htdocs", + "secret"]), + "Secret Area", + filename:join(ServerRoot, "auth/passwd"), + filename:join(ServerRoot, "auth/group"), + plain, + "group group1 group2", + filename:join(ServerRoot, "security_data")), + config_directory(filename:join([ServerRoot,"htdocs", + "secret", + "top_secret"]), + "Top Secret Area", + filename:join(ServerRoot, "auth/passwd"), + filename:join(ServerRoot, "auth/group"), + plain, + "group group3", + filename:join(ServerRoot, "security_data")), + + config_directory(filename:join([ServerRoot,"htdocs", + "dets_open"]), + "Dets Open Area", + filename:join(ServerRoot, "passwd"), + filename:join(ServerRoot, "group"), + dets, + "user one Aladdin", + filename:join(ServerRoot, "security_data")), + config_directory(filename:join([ServerRoot,"htdocs", + "dets_secret"]), + "Dets Secret Area", + filename:join(ServerRoot, "passwd"), + filename:join(ServerRoot, "group"), + dets, + "group group1 group2", + filename:join(ServerRoot, "security_data")), + config_directory(filename:join([ServerRoot,"htdocs", + "dets_secret", + "top_secret"]), + "Dets Top Secret Area", + filename:join(ServerRoot, "passwd"), + filename:join(ServerRoot, "group"), + dets, + "group group3", + filename:join(ServerRoot, "security_data")), + + config_directory(filename:join([ServerRoot,"htdocs", + "mnesia_open"]), + "Mnesia Open Area", + false, + false, + mnesia, + "user one Aladdin", + filename:join(ServerRoot, "security_data")), + config_directory(filename:join([ServerRoot,"htdocs", + "mnesia_secret"]), + "Mnesia Secret Area", + false, + false, + mnesia, + "group group1 group2", + filename:join(ServerRoot, "security_data")), + config_directory(filename:join( + [ServerRoot, "htdocs", "mnesia_secret", + "top_secret"]), + "Mnesia Top Secret Area", + false, + false, + mnesia, + "group group3", + filename:join(ServerRoot, "security_data")) + ], + ConfigFile = filename:join([TcTopDir, FileName]), + {ok, Fd} = file:open(ConfigFile, [write]), + ok = file:write(Fd, lists:flatten(HttpConfig)), + ok = file:close(Fd). + +config_directory(Dir, AuthName, AuthUserFile, AuthGroupFile, AuthDBType, + Require, SF) -> + file:delete(SF), + [ + cline([""]), + cline(["SecurityDataFile ", SF]), + cline(["SecurityMaxRetries 3"]), + cline(["SecurityFailExpireTime ", integer_to_list(?FAIL_EXPIRE_TIME)]), + cline(["SecurityBlockTime 1"]), + cline(["SecurityAuthTimeout ", integer_to_list(?AUTH_TIMEOUT)]), + cline(["SecurityCallbackModule ", "httpd_mod"]), + cline_if_set("AuthUserFile", AuthUserFile), + cline_if_set("AuthGroupFile", AuthGroupFile), + cline_if_set("AuthName", AuthName), + cline_if_set("AuthDBType", AuthDBType), + cline(["require ", Require]), + cline(["\r\n"]) + ]. + +mod_alias_config(Root) -> + [ + cline(["Alias /icons/ ", filename:join(Root,"icons"), "/"]), + cline(["Alias /pics/ ", filename:join(Root, "icons"), "/"]), + cline(["ScriptAlias /cgi-bin/ ", filename:join(Root, "cgi-bin"), "/"]), + cline(["ScriptAlias /htbin/ ", filename:join(Root, "cgi-bin"), "/"]), + cline(["ErlScriptAlias /cgi-bin/erl httpd_example io"]), + cline(["EvalScriptAlias /eval httpd_example io"]) + ]. + +cline(List) -> + lists:flatten([List, "\r\n"]). + +cline_if_set(_, false) -> + []; +cline_if_set(Name, Var) when is_list(Var) -> + cline([Name, " ", Var]); +cline_if_set(Name, Var) when is_atom(Var) -> + cline([Name, " ", atom_to_list(Var)]). + +getaddr() -> + {ok,HostName} = inet:gethostname(), + {ok,{A1,A2,A3,A4}} = inet:getaddr(HostName,inet), + lists:flatten(io_lib:format("~p.~p.~p.~p",[A1,A2,A3,A4])). + +start_mnesia(Node) -> + case rpc:call(Node, ?MODULE, cleanup_mnesia, []) of + ok -> + ok; + Other -> + tsf({failed_to_cleanup_mnesia, Other}) + end, + case rpc:call(Node, ?MODULE, setup_mnesia, []) of + {atomic, ok} -> + ok; + Other2 -> + tsf({failed_to_setup_mnesia, Other2}) + end, + ok. + +setup_mnesia() -> + setup_mnesia([node()]). + +setup_mnesia(Nodes) -> + ok = mnesia:create_schema(Nodes), + ok = mnesia:start(), + {atomic, ok} = mnesia:create_table(httpd_user, + [{attributes, + record_info(fields, httpd_user)}, + {disc_copies,Nodes}, {type, set}]), + {atomic, ok} = mnesia:create_table(httpd_group, + [{attributes, + record_info(fields, + httpd_group)}, + {disc_copies,Nodes}, {type,bag}]). + +cleanup_mnesia() -> + mnesia:start(), + mnesia:delete_table(httpd_user), + mnesia:delete_table(httpd_group), + stopped = mnesia:stop(), + mnesia:delete_schema([node()]), + ok. + +create_htaccess_data(Path, IpAddress)-> + create_htaccess_dirs(Path), + + create_html_file(filename:join([Path,"ht/open/dummy.html"])), + create_html_file(filename:join([Path,"ht/blocknet/dummy.html"])), + create_html_file(filename:join([Path,"ht/secret/dummy.html"])), + create_html_file(filename:join([Path,"ht/secret/top_secret/dummy.html"])), + + create_htaccess_file(filename:join([Path,"ht/open/.htaccess"]), + Path, "user one Aladdin"), + create_htaccess_file(filename:join([Path,"ht/secret/.htaccess"]), + Path, "group group1 group2"), + create_htaccess_file(filename:join([Path, + "ht/secret/top_secret/.htaccess"]), + Path, "user four"), + create_htaccess_file(filename:join([Path,"ht/blocknet/.htaccess"]), + Path, nouser, IpAddress), + + create_user_group_file(filename:join([Path,"ht","users.file"]), + "one:OnePassword\ntwo:TwoPassword\nthree:" + "ThreePassword\nfour:FourPassword\nAladdin:" + "AladdinPassword"), + create_user_group_file(filename:join([Path,"ht","groups.file"]), + "group1: two one\ngroup2: two three"). + +create_html_file(PathAndFileName)-> + file:write_file(PathAndFileName,list_to_binary( + "test + testar")). + +create_htaccess_file(PathAndFileName, BaseDir, RequireData)-> + file:write_file(PathAndFileName, + list_to_binary( + "AuthUserFile "++ BaseDir ++ + "/ht/users.file\nAuthGroupFile "++ BaseDir + ++ "/ht/groups.file\nAuthName Test\nAuthType" + " Basic\n\nrequire " ++ RequireData ++ + "\n")). + +create_htaccess_file(PathAndFileName, BaseDir, nouser, IpAddress)-> + file:write_file(PathAndFileName,list_to_binary( + "AuthUserFile "++ BaseDir ++ + "/ht/users.file\nAuthGroupFile " ++ + BaseDir ++ "/ht/groups.file\nAuthName" + " Test\nAuthType" + " Basic\n\n\tallow from " ++ + format_ip(IpAddress, + string:rchr(IpAddress,$.)) ++ + "\n")). + +create_user_group_file(PathAndFileName, Data)-> + file:write_file(PathAndFileName, list_to_binary(Data)). + +create_htaccess_dirs(Path)-> + ok = file:make_dir(filename:join([Path,"ht"])), + ok = file:make_dir(filename:join([Path,"ht/open"])), + ok = file:make_dir(filename:join([Path,"ht/blocknet"])), + ok = file:make_dir(filename:join([Path,"ht/secret"])), + ok = file:make_dir(filename:join([Path,"ht/secret/top_secret"])). + +remove_htaccess_dirs(Path)-> + file:del_dir(filename:join([Path,"ht/secret/top_secret"])), + file:del_dir(filename:join([Path,"ht/secret"])), + file:del_dir(filename:join([Path,"ht/blocknet"])), + file:del_dir(filename:join([Path,"ht/open"])), + file:del_dir(filename:join([Path,"ht"])). + +format_ip(IpAddress,Pos)when Pos > 0-> + case lists:nth(Pos,IpAddress) of + $.-> + case lists:nth(Pos-2,IpAddress) of + $.-> + format_ip(IpAddress,Pos-3); + _-> + lists:sublist(IpAddress,Pos-2) ++ "." + end; + _ -> + format_ip(IpAddress,Pos-1) + end; + +format_ip(IpAddress, _Pos)-> + "1" ++ IpAddress. + +remove_htaccess(Path)-> + file:delete(filename:join([Path,"ht/open/dummy.html"])), + file:delete(filename:join([Path,"ht/secret/dummy.html"])), + file:delete(filename:join([Path,"ht/secret/top_secret/dummy.html"])), + file:delete(filename:join([Path,"ht/blocknet/dummy.html"])), + file:delete(filename:join([Path,"ht/blocknet/.htaccess"])), + file:delete(filename:join([Path,"ht/open/.htaccess"])), + file:delete(filename:join([Path,"ht/secret/.htaccess"])), + file:delete(filename:join([Path,"ht/secret/top_secret/.htaccess"])), + file:delete(filename:join([Path,"ht","users.file"])), + file:delete(filename:join([Path,"ht","groups.file"])), + remove_htaccess_dirs(Path). + + +dos_hostname_poll(Type, Host, Port, Node, Hosts) -> + [dos_hostname_poll1(Type, Host, Port, Node, Host1, Code) + || {Host1,Code} <- Hosts]. + +dos_hostname_poll1(Type, Host, Port, Node, Host1, Code) -> + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + dos_hostname_request(Host1), + [{statuscode, Code}, + {version, "HTTP/1.0"}]). + +dos_hostname_request(Host) -> + "GET / HTTP/1.0\r\n" ++ Host ++ "\r\n\r\n". + +get_nof_clients(Mode, Load) -> + get_nof_clients(test_server:os_type(), Mode, Load). + +get_nof_clients(_, ip_comm, light) -> 5; +get_nof_clients(_, ssl, light) -> 2; +get_nof_clients(_, ip_comm, medium) -> 10; +get_nof_clients(_, ssl, medium) -> 4; +get_nof_clients(_, ip_comm, heavy) -> 20; +get_nof_clients(_, ssl, heavy) -> 6. + +%% Make a file 100 bytes long containing 012...9*10 +create_range_data(Path) -> + PathAndFileName=filename:join([Path,"range.txt"]), + file:write_file(PathAndFileName,list_to_binary(["12345678901234567890", + "12345678901234567890", + "12345678901234567890", + "12345678901234567890", + "12345678901234567890"])). + +create_ipv6_config(Config, FileName, Ipv6Address) -> + ServerRoot = ?config(server_root, Config), + TcTopDir = ?config(tc_top_dir, Config), + Port = ?config(port, Config), + SockType = ?config(sock_type, Config), + Mods = io_lib:format("~p", [httpd_mod]), + Funcs = io_lib:format("~p", [ssl_password_cb]), + Host = ?config(ipv6_host, Config), + + MaxHdrSz = io_lib:format("~p", [256]), + MaxHdrAct = io_lib:format("~p", [close]), + + Mod_order = "Modules mod_alias mod_auth mod_esi mod_actions mod_cgi" + " mod_include mod_dir mod_get mod_head" + " mod_log mod_disk_log mod_trace", + + SSL = + if + (SockType =:= ssl) orelse + (SockType =:= essl) -> + [cline(["SSLCertificateFile ", + filename:join(ServerRoot, "ssl/ssl_server.pem")]), + cline(["SSLCertificateKeyFile ", + filename:join(ServerRoot, "ssl/ssl_server.pem")]), + cline(["SSLCACertificateFile ", + filename:join(ServerRoot, "ssl/ssl_server.pem")]), + cline(["SSLPasswordCallbackModule ", Mods]), + cline(["SSLPasswordCallbackFunction ", Funcs]), + cline(["SSLVerifyClient 0"]), + cline(["SSLVerifyDepth 1"])]; + true -> + [] + end, + + BindAddress = "[" ++ Ipv6Address ++"]|inet6", + + HttpConfig = + [cline(["BindAddress ", BindAddress]), + cline(["Port ", integer_to_list(Port)]), + cline(["ServerName ", Host]), + cline(["SocketType ", atom_to_list(SockType)]), + cline([Mod_order]), + cline(["ServerRoot ", ServerRoot]), + cline(["DocumentRoot ", filename:join(ServerRoot, "htdocs")]), + cline(["MaxHeaderSize ",MaxHdrSz]), + cline(["MaxHeaderAction ",MaxHdrAct]), + cline(["DirectoryIndex ", "index.html "]), + cline(["DefaultType ", "text/plain"]), + SSL], + ConfigFile = filename:join([TcTopDir,FileName]), + {ok, Fd} = file:open(ConfigFile, [write]), + ok = file:write(Fd, lists:flatten(HttpConfig)), + ok = file:close(Fd). + + +tsp(F) -> + inets_test_lib:tsp("[~w]" ++ F, [?MODULE]). +tsp(F, A) -> + inets_test_lib:tsp("[~w]" ++ F, [?MODULE|A]). + +tsf(Reason) -> + inets_test_lib:tsf(Reason). diff --git a/lib/inets/test/old_httpd_SUITE_data/Makefile.src b/lib/inets/test/old_httpd_SUITE_data/Makefile.src new file mode 100644 index 0000000000..b0fdb43d8d --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/Makefile.src @@ -0,0 +1,14 @@ +CC = @CC@ +LD = @LD@ +CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@ +CROSSLDFLAGS = @CROSSLDFLAGS@ + +PROGS = cgi_echo@exe@ + +all: $(PROGS) + +cgi_echo@exe@: cgi_echo@obj@ + $(LD) $(CROSSLDFLAGS) -o cgi_echo cgi_echo@obj@ @LIBS@ + +cgi_echo@obj@: cgi_echo.c + $(CC) -c -o cgi_echo@obj@ $(CFLAGS) cgi_echo.c diff --git a/lib/inets/test/old_httpd_SUITE_data/cgi_echo.c b/lib/inets/test/old_httpd_SUITE_data/cgi_echo.c new file mode 100644 index 0000000000..4670c02c56 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/cgi_echo.c @@ -0,0 +1,97 @@ +#include +#include + +#if defined __WIN32__ +#include +#include +#endif + +static int read_exact(char *buffer, int len); +static int write_exact(char *buffer, int len); + +int main(void) +{ + char msg[100]; + int msg_len; +#ifdef __WIN32__ + _setmode(_fileno( stdin), _O_BINARY); + _setmode(_fileno( stdout), _O_BINARY); +#endif + msg_len = read_exact(msg, 100); + + write_exact("Content-type: text/plain\r\n\r\n", 28); + write_exact(msg, msg_len); + exit(EXIT_SUCCESS); +} + + +/* read from stdin */ +#ifdef __WIN32__ +static int read_exact(char *buffer, int len) +{ + HANDLE standard_input = GetStdHandle(STD_INPUT_HANDLE); + + unsigned read_result; + unsigned sofar = 0; + + if (!len) { /* Happens for "empty packages */ + return 0; + } + for (;;) { + if (!ReadFile(standard_input, buffer + sofar, + len - sofar, &read_result, NULL)) { + return -1; /* EOF */ + } + if (!read_result) { + return -2; /* Interrupted while reading? */ + } + sofar += read_result; + if (sofar == len) { + return len; + } + } +} +#else +static int read_exact(char *buffer, int len) { + int i, got = 0; + + do { + if ((i = read(0, buffer + got, len - got)) <= 0) + return(i); + got += i; + } while (got < len); + return len; + +} +#endif + +/* write to stdout */ +#ifdef __WIN32__ + static int write_exact(char *buffer, int len) + { + HANDLE standard_output = GetStdHandle(STD_OUTPUT_HANDLE); + unsigned written; + + if (!WriteFile(standard_output, buffer, len, &written, NULL)) { + return -1; /* Broken Pipe */ + } + if (written < ((unsigned) len)) { + /* This should not happen, standard output is not blocking? */ + return -2; + } + + return (int) written; +} + +#else + static int write_exact(char *buffer, int len) { + int i, wrote = 0; + + do { + if ((i = write(1, buffer + wrote, len - wrote)) <= 0) + return i; + wrote += i; + } while (wrote < len); + return len; + } +#endif diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/Makefile b/lib/inets/test/old_httpd_SUITE_data/server_root/Makefile new file mode 100644 index 0000000000..489f3cfb7a --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/Makefile @@ -0,0 +1,208 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1997-2014. 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. +# +# %CopyrightEnd% +# +# +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../../vsn.mk +VSN=$(INETS_VSN) + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN) + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- +MODULE= + +AUTH_FILES = auth/group \ + auth/passwd +CGI_FILES = cgi-bin/printenv.sh +CONF_FILES = conf/8080.conf \ + conf/8888.conf \ + conf/httpd.conf \ + conf/ssl.conf \ + conf/mime.types +OPEN_FILES = htdocs/open/dummy.html +MNESIA_OPEN_FILES = htdocs/mnesia_open/dummy.html +MISC_FILES = htdocs/misc/friedrich.html \ + htdocs/misc/oech.html +SECRET_FILES = htdocs/secret/dummy.html +MNESIA_SECRET_FILES = htdocs/mnesia_secret/dummy.html +HTDOCS_FILES = htdocs/index.html \ + htdocs/config.shtml \ + htdocs/echo.shtml \ + htdocs/exec.shtml \ + htdocs/flastmod.shtml \ + htdocs/fsize.shtml \ + htdocs/include.shtml +ICON_FILES = icons/README \ + icons/a.gif \ + icons/alert.black.gif \ + icons/alert.red.gif \ + icons/apache_pb.gif \ + icons/back.gif \ + icons/ball.gray.gif \ + icons/ball.red.gif \ + icons/binary.gif \ + icons/binhex.gif \ + icons/blank.gif \ + icons/bomb.gif \ + icons/box1.gif \ + icons/box2.gif \ + icons/broken.gif \ + icons/burst.gif \ + icons/button1.gif \ + icons/button10.gif \ + icons/button2.gif \ + icons/button3.gif \ + icons/button4.gif \ + icons/button5.gif \ + icons/button6.gif \ + icons/button7.gif \ + icons/button8.gif \ + icons/button9.gif \ + icons/buttonl.gif \ + icons/buttonr.gif \ + icons/c.gif \ + icons/comp.blue.gif \ + icons/comp.gray.gif \ + icons/compressed.gif \ + icons/continued.gif \ + icons/dir.gif \ + icons/down.gif \ + icons/dvi.gif \ + icons/f.gif \ + icons/folder.gif \ + icons/folder.open.gif \ + icons/folder.sec.gif \ + icons/forward.gif \ + icons/generic.gif \ + icons/generic.red.gif \ + icons/generic.sec.gif \ + icons/hand.right.gif \ + icons/hand.up.gif \ + icons/htdig.gif \ + icons/icon.sheet.gif \ + icons/image1.gif \ + icons/image2.gif \ + icons/image3.gif \ + icons/index.gif \ + icons/layout.gif \ + icons/left.gif \ + icons/link.gif \ + icons/movie.gif \ + icons/p.gif \ + icons/patch.gif \ + icons/pdf.gif \ + icons/pie0.gif \ + icons/pie1.gif \ + icons/pie2.gif \ + icons/pie3.gif \ + icons/pie4.gif \ + icons/pie5.gif \ + icons/pie6.gif \ + icons/pie7.gif \ + icons/pie8.gif \ + icons/portal.gif \ + icons/poweredby.gif \ + icons/ps.gif \ + icons/quill.gif \ + icons/right.gif \ + icons/screw1.gif \ + icons/screw2.gif \ + icons/script.gif \ + icons/sound1.gif \ + icons/sound2.gif \ + icons/sphere1.gif \ + icons/sphere2.gif \ + icons/star.gif \ + icons/star_blank.gif \ + icons/tar.gif \ + icons/tex.gif \ + icons/text.gif \ + icons/transfer.gif \ + icons/unknown.gif \ + icons/up.gif \ + icons/uu.gif \ + icons/uuencoded.gif \ + icons/world1.gif \ + icons/world2.gif + +SSL_FILES = ssl/ssl_client.pem \ + ssl/ssl_server.pem + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +ERL_COMPILE_FLAGS += + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +debug opt: + +clean: + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/auth + $(INSTALL_DATA) $(AUTH_FILES) $(RELSYSDIR)/examples/server_root/auth + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/cgi-bin + $(INSTALL_SCRIPT) $(CGI_FILES) $(RELSYSDIR)/examples/server_root/cgi-bin + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/conf + $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/examples/server_root/conf + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/open + $(INSTALL_DATA) $(OPEN_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/open + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open + $(INSTALL_DATA) $(MNESIA_OPEN_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/misc + $(INSTALL_DATA) $(MISC_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/misc + $(INSTALL_DIR) \ + $(RELSYSDIR)/examples/server_root/htdocs/secret/top_secret + $(INSTALL_DIR) \ + $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret/top_secret + $(INSTALL_DATA) $(SECRET_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/secret + $(INSTALL_DATA) $(MNESIA_SECRET_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs + $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/examples/server_root/htdocs + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/icons + $(INSTALL_DATA) $(ICON_FILES) $(RELSYSDIR)/examples/server_root/icons + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/ssl + $(INSTALL_DATA) $(SSL_FILES) $(RELSYSDIR)/examples/server_root/ssl + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/logs + +release_docs_spec: diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/auth/group b/lib/inets/test/old_httpd_SUITE_data/server_root/auth/group new file mode 100644 index 0000000000..b3da0ccbd3 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/auth/group @@ -0,0 +1,3 @@ +group1: one two +group2: two three +group3: three Aladdin diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/auth/passwd b/lib/inets/test/old_httpd_SUITE_data/server_root/auth/passwd new file mode 100644 index 0000000000..8c980ff547 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/auth/passwd @@ -0,0 +1,4 @@ +one:onePassword +two:twoPassword +three:threePassword +Aladdin:AladdinPassword diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.bat b/lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.bat new file mode 100644 index 0000000000..c6b1704819 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.bat @@ -0,0 +1,7 @@ +@echo off +echo tomrad > c:\cygwin\tmp\hej +echo Content-type: text/html +echo. +echo ^ ^ ^OS Environment^ ^ ^^ +set +echo ^^^ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.sh b/lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.sh new file mode 100755 index 0000000000..de81de9bde --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.sh @@ -0,0 +1,6 @@ +#!/bin/sh +echo "Content-type: text/html" +echo "" +echo " OS Environment
"
+env
+echo "
" \ No newline at end of file diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8080.conf b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8080.conf new file mode 100644 index 0000000000..48e66f0114 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8080.conf @@ -0,0 +1,79 @@ +Port 8080 +#ServerName your.server.net +SocketType ip_comm +Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_include mod_dir mod_get mod_head mod_log mod_disk_log +ServerAdmin jocke@erix.ericsson.se +ServerRoot /var/tmp/server_root +ErrorLog logs/error_log_8080 +TransferLog logs/access_log_8080 +SecurityLog logs/security_log_8080 +ErrorDiskLog logs/error_disk_log_8080 +ErrorDiskLogSize 200000 10 +TransferDiskLog logs/access_disk_log_8080 +TransferDiskLogSize 200000 10 +SecurityDiskLog logs/security_disk_log +SecurityDiskLogSize 200000 10 +MaxClients 50 +#KeepAlive 5 +#KeepAliveTimeout 10 +DocumentRoot /var/tmp/server_root/htdocs +DirectoryIndex index.html welcome.html +DefaultType text/plain +Alias /icons/ /var/tmp/server_root/icons/ +Alias /pics/ /var/tmp/server_root/icons/ +ScriptAlias /cgi-bin/ /var/tmp/server_root/cgi-bin/ +ScriptAlias /htbin/ /var/tmp/server_root/cgi-bin/ +ErlScriptAlias /cgi-bin/erl httpd_example io +EvalScriptAlias /eval httpd_example io +#Script HEAD /cgi-bin/printenv.sh +#Action image/gif /cgi-bin/printenv.sh + + +AuthDBType plain +AuthName Open Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require user one Aladdin + + + +AuthDBType plain +AuthName Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group1 group2 + + + +AuthDBType plain +AuthName Top Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group3 + + + +AuthDBType mnesia +AuthName Open Area +require user one Aladdin + + + +AuthDBType mnesia +AuthName Secret Area +require group group1 group2 + + + +AuthDBType mnesia +AuthName Top Secret Area +require group group3 +allow from 130.100.34 130.100.35 +deny from 100.234.22.12 194.100.34.1 130.100.34.25 +SecurityDataFile logs/security_data +SecurityMaxRetries 3 +SecurityBlockTime 10 +SecurityFailExpireTime 1 +SecurityAuthTimeout 1 +SecurityCallbackModule security_callback + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8888.conf b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8888.conf new file mode 100644 index 0000000000..79bb7fcca4 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8888.conf @@ -0,0 +1,63 @@ +Port 8888 +#ServerName your.server.net +SocketType ip_comm +Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_include mod_dir mod_get mod_head mod_log mod_disk_log +ServerAdmin jocke@erix.ericsson.se +ServerRoot /var/tmp/server_root +ErrorLog logs/error_log_8888 +TransferLog logs/access_log_8888 +ErrorDiskLog logs/error_disk_log_8888 +ErrorDiskLogSize 200000 10 +TransferDiskLog logs/access_disk_log_8888 +TransferDiskLogSize 200000 10 +MaxClients 150 +DocumentRoot /var/tmp/server_root/htdocs +DirectoryIndex index.html welcome.html +DefaultType text/plain +Alias /icons/ /var/tmp/server_root/icons/ +Alias /pics/ /var/tmp/server_root/icons/ +ScriptAlias /cgi-bin/ /var/tmp/server_root/cgi-bin/ +ScriptAlias /htbin/ /var/tmp/server_root/cgi-bin/ +ErlScriptAlias /cgi-bin/erl httpd_example io +EvalScriptAlias /eval httpd_example io +#Script HEAD /cgi-bin/printenv.sh +#Action image/gif /cgi-bin/printenv.sh + + +AuthName Open Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require user one Aladdin + + + +AuthName Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group1 group2 + + + +AuthName Top Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group3 + + + +AuthName Open Area +AuthMnesiaDB On +require user one Aladdin + + + +AuthName Secret Area +AuthMnesiaDB On +require group group1 group2 + + + +AuthName Top Secret Area +AuthMnesiaDB On +require group group3 + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/httpd.conf b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/httpd.conf new file mode 100644 index 0000000000..a1de5bf4a8 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/httpd.conf @@ -0,0 +1,268 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1997-2014. 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. +# +# %CopyrightEnd% +# +# + +# Port: The port the standalone listens to. For ports < 1023, you will +# need httpd to be run as root initially. + +Port 8888 + +# BindAddress: This directive is used to tell the server which IP address +# to listen to. It can either contain "*", an IP address, or a fully +# qualified Internet domain name. +# +# It is also possible to specify the ip-family with the directive. +# There ar three possible value: inet, inet6 and inet6fb4 +# inet: Use IpFamily inet when retreiving the address and +# fail if that does not work. +# inet6: Use IpFamily inet6 when retreiving the address and +# fail if that does not work. +# inet6fb4: First IpFamily inet6 is tried and if that does not work, +# inet is used as fallback. +# Default value for ip-family is inet6fb4 +# +# The syntax is:
[|] +# +#BindAddress * +#BindAddress *|inet + + +# ServerName allows you to set a host name which is sent back to clients for +# your server if it's different than the one the program would get (i.e. use +# "www" instead of the host's real name). +# +# Note: You cannot just invent host names and hope they work. The name you +# define here must be a valid DNS name for your host. If you don't understand +# this, ask your network administrator. + +#ServerName your.server.net + +# SocketType is either ip_comm, sockets or ssl. + +SocketType ip_comm + +# Modules: Server run-time plug-in modules written using the Erlang +# Web Server API (EWSAPI). The server API make it easy to add functionality +# to the server. Read more about EWSAPI in the Reference Manual. +# WARNING! Do not tamper with this directive unless you are familiar with +# EWSAPI. + +Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_responsecontrol mod_trace mod_range mod_head mod_include mod_dir mod_get mod_log mod_disk_log + +# ServerAdmin: Your address, where problems with the server should be +# e-mailed. + +ServerAdmin jocke@erix.ericsson.se + +# ServerRoot: The directory the server's config, error, and log files +# are kept in + +ServerRoot /var/tmp/server_root + +# ErrorLog: The location of the error log file. If this does not start +# with /, ServerRoot is prepended to it. + +ErrorLog logs/error_log + +# TransferLog: The location of the transfer log file. If this does not +# start with /, ServerRoot is prepended to it. + +TransferLog logs/access_log + +# SecurityLog: The location of the security log file (mod_security required) +# +SecurityLog logs/security_log + +# ErrorDiskLog: The location of the error log file. If this does not +# start with /, ServerRoot is prepended to it. This log file is managed +# with the disk_log module [See disk_log(3)]. The ErrorDiskLogSize directive +# takes two argument, i.e. MaxBytes and MaxFiles. The wrap log writes at most +# MaxBytes bytes on each file, and it uses MaxFiles files before it wraps, and +# truncates the first file. + +ErrorDiskLog logs/error_disk_log +ErrorDiskLogSize 200000 10 + +# TransferDiskLog: The location of the transfer log file. If this does not +# start with /, ServerRoot is prepended to it. This log file is managed +# with the disk_log module [See disk_log(3)]. The TransferDiskLogSize directive +# takes two argument, i.e. MaxBytes and MaxFiles. The wrap log writes at most +# MaxBytes bytes on each file, and it uses MaxFiles files before it wraps, and +# truncates the first file. + +TransferDiskLog logs/access_disk_log +TransferDiskLogSize 200000 10 + +# SecurityDiskLog: The location of the security log file. If this does not +# start with /, ServerRoot is prepended to it. This log file is managed +# with the disk_log module [See disk_log(3)]. The SecurityDiskLogSize directive +# takes two argument, i.e. MaxBytes and MaxFiles. The wrap log writes at most +# MaxBytes bytes on each file, and it uses MaxFiles files before it wraps, and +# truncates the first file. + +SecurityDiskLog logs/security_disk_log +SecurityDiskLogSize 200000 10 + +# Limit on total number of servers running, i.e., limit on the number +# of clients who can simultaneously connect --- if this limit is ever +# reached, clients will be LOCKED OUT, so it should NOT BE SET TOO LOW. +# It is intended mainly as a brake to keep a runaway server from taking +# the server with it as it spirals down... + +MaxClients 50 + +# KeepAlive set the flag for persistent connections. For peristent connections +# set KeepAlive to on. To use One request per connection set the flag to off +# Note: The value has changed since previous version of INETS. +KeepAlive on + +# KeepAliveTimeout sets the number of seconds before a persistent connection +# times out and closes. +KeepAliveTimeout 10 + +# MaxKeepAliveRequests sets the number of seconds before a persistent connection +# times out and closes. +MaxKeepAliveRequests 10 + + + +# DocumentRoot: The directory out of which you will serve your +# documents. By default, all requests are taken from this directory, but +# symbolic links and aliases may be used to point to other locations. + +DocumentRoot /var/tmp/server_root/htdocs + +# DirectoryIndex: Name of the file or files to use as a pre-written HTML +# directory index. Separate multiple entries with spaces. + +DirectoryIndex index.html welcome.html + +# DefaultType is the default MIME type for documents which the server +# cannot find the type of from filename extensions. + +DefaultType text/plain + +# Aliases: Add here as many aliases as you need (with no limit). The format is +# Alias fakename realname + +Alias /icons/ /var/tmp/server_root/icons/ +Alias /pics/ /var/tmp/server_root/icons/ + +# ScriptAlias: This controls which directories contain server scripts. +# Format: ScriptAlias fakename realname + +ScriptAlias /cgi-bin/ /var/tmp/server_root/cgi-bin/ +ScriptAlias /htbin/ /var/tmp/server_root/cgi-bin/ + +# This directive adds an action, which will activate cgi-script when a +# file is requested using the method of method, which can be one of +# GET, POST and HEAD. It sends the URL and file path of the requested +# document using the standard CGI PATH_INFO and PATH_TRANSLATED +# environment variables. + +#Script HEAD /cgi-bin/printenv.sh + +# This directive adds an action, which will activate cgi-script when a +# file of content type mime-type is requested. It sends the URL and +# file path of the requested document using the standard CGI PATH_INFO +# and PATH_TRANSLATED environment variables. + +#Action image/gif /cgi-bin/printenv.sh + +# ErlScriptAlias: This specifies how "Erl" server scripts are called. +# Format: ErlScriptAlias fakename realname allowed_modules + +ErlScriptAlias /down/erl httpd_example io + +# EvalScriptAlias: This specifies how "Eval" server scripts are called. +# Format: EvalScriptAlias fakename realname allowed_modules + +EvalScriptAlias /eval httpd_example io + +# Point SSLCertificateFile at a PEM encoded certificate. + +SSLCertificateFile /var/tmp/server_root/ssl/ssl_server.pem + +# If the key is not combined with the certificate, use this directive to +# point at the key file. + +SSLCertificateKeyFile /var/tmp/server_root/ssl/ssl_server.pem + +# Set SSLVerifyClient to: +# 0 if no certicate is required +# 1 if the client may present a valid certificate +# 2 if the client must present a valid certificate +# 3 if the client may present a valid certificate but it is not required to +# have a valid CA + +SSLVerifyClient 0 + +# Each directory to which INETS has access, can be configured with respect +# to which services and features are allowed and/or disabled in that +# directory (and its subdirectories). + + +AuthDBType plain +AuthName Open Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require user one Aladdin + + + +AuthDBType plain +AuthName Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group1 group2 + + + +AuthDBType plain +AuthName Top Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group3 + + + +AuthDBType mnesia +AuthName Open Area +require user one Aladdin + + + +AuthDBType mnesia +AuthName Secret Area +require group group1 group2 + + + +AuthDBType mnesia +AuthName Top Secret Area +require group group3 +allow from 130.100.34 130.100.35 +deny from 100.234.22.12 194.100.34.1 130.100.34.25 +SecurityDataFile logs/security_data +SecurityMaxRetries 3 +SecurityBlockTime 10 +SecurityFailExpireTime 1 +SecurityAuthTimeout 1 +SecurityCallbackModule security_callback + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/mime.types b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/mime.types new file mode 100644 index 0000000000..cb23079659 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/mime.types @@ -0,0 +1,462 @@ +# This is a comment. I love comments. + +# MIME type Extension +application/EDI-Consent +application/EDI-X12 +application/EDIFACT +application/activemessage +application/andrew-inset ez +application/applefile +application/atomicmail +application/batch-SMTP +application/beep+xml +application/cals-1840 +application/commonground +application/cybercash +application/dca-rft +application/dec-dx +application/dvcs +application/eshop +application/http +application/hyperstudio +application/iges +application/index +application/index.cmd +application/index.obj +application/index.response +application/index.vnd +application/iotp +application/ipp +application/isup +application/font-tdpfr +application/mac-binhex40 hqx +application/mac-compactpro cpt +application/macwriteii +application/marc +application/mathematica +application/mathematica-old +application/msword doc +application/news-message-id +application/news-transmission +application/ocsp-request +application/ocsp-response +application/octet-stream bin dms lha lzh exe class so dll +application/oda oda +application/parityfec +application/pdf pdf +application/pgp-encrypted +application/pgp-keys +application/pgp-signature +application/pkcs10 +application/pkcs7-mime +application/pkcs7-signature +application/pkix-cert +application/pkix-crl +application/pkixcmp +application/postscript ai eps ps +application/prs.alvestrand.titrax-sheet +application/prs.cww +application/prs.nprend +application/qsig +application/remote-printing +application/riscos +application/rtf +application/sdp +application/set-payment +application/set-payment-initiation +application/set-registration +application/set-registration-initiation +application/sgml +application/sgml-open-catalog +application/sieve +application/slate +application/smil smi smil +application/timestamp-query +application/timestamp-reply +application/vemmi +application/vnd.3M.Post-it-Notes +application/vnd.FloGraphIt +application/vnd.accpac.simply.aso +application/vnd.accpac.simply.imp +application/vnd.acucobol +application/vnd.aether.imp +application/vnd.anser-web-certificate-issue-initiation +application/vnd.anser-web-funds-transfer-initiation +application/vnd.audiograph +application/vnd.businessobjects +application/vnd.bmi +application/vnd.canon-cpdl +application/vnd.canon-lips +application/vnd.claymore +application/vnd.commerce-battelle +application/vnd.commonspace +application/vnd.comsocaller +application/vnd.contact.cmsg +application/vnd.cosmocaller +application/vnd.cups-postscript +application/vnd.cups-raster +application/vnd.cups-raw +application/vnd.ctc-posml +application/vnd.cybank +application/vnd.dna +application/vnd.dpgraph +application/vnd.dxr +application/vnd.ecdis-update +application/vnd.ecowin.chart +application/vnd.ecowin.filerequest +application/vnd.ecowin.fileupdate +application/vnd.ecowin.series +application/vnd.ecowin.seriesrequest +application/vnd.ecowin.seriesupdate +application/vnd.enliven +application/vnd.epson.esf +application/vnd.epson.msf +application/vnd.epson.quickanime +application/vnd.epson.salt +application/vnd.epson.ssf +application/vnd.ericsson.quickcall +application/vnd.eudora.data +application/vnd.fdf +application/vnd.ffsns +application/vnd.framemaker +application/vnd.fsc.weblaunch +application/vnd.fujitsu.oasys +application/vnd.fujitsu.oasys2 +application/vnd.fujitsu.oasys3 +application/vnd.fujitsu.oasysgp +application/vnd.fujitsu.oasysprs +application/vnd.fujixerox.ddd +application/vnd.fujixerox.docuworks +application/vnd.fujixerox.docuworks.binder +application/vnd.fut-misnet +application/vnd.grafeq +application/vnd.groove-account +application/vnd.groove-identity-message +application/vnd.groove-injector +application/vnd.groove-tool-message +application/vnd.groove-tool-template +application/vnd.groove-vcard +application/vnd.hhe.lesson-player +application/vnd.hp-HPGL +application/vnd.hp-PCL +application/vnd.hp-PCLXL +application/vnd.hp-hpid +application/vnd.hp-hps +application/vnd.httphone +application/vnd.hzn-3d-crossword +application/vnd.ibm.afplinedata +application/vnd.ibm.MiniPay +application/vnd.ibm.modcap +application/vnd.informix-visionary +application/vnd.intercon.formnet +application/vnd.intertrust.digibox +application/vnd.intertrust.nncp +application/vnd.intu.qbo +application/vnd.intu.qfx +application/vnd.irepository.package+xml +application/vnd.is-xpr +application/vnd.japannet-directory-service +application/vnd.japannet-jpnstore-wakeup +application/vnd.japannet-payment-wakeup +application/vnd.japannet-registration +application/vnd.japannet-registration-wakeup +application/vnd.japannet-setstore-wakeup +application/vnd.japannet-verification +application/vnd.japannet-verification-wakeup +application/vnd.koan +application/vnd.lotus-1-2-3 +application/vnd.lotus-approach +application/vnd.lotus-freelance +application/vnd.lotus-notes +application/vnd.lotus-organizer +application/vnd.lotus-screencam +application/vnd.lotus-wordpro +application/vnd.mcd +application/vnd.mediastation.cdkey +application/vnd.meridian-slingshot +application/vnd.mif mif +application/vnd.minisoft-hp3000-save +application/vnd.mitsubishi.misty-guard.trustweb +application/vnd.mobius.daf +application/vnd.mobius.dis +application/vnd.mobius.msl +application/vnd.mobius.plc +application/vnd.mobius.txf +application/vnd.motorola.flexsuite +application/vnd.motorola.flexsuite.adsi +application/vnd.motorola.flexsuite.fis +application/vnd.motorola.flexsuite.gotap +application/vnd.motorola.flexsuite.kmr +application/vnd.motorola.flexsuite.ttc +application/vnd.motorola.flexsuite.wem +application/vnd.mozilla.xul+xml +application/vnd.ms-artgalry +application/vnd.ms-asf +application/vnd.ms-excel xls +application/vnd.ms-lrm +application/vnd.ms-powerpoint ppt +application/vnd.ms-project +application/vnd.ms-tnef +application/vnd.ms-works +application/vnd.mseq +application/vnd.msign +application/vnd.music-niff +application/vnd.musician +application/vnd.netfpx +application/vnd.noblenet-directory +application/vnd.noblenet-sealer +application/vnd.noblenet-web +application/vnd.novadigm.EDM +application/vnd.novadigm.EDX +application/vnd.novadigm.EXT +application/vnd.osa.netdeploy +application/vnd.palm +application/vnd.pg.format +application/vnd.pg.osasli +application/vnd.powerbuilder6 +application/vnd.powerbuilder6-s +application/vnd.powerbuilder7 +application/vnd.powerbuilder7-s +application/vnd.powerbuilder75 +application/vnd.powerbuilder75-s +application/vnd.previewsystems.box +application/vnd.publishare-delta-tree +application/vnd.pvi.ptid1 +application/vnd.pwg-xhtml-print+xml +application/vnd.rapid +application/vnd.s3sms +application/vnd.seemail +application/vnd.shana.informed.formdata +application/vnd.shana.informed.formtemplate +application/vnd.shana.informed.interchange +application/vnd.shana.informed.package +application/vnd.sss-cod +application/vnd.sss-dtf +application/vnd.sss-ntf +application/vnd.street-stream +application/vnd.svd +application/vnd.swiftview-ics +application/vnd.triscape.mxs +application/vnd.trueapp +application/vnd.truedoc +application/vnd.tve-trigger +application/vnd.ufdl +application/vnd.uplanet.alert +application/vnd.uplanet.alert-wbxml +application/vnd.uplanet.bearer-choice-wbxml +application/vnd.uplanet.bearer-choice +application/vnd.uplanet.cacheop +application/vnd.uplanet.cacheop-wbxml +application/vnd.uplanet.channel +application/vnd.uplanet.channel-wbxml +application/vnd.uplanet.list +application/vnd.uplanet.list-wbxml +application/vnd.uplanet.listcmd +application/vnd.uplanet.listcmd-wbxml +application/vnd.uplanet.signal +application/vnd.vcx +application/vnd.vectorworks +application/vnd.vidsoft.vidconference +application/vnd.visio +application/vnd.vividence.scriptfile +application/vnd.wap.sic +application/vnd.wap.slc +application/vnd.wap.wbxml wbxml +application/vnd.wap.wmlc wmlc +application/vnd.wap.wmlscriptc wmlsc +application/vnd.webturbo +application/vnd.wrq-hp3000-labelled +application/vnd.wt.stf +application/vnd.xara +application/vnd.xfdl +application/vnd.yellowriver-custom-menu +application/whoispp-query +application/whoispp-response +application/wita +application/wordperfect5.1 +application/x-bcpio bcpio +application/x-cdlink vcd +application/x-chess-pgn pgn +application/x-compress +application/x-cpio cpio +application/x-csh csh +application/x-director dcr dir dxr +application/x-dvi dvi +application/x-futuresplash spl +application/x-gtar gtar +application/x-gzip +application/x-hdf hdf +application/x-javascript js +application/x-koan skp skd skt skm +application/x-latex latex +application/x-netcdf nc cdf +application/x-sh sh +application/x-shar shar +application/x-shockwave-flash swf +application/x-stuffit sit +application/x-sv4cpio sv4cpio +application/x-sv4crc sv4crc +application/x-tar tar +application/x-tcl tcl +application/x-tex tex +application/x-texinfo texinfo texi +application/x-troff t tr roff +application/x-troff-man man +application/x-troff-me me +application/x-troff-ms ms +application/x-ustar ustar +application/x-wais-source src +application/x400-bp +application/xml +application/xml-dtd +application/xml-external-parsed-entity +application/zip zip +audio/32kadpcm +audio/basic au snd +audio/g.722.1 +audio/l16 +audio/midi mid midi kar +audio/mp4a-latm +audio/mpa-robust +audio/mpeg mpga mp2 mp3 +audio/parityfec +audio/prs.sid +audio/telephone-event +audio/tone +audio/vnd.cisco.nse +audio/vnd.cns.anp1 +audio/vnd.cns.inf1 +audio/vnd.digital-winds +audio/vnd.everad.plj +audio/vnd.lucent.voice +audio/vnd.nortel.vbk +audio/vnd.nuera.ecelp4800 +audio/vnd.nuera.ecelp7470 +audio/vnd.nuera.ecelp9600 +audio/vnd.octel.sbc +audio/vnd.qcelp +audio/vnd.rhetorex.32kadpcm +audio/vnd.vmx.cvsd +audio/x-aiff aif aiff aifc +audio/x-mpegurl m3u +audio/x-pn-realaudio ram rm +audio/x-pn-realaudio-plugin rpm +audio/x-realaudio ra +audio/x-wav wav +chemical/x-pdb pdb +chemical/x-xyz xyz +image/bmp bmp +image/cgm +image/g3fax +image/gif gif +image/ief ief +image/jpeg jpeg jpg jpe +image/naplps +image/png png +image/prs.btif +image/prs.pti +image/tiff tiff tif +image/vnd.cns.inf2 +image/vnd.dwg +image/vnd.dxf +image/vnd.fastbidsheet +image/vnd.fpx +image/vnd.fst +image/vnd.fujixerox.edmics-mmr +image/vnd.fujixerox.edmics-rlc +image/vnd.mix +image/vnd.net-fpx +image/vnd.svf +image/vnd.wap.wbmp wbmp +image/vnd.xiff +image/x-cmu-raster ras +image/x-portable-anymap pnm +image/x-portable-bitmap pbm +image/x-portable-graymap pgm +image/x-portable-pixmap ppm +image/x-rgb rgb +image/x-xbitmap xbm +image/x-xpixmap xpm +image/x-xwindowdump xwd +message/delivery-status +message/disposition-notification +message/external-body +message/http +message/news +message/partial +message/rfc822 +message/s-http +model/iges igs iges +model/mesh msh mesh silo +model/vnd.dwf +model/vnd.flatland.3dml +model/vnd.gdl +model/vnd.gs-gdl +model/vnd.gtw +model/vnd.mts +model/vnd.vtu +model/vrml wrl vrml +multipart/alternative +multipart/appledouble +multipart/byteranges +multipart/digest +multipart/encrypted +multipart/form-data +multipart/header-set +multipart/mixed +multipart/parallel +multipart/related +multipart/report +multipart/signed +multipart/voice-message +text/calendar +text/css css +text/directory +text/enriched +text/html html htm +text/parityfec +text/plain asc txt +text/prs.lines.tag +text/rfc822-headers +text/richtext rtx +text/rtf rtf +text/sgml sgml sgm +text/tab-separated-values tsv +text/t140 +text/uri-list +text/vnd.DMClientScript +text/vnd.IPTC.NITF +text/vnd.IPTC.NewsML +text/vnd.abc +text/vnd.curl +text/vnd.flatland.3dml +text/vnd.fly +text/vnd.fmi.flexstor +text/vnd.in3d.3dml +text/vnd.in3d.spot +text/vnd.latex-z +text/vnd.motorola.reflex +text/vnd.ms-mediapackage +text/vnd.wap.si +text/vnd.wap.sl +text/vnd.wap.wml wml +text/vnd.wap.wmlscript wmls +text/x-setext etx +text/x-server-parsed-html shtml +text/xml xml xsl +text/xml-external-parsed-entity +video/mp4v-es +video/mpeg mpeg mpg mpe +video/parityfec +video/pointer +video/quicktime qt mov +video/vnd.fvt +video/vnd.motorola.video +video/vnd.motorola.videop +video/vnd.mpegurl mxu +video/vnd.mts +video/vnd.nokia.interleaved-multimedia +video/vnd.vivo +video/x-msvideo avi +video/x-sgi-movie movie +x-conference/x-cooltalk ice diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/ssl.conf b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/ssl.conf new file mode 100644 index 0000000000..8b8c57a98b --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/ssl.conf @@ -0,0 +1,66 @@ +Port 8088 +#ServerName your.server.net +SocketType ssl +Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_include mod_dir mod_get mod_head mod_log mod_disk_log +ServerAdmin jocke@erix.ericsson.se +ServerRoot /var/tmp/server_root +ErrorLog logs/error_log_8088 +TransferLog logs/access_log_8088 +ErrorDiskLog logs/error_disk_log_8088 +ErrorDiskLogSize 200000 10 +TransferDiskLog logs/access_disk_log_8088 +TransferDiskLogSize 200000 10 +MaxClients 150 +DocumentRoot /var/tmp/server_root/htdocs +DirectoryIndex index.html welcome.html +DefaultType text/plain +Alias /icons/ /var/tmp/server_root/icons/ +Alias /pics/ /var/tmp/server_root/icons/ +ScriptAlias /cgi-bin/ /var/tmp/server_root/cgi-bin/ +ScriptAlias /htbin/ /var/tmp/server_root/cgi-bin/ +ErlScriptAlias /cgi-bin/erl httpd_example io +EvalScriptAlias /eval httpd_example io +SSLCertificateFile /var/tmp/server_root/ssl/ssl_server.pem +SSLCertificateKeyFile /var/tmp/server_root/ssl/ssl_server.pem +SSLVerifyClient 0 +#Script HEAD /cgi-bin/printenv.sh +#Action image/gif /cgi-bin/printenv.sh + + +AuthName Open Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require user one Aladdin + + + +AuthName Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group1 group2 + + + +AuthName Top Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group3 + + + +AuthName Open Area +AuthMnesiaDB On +require user one Aladdin + + + +AuthName Secret Area +AuthMnesiaDB On +require group group1 group2 + + + +AuthName Top Secret Area +AuthMnesiaDB On +require group group3 + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/config.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/config.shtml new file mode 100644 index 0000000000..4bfddf4ed5 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/config.shtml @@ -0,0 +1,60 @@ + + +/ssi.html (17-Apr-1997) + + +

/ssi.html

+ + + + + + + + +

Include /misc/friedrich.html: + +

Include /misc/not_defined.html: +

Include misc/friedrich.html: + +

Include not_defined.html: + +


+ + + +

DOCUMENT_NAME: +

DOCUMENT_URI: +

QUERY_STRING_UNESCAPED: +

DATE_LOCAL: +

DATE_GMT: +

LAST_MODIFIED: +

NOT_DEFINED: + +


+ + + +

Size of index.html: +

Size of not_defined.html: + +

Size of /misc/friedrich.html: +

Size of /misc/not_defined.html: + +


+ + + +

Last modification of index.html: +

Last modification of not_defined.html: +

Last modification of /misc/friedrich.html: +

Last modification of /misc/not_defined.html: + + + + + + + + + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_open/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_open/dummy.html new file mode 100644 index 0000000000..a6e8a35a04 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_open/dummy.html @@ -0,0 +1,10 @@ + + +/open/dummy.html (17-Apr-1997) + + + + +

/open/dummy.html

+ + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/dummy.html new file mode 100644 index 0000000000..016b04e540 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/dummy.html @@ -0,0 +1,10 @@ + + +/secret/dummy.html (17-Apr-1997) + + + + +

/secret/dummy.html

+ + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/top_secret/index.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/top_secret/index.html new file mode 100644 index 0000000000..34db3d5d1a --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/top_secret/index.html @@ -0,0 +1,9 @@ + + +/secret/top_secret/index.html (04-Feb-1998) + + + +

/secret/top_secret/index.html

+ + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/echo.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/echo.shtml new file mode 100644 index 0000000000..168ae1c05a --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/echo.shtml @@ -0,0 +1,25 @@ + + +/echo.shtml + + +

/echo.shtml

+ +

DOCUMENT_NAME: + +

DOCUMENT_URI: + +

QUERY_STRING_UNESCAPED: + +

DATE_LOCAL: + +

DATE_GMT: + +

LAST_MODIFIED: + +

NOT_DEFINED: + +

[Back] + + + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/exec.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/exec.shtml new file mode 100644 index 0000000000..b4cb3a7815 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/exec.shtml @@ -0,0 +1,20 @@ + + +/exec.shtml + + +

/exec.shtml

+
+
+
+ +
+ +
+ +
+ +

[Back] + + + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/flastmod.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/flastmod.shtml new file mode 100644 index 0000000000..9b02de1338 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/flastmod.shtml @@ -0,0 +1,19 @@ + + +/flastmod.shtml + + +

/flastmod.shtml

+ +

Last modification of index.html: + +

Last modification of not_defined.html: + +

Last modification of /misc/friedrich.html: + +

Last modification of /misc/not_defined.html: + +

[Back] + + + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/fsize.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/fsize.shtml new file mode 100644 index 0000000000..aed7384898 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/fsize.shtml @@ -0,0 +1,19 @@ + + +/fsize.shtml + + +

/fsize.shtml

+ +

Size of index.html: + +

Size of not_defined.html: + +

Size of /misc/friedrich.html: + +

Size of /misc/not_defined.html: + +

[Back] + + + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/include.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/include.shtml new file mode 100644 index 0000000000..71fcc4bce9 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/include.shtml @@ -0,0 +1,23 @@ + + +/include.shtml + + +

/include.shtml

+ +

Include /misc/friedrich.html: + + +

Include /misc/not_defined.html: + + +

Include misc/friedrich.html: + + +

Include not_defined.html: + + +

[Back] + + + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/index.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/index.html new file mode 100644 index 0000000000..cfdc9f9ab7 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/index.html @@ -0,0 +1,25 @@ + + +/index.html + + +

/index.html

+ +Server-Side Include (SSI) commands:
+config
+echo
+exec
+flastmod
+fsize
+include
+ +
+
+ +ESI callback:
+cgi-bin/erl/httpd_example/get
+cgi-bin/erl/httpd_example/yahoo
+cgi-bin/erl/httpd_example/test1
+ + + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/last_modified.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/last_modified.html new file mode 100644 index 0000000000..02c0e698b5 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/last_modified.html @@ -0,0 +1,12 @@ + + +/last_modified.html + + +

/last_modified.html

+ +

This document is only used for test of illegal last-modified date.

+ + + + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/friedrich.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/friedrich.html new file mode 100644 index 0000000000..d7953d5df4 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/friedrich.html @@ -0,0 +1,7 @@ +

+Talking much about oneself can also be a means to conceal oneself.
+-- Friedrich Nietzsche +
+ +

Nested Include: + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/oech.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/oech.html new file mode 100644 index 0000000000..506064bf04 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/oech.html @@ -0,0 +1,4 @@ +

+What excuses stand in your way? How can you eliminate them?
+-- Roger von Oech +
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/welcome.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/welcome.html new file mode 100644 index 0000000000..8c17451f91 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/welcome.html @@ -0,0 +1 @@ + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_open/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_open/dummy.html new file mode 100644 index 0000000000..a6e8a35a04 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_open/dummy.html @@ -0,0 +1,10 @@ + + +/open/dummy.html (17-Apr-1997) + + + + +

/open/dummy.html

+ + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/dummy.html new file mode 100644 index 0000000000..016b04e540 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/dummy.html @@ -0,0 +1,10 @@ + + +/secret/dummy.html (17-Apr-1997) + + + + +

/secret/dummy.html

+ + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/top_secret/index.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/top_secret/index.html new file mode 100644 index 0000000000..2d17e8b596 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/top_secret/index.html @@ -0,0 +1,9 @@ + + +/mnesia_secret/top_secret/index.html (04-Feb-1998) + + + +

/mnesia_secret/top_secret/index.html

+ + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/open/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/open/dummy.html new file mode 100644 index 0000000000..a6e8a35a04 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/open/dummy.html @@ -0,0 +1,10 @@ + + +/open/dummy.html (17-Apr-1997) + + + + +

/open/dummy.html

+ + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/dummy.html new file mode 100644 index 0000000000..016b04e540 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/dummy.html @@ -0,0 +1,10 @@ + + +/secret/dummy.html (17-Apr-1997) + + + + +

/secret/dummy.html

+ + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/top_secret/index.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/top_secret/index.html new file mode 100644 index 0000000000..34db3d5d1a --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/top_secret/index.html @@ -0,0 +1,9 @@ + + +/secret/top_secret/index.html (04-Feb-1998) + + + +

/secret/top_secret/index.html

+ + diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/README b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/README new file mode 100644 index 0000000000..a1fc5a5a9c --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/README @@ -0,0 +1,161 @@ +Public Domain Icons + + These icons were originally made for Mosaic for X and have been + included in the NCSA httpd and Apache server distributions in the + past. They are in the public domain and may be freely included in any + application. The originals were done by Kevin Hughes (kevinh@eit.com). + + Many thanks to Andy Polyakov for tuning the icon colors and adding a + few new images. If you'd like to contribute additions or ideas to + this set, please let me know. + + The distribution site for these icons is at: + + http://www.eit.com/goodies/www.icons/ + + Kevin Hughes + September 11, 1995 + + +Suggested Uses + +The following are a few suggestions, to serve as a starting point for ideas. +Please feel free to tweak and rename the icons as you like. + + a.gif + This might be used to represent PostScript or text layout + languages. + + alert.black.gif, alert.red.gif + These can be used to highlight any important items, such as a + README file in a directory. + + back.gif, forward.gif + These can be used as links to go to previous and next areas. + + ball.gray.gif, ball.red.gif + These might be used as bullets. + + binary.gif + This can be used to represent binary files. + + binhex.gif + This can represent BinHex-encoded data. + + blank.gif + This can be used as a placeholder or a spacing element. + + bomb.gif + This can be used to repreesnt core files. + + box1.gif, box2.gif + These icons can be used to represent generic 3D applications and + related files. + + broken.gif + This can represent corrupted data. + + burst.gif + This can call attention to new and important items. + + c.gif + This might represent C source code. + + comp.blue.gif, comp.red.gif + These little computer icons can stand for telnet or FTP + sessions. + + compressed.gif + This may represent compressed data. + + continued.gif + This can be a link to a continued listing of a directory. + + down.gif, up.gif, left.gif, right.gif + These can be used to scroll up, down, left and right in a + listing or may be used to denote items in an outline. + + dvi.gif + This can represent DVI files. + + f.gif + This might represent FORTRAN or Forth source code. + + folder.gif, folder.open.gif, folder.sec.gif + The folder can represent directories. There is also a version + that can represent secure directories or directories that cannot + be viewed. + + generic.gif, generic.sec.gif, generic.red.gif + These can represent generic files, secure files, and important + files, respectively. + + hand.right.gif, hand.up.gif + These can point out important items (pun intended). + + image1.gif, image2.gif, image3.gif + These can represent image formats of various types. + + index.gif + This might represent a WAIS index or search facility. + + layout.gif + This might represent files and formats that contain graphics as + well as text layout, such as HTML and PDF files. + + link.gif + This might represent files that are symbolic links. + + movie.gif + This can represent various movie formats. + + p.gif + This may stand for Perl or Python source code. + + pie0.gif ... pie8.gif + These icons can be used in applications where a list of + documents is returned from a search. The little pie chart images + can denote how relevant the documents may be to your search + query. + + patch.gif + This may stand for patches and diff files. + + portal.gif + This might be a link to an online service or a 3D world. + + ps.gif, quill.gif + These may represent PostScript files. + + screw1.gif, screw2.gif + These may represent CAD or engineering data and formats. + + script.gif + This can represent any of various interpreted languages, such as + Perl, python, TCL, and shell scripts, as well as server + configuration files. + + sound1.gif, sound2.gif + These can represent sound files. + + sphere1.gif, sphere2.gif + These can represent 3D worlds or rendering applications and + formats. + + tex.gif + This can represent TeX files. + + text.gif + This can represent generic (plain) text files. + + transfer.gif + This can represent FTP transfers or uploads/downloads. + + unknown.gif + This may represent a file of an unknown type. + + uuencoded.gif + This can stand for uuencoded data. + + world1.gif, world2.gif + These can represent 3D worlds or other 3D formats. diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/a.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/a.gif new file mode 100644 index 0000000000..bb23d971f4 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/a.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.black.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.black.gif new file mode 100644 index 0000000000..eaecd2172a Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.black.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.red.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.red.gif new file mode 100644 index 0000000000..a423894043 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.red.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/apache_pb.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/apache_pb.gif new file mode 100644 index 0000000000..3a1c139fc4 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/apache_pb.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/back.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/back.gif new file mode 100644 index 0000000000..a694ae1ec3 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/back.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.gray.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.gray.gif new file mode 100644 index 0000000000..eb84268c4c Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.gray.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.red.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.red.gif new file mode 100644 index 0000000000..a8425cb574 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.red.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binary.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binary.gif new file mode 100644 index 0000000000..9a15cbae04 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binary.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binhex.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binhex.gif new file mode 100644 index 0000000000..62d0363108 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binhex.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/blank.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/blank.gif new file mode 100644 index 0000000000..0ccf01e198 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/blank.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/bomb.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/bomb.gif new file mode 100644 index 0000000000..270fdb1c06 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/bomb.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box1.gif new file mode 100644 index 0000000000..65dcd002ea Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box1.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box2.gif new file mode 100644 index 0000000000..c43bc4faec Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box2.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/broken.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/broken.gif new file mode 100644 index 0000000000..9f8cbe9f76 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/broken.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/burst.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/burst.gif new file mode 100644 index 0000000000..fbdcf575f7 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/burst.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button1.gif new file mode 100644 index 0000000000..eb97cb7333 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button1.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button10.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button10.gif new file mode 100644 index 0000000000..fe0c97998c Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button10.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button2.gif new file mode 100644 index 0000000000..7698455bf9 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button2.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button3.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button3.gif new file mode 100644 index 0000000000..a8b8319232 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button3.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button4.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button4.gif new file mode 100644 index 0000000000..0fd15a0d7f Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button4.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button5.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button5.gif new file mode 100644 index 0000000000..64241e5c5d Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button5.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button6.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button6.gif new file mode 100644 index 0000000000..867cfd1212 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button6.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button7.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button7.gif new file mode 100644 index 0000000000..b3f5fb248f Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button7.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button8.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button8.gif new file mode 100644 index 0000000000..7a308be8f6 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button8.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button9.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button9.gif new file mode 100644 index 0000000000..9acba576c0 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button9.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonl.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonl.gif new file mode 100644 index 0000000000..3883088e7a Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonl.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonr.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonr.gif new file mode 100644 index 0000000000..c4dc3887db Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonr.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/c.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/c.gif new file mode 100644 index 0000000000..7555b6c164 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/c.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.blue.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.blue.gif new file mode 100644 index 0000000000..f8d76a8c23 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.blue.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.gray.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.gray.gif new file mode 100644 index 0000000000..7664cd0364 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.gray.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/compressed.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/compressed.gif new file mode 100644 index 0000000000..39e732739f Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/compressed.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/continued.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/continued.gif new file mode 100644 index 0000000000..b0ffb7e0cc Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/continued.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dir.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dir.gif new file mode 100644 index 0000000000..48264601ae Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dir.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/down.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/down.gif new file mode 100644 index 0000000000..a354c871cd Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/down.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dvi.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dvi.gif new file mode 100644 index 0000000000..791be33105 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dvi.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/f.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/f.gif new file mode 100644 index 0000000000..fbe353c282 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/f.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.gif new file mode 100644 index 0000000000..48264601ae Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.open.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.open.gif new file mode 100644 index 0000000000..30979cb528 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.open.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.sec.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.sec.gif new file mode 100644 index 0000000000..75332d9e59 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.sec.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/forward.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/forward.gif new file mode 100644 index 0000000000..b2959b4c85 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/forward.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.gif new file mode 100644 index 0000000000..de60b2940f Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.red.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.red.gif new file mode 100644 index 0000000000..94743981d9 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.red.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.sec.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.sec.gif new file mode 100644 index 0000000000..88d5240c3c Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.sec.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.right.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.right.gif new file mode 100644 index 0000000000..5cdbc7206d Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.right.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.up.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.up.gif new file mode 100644 index 0000000000..85a5d68317 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.up.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/htdig.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/htdig.gif new file mode 100644 index 0000000000..35443fb63a Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/htdig.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/icon.sheet.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/icon.sheet.gif new file mode 100644 index 0000000000..ad1686e448 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/icon.sheet.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image1.gif new file mode 100644 index 0000000000..01e442bfa9 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image1.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image2.gif new file mode 100644 index 0000000000..751faeea36 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image2.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image3.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image3.gif new file mode 100644 index 0000000000..4f30484ff6 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image3.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/index.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/index.gif new file mode 100644 index 0000000000..162478fb3a Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/index.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/layout.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/layout.gif new file mode 100644 index 0000000000..c96338a152 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/layout.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/left.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/left.gif new file mode 100644 index 0000000000..279e6710d4 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/left.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/link.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/link.gif new file mode 100644 index 0000000000..c5b6889a76 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/link.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/movie.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/movie.gif new file mode 100644 index 0000000000..0035183774 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/movie.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/p.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/p.gif new file mode 100644 index 0000000000..7b917b4e91 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/p.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/patch.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/patch.gif new file mode 100644 index 0000000000..39bc90e795 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/patch.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pdf.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pdf.gif new file mode 100644 index 0000000000..c88fd777c4 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pdf.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie0.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie0.gif new file mode 100644 index 0000000000..6f7a0ae7a7 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie0.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie1.gif new file mode 100644 index 0000000000..03aa6be71e Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie1.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie2.gif new file mode 100644 index 0000000000..b04c5e0908 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie2.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie3.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie3.gif new file mode 100644 index 0000000000..4db9d023ed Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie3.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie4.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie4.gif new file mode 100644 index 0000000000..93471fdd88 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie4.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie5.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie5.gif new file mode 100644 index 0000000000..57aee93f07 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie5.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie6.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie6.gif new file mode 100644 index 0000000000..0dc327b569 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie6.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie7.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie7.gif new file mode 100644 index 0000000000..8661337f06 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie7.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie8.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie8.gif new file mode 100644 index 0000000000..59ddb34ce0 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie8.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/portal.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/portal.gif new file mode 100644 index 0000000000..0e6e506e00 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/portal.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/poweredby.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/poweredby.gif new file mode 100644 index 0000000000..d324ab80ea Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/poweredby.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ps.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ps.gif new file mode 100644 index 0000000000..0f565bc1db Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ps.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/quill.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/quill.gif new file mode 100644 index 0000000000..818a5cdc7e Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/quill.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/right.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/right.gif new file mode 100644 index 0000000000..b256e5f75f Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/right.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw1.gif new file mode 100644 index 0000000000..af6ba2b097 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw1.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw2.gif new file mode 100644 index 0000000000..06dccb3e44 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw2.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/script.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/script.gif new file mode 100644 index 0000000000..d8a853bc58 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/script.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound1.gif new file mode 100644 index 0000000000..8efb49f55d Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound1.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound2.gif new file mode 100644 index 0000000000..48e6a7fb2f Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound2.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere1.gif new file mode 100644 index 0000000000..7067070da2 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere1.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere2.gif new file mode 100644 index 0000000000..a9e462a377 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere2.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star.gif new file mode 100644 index 0000000000..4cfe0a5e0f Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star_blank.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star_blank.gif new file mode 100644 index 0000000000..a0c83cb85b Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star_blank.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tar.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tar.gif new file mode 100644 index 0000000000..617e779efa Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tar.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tex.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tex.gif new file mode 100644 index 0000000000..45e43233b8 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tex.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/text.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/text.gif new file mode 100644 index 0000000000..4c623909fb Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/text.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/transfer.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/transfer.gif new file mode 100644 index 0000000000..33697dbb66 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/transfer.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/unknown.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/unknown.gif new file mode 100644 index 0000000000..32b1ea23fb Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/unknown.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/up.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/up.gif new file mode 100644 index 0000000000..6d6d6d1ebf Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/up.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uu.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uu.gif new file mode 100644 index 0000000000..4387d529f6 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uu.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uuencoded.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uuencoded.gif new file mode 100644 index 0000000000..4387d529f6 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uuencoded.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/world1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/world1.gif new file mode 100644 index 0000000000..05b4ec2058 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/world1.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/world2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/world2.gif new file mode 100644 index 0000000000..e3203f7a88 Binary files /dev/null and b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/world2.gif differ diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip b/lib/inets/test/old_httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_client.pem b/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_client.pem new file mode 100644 index 0000000000..427447958d --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_client.pem @@ -0,0 +1,31 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSVwC+n +0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53h2Zr +3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwIDAQAB +AoGACdIVYe/LTeydUihtInC8lZ2QuPgJmoBNocRjqJFipEihoL4scHAx25n1bBvB +I0HZphffzBkGp28oBAtl2LRPWXqu527unc/RWRfLMqSK1xNSq1DxD1a30zkrZPna +QiV65vEJuNSJTtlDy/Zqc/BVZXCpxWlzYQedZgkmf0Qse8ECQQCmaz02Yur8zC9f +eSQKU5OSzGw3bSIumEzziCfHdTheK6MEoccf5TCAyLXhZwA7QlKja4tFXfeyVxws +/LlnUJN9AkEA4j+xnOeYUyGKXL5i+BAbnqpI4MzPiq+IoCYkaRlD/wAws24r5HNI +ZQmEHWqD/NNzOf/A2XuyLtMiTGJPW/DftwJBAKKpJP6Ytuh6xz8BUCnLwO12Y7vV +LtjuQiCzD3aUa5EYA9HOMqxJPxxRkf0LyR0i2VUkE8+sZiPpov+R0cJa7p0CQQCj +40GUiArGRSiF7/+e84QeVfl+pb29F1QftiFv5DZmFEwy3Z572KpbTh5edJbxYHY6 +UDHxGHJFCvnwXNJhpkVXAkBJqfEfiMJ3Q/E5Gpf3sQizacouW92iiN8ojlF1oB80 +t34RysJH7SgI3gdMhTribCo2UUaV0StjR6yodPN+TB2J +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIChzCCAfCgAwIBAgIGAIsapa8BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT +BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE +BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD +VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw +MDAwWjB7MQ8wDQYDVQQDEwZjbGllbnQxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl +cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD +VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSV +wC+n0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53 +h2Zr3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwID +AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAG8t6f1A +PF7xayGxtUpG2r6W5ETylC3ZIKPS2kfJk9aYi7AZNTp7/xTU6SgqvFBN8aBPzxCD +4jHrSNC8DSb4X1x9uimarb6qdZDHEdij+DRAd2eygJHZxEf7+8B4Fx34thQeU9hZ +S1Izke5AlsyFMkvB7h0anE4k9BfuU70vl6v5 +-----END CERTIFICATE----- diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_server.pem b/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_server.pem new file mode 100644 index 0000000000..4aac86db49 --- /dev/null +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_server.pem @@ -0,0 +1,31 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9Adq6 +7k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ4UAt +NHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQIDAQAB +AoGAQIlma0r6W6bcRj4+Wd4fXCFvHuq5Psu1fYEeC5Yvz8761xVjjSfbrDHJZ9pm +FjOEgedK+s5lbDXqYVyjbdyZSugStBRocSmbG8SQHcAsxR2ZIkNzX2hYzB+lslWo +T3YJojDyB134O7XJznCu+ZFXP86jyJ1JT6k6a+OIHcwnJ+ECQQDYn57dY4Px3mEd +VBLStN3YkRF5oFyT+xk7IaKeLLB6n4gCnoVbBoHut7PFbPYPzoNzEwPk3MQKDIHb +Kig3S5CpAkEAvPA1VmoJWAlN6kUi+F2L8HXEArzE8x7vwdsslrwMKUe4dFS+ZC/7 +5iDOaxcZ7TYkCgwzBt341++DCgP6j3fY1QJBALB6AcOcwi52m6l4B8mu3ZkEPjdX +BHTuONTqhv/TqoaLlxODL2NDvvDKqeMp7KBd/srt79swW2lQXS4+fvrlTdkCQQCm +zxj4O1QWkthkfje6ubSkTwUIOatUzrp1F9GNH2dJRtX2dx9FCwxGCC7WY6XzRXqa +GF0wsedSllbGD+82nWQlAkAicMGqCqRq4hKR/cVmFatOqKVWCVkx6OFF2FhuiI5Z +h5eIOPGCt8dVRs1P9DNSld/D98Sfm65m85z8BtXovvYV +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIChzCCAfCgAwIBAgIGANUxXM9BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT +BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE +BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD +VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw +MDAwWjB7MQ8wDQYDVQQDEwZzZXJ2ZXIxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl +cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD +VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9 +Adq67k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ +4UAtNHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQID +AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAGF5Pfwk +QDdwJup/mVITPxbBls4Yl7anDooUQsq8066lA1g54H/PRfXscGkyCFGh1ifXvf1L +psMRoBAdDHL/wSJplk3rRavkC94eBgnTFZmfKL6844g1j53yameiYL8IEVExYMBg +/XGyc0qwq57WT8B/K4aElrvlBlQ0wF3wN54M +-----END CERTIFICATE----- -- cgit v1.2.3 From e848019d1308e4dc20249ef7368ba1bdc01a0d9b Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 17 Dec 2013 09:14:56 +0100 Subject: inets: Mend broken max_clients check Conflicts: lib/inets/src/http_server/httpd_manager.erl Conflicts: lib/inets/test/inets_sup_SUITE.erl --- lib/inets/src/http_server/Makefile | 3 +- lib/inets/src/http_server/httpd_acceptor.erl | 112 ++-- lib/inets/src/http_server/httpd_acceptor_sup.erl | 84 ++- lib/inets/src/http_server/httpd_connection_sup.erl | 65 +++ lib/inets/src/http_server/httpd_instance_sup.erl | 30 +- lib/inets/src/http_server/httpd_manager.erl | 565 ++++++--------------- .../src/http_server/httpd_request_handler.erl | 95 +--- lib/inets/src/inets_app/inets.app.src | 3 +- lib/inets/test/Makefile | 2 +- lib/inets/test/httpd_SUITE.erl | 142 +++++- lib/inets/test/httpd_block.erl | 35 +- lib/inets/test/httpd_test_lib.erl | 16 +- lib/inets/test/inets_sup_SUITE.erl | 7 +- lib/inets/test/inets_test_lib.erl | 6 +- 14 files changed, 526 insertions(+), 639 deletions(-) create mode 100644 lib/inets/src/http_server/httpd_connection_sup.erl diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile index 67555d5f1c..ae75bc2b54 100644 --- a/lib/inets/src/http_server/Makefile +++ b/lib/inets/src/http_server/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2012. All Rights Reserved. +# Copyright Ericsson AB 2005-2014. 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 @@ -43,6 +43,7 @@ MODULES = \ httpd \ httpd_acceptor \ httpd_acceptor_sup \ + httpd_connection_sup\ httpd_cgi \ httpd_conf \ httpd_example \ diff --git a/lib/inets/src/http_server/httpd_acceptor.erl b/lib/inets/src/http_server/httpd_acceptor.erl index e79dfce0cb..73ed5c0769 100644 --- a/lib/inets/src/http_server/httpd_acceptor.erl +++ b/lib/inets/src/http_server/httpd_acceptor.erl @@ -21,13 +21,13 @@ -include("httpd.hrl"). -include("httpd_internal.hrl"). --include("inets_internal.hrl"). +%%-include("inets_internal.hrl"). %% Internal application API --export([start_link/6, start_link/7]). +-export([start_link/7, start_link/8]). %% Other exports (for spawn's etc.) --export([acceptor_init/7, acceptor_init/8, acceptor_loop/6]). +-export([acceptor_init/8, acceptor_init/9, acceptor_loop/8]). %% %% External API @@ -36,51 +36,52 @@ %% start_link start_link(Manager, SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout) -> - ?hdrd("start link", - [{manager, Manager}, - {socket_type, SocketType}, - {address, Addr}, - {port, Port}, - {timeout, AcceptTimeout}]), + %% ?hdrd("start link", + %% [{manager, Manager}, + %% {socket_type, SocketType}, + %% {address, Addr}, + %% {port, Port}, + %% {timeout, AcceptTimeout}]), Args = [self(), Manager, SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout], proc_lib:start_link(?MODULE, acceptor_init, Args). -start_link(Manager, SocketType, ListenSocket, IpFamily, ConfigDb, AcceptTimeout) -> - ?hdrd("start link", - [{manager, Manager}, - {socket_type, SocketType}, - {listen_socket, ListenSocket}, - {timeout, AcceptTimeout}]), - Args = [self(), Manager, SocketType, ListenSocket, IpFamily, +start_link(Manager, SocketType, Addr, Port, ListenSocket, IpFamily, ConfigDb, AcceptTimeout) -> + %% ?hdrd("start link", + %% [{manager, Manager}, + %% {socket_type, SocketType}, + %% {listen_socket, ListenSocket}, + %% {timeout, AcceptTimeout}]), + Args = [self(), Manager, SocketType, Addr, Port, ListenSocket, IpFamily, ConfigDb, AcceptTimeout], proc_lib:start_link(?MODULE, acceptor_init, Args). -acceptor_init(Parent, Manager, SocketType, {ListenOwner, ListenSocket}, IpFamily, +acceptor_init(Parent, Manager, SocketType, Addr, Port, {ListenOwner, ListenSocket}, IpFamily, ConfigDb, AcceptTimeout) -> - ?hdrd("acceptor init", - [{parent, Parent}, - {manager, Manager}, - {socket_type, SocketType}, - {listen_owner, ListenOwner}, - {listen_socket, ListenSocket}, - {timeout, AcceptTimeout}]), + %% ?hdrd("acceptor init", + %% [{parent, Parent}, + %% {manager, Manager}, + %% {socket_type, SocketType}, + %% {listen_owner, ListenOwner}, + %% {listen_socket, ListenSocket}, + %% {timeout, AcceptTimeout}]), link(ListenOwner), proc_lib:init_ack(Parent, {ok, self()}), - acceptor_loop(Manager, SocketType, ListenSocket, IpFamily, ConfigDb, AcceptTimeout). + acceptor_loop(Manager, SocketType, Addr, Port, + ListenSocket, IpFamily, ConfigDb, AcceptTimeout). acceptor_init(Parent, Manager, SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout) -> - ?hdrd("acceptor init", - [{parent, Parent}, - {manager, Manager}, - {socket_type, SocketType}, - {address, Addr}, - {port, Port}, - {timeout, AcceptTimeout}]), + %% ?hdrd("acceptor init", + %% [{parent, Parent}, + %% {manager, Manager}, + %% {socket_type, SocketType}, + %% {address, Addr}, + %% {port, Port}, + %% {timeout, AcceptTimeout}]), case (catch do_init(SocketType, Addr, Port, IpFamily)) of {ok, ListenSocket} -> proc_lib:init_ack(Parent, {ok, self()}), - acceptor_loop(Manager, SocketType, + acceptor_loop(Manager, SocketType, Addr, Port, ListenSocket, IpFamily,ConfigDb, AcceptTimeout); Error -> proc_lib:init_ack(Parent, Error), @@ -88,67 +89,68 @@ acceptor_init(Parent, Manager, SocketType, Addr, Port, IpFamily, end. do_init(SocketType, Addr, Port, IpFamily) -> - ?hdrt("do init", []), + %% ?hdrt("do init", []), do_socket_start(SocketType), ListenSocket = do_socket_listen(SocketType, Addr, Port, IpFamily), {ok, ListenSocket}. do_socket_start(SocketType) -> - ?hdrt("do socket start", []), + %% ?hdrt("do socket start", []), case http_transport:start(SocketType) of ok -> ok; {error, Reason} -> - ?hdrv("failed starting transport", [{reason, Reason}]), + %% ?hdrv("failed starting transport", [{reason, Reason}]), throw({error, {socket_start_failed, Reason}}) end. do_socket_listen(SocketType, Addr, Port, IpFamily) -> - ?hdrt("do socket listen", []), + %% ?hdrt("do socket listen", []), case http_transport:listen(SocketType, Addr, Port, IpFamily) of {ok, ListenSocket} -> ListenSocket; {error, Reason} -> - ?hdrv("listen failed", [{reason, Reason}, - {socket_type, SocketType}, - {addr, Addr}, - {port, Port}]), + %% ?hdrv("listen failed", [{reason, Reason}, + %% {socket_type, SocketType}, + %% {addr, Addr}, + %% {port, Port}]), throw({error, {listen, Reason}}) end. %% acceptor -acceptor_loop(Manager, SocketType, ListenSocket, IpFamily, ConfigDb, AcceptTimeout) -> - ?hdrd("awaiting accept", - [{manager, Manager}, - {socket_type, SocketType}, - {listen_socket, ListenSocket}, - {timeout, AcceptTimeout}]), +acceptor_loop(Manager, SocketType, Addr, Port, ListenSocket, IpFamily, ConfigDb, AcceptTimeout) -> + %% ?hdrd("awaiting accept", + %% [{manager, Manager}, + %% {socket_type, SocketType}, + %% {listen_socket, ListenSocket}, + %% {timeout, AcceptTimeout}]), case (catch http_transport:accept(SocketType, ListenSocket, 50000)) of {ok, Socket} -> - ?hdrv("accepted", [{socket, Socket}]), - handle_connection(Manager, ConfigDb, AcceptTimeout, + %% ?hdrv("accepted", [{socket, Socket}]), + handle_connection(Addr, Port, Manager, ConfigDb, AcceptTimeout, SocketType, Socket), - ?MODULE:acceptor_loop(Manager, SocketType, + ?MODULE:acceptor_loop(Manager, SocketType, Addr, Port, ListenSocket, IpFamily, ConfigDb,AcceptTimeout); {error, Reason} -> - ?hdri("accept failed", [{reason, Reason}]), + %% ?hdri("accept failed", [{reason, Reason}]), handle_error(Reason, ConfigDb), - ?MODULE:acceptor_loop(Manager, SocketType, ListenSocket, + ?MODULE:acceptor_loop(Manager, SocketType, Addr, Port, ListenSocket, IpFamily, ConfigDb, AcceptTimeout); {'EXIT', Reason} -> - ?hdri("accept exited", [{reason, Reason}]), + %% ?hdri("accept exited", [{reason, Reason}]), ReasonString = lists:flatten(io_lib:format("Accept exit: ~p", [Reason])), accept_failed(ConfigDb, ReasonString) end. -handle_connection(Manager, ConfigDb, AcceptTimeout, SocketType, Socket) -> - {ok, Pid} = httpd_request_handler:start(Manager, ConfigDb, AcceptTimeout), +handle_connection(Address, Port, Manager, ConfigDb, AcceptTimeout, SocketType, Socket) -> + Sup = httpd_connection_sup:connection_sup(Address, Port), + {ok, Pid} = httpd_connection_sup:start_child(Sup, [Manager, ConfigDb, AcceptTimeout]), http_transport:controlling_process(SocketType, Socket, Pid), httpd_request_handler:socket_ownership_transfered(Pid, SocketType, Socket). diff --git a/lib/inets/src/http_server/httpd_acceptor_sup.erl b/lib/inets/src/http_server/httpd_acceptor_sup.erl index 0cae8dbe47..9173a6d1b0 100644 --- a/lib/inets/src/http_server/httpd_acceptor_sup.erl +++ b/lib/inets/src/http_server/httpd_acceptor_sup.erl @@ -27,7 +27,8 @@ -behaviour(supervisor). %% API --export([start_link/2, start_acceptor/6, start_acceptor/7, stop_acceptor/2]). +-export([start_link/1]). +%%, start_acceptor/6, start_acceptor/7, stop_acceptor/2]). %% Supervisor callback -export([init/1]). @@ -35,63 +36,48 @@ %%%========================================================================= %%% API %%%========================================================================= -start_link(Addr, Port) -> +start_link([Addr, Port| _] = Args) -> SupName = make_name(Addr, Port), - supervisor:start_link({local, SupName}, ?MODULE, []). - -%%---------------------------------------------------------------------- -%% Function: [start|stop]_acceptor/5 -%% Description: Starts/stops an [auth | security] worker (child) process -%%---------------------------------------------------------------------- -start_acceptor(SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout) -> - start_worker(httpd_acceptor, SocketType, Addr, Port, IpFamily, - ConfigDb, AcceptTimeout, self(), []). -start_acceptor(SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout, ListenSocket) -> - start_worker(httpd_acceptor, SocketType, Addr, Port, IpFamily, - ConfigDb, AcceptTimeout, ListenSocket, self(), []). - - -stop_acceptor(Addr, Port) -> - stop_worker(httpd_acceptor, Addr, Port). + supervisor:start_link({local, SupName}, ?MODULE, [Args]). %%%========================================================================= %%% Supervisor callback %%%========================================================================= -init(_) -> - Flags = {one_for_one, 500, 100}, - Workers = [], - {ok, {Flags, Workers}}. +init([Args]) -> + RestartStrategy = one_for_one, + MaxR = 10, + MaxT = 3600, + Children = [child_spec(Args)], + {ok, {{RestartStrategy, MaxR, MaxT}, Children}}. %%%========================================================================= %%% Internal functions %%%========================================================================= +child_spec([Address, Port, ConfigList, AcceptTimeout, ListenInfo]) -> + Name = id(Address, Port), + Manager = httpd_util:make_name("httpd", Address, Port), + SockType = proplists:get_value(socket_type, ConfigList, ip_comm), + IpFamily = proplists:get_value(ipfamily, ConfigList, inet), + StartFunc = case ListenInfo of + undefined -> + {httpd_acceptor, start_link, [Manager, SockType, Address, Port, IpFamily, + httpd_util:make_name("httpd_conf", Address, Port), + AcceptTimeout]}; + _ -> + {httpd_acceptor, start_link, [Manager, SockType, Address, Port, ListenInfo, + IpFamily, + httpd_util:make_name("httpd_conf", Address, Port), + AcceptTimeout]} + end, + Restart = transient, + Shutdown = brutal_kill, + Modules = [httpd_acceptor], + Type = worker, + {Name, StartFunc, Restart, Shutdown, Type, Modules}. -make_name(Addr,Port) -> - httpd_util:make_name("httpd_acc_sup", Addr, Port). +id(Address, Port) -> + {httpd_acceptor_sup, Address, Port}. -start_worker(M, SocketType, Addr, Port, IpFamily, ConfigDB, AcceptTimeout, Manager, Modules) -> - SupName = make_name(Addr, Port), - Args = [Manager, SocketType, Addr, Port, IpFamily, ConfigDB, AcceptTimeout], - Spec = {{M, Addr, Port}, - {M, start_link, Args}, - permanent, timer:seconds(1), worker, [M] ++ Modules}, - supervisor:start_child(SupName, Spec). - -start_worker(M, SocketType, Addr, Port, IpFamily, ConfigDB, AcceptTimeout, ListenSocket, - Manager, Modules) -> - SupName = make_name(Addr, Port), - Args = [Manager, SocketType, ListenSocket, IpFamily, ConfigDB, AcceptTimeout], - Spec = {{M, Addr, Port}, - {M, start_link, Args}, - permanent, timer:seconds(1), worker, [M] ++ Modules}, - supervisor:start_child(SupName, Spec). +make_name(Addr,Port) -> + httpd_util:make_name("httpd_acceptor_sup", Addr, Port). -stop_worker(M, Addr, Port) -> - SupName = make_name(Addr, Port), - Name = {M, Addr, Port}, - case supervisor:terminate_child(SupName, Name) of - ok -> - supervisor:delete_child(SupName, Name); - Error -> - Error - end. diff --git a/lib/inets/src/http_server/httpd_connection_sup.erl b/lib/inets/src/http_server/httpd_connection_sup.erl new file mode 100644 index 0000000000..eac5e1b550 --- /dev/null +++ b/lib/inets/src/http_server/httpd_connection_sup.erl @@ -0,0 +1,65 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2014. 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. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% Purpose: Ssh connection supervisor. +%%---------------------------------------------------------------------- + +-module(httpd_connection_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/1]). +-export([start_child/2, connection_sup/2]). + +%% Supervisor callback +-export([init/1]). + +%%%========================================================================= +%%% API +%%%========================================================================= +start_link(Args) -> + supervisor:start_link(?MODULE, [Args]). + +start_child(Sup, Args) -> + supervisor:start_child(Sup, Args). + +connection_sup(Addr, Port) -> + httpd_util:make_name("httpd_connection_sup", Addr, Port). + +%%%========================================================================= +%%% Supervisor callback +%%%========================================================================= +init([[Addr, Port]]) -> + RegName = connection_sup(Addr, Port), + register(RegName, self()), + RestartStrategy = simple_one_for_one, + MaxR = 0, + MaxT = 3600, + + Name = undefined, % As simple_one_for_one is used. + StartFunc = {httpd_request_handler, start_link, []}, + Restart = temporary, % E.g. should not be restarted + Shutdown = 4000, + Modules = [httpd_request_handler], + Type = worker, + + ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules}, + {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}. diff --git a/lib/inets/src/http_server/httpd_instance_sup.erl b/lib/inets/src/http_server/httpd_instance_sup.erl index baa60d318c..383a55f594 100644 --- a/lib/inets/src/http_server/httpd_instance_sup.erl +++ b/lib/inets/src/http_server/httpd_instance_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. 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 @@ -100,7 +100,9 @@ start_link(ConfigFile, AcceptTimeout, ListenInfo, Debug) -> init([ConfigFile, ConfigList, AcceptTimeout, Debug, Address, Port]) -> httpd_util:enable_debug(Debug), Flags = {one_for_one, 0, 1}, - Children = [sup_spec(httpd_acceptor_sup, Address, Port), + Children = [httpd_connection_sup_spec(Address, Port), + httpd_acceptor_sup_spec(Address, Port, ConfigList, AcceptTimeout, + undefined), sup_spec(httpd_misc_sup, Address, Port), worker_spec(httpd_manager, Address, Port, ConfigFile, ConfigList,AcceptTimeout)], @@ -108,7 +110,9 @@ init([ConfigFile, ConfigList, AcceptTimeout, Debug, Address, Port]) -> init([ConfigFile, ConfigList, AcceptTimeout, Debug, Address, Port, ListenInfo]) -> httpd_util:enable_debug(Debug), Flags = {one_for_one, 0, 1}, - Children = [sup_spec(httpd_acceptor_sup, Address, Port), + Children = [httpd_connection_sup_spec(Address, Port), + httpd_acceptor_sup_spec(Address, Port, ConfigList, AcceptTimeout, + ListenInfo), sup_spec(httpd_misc_sup, Address, Port), worker_spec(httpd_manager, Address, Port, ListenInfo, ConfigFile, ConfigList, AcceptTimeout)], @@ -118,6 +122,24 @@ init([ConfigFile, ConfigList, AcceptTimeout, Debug, Address, Port, ListenInfo]) %%%========================================================================= %%% Internal functions %%%========================================================================= +httpd_connection_sup_spec(Address, Port) -> + Name = {httpd_connection_sup, Address, Port}, + StartFunc = {httpd_connection_sup, start_link, [[Address, Port]]}, + Restart = permanent, + Shutdown = 5000, + Modules = [httpd_connection_sup], + Type = supervisor, + {Name, StartFunc, Restart, Shutdown, Type, Modules}. + +httpd_acceptor_sup_spec(Address, Port, ConfigList, AcceptTimeout, ListenInfo) -> + Name = {httpd_acceptor_sup, Address, Port}, + StartFunc = {httpd_acceptor_sup, start_link, [[Address, Port, ConfigList, AcceptTimeout, ListenInfo]]}, + Restart = permanent, + Shutdown = infinity, + Modules = [httpd_acceptor_sup], + Type = supervisor, + {Name, StartFunc, Restart, Shutdown, Type, Modules}. + sup_spec(SupModule, Address, Port) -> Name = {SupModule, Address, Port}, StartFunc = {SupModule, start_link, [Address, Port]}, @@ -167,5 +189,3 @@ file_2_config(ConfigFile) -> Error -> Error end. - - diff --git a/lib/inets/src/http_server/httpd_manager.erl b/lib/inets/src/http_server/httpd_manager.erl index f6ae0d0933..82dbb2a149 100644 --- a/lib/inets/src/http_server/httpd_manager.erl +++ b/lib/inets/src/http_server/httpd_manager.erl @@ -25,9 +25,11 @@ -behaviour(gen_server). %% Application internal API --export([start/2, start_link/2, start_link/3, start_link/4, stop/1, reload/2]). --export([new_connection/1, done_connection/1]). +-export([start/2, start_link/2, start_link/3, start_link/4, + stop/1, reload/2]). +-export([new_connection/1]). -export([config_match/2, config_match/3]). +-export([block/2, block/3, unblock/1]). %% gen_server exports -export([init/1, @@ -35,26 +37,19 @@ terminate/2, code_change/3]). - -%% Management exports --export([block/2, block/3, unblock/1]). - -record(state,{socket_type = ip_comm, config_file, config_db = null, - connections, %% Current request handlers + connection_sup, admin_state = unblocked, blocker_ref = undefined, - blocking_tmr = undefined, + blocking_from = undefined, + shutdown_poller = undefined, status = []}). +%%%-------------------------------------------------------------------- +%%% Application internal API +%%%-------------------------------------------------------------------- - -%%TODO: Clean up this module! - - -%% -%% External API -%% %% Deprecated start(ConfigFile, ConfigList) -> Port = proplists:get_value(port,ConfigList,80), @@ -73,7 +68,8 @@ start_link(ConfigFile, ConfigList, AcceptTimeout) -> Name = make_name(Addr, Port), gen_server:start_link({local, Name},?MODULE, - [ConfigFile, ConfigList, AcceptTimeout, Addr, Port],[]). + [ConfigFile, ConfigList, + AcceptTimeout, Addr, Port],[]). start_link(ConfigFile, ConfigList, AcceptTimeout, ListenSocket) -> Port = proplists:get_value(port, ConfigList, 80), @@ -83,81 +79,33 @@ start_link(ConfigFile, ConfigList, AcceptTimeout, ListenSocket) -> gen_server:start_link({local, Name},?MODULE, [ConfigFile, ConfigList, AcceptTimeout, Addr, Port, ListenSocket],[]). - stop(ServerRef) -> call(ServerRef, stop). reload(ServerRef, Conf) -> call(ServerRef, {reload, Conf}). - -%%%---------------------------------------------------------------- - -block(ServerRef, disturbing) -> - call(ServerRef,block); - -block(ServerRef, non_disturbing) -> - do_block(ServerRef, non_disturbing, infinity). +block(ServerRef, Method) -> + block(ServerRef, Method, infinity). block(ServerRef, Method, Timeout) -> - do_block(ServerRef, Method, Timeout). - - -%% The reason for not using call here, is that the manager cannot -%% _wait_ for completion of the requests. It must be able to do -%% do other things at the same time as the blocking goes on. -do_block(ServerRef, Method, infinity) -> - Ref = make_ref(), - cast(ServerRef, {block, Method, infinity, self(), Ref}), - receive - {block_reply, Reply, Ref} -> - Reply - end; -do_block(ServerRef,Method,Timeout) when Timeout > 0 -> - Ref = make_ref(), - cast(ServerRef,{block,Method,Timeout,self(),Ref}), - receive - {block_reply,Reply,Ref} -> - Reply - end. - - -%%%---------------------------------------------------------------- - -%% unblock + call(ServerRef, {block, self(), Method, Timeout}). unblock(ServerRef) -> - call(ServerRef,unblock). - - -%% Internal API -%% - - -%% new_connection + call(ServerRef,{unblock, self()}). new_connection(Manager) -> call(Manager, {new_connection, self()}). -%% done - -done_connection(Manager) -> - cast(Manager, {done_connection, self()}). - - config_match(Port, Pattern) -> config_match(undefined,Port,Pattern). config_match(Addr, Port, Pattern) -> Name = httpd_util:make_name("httpd",Addr,Port), call(whereis(Name), {config_match, Pattern}). - -%% -%% Server call-back functions -%% - -%% init - +%%%-------------------------------------------------------------------- +%%% gen_server callbacks functions +%%%-------------------------------------------------------------------- init([ConfigFile, ConfigList, AcceptTimeout, Addr, Port]) -> process_flag(trap_exit, true), case (catch do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port)) of @@ -188,47 +136,35 @@ init([ConfigFile, ConfigList, AcceptTimeout, Addr, Port, ListenInfo]) -> {ok, State} end. -do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port) -> - IpFamily = proplists:get_value(ipfamily, ConfigList, inet6fb4), +do_init(ConfigFile, ConfigList, _AcceptTimeout, Addr, Port) -> + Sup = httpd_util:make_name("httpd_connection_sup", Addr, Port), NewConfigFile = proplists:get_value(file, ConfigList, ConfigFile), ConfigDB = do_initial_store(ConfigList), SocketType = httpd_conf:lookup_socket_type(ConfigDB), - case httpd_acceptor_sup:start_acceptor(SocketType, Addr, - Port, IpFamily, ConfigDB, AcceptTimeout) of - {ok, _Pid} -> - Status = [{max_conn, 0}, - {last_heavy_load, never}, - {last_connection, never}], + Status = [{max_conn, 0}, + {last_heavy_load, never}, + {last_connection, never}], State = #state{socket_type = SocketType, config_file = NewConfigFile, config_db = ConfigDB, - connections = [], + connection_sup = Sup, status = Status}, - {ok, State}; - Else -> - Else - end. + {ok, State}. -do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port, ListenInfo) -> - IpFamily = proplists:get_value(ipfamily, ConfigList, inet6fb4), +do_init(ConfigFile, ConfigList, _AcceptTimeout, Addr, Port, _ListenInfo) -> + Sup = httpd_util:make_name("httpd_connection_sup", Addr, Port), NewConfigFile = proplists:get_value(file, ConfigList, ConfigFile), ConfigDB = do_initial_store(ConfigList), SocketType = httpd_conf:lookup_socket_type(ConfigDB), - case httpd_acceptor_sup:start_acceptor(SocketType, Addr, - Port, IpFamily, ConfigDB, - AcceptTimeout, ListenInfo) of - {ok, _Pid} -> - Status = [{max_conn,0}, {last_heavy_load,never}, - {last_connection,never}], + Status = [{max_conn,0}, {last_heavy_load,never}, + {last_connection,never}], State = #state{socket_type = SocketType, config_file = NewConfigFile, config_db = ConfigDB, - connections = [], + connection_sup = Sup, status = Status}, - {ok, State}; - Else -> - Else - end. + {ok, State}. + do_initial_store(ConfigList) -> case httpd_conf:store(ConfigList) of @@ -238,10 +174,6 @@ do_initial_store(ConfigList) -> throw({error, Reason}) end. - - -%% handle_call - handle_call(stop, _From, State) -> {stop, normal, ok, State}; @@ -249,8 +181,7 @@ handle_call({config_match, Query}, _From, State) -> Res = ets:match_object(State#state.config_db, Query), {reply, Res, State}; -handle_call({reload, Conf}, _From, State) - when State#state.admin_state =:= blocked -> +handle_call({reload, Conf}, _From, #state{admin_state = blocked} = State) -> case handle_reload(Conf, State) of {stop, Reply,S1} -> {stop, Reply, S1}; @@ -261,13 +192,32 @@ handle_call({reload, Conf}, _From, State) handle_call({reload, _}, _From, State) -> {reply,{error,{invalid_admin_state,State#state.admin_state}},State}; -handle_call(block, _From, State) -> - {Reply,S1} = handle_block(State), - {reply,Reply,S1}; +handle_call({block , Blocker, Mode, Timeout}, From, + #state{admin_state = unblocked, + connection_sup = CSup} = State) -> + Monitor = erlang:monitor(process, Blocker), + case count_children(CSup) of + 0 -> + %% Already in idle usage state => go directly to blocked + {reply, ok, State#state{admin_state = blocked, + blocker_ref = {Blocker, Monitor}, + blocking_from = From}}; + _ -> + handle_block(Mode, Timeout, + State#state{blocker_ref = {Blocker, Monitor}, + blocking_from = From}) + end; +handle_call({block , _, _, _}, _, State) -> + {reply, {error, blocked}, State}; + +handle_call({unblock, Blocker}, _, #state{blocker_ref = {Blocker,_}, + admin_state = blocked} = State) -> + + {reply, ok, + State#state{admin_state = unblocked, blocker_ref = undefined}}; -handle_call(unblock, {From,_Tag}, State) -> - {Reply,S1} = handle_unblock(State,From), - {reply, Reply, S1}; +handle_call({unblock, _}, _, State) -> + {reply, {error, only_blocker_may_unblock}, State}; handle_call({new_connection, Pid}, _From, State) -> {Status, NewState} = handle_new_connection(State, Pid), @@ -284,21 +234,6 @@ handle_call(Request, From, State) -> report_error(State,String), {reply, ok, State}. - -%% handle_cast - -handle_cast({done_connection, Pid}, State) -> - S1 = handle_done_connection(State, Pid), - {noreply, S1}; - -handle_cast({block, disturbing, Timeout, From, Ref}, State) -> - S1 = handle_block(State, Timeout, From, Ref), - {noreply,S1}; - -handle_cast({block, non_disturbing, Timeout, From, Ref}, State) -> - S1 = handle_nd_block(State, Timeout, From, Ref), - {noreply,S1}; - handle_cast(Message, State) -> String = lists:flatten( @@ -309,32 +244,51 @@ handle_cast(Message, State) -> report_error(State, String), {noreply, State}. -%% handle_info +handle_info(connections_terminated, #state{admin_state = shutting_down, + blocking_from = From} = State) -> + gen_server:reply(From, ok), + {noreply, State#state{admin_state = blocked, blocking_from = undefined, + blocker_ref = undefined}}; +handle_info(connections_terminated, State) -> + {noreply, State}; -handle_info({block_timeout, Method}, State) -> - S1 = handle_block_timeout(State,Method), - {noreply, S1}; +handle_info({block_timeout, non_disturbing}, + #state{admin_state = shutting_down, + blocking_from = From, + blocker_ref = {_, Monitor}} = State) -> + erlang:demonitor(Monitor), + gen_server:reply(From, {error, timeout}), + {noreply, State#state{admin_state = unblocked, blocking_from = undefined, + blocker_ref = undefined}}; +handle_info({block_timeout, disturbing}, + #state{admin_state = shutting_down, + blocking_from = From, + blocker_ref = {_, Monitor}, + connection_sup = Sup} = State) -> + SupPid = whereis(Sup), + shutdown_connections(SupPid), + erlang:demonitor(Monitor), + gen_server:reply(From, ok), + {noreply, State#state{admin_state = blocked, blocker_ref = undefined, + blocking_from = undefined}}; +handle_info({block_timeout, _, _}, State) -> + {noreply, State}; -handle_info({'DOWN', Ref, process, _Object, _Info}, State) -> - S1 = - case State#state.blocker_ref of - Ref -> - handle_blocker_exit(State); - _ -> - %% Not our blocker, so ignore - State - end, - {noreply, S1}; +handle_info({'DOWN', _, process, Pid, _Info}, + #state{admin_state = Admin, + blocker_ref = {Pid, _}} = State) when + Admin =/= unblocked -> + {noreply, State#state{admin_state = unblocked, + blocking_from = undefined, + blocker_ref = undefined}}; +handle_info({'DOWN', _, process, _, _}, State) -> + {noreply, State}; handle_info({'EXIT', _, normal}, State) -> {noreply, State}; -handle_info({'EXIT', _, blocked}, S) -> - {noreply, S}; - -handle_info({'EXIT', Pid, Reason}, State) -> - S1 = check_connections(State, Pid, Reason), - {noreply, S1}; +handle_info({'EXIT', _, shutdown}, State) -> + {stop, shutdown, State}; handle_info(Info, State) -> String = @@ -346,217 +300,66 @@ handle_info(Info, State) -> report_error(State, String), {noreply, State}. - -%% terminate - terminate(_, #state{config_db = Db}) -> httpd_conf:remove_all(Db), ok. - -%% code_change({down,ToVsn}, State, Extra) -%% - code_change({down,_ToVsn}, State, _Extra) -> {ok,State}; -%% code_change(FromVsn, State, Extra) -%% code_change(_FromVsn, State, _Extra) -> {ok,State}. - - -%% ------------------------------------------------------------------------- -%% check_connection -%% -%% -%% -%% - -check_connections(#state{connections = []} = State, _Pid, _Reason) -> - State; -check_connections(#state{connections = Connections} = State, Pid, _Reason) -> - State#state{connections = lists:delete(Pid, Connections)}. - - -%% ------------------------------------------------------------------------- -%% handle_[new | done]_connection -%% -%% -%% -%% - -handle_new_connection(State, Handler) -> +%%%-------------------------------------------------------------------- +%%% Internal functions +%%%-------------------------------------------------------------------- +handle_new_connection(#state{admin_state = AdminState} = State, Handler) -> UsageState = get_ustate(State), - AdminState = get_astate(State), handle_new_connection(UsageState, AdminState, State, Handler). -handle_new_connection(busy, unblocked, State, _Handler) -> - Status = update_heavy_load_status(State#state.status), - {{reject, busy}, - State#state{status = Status}}; - -handle_new_connection(_UsageState, unblocked, State, Handler) -> - Connections = State#state.connections, - Status = update_connection_status(State#state.status, - length(Connections)+1), - link(Handler), - {{ok, accept}, - State#state{connections = [Handler|Connections], status = Status}}; - -handle_new_connection(_UsageState, _AdminState, State, _Handler) -> - {{reject, blocked}, - State}. - -handle_done_connection(#state{admin_state = shutting_down, - connections = Connections} = State, Handler) -> - unlink(Handler), - case lists:delete(Handler, Connections) of - [] -> % Ok, block complete - demonitor_blocker(State#state.blocker_ref), - {Tmr,From,Ref} = State#state.blocking_tmr, - stop_block_tmr(Tmr), - From ! {block_reply,ok,Ref}, - State#state{admin_state = blocked, connections = [], - blocker_ref = undefined}; - Connections1 -> - State#state{connections = Connections1} - end; - -handle_done_connection(#state{connections = Connections} = State, Handler) -> - State#state{connections = lists:delete(Handler, Connections)}. - - -%% ------------------------------------------------------------------------- -%% handle_block -%% -%% -%% -%% -handle_block(#state{admin_state = AdminState} = S) -> - handle_block(S, AdminState). - -handle_block(S,unblocked) -> - %% Kill all connections - [kill_handler(Pid) || Pid <- S#state.connections], - {ok,S#state{connections = [], admin_state = blocked}}; -handle_block(S,blocked) -> - {ok,S}; -handle_block(S,shutting_down) -> - {{error,shutting_down},S}. - - -kill_handler(Pid) -> - exit(Pid, blocked). - -handle_block(S,Timeout,From,Ref) when Timeout >= 0 -> - do_block(S,Timeout,From,Ref); - -handle_block(S,Timeout,From,Ref) -> - Reply = {error,{invalid_block_request,Timeout}}, - From ! {block_reply,Reply,Ref}, - S. - -do_block(S,Timeout,From,Ref) -> - case S#state.connections of - [] -> - %% Already in idle usage state => go directly to blocked - From ! {block_reply,ok,Ref}, - S#state{admin_state = blocked}; - _ -> - %% Active or Busy usage state => go to shutting_down - %% Make sure we get to know if blocker dies... - MonitorRef = monitor_blocker(From), - Tmr = {start_block_tmr(Timeout,disturbing),From,Ref}, - S#state{admin_state = shutting_down, - blocker_ref = MonitorRef, blocking_tmr = Tmr} - end. - -handle_nd_block(S,infinity,From,Ref) -> - do_nd_block(S,infinity,From,Ref); - -handle_nd_block(S,Timeout,From,Ref) when Timeout >= 0 -> - do_nd_block(S,Timeout,From,Ref); - -handle_nd_block(S,Timeout,From,Ref) -> - Reply = {error,{invalid_block_request,Timeout}}, - From ! {block_reply,Reply,Ref}, - S. - -do_nd_block(S,Timeout,From,Ref) -> - case S#state.connections of - [] -> - %% Already in idle usage state => go directly to blocked - From ! {block_reply,ok,Ref}, - S#state{admin_state = blocked}; +handle_new_connection(_UsageState, unblocked, + #state{config_db = Db, connection_sup = CSup} = + State, _) -> + Max = httpd_util:lookup(Db, max_clients), + case count_children(CSup) of + Count when Count =< Max -> + {{ok, accept}, State}; _ -> - %% Active or Busy usage state => go to shutting_down - %% Make sure we get to know if blocker dies... - MonitorRef = monitor_blocker(From), - Tmr = {start_block_tmr(Timeout,non_disturbing),From,Ref}, - S#state{admin_state = shutting_down, - blocker_ref = MonitorRef, blocking_tmr = Tmr} - end. + {{reject, busy}, State} + end; -handle_block_timeout(S,Method) -> - %% Time to take this to the road... - demonitor_blocker(S#state.blocker_ref), - handle_block_timeout1(S,Method,S#state.blocking_tmr). - -handle_block_timeout1(S,non_disturbing,{_,From,Ref}) -> - From ! {block_reply,{error,timeout},Ref}, - S#state{admin_state = unblocked, - blocker_ref = undefined, blocking_tmr = undefined}; - -handle_block_timeout1(S,disturbing,{_,From,Ref}) -> - [exit(Pid,blocked) || Pid <- S#state.connections], - - From ! {block_reply,ok,Ref}, - S#state{admin_state = blocked, connections = [], - blocker_ref = undefined, blocking_tmr = undefined}; - -handle_block_timeout1(S,Method,{_,From,Ref}) -> - From ! {block_reply,{error,{unknown_block_method,Method}},Ref}, - S#state{admin_state = blocked, connections = [], - blocker_ref = undefined, blocking_tmr = undefined}; - -handle_block_timeout1(S, _Method, _TmrInfo) -> - S#state{admin_state = unblocked, - blocker_ref = undefined, blocking_tmr = undefined}. - -handle_unblock(S, FromA) -> - handle_unblock(S, FromA, S#state.admin_state). - -handle_unblock(S, _FromA, unblocked) -> - {ok,S}; -handle_unblock(S, FromA, _AdminState) -> - case S#state.blocking_tmr of - {Tmr,FromB,Ref} -> - %% Another process is trying to unblock - %% Inform the blocker - stop_block_tmr(Tmr), - FromB ! {block_reply, {error,{unblocked,FromA}},Ref}; - _ -> - ok - end, - {ok,S#state{admin_state = unblocked, blocking_tmr = undefined}}. - -%% The blocker died so we give up on the block. -handle_blocker_exit(S) -> - {Tmr,_From,_Ref} = S#state.blocking_tmr, - stop_block_tmr(Tmr), - S#state{admin_state = unblocked, - blocker_ref = undefined, blocking_tmr = undefined}. +handle_new_connection(_UsageState, _AdminState, State, _Handler) -> + {{reject, blocked}, State}. + +handle_block(disturbing, infinity, + #state{connection_sup = CSup, + blocking_from = From, + blocker_ref = {_, Monitor}} = State) -> + SupPid = whereis(CSup), + shutdown_connections(SupPid), + erlang:demonitor(Monitor), + gen_server:reply(From, ok), + {noreply, State#state{admin_state = blocked, blocker_ref = undefined, + blocking_from = undefined}}; +handle_block(disturbing, Timeout, #state{connection_sup = CSup} = State) -> + Manager = self(), + spawn_link(fun() -> wait_for_shutdown(CSup, Manager) end), + erlang:send_after(Timeout, self(), {block_timeout, disturbing}), + {noreply, State#state{admin_state = shutting_down}}; + +handle_block(non_disturbing, infinity, + #state{connection_sup = CSup} = State) -> + Manager = self(), + spawn_link(fun() -> wait_for_shutdown(CSup, Manager) end), + {noreply, State#state{admin_state = shutting_down}}; + +handle_block(non_disturbing, Timeout, + #state{connection_sup = CSup} = State) -> + Manager = self(), + spawn_link(fun() -> wait_for_shutdown(CSup, Manager) end), + erlang:send_after(Timeout, self(), {block_timeout, non_disturbing}), + {noreply, State#state{admin_state = shutting_down}}. - - -%% ------------------------------------------------------------------------- -%% handle_reload -%% -%% -%% -%% handle_reload(undefined, #state{config_file = undefined} = State) -> {continue, {error, undefined_config_file}, State}; handle_reload(undefined, #state{config_file = ConfigFile} = State) -> @@ -632,7 +435,7 @@ check_constant_values(Db, Config) -> %% Otherwise -> active %% get_ustate(State) -> - get_ustate(length(State#state.connections),State). + get_ustate(count_children(State#state.connection_sup),State). get_ustate(0,_State) -> idle; @@ -645,60 +448,6 @@ get_ustate(ConnectionCnt,State) -> active end. - -get_astate(S) -> S#state.admin_state. - - -%% Timer handling functions -start_block_tmr(infinity,_) -> - undefined; -start_block_tmr(T,M) -> - erlang:send_after(T,self(),{block_timeout,M}). - -stop_block_tmr(undefined) -> - ok; -stop_block_tmr(Ref) -> - erlang:cancel_timer(Ref). - - -%% Monitor blocker functions -monitor_blocker(Pid) when is_pid(Pid) -> - case (catch erlang:monitor(process,Pid)) of - {'EXIT', _Reason} -> - undefined; - MonitorRef -> - MonitorRef - end; -monitor_blocker(_) -> - undefined. - -demonitor_blocker(undefined) -> - ok; -demonitor_blocker(Ref) -> - (catch erlang:demonitor(Ref)). - - -%% Some status utility functions - -update_heavy_load_status(Status) -> - update_status_with_time(Status,last_heavy_load). - -update_connection_status(Status,ConnCount) -> - S1 = case lists:keysearch(max_conn,1,Status) of - {value, {max_conn, C1}} when ConnCount > C1 -> - lists:keyreplace(max_conn,1,Status,{max_conn,ConnCount}); - {value, {max_conn, _C2}} -> - Status; - false -> - [{max_conn, ConnCount} | Status] - end, - update_status_with_time(S1,last_connection). - -update_status_with_time(Status,Key) -> - lists:keyreplace(Key,1,Status,{Key,universal_time()}). - -universal_time() -> calendar:universal_time(). - make_name(Addr,Port) -> httpd_util:make_name("httpd",Addr,Port). @@ -708,8 +457,7 @@ report_error(State,String) -> error_logger:error_report(String), mod_log:report_error(Cdb,String), mod_disk_log:report_error(Cdb,String). - -%% + call(ServerRef, Request) -> try gen_server:call(ServerRef, Request, infinity) catch @@ -717,11 +465,24 @@ call(ServerRef, Request) -> {error, closed} end. -cast(ServerRef, Message) -> - try gen_server:cast(ServerRef, Message) - catch - exit:_ -> - {error, closed} - end. +count_children(Sup) -> + Children = supervisor:count_children(whereis(Sup)), + proplists:get_value(workers, Children). +shutdown_connections(Sup) -> + Children = [Child || {_,Child,_,_} <- supervisor:which_children(Sup)], + lists:foreach(fun(Pid) -> exit(Pid, kill) end, + Children). + +wait_for_shutdown(CSup, Manager) -> + case count_children(CSup) of + 0 -> + Manager ! connections_terminated; + _ -> + receive + after 500 -> + ok + end, + wait_for_shutdown(CSup, Manager) + end. diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index fb458f61cd..6f38090f58 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2012. All Rights Reserved. +%% Copyright Ericsson AB 1997-2014. 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 @@ -25,7 +25,7 @@ -behaviour(gen_server). %% Application internal API --export([start/2, start/3, socket_ownership_transfered/3]). +-export([start_link/2, start_link/3, socket_ownership_transfered/3]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -57,10 +57,10 @@ %% Description: Starts a httpd-request handler process. Intended to be %% called by the httpd acceptor process. %%-------------------------------------------------------------------- -start(Manager, ConfigDB) -> - start(Manager, ConfigDB, 15000). -start(Manager, ConfigDB, AcceptTimeout) -> - proc_lib:start(?MODULE, init, [[Manager, ConfigDB,AcceptTimeout]]). +start_link(Manager, ConfigDB) -> + start_link(Manager, ConfigDB, 15000). +start_link(Manager, ConfigDB, AcceptTimeout) -> + proc_lib:start_link(?MODULE, init, [[Manager, ConfigDB,AcceptTimeout]]). %%-------------------------------------------------------------------- @@ -87,34 +87,27 @@ socket_ownership_transfered(Pid, SocketType, Socket) -> %% gen_server provides is needed. %%-------------------------------------------------------------------- init([Manager, ConfigDB, AcceptTimeout]) -> - ?hdrd("initiate", - [{manager, Manager}, {cdb, ConfigDB}, {timeout, AcceptTimeout}]), + process_flag(trap_exit, true), %% Make sure this process terminates if the httpd manager process %% should die! - link(Manager), + %%link(Manager), %% At this point the function httpd_request_handler:start/2 will return. proc_lib:init_ack({ok, self()}), {SocketType, Socket} = await_socket_ownership_transfer(AcceptTimeout), - ?hdrd("socket ownership transfered", - [{socket_type, SocketType}, {socket, Socket}]), TimeOut = httpd_util:lookup(ConfigDB, keep_alive_timeout, 150000), Then = erlang:now(), - ?hdrd("negotiate", []), case http_transport:negotiate(SocketType, Socket, TimeOut) of - {error, Error} -> - ?hdrd("negotiation failed", [{error, Error}]), + {error, _Error} -> exit(shutdown); %% Can be 'normal'. ok -> - ?hdrt("negotiation successfull", []), NewTimeout = TimeOut - timer:now_diff(now(),Then) div 1000, continue_init(Manager, ConfigDB, SocketType, Socket, NewTimeout) end. continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) -> - ?hdrt("continue init", [{timeout, TimeOut}]), Resolve = http_transport:resolve(), Peername = httpd_socket:peername(SocketType, Socket), @@ -139,14 +132,10 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) -> max_keep_alive_request = NrOfRequest, mfa = MFA}, - ?hdrt("activate request timeout", []), - - ?hdrt("set socket options (binary, packet & active)", []), http_transport:setopts(SocketType, Socket, [binary, {packet, 0}, {active, once}]), NewState = data_receive_counter(activate_request_timeout(State), httpd_util:lookup(ConfigDB, minimum_bytes_per_second, false)), - ?hdrt("init done", []), - gen_server:enter_loop(?MODULE, [], NewState). + gen_server:enter_loop(?MODULE, [], NewState). %%==================================================================== @@ -195,18 +184,13 @@ handle_cast(Msg, #state{mod = ModData} = State) -> %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- handle_info({Proto, Socket, Data}, - #state{mfa = {Module, Function, Args} = MFA, + #state{mfa = {Module, Function, Args}, mod = #mod{socket_type = SockType, socket = Socket} = ModData} = State) when (((Proto =:= tcp) orelse (Proto =:= ssl) orelse (Proto =:= dummy)) andalso is_binary(Data)) -> - ?hdrd("received data", - [{data, Data}, {proto, Proto}, - {socket, Socket}, {socket_type, SockType}, {mfa, MFA}]), - -%% case (catch Module:Function([Data | Args])) of PROCESSED = (catch Module:Function([Data | Args])), NewDataSize = case State#state.byte_limit of undefined -> @@ -214,10 +198,8 @@ handle_info({Proto, Socket, Data}, _ -> State#state.data + byte_size(Data) end, - ?hdrt("data processed", [{processing_result, PROCESSED}]), case PROCESSED of {ok, Result} -> - ?hdrd("data processed", [{result, Result}]), NewState = case NewDataSize of undefined -> cancel_request_timeout(State); @@ -227,7 +209,6 @@ handle_info({Proto, Socket, Data}, handle_http_msg(Result, NewState); {error, {uri_too_long, MaxSize}, Version} -> - ?hdrv("uri too long", [{max_size, MaxSize}, {version, Version}]), NewModData = ModData#mod{http_version = Version}, httpd_response:send_status(NewModData, 414, "URI too long"), Reason = io_lib:format("Uri too long, max size is ~p~n", @@ -236,8 +217,6 @@ handle_info({Proto, Socket, Data}, {stop, normal, State#state{response_sent = true, mod = NewModData}}; {error, {header_too_long, MaxSize}, Version} -> - ?hdrv("header too long", - [{max_size, MaxSize}, {version, Version}]), NewModData = ModData#mod{http_version = Version}, httpd_response:send_status(NewModData, 413, "Header too long"), Reason = io_lib:format("Header too long, max size is ~p~n", @@ -246,7 +225,6 @@ handle_info({Proto, Socket, Data}, {stop, normal, State#state{response_sent = true, mod = NewModData}}; NewMFA -> - ?hdrd("data processed - reactivate socket", [{new_mfa, NewMFA}]), http_transport:setopts(SockType, Socket, [{active, once}]), case NewDataSize of undefined -> @@ -293,6 +271,10 @@ handle_info(check_data, #state{data = Data, byte_limit = Byte_Limit} = State) -> _ -> {stop, normal, State#state{response_sent = true}} end; + +handle_info({'EXIT', _, Reason}, State) -> + {stop, Reason, State}; + %% Default case handle_info(Info, #state{mod = ModData} = State) -> Error = lists:flatten( @@ -324,10 +306,8 @@ terminate(Reason, #state{response_sent = false, mod = ModData} = State) -> terminate(_Reason, State) -> do_terminate(State). -do_terminate(#state{mod = ModData, manager = Manager} = State) -> - catch httpd_manager:done_connection(Manager), +do_terminate(#state{mod = ModData} = State) -> cancel_request_timeout(State), - %% receive after 5000 -> ok end, httpd_socket:close(ModData#mod.socket_type, ModData#mod.socket). @@ -355,30 +335,24 @@ await_socket_ownership_transfer(AcceptTimeout) -> handle_http_msg({_, _, Version, {_, _}, _}, #state{status = busy, mod = ModData} = State) -> - ?hdrt("handle http msg when manager busy", [{mod, ModData}]), handle_manager_busy(State#state{mod = ModData#mod{http_version = Version}}), {stop, normal, State}; handle_http_msg({_, _, Version, {_, _}, _}, #state{status = blocked, mod = ModData} = State) -> - ?hdrt("handle http msg when manager blocket", [{mod, ModData}]), handle_manager_blocked(State#state{mod = ModData#mod{http_version = Version}}), {stop, normal, State}; handle_http_msg({Method, Uri, Version, {RecordHeaders, Headers}, Body}, #state{status = accept, mod = ModData} = State) -> - ?hdrt("handle http msg when manager accepting", - [{method, Method}, {mod, ModData}]), case httpd_request:validate(Method, Uri, Version) of ok -> - ?hdrt("request validated", []), {ok, NewModData} = httpd_request:update_mod_data(ModData, Method, Uri, Version, Headers), - ?hdrt("new mod data", [{mod, NewModData}]), case is_host_specified_if_required(NewModData#mod.absolute_uri, RecordHeaders, Version) of true -> @@ -392,23 +366,18 @@ handle_http_msg({Method, Uri, Version, {RecordHeaders, Headers}, Body}, {stop, normal, State#state{response_sent = true}} end; {error, {not_supported, What}} -> - ?hdrd("validation failed: not supported", [{what, What}]), httpd_response:send_status(ModData#mod{http_version = Version}, 501, {Method, Uri, Version}), Reason = io_lib:format("Not supported: ~p~n", [What]), error_log(Reason, ModData), {stop, normal, State#state{response_sent = true}}; {error, {bad_request, {forbidden, URI}}} -> - ?hdrd("validation failed: bad request - forbidden", - [{uri, URI}]), httpd_response:send_status(ModData#mod{http_version = Version}, 403, URI), Reason = io_lib:format("Forbidden URI: ~p~n", [URI]), error_log(Reason, ModData), {stop, normal, State#state{response_sent = true}}; {error, {bad_request, {malformed_syntax, URI}}} -> - ?hdrd("validation failed: bad request - malformed syntax", - [{uri, URI}]), httpd_response:send_status(ModData#mod{http_version = Version}, 400, URI), Reason = io_lib:format("Malformed syntax in URI: ~p~n", [URI]), @@ -417,12 +386,9 @@ handle_http_msg({Method, Uri, Version, {RecordHeaders, Headers}, Body}, end; handle_http_msg({ChunkedHeaders, Body}, State = #state{headers = Headers}) -> - ?hdrt("handle http msg", - [{chunked_headers, ChunkedHeaders}, {body, Body}]), NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders), handle_response(State#state{headers = NewHeaders, body = Body}); handle_http_msg(Body, State) -> - ?hdrt("handle http msg", [{body, Body}]), handle_response(State#state{body = Body}). handle_manager_busy(#state{mod = #mod{config_db = ConfigDB}} = State) -> @@ -445,7 +411,6 @@ is_host_specified_if_required(_, _, _) -> true. handle_body(#state{mod = #mod{config_db = ConfigDB}} = State) -> - ?hdrt("handle body", []), MaxHeaderSize = max_header_size(ConfigDB), MaxBodySize = max_body_size(ConfigDB), @@ -459,34 +424,22 @@ handle_body(#state{mod = #mod{config_db = ConfigDB}} = State) -> handle_body(#state{headers = Headers, body = Body, mod = ModData} = State, MaxHeaderSize, MaxBodySize) -> - ?hdrt("handle body", [{headers, Headers}, {body, Body}]), case Headers#http_request_h.'transfer-encoding' of "chunked" -> - ?hdrt("chunked - attempt decode", []), case http_chunk:decode(Body, MaxBodySize, MaxHeaderSize) of {Module, Function, Args} -> - ?hdrt("chunk decoded", - [{module, Module}, - {function, Function}, - {args, Args}]), http_transport:setopts(ModData#mod.socket_type, ModData#mod.socket, [{active, once}]), {noreply, State#state{mfa = {Module, Function, Args}}}; {ok, {ChunkedHeaders, NewBody}} -> - ?hdrt("chunk decoded", - [{chunked_headers, ChunkedHeaders}, - {new_body, NewBody}]), NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders), - ?hdrt("chunked - headers handled", - [{new_headers, NewHeaders}]), handle_response(State#state{headers = NewHeaders, body = NewBody}) end; Encoding when is_list(Encoding) -> - ?hdrt("not chunked - encoding", [{encoding, Encoding}]), httpd_response:send_status(ModData, 501, "Unknown Transfer-Encoding"), Reason = io_lib:format("Unknown Transfer-Encoding: ~p~n", @@ -494,17 +447,12 @@ handle_body(#state{headers = Headers, body = Body, mod = ModData} = State, error_log(Reason, ModData), {stop, normal, State#state{response_sent = true}}; _ -> - ?hdrt("not chunked", []), Length = list_to_integer(Headers#http_request_h.'content-length'), case ((Length =< MaxBodySize) or (MaxBodySize == nolimit)) of true -> case httpd_request:whole_body(Body, Length) of {Module, Function, Args} -> - ?hdrt("whole body", - [{module, Module}, - {function, Function}, - {args, Args}]), http_transport:setopts(ModData#mod.socket_type, ModData#mod.socket, [{active, once}]), @@ -512,15 +460,11 @@ handle_body(#state{headers = Headers, body = Body, mod = ModData} = State, {Module, Function, Args}}}; {ok, NewBody} -> - ?hdrt("whole body", - [{new_body, NewBody}]), handle_response( State#state{headers = Headers, body = NewBody}) end; false -> - ?hdrd("body too long", - [{length, Length}, {max_body_size, MaxBodySize}]), httpd_response:send_status(ModData, 413, "Body too long"), error_log("Body too long", ModData), {stop, normal, State#state{response_sent = true}} @@ -582,8 +526,6 @@ handle_response(#state{body = Body, mod = ModData, headers = Headers, max_keep_alive_request = Max} = State) when Max > 0 -> - ?hdrt("handle response", - [{body, Body}, {mod, ModData}, {headers, Headers}, {max, Max}]), {NewBody, Data} = httpd_request:body_data(Headers, Body), ok = httpd_response:generate_and_send_response( ModData#mod{entity_body = NewBody}), @@ -592,8 +534,6 @@ handle_response(#state{body = Body, handle_response(#state{body = Body, headers = Headers, mod = ModData} = State) -> - ?hdrt("handle response", - [{body, Body}, {mod, ModData}, {headers, Headers}]), {NewBody, _} = httpd_request:body_data(Headers, Body), ok = httpd_response:generate_and_send_response( ModData#mod{entity_body = NewBody}), @@ -601,7 +541,6 @@ handle_response(#state{body = Body, handle_next_request(#state{mod = #mod{connection = true} = ModData, max_keep_alive_request = Max} = State, Data) -> - ?hdrt("handle next request", [{max, Max}]), NewModData = #mod{socket_type = ModData#mod.socket_type, socket = ModData#mod.socket, @@ -630,11 +569,9 @@ handle_next_request(#state{mod = #mod{connection = true} = ModData, end; handle_next_request(State, _) -> - ?hdrt("handle next request - stop", []), {stop, normal, State}. activate_request_timeout(#state{timeout = Time} = State) -> - ?hdrt("activate request timeout", [{time, Time}]), Ref = erlang:send_after(Time, self(), timeout), State#state{timer = Ref}. data_receive_counter(State, Byte_limit) -> diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src index 4aea2ef3d7..a6dd364c2d 100644 --- a/lib/inets/src/inets_app/inets.app.src +++ b/lib/inets/src/inets_app/inets.app.src @@ -1,7 +1,7 @@ %% This is an -*- erlang -*- file. %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2014. 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 @@ -59,6 +59,7 @@ httpd_acceptor, httpd_acceptor_sup, httpd_cgi, + httpd_connection_sup, httpd_conf, httpd_esi, httpd_example, diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile index 096d029865..609396273d 100644 --- a/lib/inets/test/Makefile +++ b/lib/inets/test/Makefile @@ -203,7 +203,7 @@ INETS_FILES = inets.config $(INETS_SPECS) # inets_tftp_suite INETS_DATADIRS = inets_SUITE_data inets_sup_SUITE_data -HTTPD_DATADIRS = httpd_test_data httpd_SUITE_data httpd_basic_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 diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index 021ea89e14..2f382d300c 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -31,6 +31,9 @@ %% Note: This directive should only be used in test suites. -compile(export_all). +-record(httpd_user, {user_name, password, user_data}). +-record(httpd_group, {group_name, userlist}). + %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- @@ -47,7 +50,8 @@ groups() -> [ {http, [], all_groups()}, %{https, [], all_groups()}, - {http_1_1, [], [host, chunked, expect, cgi] ++ http_head() ++ http_get()}, + {http_1_1, [], [host, chunked, expect, cgi, max_clients + ] ++ http_head() ++ http_get()}, {http_1_0, [], [host, cgi] ++ http_head() ++ http_get()}, {http_0_9, [], http_head() ++ http_get()} ]. @@ -61,7 +65,9 @@ all_groups ()-> http_head() -> [head]. http_get() -> - [alias, get, basic_auth, esi, ssi]. + [alias, get, + basic_auth, + esi, ssi]. init_per_suite(Config) -> PrivDir = ?config(priv_dir, Config), @@ -99,14 +105,21 @@ init_per_group(http_0_9, Config) -> [{http_version, "HTTP/0.9"} | Config]; init_per_group(_, Config) -> Config. -end_per_group(_, _Config) -> +end_per_group(http, _Config) -> + inets:stop(); +end_per_group(https, _Config) -> + ssl:stop(), + inets:stop(); +end_per_group(_, _) -> ok. + init_httpd(Group, Config0) -> - Config = proplists:delete(port, Config0), - Port = server_start(Group, server_config(Group, Config)), - [{port, Port} | Config]. + Config1 = proplists:delete(port, Config0), + Config = proplists:delete(server_pid, Config1), + {Pid, Port} = server_start(Group, server_config(Group, Config)), + [{server_pid, Pid}, {port, Port} | Config]. %%-------------------------------------------------------------------- -init_per_testcase(host, Config) -> +init_per_testcase(host = Case, Config) -> Prop = ?config(tc_group_properties, Config), Name = proplists:get_value(name, Prop), Cb = case Name of @@ -115,11 +128,47 @@ init_per_testcase(host, Config) -> http_1_1 -> httpd_1_1 end, - [{version_cb, Cb} | proplists:delete(version_cb, Config)]; + common_init_per_test_case(Case, [{version_cb, Cb} | proplists:delete(version_cb, Config)]); -init_per_testcase(_Case, Config) -> - Config. +%% init_per_testcase(basic_auth = Case, Config) -> +%% start_mnesia(?config(node, Config)), +%% common_init_per_test_case(Case, Config); + +init_per_testcase(max_clients, Config) -> + Pid = ?config(server_pid, Config), + Prop = httpd:info(Pid), + Port = proplists:get_value(port, Prop), + TempProp = [{port, Port} | proplists:delete(port, server_config(http, Config))], + NewProp = [{max_clients, 1} | TempProp], + httpd:reload_config(NewProp, non_disturbing), + Config; +init_per_testcase(_Case, Config) -> + common_init_per_test_case(_Case, Config). + +%%% Should be run by all test cases except max_clients, to make +%%% sure failiure of max_clients does not affect other test cases +common_init_per_test_case(_Case, Config) -> + Pid = ?config(server_pid, Config), + Prop = httpd:info(Pid), + case proplists:get_value(max_clients, Prop, 150) of + 150 -> + Config; + _ -> + end_per_testcase(max_clients, Config) + end. + +end_per_testcase(max_clients, Config) -> + Pid = ?config(server_pid, Config), + Prop = httpd:info(Pid), + Port = proplists:get_value(port, Prop), + TempProp = [{port, Port} | proplists:delete(port, server_config(http, Config))], + NewProp = proplists:delete(max_clients, TempProp), + httpd:reload_config(NewProp, non_disturbing), + Config; + +%% end_per_testcase(basic_auth, Config) -> +%% cleanup_mnesia(); end_per_testcase(_Case, _Config) -> ok. @@ -221,6 +270,35 @@ expect(Config) when is_list(Config) -> httpd_1_1:expect(?config(type, Config), ?config(port, Config), ?config(host, Config), ?config(node, Config)). +max_clients() -> + [{doc, "Test max clients limit"}]. + +max_clients(Config) when is_list(Config) -> + Version = ?config(http_version, Config), + Host = ?config(host, Config), + Pid = ?config(server_pid, Config), + ct:pal("Configurartion: ~p~n", [httpd:info(Pid)]), + spawn(fun() -> httpd_test_lib:verify_request(?config(type, Config), Host, + ?config(port, Config), ?config(node, Config), + http_request("GET /eval?httpd_example:delay(1000) ", + Version, Host), + [{statuscode, 200}, + {version, Version}]) + end), + ok = httpd_test_lib:verify_request(?config(type, Config), Host, + ?config(port, Config), ?config(node, Config), + http_request("GET /index.html ", Version, Host), + [{statuscode, 503}, + {version, Version}]), + receive + after 1000 -> + ok = httpd_test_lib:verify_request(?config(type, Config), Host, + ?config(port, Config), ?config(node, Config), + http_request("GET /index.html ", Version, Host), + [{statuscode, 200}, + {version, Version}]) + end. + esi() -> [{doc, "Test mod_esi"}]. @@ -560,7 +638,7 @@ server_start(_, HttpdConfig) -> {ok, Pid} = inets:start(httpd, HttpdConfig), Serv = inets:services_info(), {value, {_, _, Info}} = lists:keysearch(Pid, 2, Serv), - proplists:get_value(port, Info). + {Pid, proplists:get_value(port, Info)}. server_config(http, Config) -> @@ -670,12 +748,48 @@ auth_status(AuthRequest, Config, Expected) -> Expected ++ [{version, Version}]). basic_auth_requiered(Config) -> - ok = http_status("GET /secret/dummy.html ", Config, [{statuscode, 401}, - {header, "WWW-Authenticate"}]), ok = http_status("GET /open/ ", Config, [{statuscode, 401}, {header, "WWW-Authenticate"}]), ok = http_status("GET /secret/ ", Config, [{statuscode, 401}, {header, "WWW-Authenticate"}]), - ok = http_status("GET /secret/top_secret ", Config, [{statuscode, 401}, + ok = http_status("GET /secret/top_secret/ ", Config, [{statuscode, 401}, {header, "WWW-Authenticate"}]). +start_mnesia(Node) -> + case rpc:call(Node, ?MODULE, cleanup_mnesia, []) of + ok -> + ok; + Other -> + ct:fail({failed_to_cleanup_mnesia, Other}) + end, + case rpc:call(Node, ?MODULE, setup_mnesia, []) of + {atomic, ok} -> + ok; + Other2 -> + ct:fail({failed_to_setup_mnesia, Other2}) + end, + ok. + +setup_mnesia() -> + setup_mnesia([node()]). + +setup_mnesia(Nodes) -> + ok = mnesia:create_schema(Nodes), + ok = mnesia:start(), + {atomic, ok} = mnesia:create_table(httpd_user, + [{attributes, + record_info(fields, httpd_user)}, + {disc_copies,Nodes}, {type, set}]), + {atomic, ok} = mnesia:create_table(httpd_group, + [{attributes, + record_info(fields, + httpd_group)}, + {disc_copies,Nodes}, {type,bag}]). + +cleanup_mnesia() -> + mnesia:start(), + mnesia:delete_table(httpd_user), + mnesia:delete_table(httpd_group), + stopped = mnesia:stop(), + mnesia:delete_schema([node()]), + ok. diff --git a/lib/inets/test/httpd_block.erl b/lib/inets/test/httpd_block.erl index 2f56096f75..5c2cb3c51c 100644 --- a/lib/inets/test/httpd_block.erl +++ b/lib/inets/test/httpd_block.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2013. All Rights Reserved. +%% Copyright Ericsson AB 2005-2014. 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 @@ -19,8 +19,7 @@ %% -module(httpd_block). --include("test_server.hrl"). --include("test_server_line.hrl"). +-include_lib("common_test/include/ct.hrl"). %% General testcases bodies called from httpd_SUITE -export([block_disturbing_idle/4, block_non_disturbing_idle/4, @@ -88,7 +87,7 @@ block_503(Type, Port, Host, Node) -> block_disturbing_active(Type, Port, Host, Node) -> process_flag(trap_exit, true), Pid = long_poll(Type, Host, Port, Node, 200, 60000), - test_server:sleep(15000), + ct:sleep(15000), block_server(Node, Host, Port), await_suite_failed_process_exit(Pid, "poller", 60000, connection_closed), @@ -100,7 +99,7 @@ block_disturbing_active(Type, Port, Host, Node) -> block_non_disturbing_active(Type, Port, Host, Node) -> process_flag(trap_exit, true), Poller = long_poll(Type, Host, Port, Node, 200, 60000), - test_server:sleep(15000), + ct:sleep(15000), ok = block_nd_server(Node, Host, Port), await_normal_process_exit(Poller, "poller", 60000), blocked = get_admin_state(Node, Host, Port), @@ -111,7 +110,7 @@ block_non_disturbing_active(Type, Port, Host, Node) -> block_disturbing_active_timeout_not_released(Type, Port, Host, Node) -> process_flag(trap_exit, true), Poller = long_poll(Type, Host, Port, Node, 200, 60000), - test_server:sleep(15000), + ct:sleep(15000), Blocker = blocker(Node, Host, Port, 50000), await_normal_process_exit(Blocker, "blocker", 50000), await_normal_process_exit(Poller, "poller", 30000), @@ -123,7 +122,7 @@ block_disturbing_active_timeout_not_released(Type, Port, Host, Node) -> block_disturbing_active_timeout_released(Type, Port, Host, Node) -> process_flag(trap_exit, true), Poller = long_poll(Type, Host, Port, Node, 200, 40000), - test_server:sleep(5000), + ct:sleep(5000), Blocker = blocker(Node, Host, Port, 10000), await_normal_process_exit(Blocker, "blocker", 15000), await_suite_failed_process_exit(Poller, "poller", 40000, @@ -146,7 +145,7 @@ block_non_disturbing_active_timeout_not_released(Type, Port, Host, Node) -> block_non_disturbing_active_timeout_released(Type, Port, Host, Node) -> process_flag(trap_exit, true), Poller = long_poll(Type, Host, Port, Node, 200, 45000), - test_server:sleep(5000), + ct:sleep(5000), Blocker = blocker_nd(Node, Host, Port ,10000, {error,timeout}), await_normal_process_exit(Blocker, "blocker", 15000), await_normal_process_exit(Poller, "poller", 50000), @@ -157,9 +156,9 @@ block_non_disturbing_active_timeout_released(Type, Port, Host, Node) -> disturbing_blocker_dies(Type, Port, Host, Node) -> process_flag(trap_exit, true), Poller = long_poll(Type, Host, Port, Node, 200, 60000), - test_server:sleep(5000), + ct:sleep(5000), Blocker = blocker(Node, Host, Port, 10000), - test_server:sleep(5000), + ct:sleep(5000), exit(Blocker,simulate_blocker_crash), await_normal_process_exit(Poller, "poller", 60000), unblocked = get_admin_state(Node, Host, Port), @@ -170,9 +169,9 @@ disturbing_blocker_dies(Type, Port, Host, Node) -> non_disturbing_blocker_dies(Type, Port, Host, Node) -> process_flag(trap_exit, true), Poller = long_poll(Type, Host, Port, Node, 200, 60000), - test_server:sleep(5000), + ct:sleep(5000), Blocker = blocker_nd(Node, Host, Port, 10000, ok), - test_server:sleep(5000), + ct:sleep(5000), exit(Blocker, simulate_blocker_crash), await_normal_process_exit(Poller, "poller", 60000), unblocked = get_admin_state(Node, Host, Port), @@ -326,15 +325,15 @@ await_normal_process_exit(Pid, Name, Timeout) -> io_lib:format("expected normal exit, " "unexpected exit of ~s process: ~p", [Name, Reason])), - test_server:fail(Err) + ct:fail(Err) after Timeout -> - test_server:fail("timeout while waiting for " ++ Name) + ct:fail("timeout while waiting for " ++ Name) end. await_suite_failed_process_exit(Pid, Name, Timeout, Why) -> receive - {'EXIT', Pid, {suite_failed, Why}} -> + {'EXIT', Pid, {test_failed, Why}} -> ok; {'EXIT', Pid, Reason} -> Err = @@ -342,9 +341,9 @@ await_suite_failed_process_exit(Pid, Name, Timeout, Why) -> io_lib:format("expected connection_closed, " "unexpected exit of ~s process: ~p", [Name, Reason])), - test_server:fail(Err) + ct:fail(Err) after Timeout -> - test_server:fail("timeout while waiting for " ++ Name) + ct:fail("timeout while waiting for " ++ Name) end. long_poll(Type, Host, Port, Node, StatusCode, Timeout) -> @@ -362,7 +361,7 @@ do_long_poll(Type, Host, Port, Node, StatusCode, Timeout) -> ok -> exit(normal); Reason -> - test_server:fail(Reason) + exit({test_failed, Reason}) end. diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl index 11d41726e5..970b0cd475 100644 --- a/lib/inets/test/httpd_test_lib.erl +++ b/lib/inets/test/httpd_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2013. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. 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 @@ -187,12 +187,12 @@ request(#state{mfa = {Module, Function, Args}, {tcp_closed, Socket} -> io:format("~p ~w[~w]request -> received (tcp) closed" "~n", [self(), ?MODULE, ?LINE]), - test_server:fail(connection_closed); + exit({test_failed, connection_closed}); {tcp_error, Socket, Reason} -> io:format("~p ~w[~w]request -> received (tcp) error" "~n Reason: ~p" "~n", [self(), ?MODULE, ?LINE, Reason]), - test_server:fail({tcp_error, Reason}); + ct:fail({tcp_error, Reason}); {ssl, Socket, Data} -> print(ssl, Data, State), case Module:Function([Data | Args]) of @@ -207,13 +207,13 @@ request(#state{mfa = {Module, Function, Args}, print(ssl, "closed", State), State#state{body = hd(Args)}; {ssl_closed, Socket} -> - test_server:fail(connection_closed); + exit({test_failed, connection_closed}); {ssl_error, Socket, Reason} -> - test_server:fail({ssl_error, Reason}) + ct:fail({ssl_error, Reason}) after TimeOut -> io:format("~p ~w[~w]request -> timeout" "~n", [self(), ?MODULE, ?LINE]), - test_server:fail(connection_timed_out) + ct:fail(connection_timed_out) end. handle_http_msg({Version, StatusCode, ReasonPharse, Headers, Body}, @@ -277,7 +277,7 @@ handle_http_body(Body, State = #state{headers = Headers, request(State#state{mfa = MFA}, 5000) end; false -> - test_server:fail(body_too_big) + ct:fail(body_too_big) end end. @@ -405,7 +405,7 @@ check_body(_, _, _, _,_) -> ok. print(Proto, Data, #state{print = true}) -> - test_server:format("Received ~p: ~p~n", [Proto, Data]); + ct:pal("Received ~p: ~p~n", [Proto, Data]); print(_, _, #state{print = false}) -> ok. diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl index 65f0f0e09a..12b85a816f 100644 --- a/lib/inets/test/inets_sup_SUITE.erl +++ b/lib/inets/test/inets_sup_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2014. 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 @@ -21,7 +21,7 @@ -module(inets_sup_SUITE). -include_lib("common_test/include/ct.hrl"). --include("test_server_line.hrl"). + %% Note: This directive should only be used in test suites. -compile(export_all). @@ -297,13 +297,14 @@ httpd_subtree(Config) when is_list(Config) -> %% Check that we have the expected httpd instance children io:format("httpd_subtree -> verify httpd instance children " "(acceptor, misc and manager)~n", []), + {ok, _} = verify_child(Instance, httpd_connection_sup, supervisor), {ok, _} = verify_child(Instance, httpd_acceptor_sup, supervisor), {ok, _} = verify_child(Instance, httpd_misc_sup, supervisor), {ok, _} = verify_child(Instance, httpd_manager, worker), %% Check that the httpd instance acc supervisor has children io:format("httpd_subtree -> verify acc~n", []), - InstanceAcc = httpd_util:make_name("httpd_acc_sup", Addr, Port), + InstanceAcc = httpd_util:make_name("httpd_acceptor_sup", Addr, Port), case supervisor:which_children(InstanceAcc) of [_ | _] -> ok; diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl index 4755776ee8..b60a2fb30b 100644 --- a/lib/inets/test/inets_test_lib.erl +++ b/lib/inets/test/inets_test_lib.erl @@ -547,14 +547,14 @@ tsp(F) -> tsp(F, []). tsp(F, A) -> Timestamp = formated_timestamp(), - test_server:format("*** ~s ~p ~p " ++ F ++ "~n", + ct:pal("*** ~s ~p ~p " ++ F ++ "~n", [Timestamp, node(), self() | A]). tsf(Reason) -> - test_server:fail(Reason). + ct:fail(Reason). tss(Time) -> - test_server:sleep(Time). + ct:sleep(Time). timestamp() -> http_util:timestamp(). -- cgit v1.2.3 From ad4ff4a083c84c99c054f7948368e7d15f64b701 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Mon, 13 Jan 2014 12:29:08 +0100 Subject: inets: Remove log message as it causes more harm than use at the moment Conflicts: lib/inets/src/http_lib/http_request.erl --- lib/inets/src/http_lib/http_request.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/inets/src/http_lib/http_request.erl b/lib/inets/src/http_lib/http_request.erl index b7f2379438..5f787db0ce 100644 --- a/lib/inets/src/http_lib/http_request.erl +++ b/lib/inets/src/http_lib/http_request.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2014. 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 @@ -40,9 +40,6 @@ headers([Header | Tail], Headers) -> headers(Tail, headers(http_util:to_lower(string:strip(Key)), string:strip(Value), Headers)); {_, []} -> - Report = io_lib:format("Ignored invalid HTTP-header: ~p~n", - [Header]), - error_logger:error_report(Report), headers(Tail, Headers) end. -- cgit v1.2.3 From 2e308dab294943f3f752b315ad5d5ce52ae4bf52 Mon Sep 17 00:00:00 2001 From: Artur Wilniewczyc Date: Thu, 11 Jul 2013 12:47:46 +0200 Subject: Add missing brackets to report formatting on ftp_progress process exit When progress reporting process exits, ftp client crashes because of missing brackets in io_lib:format argument. --- lib/inets/src/ftp/ftp.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index 6197838d92..6b891522fe 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -1463,7 +1463,7 @@ handle_info({'DOWN', _Ref, _Type, Process, Reason}, State) -> handle_info({'EXIT', Pid, Reason}, #state{progress = Pid} = State) -> Report = io_lib:format("Progress reporting stopped for reason ~p~n", - Reason), + [Reason]), error_logger:info_report(Report), {noreply, State#state{progress = ignore}}; -- cgit v1.2.3 From ef97fd2671d966c5b5e005d4a223d5197e5bbf37 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Fri, 24 Jan 2014 12:52:37 +0100 Subject: inets: Rewrite of test case to avoid timing issues in test code Conflicts: lib/inets/test/httpd_SUITE.erl --- lib/inets/test/httpd_SUITE.erl | 148 +++++++++++++++++++++-------------------- 1 file changed, 75 insertions(+), 73 deletions(-) diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index 2f382d300c..74b85acd3f 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -1,8 +1,8 @@ %% %% %CopyrightBegin% -%% +%% %% Copyright Ericsson AB 2013-2014. 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 @@ -42,16 +42,17 @@ suite() -> all() -> [ - {group, http} - %{group, https} + {group, http}, + {group, http_limit} + %%{group, https} ]. groups() -> [ {http, [], all_groups()}, - %{https, [], all_groups()}, - {http_1_1, [], [host, chunked, expect, cgi, max_clients - ] ++ http_head() ++ http_get()}, + %%{https, [], all_groups()}, + {http_limit, [], [max_clients_1_1, max_clients_1_0, max_clients_0_9]}, + {http_1_1, [], [host, chunked, expect, cgi] ++ http_head() ++ http_get()}, {http_1_0, [], [host, cgi] ++ http_head() ++ http_get()}, {http_0_9, [], http_head() ++ http_get()} ]. @@ -95,7 +96,7 @@ init_per_group(https = Group, Config0) -> {skip, "Could not start https apps"} end; -init_per_group(http = Group, Config0) -> +init_per_group(Group, Config0) when Group == http; Group == http_limit -> init_httpd(Group, [{type, ip_comm} | Config0]); init_per_group(http_1_1, Config) -> [{http_version, "HTTP/1.1"} | Config]; @@ -106,10 +107,9 @@ init_per_group(http_0_9, Config) -> init_per_group(_, Config) -> Config. end_per_group(http, _Config) -> - inets:stop(); + ok; end_per_group(https, _Config) -> - ssl:stop(), - inets:stop(); + ssl:stop(); end_per_group(_, _) -> ok. @@ -119,7 +119,7 @@ init_httpd(Group, Config0) -> {Pid, Port} = server_start(Group, server_config(Group, Config)), [{server_pid, Pid}, {port, Port} | Config]. %%-------------------------------------------------------------------- -init_per_testcase(host = Case, Config) -> +init_per_testcase(host, Config) -> Prop = ?config(tc_group_properties, Config), Name = proplists:get_value(name, Prop), Cb = case Name of @@ -128,45 +128,14 @@ init_per_testcase(host = Case, Config) -> http_1_1 -> httpd_1_1 end, - common_init_per_test_case(Case, [{version_cb, Cb} | proplists:delete(version_cb, Config)]); + [{version_cb, Cb} | proplists:delete(version_cb, Config)]; +init_per_testcase(_, Config) -> + Config. %% init_per_testcase(basic_auth = Case, Config) -> %% start_mnesia(?config(node, Config)), %% common_init_per_test_case(Case, Config); -init_per_testcase(max_clients, Config) -> - Pid = ?config(server_pid, Config), - Prop = httpd:info(Pid), - Port = proplists:get_value(port, Prop), - TempProp = [{port, Port} | proplists:delete(port, server_config(http, Config))], - NewProp = [{max_clients, 1} | TempProp], - httpd:reload_config(NewProp, non_disturbing), - Config; - -init_per_testcase(_Case, Config) -> - common_init_per_test_case(_Case, Config). - -%%% Should be run by all test cases except max_clients, to make -%%% sure failiure of max_clients does not affect other test cases -common_init_per_test_case(_Case, Config) -> - Pid = ?config(server_pid, Config), - Prop = httpd:info(Pid), - case proplists:get_value(max_clients, Prop, 150) of - 150 -> - Config; - _ -> - end_per_testcase(max_clients, Config) - end. - -end_per_testcase(max_clients, Config) -> - Pid = ?config(server_pid, Config), - Prop = httpd:info(Pid), - Port = proplists:get_value(port, Prop), - TempProp = [{port, Port} | proplists:delete(port, server_config(http, Config))], - NewProp = proplists:delete(max_clients, TempProp), - httpd:reload_config(NewProp, non_disturbing), - Config; - %% end_per_testcase(basic_auth, Config) -> %% cleanup_mnesia(); end_per_testcase(_Case, _Config) -> @@ -270,34 +239,24 @@ expect(Config) when is_list(Config) -> httpd_1_1:expect(?config(type, Config), ?config(port, Config), ?config(host, Config), ?config(node, Config)). -max_clients() -> +max_clients_1_1() -> [{doc, "Test max clients limit"}]. -max_clients(Config) when is_list(Config) -> - Version = ?config(http_version, Config), - Host = ?config(host, Config), - Pid = ?config(server_pid, Config), - ct:pal("Configurartion: ~p~n", [httpd:info(Pid)]), - spawn(fun() -> httpd_test_lib:verify_request(?config(type, Config), Host, - ?config(port, Config), ?config(node, Config), - http_request("GET /eval?httpd_example:delay(1000) ", - Version, Host), - [{statuscode, 200}, - {version, Version}]) - end), - ok = httpd_test_lib:verify_request(?config(type, Config), Host, - ?config(port, Config), ?config(node, Config), - http_request("GET /index.html ", Version, Host), - [{statuscode, 503}, - {version, Version}]), - receive - after 1000 -> - ok = httpd_test_lib:verify_request(?config(type, Config), Host, - ?config(port, Config), ?config(node, Config), - http_request("GET /index.html ", Version, Host), - [{statuscode, 200}, - {version, Version}]) - end. + +max_clients_1_1(Config) when is_list(Config) -> + do_max_clients([{http_version, "HTTP/1.1"} | Config]). + +max_clients_1_0() -> + [{doc, "Test max clients limit"}]. + +max_clients_1_0(Config) when is_list(Config) -> + do_max_clients([{http_version, "HTTP/1.0"} | Config]). + +max_clients_0_9() -> + [{doc, "Test max clients limit"}]. + +max_clients_0_9(Config) when is_list(Config) -> + do_max_clients([{http_version, "HTTP/0.9"} | Config]). esi() -> [{doc, "Test mod_esi"}]. @@ -591,7 +550,25 @@ alias(Config) when is_list(Config) -> %% Internal functions ----------------------------------- %%-------------------------------------------------------------------- -setup_server_dirs(ServerRoot, DocRoot, DataDir) -> +do_max_clients(Config) -> + Version = ?config(http_version, Config), + Host = ?config(host, Config), + start_blocker(Config), + ok = httpd_test_lib:verify_request(?config(type, Config), Host, + ?config(port, Config), ?config(node, Config), + http_request("GET /index.html ", Version, Host), + [{statuscode, 503}, + {version, Version}]), + receive + after 2000 -> + ok = httpd_test_lib:verify_request(?config(type, Config), Host, + ?config(port, Config), ?config(node, Config), + http_request("GET /index.html ", Version, Host), + [{statuscode, 200}, + {version, Version}]) + end. + +setup_server_dirs(ServerRoot, DocRoot, DataDir) -> CgiDir = filename:join(ServerRoot, "cgi-bin"), AuthDir = filename:join(ServerRoot, "auth"), PicsDir = filename:join(ServerRoot, "icons"), @@ -660,6 +637,10 @@ server_config(http, Config) -> {erl_script_alias, {"/cgi-bin/erl", [httpd_example, io]}}, {eval_script_alias, {"/eval", [httpd_example, io]}} ] ++ auth_conf(ServerRoot); + +server_config(http_limit, Config) -> + [{max_clients, 1}] ++ server_config(http, Config); + server_config(_, _) -> []. @@ -793,3 +774,24 @@ cleanup_mnesia() -> stopped = mnesia:stop(), mnesia:delete_schema([node()]), ok. + +start_blocker(Config) -> + spawn(httpd_SUITE, init_blocker, [self(), Config]), + receive + blocker_start -> + ok + end. + +init_blocker(From, Config) -> + From ! blocker_start, + block(Config). + +block(Config) -> + Version = ?config(http_version, Config), + Host = ?config(host, Config), + httpd_test_lib:verify_request(?config(type, Config), Host, + ?config(port, Config), ?config(node, Config), + http_request("GET /eval?httpd_example:delay(1000) ", + Version, Host), + [{statuscode, 200}, + {version, Version}]). -- cgit v1.2.3 From 63b793948b8561a7d541a19ead728bc4e16f0a2d Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Mon, 3 Feb 2014 16:22:12 +0100 Subject: inets: Make test suites independent of each other --- lib/inets/test/httpd_basic_SUITE.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl index fa5c58c509..f75655db1d 100644 --- a/lib/inets/test/httpd_basic_SUITE.erl +++ b/lib/inets/test/httpd_basic_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2014. 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 @@ -65,7 +65,8 @@ end_per_group(_GroupName, Config) -> init_per_suite(Config) -> tsp("init_per_suite -> entry with" "~n Config: ~p", [Config]), - ok = inets:start(), + inets_test_lib:stop_apps([inets]), + inets_test_lib:start_apps([inets]), PrivDir = ?config(priv_dir, Config), DataDir = ?config(data_dir, Config), -- cgit v1.2.3 From d8795f7c4b55a9bd2a05719f0fa6297b733ee5ce Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Wed, 5 Feb 2014 12:24:59 +0100 Subject: inets: Prepare for release --- lib/inets/vsn.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index 4aa5de8f1a..cccfb7a44f 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2012. All Rights Reserved. +# Copyright Ericsson AB 2001-2014. 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 @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.9.2.2 +INETS_VSN = 5.9.8 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" -- cgit v1.2.3 From 6a6653e1fa03d105fa66da54f7291dd592e6f203 Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Thu, 24 Jan 2013 12:28:47 +0100 Subject: Changed to correct errorcode in testcase --- lib/inets/test/ftp_format_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/inets/test/ftp_format_SUITE.erl b/lib/inets/test/ftp_format_SUITE.erl index cbc1b04bbb..02b9000dd9 100644 --- a/lib/inets/test/ftp_format_SUITE.erl +++ b/lib/inets/test/ftp_format_SUITE.erl @@ -273,7 +273,7 @@ ftp_other_status_codes(Config) when is_list(Config) -> %% 4XX {trans_neg_compl, _ } = ftp_response:interpret("421 Foobar\r\n"), {trans_neg_compl, _ } = ftp_response:interpret("426 Foobar\r\n"), - {trans_neg_compl, _ } = ftp_response:interpret("450 Foobar\r\n"), + {enofile, _ } = ftp_response:interpret("450 Foobar\r\n"), {trans_neg_compl, _ } = ftp_response:interpret("451 Foobar\r\n"), {etnospc, _ } = ftp_response:interpret("452 Foobar\r\n"), -- cgit v1.2.3 From 5a137003f1eb045a39c18438ecc1b22081747487 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Wed, 5 Feb 2014 13:56:31 +0100 Subject: Prepare release --- lib/inets/doc/src/notes.xml | 18 +++++++++++++++++- lib/ssl/vsn.mk | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index f99bb14c5d..cff2ca427d 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -32,7 +32,23 @@ notes.xml
-
Inets 5.9.2.2 +
Inets 5.9.8 + +
Improvements and New Features + + +

+ Mend max_clients check that was broken and avoid too + extensive logging that could cause memory problems.

+

+ Own Id: OTP-11557 Aux Id: seq12478

+
+
+
+ +
+ +
Inets 5.9.2.2
Improvements and New Features diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 7b9b6d03bc..f7abd84345 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 5.1.2.2 +SSL_VSN = 5.2.1.2 -- cgit v1.2.3