diff options
Diffstat (limited to 'lib/inets/test')
310 files changed, 20205 insertions, 0 deletions
diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile new file mode 100644 index 0000000000..d13ffea544 --- /dev/null +++ b/lib/inets/test/Makefile @@ -0,0 +1,317 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1997-2009. 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% +# +# +# For an outline of how this all_SUITE_data stuff works, see the +# make file ../../ssl/test/Makefile. +# +include $(ERL_TOP)/make/target.mk + +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../vsn.mk +VSN = $(INETS_VSN) + + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- +INCLUDES = -I. \ + -I$(ERL_TOP)/lib/test_server/include/ \ + -I$(ERL_TOP)/lib/inets/src/inets_app \ + -I$(ERL_TOP)/lib/inets/src/http_lib \ + -I$(ERL_TOP)/lib/inets/src/http_client \ + -I$(ERL_TOP)/lib/inets/src/ftp + +CP = cp + +ifeq ($(TESTROOT_DIR),) +TESTROOT_DIR = /ldisk/tests/$(USER)/inets +endif + +ifeq ($(INETS_DATA_DIR),) +INETS_DATA_DIR = $(TESTROOT_DIR)/data_dir +endif + +ifeq ($(INETS_PRIV_DIR),) +INETS_PRIV_DIR = $(TESTROOT_DIR)/priv_dir +endif + +INETS_FLAGS = -Dinets_data_dir='"$(INETS_DATA_DIR)"' \ + -Dinets_priv_dir='"$(INETS_PRIV_DIR)"' + + +### +### test suite debug flags +### +ifeq ($(FTP_DEBUG_CLIENT),) + FTP_DEBUG_CLIENT = y +endif + +ifeq ($(FTP_DEBUG_CLIENT),) + FTP_FLAGS += -Dftp_debug_client +endif + +ifeq ($(FTP_TRACE_CLIENT),) + FTP_DEBUG_CLIENT = y +endif + +ifeq ($(FTP_TRACE_CLIENT),y) + FTP_FLAGS += -Dftp_trace_client +endif + +ifneq ($(FTP_DEBUG),) + FTP_DEBUG = s +endif + +ifeq ($(FTP_DEBUG),l) + FTP_FLAGS += -Dftp_log +endif + +ifeq ($(FTP_DEBUG),d) + FTP_FLAGS += -Dftp_debug -Dftp_log +endif + +ifeq ($(INETS_DEBUG),) + INETS_DEBUG = d +endif + +ifeq ($(INETS_DEBUG),l) + INETS_FLAGS += -Dinets_log +endif + +ifeq ($(INETS_DEBUG),d) + INETS_FLAGS += -Dinets_debug -Dinets_log +endif + + +### +### HTTPD verbosity flags +### + +ifneq ($(MANV),) + INETS_FLAGS += -Dhttpd_manager_verbosity=$(MANV) +else + INETS_FLAGS += -Dhttpd_manager_verbosity=trace +endif + +ifneq ($(REQV),) + INETS_FLAGS += -Dhttpd_request_handler_verbosity=$(REQV) +else + INETS_FLAGS += -Dhttpd_request_handler_verbosity=trace +endif + +ifneq ($(ACCV),) + INETS_FLAGS += -Dhttpd_acceptor_verbosity=$(ACCV) +else + INETS_FLAGS += -Dhttpd_acceptor_verbosity=trace +endif + +ifneq ($(AUTHV),) + INETS_FLAGS += -Dhttpd_auth_verbosity=$(AUTHV) +else + INETS_FLAGS += -Dhttpd_auth_verbosity=log +endif + +ifneq ($(SECV),) + INETS_FLAGS += -Dhttpd_security_verbosity=$(SECV) +else + INETS_FLAGS += -Dhttpd_security_verbosity=log +endif + +INETS_ROOT = ../../inets + +MODULES = \ + inets_test_lib \ + 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 \ + httpd_SUITE \ + httpd_basic_SUITE \ + httpd_mod \ + httpd_block \ + httpd_load \ + httpd_time_test \ + httpd_1_1 \ + httpd_test_lib \ + inets_sup_SUITE \ + inets_SUITE \ + inets_app_test \ + inets_appup_test \ + tftp_test_lib \ + tftp_SUITE + + +EBIN = . + +HRL_FILES = inets_test_lib.hrl \ + inets_internal.hrl \ + ftp_internal.hrl \ + httpc_internal.hrl \ + http_internal.hrl \ + tftp_test_lib.hrl + +ERL_FILES = $(MODULES:%=%.erl) + +TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR)) + +INETS_FILES = Makefile.inets rules.mk suite_targets.mk \ + inets.config inets.spec inets.spec.vxworks + + +SOURCE = $(ERL_FILES) $(HRL_FILES) + +INETS_SPECS = inets.spec inets.spec.vxworks + +INETS_DATADIR = inets_SUITE_data inets_sup_SUITE_data +HTTPD_DATADIR = httpd_test_data httpd_SUITE_data +HTTPC_DATADIR = httpc_SUITE_data +FTP_DATADIR = ftp_SUITE_data + +DATADIRS = $(INETS_DATADIR) $(HTTPD_DATADIR) $(HTTPC_DATADIR) $(FTP_DATADIR) + +EMAKEFILE = Emakefile +MAKE_EMAKE = $(wildcard $(ERL_TOP)/make/make_emakefile) + +ifeq ($(MAKE_EMAKE),) +BUILDTARGET = $(TARGET_FILES) +RELTEST_FILES = $(INETS_SPECS) $(SOURCE) +else +BUILDTARGET = emakebuild +RELTEST_FILES = $(EMAKEFILE) $(INETS_SPECS) $(SOURCE) +endif + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN) + +RELTESTSYSDIR = $(RELEASE_PATH)/inets_test +RELTESTSYSALLDATADIR = $(RELTESTSYSDIR)/all_SUITE_data +RELTESTSYSBINDIR = $(RELTESTSYSALLDATADIR)/bin + +MAKEFILE_SRC = Makefile.src +INETS_SSL_LIB_DIR = ../../ssl/usr/ssleay/ + + +# ---------------------------------------------------- +# FLAGS +# The path to the test_server ebin dir is needed when +# running the target "targets". +# ---------------------------------------------------- +ERL_COMPILE_FLAGS += -pa ../../../internal_tools/test_server/ebin \ + $(INCLUDES) $(FTP_FLAGS) $(INETS_FLAGS) + +# ---------------------------------------------------- +# Targets +# erl -sname kalle -pa ../ebin +# If you intend to run the test suite locally (private), then +# there is some requirements: +# 1) INETS_PRIV_DIR must be created +# 2) INETS_SSL_LIB_DIR must be created with the same content as +# content comparable to all_SUITE_data/bin (see test-server). +# ---------------------------------------------------- + +tests debug opt: $(BUILDTARGET) + +targets: $(TARGET_FILES) + +.PHONY: emakebuild + +emakebuild: $(EMAKEFILE) + +$(EMAKEFILE): + $(MAKE_EMAKE) $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' | grep -v Warning > $(EMAKEFILE) + $(MAKE_EMAKE) $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) | grep -v Warning >> $(EMAKEFILE) + +clean: + rm -f $(EMAKEFILE) + rm -f $(TARGET_FILES) + rm -f core *~ + +docs: + + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + $(INSTALL_DIR) $(RELSYSDIR)/test + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/test + $(INSTALL_DATA) $(INETS_FILES) $(RELSYSDIR)/test + $(INSTALL_DIR) $(RELSYSDIR)/test/$(HTTPD_DATADIR) + tar chf - -C $(HTTPD_DATADIR) . | (cd $(RELSYSDIR)/test/$(HTTPD_DATADIR); tar xf -) + (cd $(RELSYSDIR)/test; mv Makefile.inets Makefile) + +release_tests_spec: opt + $(INSTALL_DIR) $(RELTESTSYSDIR) + $(INSTALL_DATA) $(RELTEST_FILES) $(RELTESTSYSDIR) + chmod -f -R u+w $(RELTESTSYSDIR) + tar chf - $(DATADIRS) | (cd $(RELTESTSYSDIR); tar xf -) + $(INSTALL_DIR) $(RELTESTSYSALLDATADIR) + $(INSTALL_DATA) $(MAKEFILE_SRC) $(RELTESTSYSALLDATADIR)/Makefile.src + $(INSTALL_DATA) copy_files.erl $(RELTESTSYSALLDATADIR)/copy_files.erl + $(INSTALL_DIR) $(RELTESTSYSBINDIR) + chmod -f -R +x $(RELTESTSYSBINDIR) + tar cf - -C $(INETS_SSL_LIB_DIR) . | (cd $(RELTESTSYSALLDATADIR); tar xf -) + $(INSTALL_DIR) $(RELTESTSYSALLDATADIR)/win32/lib + tar cf - -C $(INETS_SSL_LIB_DIR)/win32/out32dll . | \ + (cd $(RELTESTSYSALLDATADIR)/win32/lib; tar xf -) + +release_docs_spec: + +info: + @echo "MAKE_EMAKE = $(MAKE_EMAKE)" + @echo "EMAKEFILE = $(EMAKEFILE)" + @echo "BUILDTARGET = $(BUILDTARGET)" + @echo "TARGET_FILES = $(TARGET_FILES)" + @echo "INETS_DATA_DIR = $(INETS_DATA_DIR)" + @echo "INETS_PRIV_DIR = $(INETS_PRIV_DIR)" + @echo "INETS_SSL_LIB_DIR = $(INETS_SSL_LIB_DIR)" + @echo "INETS_ROOT = $(INETS_ROOT)" + @echo "INETS_FLAGS = $(INETS_FLAGS)" + @echo "FTP_FLAGS = $(FTP_FLAGS)" + +tftp: + erlc $(ERL_COMPILE_FLAGS) tftp_test_lib.erl tftp_SUITE.erl && erl -pa ../../inets/ebin -s tftp_SUITE t -s erlang halt + +tftp_work: + echo "tftp_test_lib:t([{tftp_SUITE, all}])." + erlc $(ERL_COMPILE_FLAGS) tftp_test_lib.erl tftp_SUITE.erl && erl -pa ../../inets/ebin diff --git a/lib/inets/test/Makefile.src b/lib/inets/test/Makefile.src new file mode 120000 index 0000000000..f361bfce20 --- /dev/null +++ b/lib/inets/test/Makefile.src @@ -0,0 +1 @@ +../../ssl/test/Makefile.src
\ No newline at end of file diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl new file mode 100644 index 0000000000..e7404f945b --- /dev/null +++ b/lib/inets/test/ftp_SUITE.erl @@ -0,0 +1,143 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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). + +-include("test_server.hrl"). +-include("test_server_line.hrl"). + +%% Test server specific exports +-export([all/1]). +% -export([init_per_testcase/2, end_per_testcase/2]). +-export([init_per_suite/1, end_per_suite/1]). + +%% Test cases must be exported. +-export([solaris8_test/1, + solaris9_test/1, + solaris10_test/1, + linux_x86_test/1, + linux_ppc_test/1, + macosx_x86_test/1, + macosx_ppc_test/1, + openbsd_test/1, + freebsd_test/1, + netbsd_test/1, + windows_xp_test/1, + windows_2003_server_test/1, + ticket_tests/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. +%%-------------------------------------------------------------------- +all(doc) -> + ["Test the ftp client in the inets application."]; +all(suite) -> + [ + solaris8_test, + solaris9_test, + solaris10_test, + linux_x86_test, + linux_ppc_test, + macosx_x86_test, + macosx_ppc_test, + openbsd_test, + freebsd_test, + netbsd_test, + windows_xp_test, + windows_2003_server_test, + ticket_tests + ]. + +solaris8_test(suite) -> + [{ftp_solaris8_sparc_test,all}]. +solaris9_test(suite) -> + [{ftp_solaris9_sparc_test,all}]. +solaris10_test(suite) -> + [{ftp_solaris10_sparc_test,all}, {ftp_solaris10_x86_test,all}]. +linux_x86_test(suite) -> + [{ftp_linux_x86_test,all}]. +linux_ppc_test(suite) -> + [{ftp_linux_ppc_test,all}]. +macosx_x86_test(suite) -> + [{ftp_macosx_x86_test,all}]. +macosx_ppc_test(suite) -> + [{ftp_macosx_ppc_test,all}]. +openbsd_test(suite) -> + [{ftp_openbsd_x86_test,all}]. +freebsd_test(suite) -> + [{ftp_freebsd_x86_test,all}]. +netbsd_test(suite) -> + [{ftp_netbsd_x86_test,all}]. +windows_xp_test(suite) -> + [{ftp_windows_xp_test,all}]. +windows_2003_server_test(suite) -> + [{ftp_windows_2003_server_test,all}]. + +ticket_tests(suite) -> + [{ftp_ticket_test, all}]. + +%%-------------------------------------------------------------------- +%% 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/ftp_SUITE_data/inets_test_hosts b/lib/inets/test/ftp_SUITE_data/inets_test_hosts new file mode 100644 index 0000000000..20623b9edc --- /dev/null +++ b/lib/inets/test/ftp_SUITE_data/inets_test_hosts @@ -0,0 +1,15 @@ +%% Add a host name in the appropriate list +[{solaris8_sparc, ["fingon"]}, + {solaris9_sparc, []}, + {solaris10_sparc, []}, + {solaris10_x86, []}, + {linux_x86, []}, + {linux_ppc, []}, + {macosx_ppc, []}, + {macosx_x86, []}, + {openbsd_x86, []}, + {freebsd_x86, []}, + {netbsd_x86, []}, + {windows_xp, []}, + {windows_2003_server, ["fobi"]}, + {ticket_test,["fingon","finrod"]}]. diff --git a/lib/inets/test/ftp_format_SUITE.erl b/lib/inets/test/ftp_format_SUITE.erl new file mode 100644 index 0000000000..9ca6575b2d --- /dev/null +++ b/lib/inets/test/ftp_format_SUITE.erl @@ -0,0 +1,341 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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_format_SUITE). +-author('[email protected]'). + +-include("test_server.hrl"). +-include("test_server_line.hrl"). +-include("ftp_internal.hrl"). + +%% Test server specific exports +-export([all/1, init_per_testcase/2, end_per_testcase/2]). + +%% Test cases must be exported. +-export([ftp_response/1, ftp_150/1, + ftp_200/1, ftp_220/1, ftp_226/1, ftp_257/1, ftp_331/1, ftp_425/1, + ftp_other_status_codes/1, ftp_multiple_lines/1, + ftp_multipel_ctrl_messages/1, format_error/1]). + +all(doc) -> + ["Test library functions for the ftp client."]; +all(suite) -> + [ftp_response, format_error]. + +init_per_testcase(_, Config) -> + Dog = test_server:timetrap(?t:minutes(1)), + NewConfig = lists:keydelete(watchdog, 1, Config), + [{watchdog, Dog} | NewConfig]. + +end_per_testcase(_, Config) -> + Dog = ?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- +ftp_response(doc) -> + ["Test ftp_response:parse_lines/3 and ftp_response:interpret/1." + " This test case will simulate that the " + "message will be recived a little at the time on a socket and the " + "package may be broken up into smaller parts at arbitrary point."]; +ftp_response(suite) -> + [ftp_150, ftp_200, ftp_220, ftp_226, ftp_257, ftp_331, ftp_425, + ftp_other_status_codes, ftp_multiple_lines, ftp_multipel_ctrl_messages]. + +ftp_150(doc) -> + ["Especially check that respons can be devided in a random place."]; +ftp_150(suite) -> + []; +ftp_150(Config) when is_list(Config) -> + FtpResponse = ["150 ASCII data conn", "ection for /bin/ls ", + "(134.138.177", ".89,50434) (0 bytes).\r\n"], + + "150 ASCII data connection for /bin/ls " + "(134.138.177.89,50434) (0 bytes).\r\n" = Msg = + parse(ftp_response, parse_lines, [[], start], FtpResponse), + {pos_prel, _} = ftp_response:interpret(Msg), + ok. + +ftp_200(doc) -> + ["Especially check that respons can be devided after the first status " + "code character and in the end delimiter."]; +ftp_200(suite) -> + []; +ftp_200(Config) when is_list(Config) -> + FtpResponse = ["2", "00 PORT command successful.", [?CR], [?LF]], + + "200 PORT command successful.\r\n" = Msg = + parse(ftp_response, parse_lines, [[], start], FtpResponse), + {pos_compl, _} = ftp_response:interpret(Msg), + ok. + +ftp_220(doc) -> + ["Especially check that respons can be devided after the " + "first with space "]; +ftp_220(suite) -> + []; +ftp_220(Config) when is_list(Config) -> + FtpResponse = ["220 ","fingon FTP server (SunOS 5.8) ready.\r\n"], + + "220 fingon FTP server (SunOS 5.8) ready.\r\n" = Msg = + parse(ftp_response, parse_lines, [[], start], FtpResponse), + {pos_compl, _} = ftp_response:interpret(Msg), + ok. + +ftp_226(doc) -> + ["Especially check that respons can be devided after second status code" + " character and in the end delimiter."]; +ftp_226(suite) -> + []; +ftp_226(Config) when is_list(Config) -> + FtpResponse = ["22" "6 Transfer complete.\r", [?LF]], + + "226 Transfer complete.\r\n" = Msg = + parse(ftp_response, parse_lines, [[], start], FtpResponse), + {pos_compl, _} = ftp_response:interpret(Msg), + ok. + +ftp_257(doc) -> + ["Especially check that quoted chars do not cause a problem."]; +ftp_257(suite) -> + []; +ftp_257(Config) when is_list(Config) -> + FtpResponse = ["257 \"/\" is current directory.\r\n"], + + "257 \"/\" is current directory.\r\n" = Msg = + parse(ftp_response, parse_lines, [[], start], FtpResponse), + {pos_compl, _} = ftp_response:interpret(Msg), + ok. + +ftp_331(doc) -> + ["Especially check that respons can be devided after the third status " + " status code character."]; +ftp_331(suite) -> + []; +ftp_331(Config) when is_list(Config) -> + %% Brake before white space after code + FtpResponse = + ["331"," Guest login ok, send ient as password.\r\n"], + + "331 Guest login ok, send ient as password.\r\n" = Msg = + parse(ftp_response, parse_lines, [[], start], FtpResponse), + {pos_interm, _} = ftp_response:interpret(Msg), + ok. + +ftp_425(doc) -> + ["Especially check a message that was received in only one part."]; +ftp_425(suite) -> + []; +ftp_425(Config) when is_list(Config) -> + FtpResponse = + ["425 Can't build data connection: Connection refused.\r\n"], + + "425 Can't build data connection: Connection refused.\r\n" + = Msg = parse(ftp_response, parse_lines, [[], start], FtpResponse), + {trans_neg_compl, _} = ftp_response:interpret(Msg), + ok. + +ftp_multiple_lines(doc) -> + ["Especially check multiple lines devided in significant places"]; +ftp_multiple_lines(suite) -> + []; +ftp_multiple_lines(Config) when is_list(Config) -> + FtpResponse = ["21", "4","-The", + " following commands are recognized:\r\n" + " USER EPRT STRU MAIL* ALLO CWD", + " STAT* XRMD \r\n" + " PASS LPRT MODE MSND* " + " REST* XCWD HELP PWD ", [?CRLF], + " ACCT* EPSV RETR MSOM* RNFR LIST " + " NOOP XPWD \r\n", + " REIN* LPSV STOR MSAM* RNTO NLST " + " MKD CDUP \r\n" + " QUIT PASV APPE MRSQ* ABOR SITE* " + " XMKD XCUP \r\n" + " PORT TYPE MLFL* MRCP* DELE SYST " + " RMD STOU \r\n" + "214 (*'s => unimplemented)", [?CR], [?LF]], + + + FtpResponse1 = ["214-", "The", + " following commands are recognized:\r\n" + " USER EPRT STRU MAIL* ALLO CWD", + " STAT* XRMD \r\n" + " PASS LPRT MODE MSND* " + " REST* XCWD HELP PWD ", [?CRLF], + " ACCT* EPSV RETR MSOM* RNFR LIST " + " NOOP XPWD \r\n", + " REIN* LPSV STOR MSAM* RNTO NLST " + " MKD CDUP \r\n" + " QUIT PASV APPE MRSQ* ABOR SITE* " + " XMKD XCUP \r\n" + " PORT TYPE MLFL* MRCP* DELE SYST " + " RMD STOU \r\n" + "2", "14 (*'s => unimplemented)", [?CR], [?LF]], + + FtpResponse2 = ["214-", "The", + " following commands are recognized:\r\n" + " USER EPRT STRU MAIL* ALLO CWD", + " STAT* XRMD \r\n" + " PASS LPRT MODE MSND* " + " REST* XCWD HELP PWD ", [?CRLF], + " ACCT* EPSV RETR MSOM* RNFR LIST " + " NOOP XPWD \r\n", + " REIN* LPSV STOR MSAM* RNTO NLST " + " MKD CDUP \r\n" + " QUIT PASV APPE MRSQ* ABOR SITE* " + " XMKD XCUP \r\n" + " PORT TYPE MLFL* MRCP* DELE SYST " + " RMD STOU \r\n" + "21", "4"," (*'s => unimplemented)", [?CR], [?LF]], + + MultiLineResultStr = + "214-The following commands are recognized:\r\n" + " USER EPRT STRU MAIL* ALLO CWD STAT* " + "XRMD \r\n" + " PASS LPRT MODE MSND* REST* XCWD HELP " + "PWD \r\n" + " ACCT* EPSV RETR MSOM* RNFR LIST NOOP " + "XPWD \r\n" + " REIN* LPSV STOR MSAM* RNTO NLST MKD " + "CDUP \r\n" + " QUIT PASV APPE MRSQ* ABOR SITE* XMKD " + "XCUP \r\n" + " PORT TYPE MLFL* MRCP* DELE SYST RMD " + "STOU \r\n" + "214 (*'s => unimplemented)\r\n", + + MultiLineResultStr = + parse(ftp_response, parse_lines, [[], start], FtpResponse), + {pos_compl, _} = ftp_response:interpret(MultiLineResultStr), + + MultiLineResultStr = parse(ftp_response, parse_lines, [[], start], + FtpResponse1), + + MultiLineResultStr = parse(ftp_response, parse_lines, [[], start], + FtpResponse2), + ok. + +ftp_other_status_codes(doc) -> + ["Check that other valid status codes, than the ones above, are handled" + "by ftp_response:interpret/1. Note there are som ftp status codes" + "that will not be received with the current ftp instruction support," + "they are not included here."]; +ftp_other_status_codes(suite) -> + []; +ftp_other_status_codes(Config) when is_list(Config) -> + + %% 1XX + {pos_prel, _ } = ftp_response:interpret("120 Foobar\r\n"), + + %% 2XX + {pos_compl, _ } = ftp_response:interpret("202 Foobar\r\n"), + {pos_compl, _ } = ftp_response:interpret("221 Foobar\r\n"), + {pos_compl, _ } = ftp_response:interpret("227 Foobar\r\n"), + {pos_compl, _ } = ftp_response:interpret("230 Foobar\r\n"), + {pos_compl, _ } = ftp_response:interpret("250 Foobar\r\n"), + + %% 3XX + {pos_interm_acct, _ } = ftp_response:interpret("332 Foobar\r\n"), + {pos_interm, _ } = ftp_response:interpret("350 Foobar\r\n"), + + %% 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"), + {trans_neg_compl, _ } = ftp_response:interpret("451 Foobar\r\n"), + {etnospc, _ } = ftp_response:interpret("452 Foobar\r\n"), + + %% 5XX + {perm_neg_compl, _ } = ftp_response:interpret("500 Foobar\r\n"), + {perm_neg_compl, _ } = ftp_response:interpret("501 Foobar\r\n"), + {perm_neg_compl, _ } = ftp_response:interpret("503 Foobar\r\n"), + {perm_neg_compl, _ } = ftp_response:interpret("504 Foobar\r\n"), + {perm_neg_compl, _ } = ftp_response:interpret("530 Foobar\r\n"), + {perm_neg_compl, _ } = ftp_response:interpret("532 Foobar\r\n"), + {epath, _ } = ftp_response:interpret("550 Foobar\r\n"), + {epnospc, _ } = ftp_response:interpret("552 Foobar\r\n"), + {efnamena, _ } = ftp_response:interpret("553 Foobar\r\n"), + ok. + +ftp_multipel_ctrl_messages(doc) -> + ["The ftp server may send more than one control message as a reply," + "check that they are handled one at the time."]; +ftp_multipel_ctrl_messages(suite) -> + []; +ftp_multipel_ctrl_messages(Config) when is_list(Config) -> + FtpResponse = ["200 PORT command successful.\r\n200 Foobar\r\n"], + + {"200 PORT command successful.\r\n" = Msg, NextMsg} = + parse(ftp_response, parse_lines, [[], start], FtpResponse), + {pos_compl, _} = ftp_response:interpret(Msg), + NewMsg = parse(ftp_response, parse_lines, [[], start], NextMsg), + {pos_compl, _} = ftp_response:interpret(NewMsg), + ok. + + +%%------------------------------------------------------------------------- +format_error(doc) -> + [""]; +format_error(suite) -> + []; +format_error(Config) when is_list(Config) -> + "Synchronisation error during chunk sending." = + ftp:formaterror(echunk), + "Session has been closed." = ftp:formaterror(eclosed), + "Connection to remote server prematurely closed." = + ftp:formaterror(econn), + "File or directory already exists." = ftp:formaterror(eexists), + "Host not found, FTP server not found, or connection rejected." = + ftp:formaterror(ehost), + "User not logged in." = ftp:formaterror(elogin), + "Term is not a binary." = ftp:formaterror(enotbinary), + "No such file or directory, already exists, or permission denied." + = ftp:formaterror(epath), + "No such type." = ftp:formaterror(etype), + "User name or password not valid." = ftp:formaterror(euser), + "Insufficient storage space in system." = ftp:formaterror(etnospc), + "Exceeded storage allocation (for current directory or dataset)." + = ftp:formaterror(epnospc), + "File name not allowed." = ftp:formaterror(efnamena), + "Unknown error: foobar" = ftp:formaterror({error, foobar}). + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +parse(Module, Function, Args, Bin) when is_binary(Bin) -> + parse(Module, Function, Args, [binary_to_list(Bin)]); + +parse(Module, Function, [AccLines, StatusCode], [Data | Rest]) -> + case Module:Function(list_to_binary(Data), AccLines, StatusCode) of + {ok, Result, <<>>} -> + Result; + {ok, Result, Next} -> + {Result, Next}; + {continue, {NewData, NewAccLines, NewStatusCode}} -> + case Rest of + [] -> + test_server:fail({wrong_input, Data, Rest}); + [_ | _] -> + parse(Module, Function, [NewAccLines, NewStatusCode], + [binary_to_list(NewData) ++ hd(Rest) | tl(Rest)]) + end + end. diff --git a/lib/inets/test/ftp_freebsd_x86_test.erl b/lib/inets/test/ftp_freebsd_x86_test.erl new file mode 100644 index 0000000000..457e18ffbe --- /dev/null +++ b/lib/inets/test/ftp_freebsd_x86_test.erl @@ -0,0 +1,153 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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("test_server.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(doc) -> + ["Test ftp client"]; + +all(suite) -> + [open, open_port, passive, active, api_missuse, + not_owner, progress_report]. + +%% 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). +passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). +active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/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). +progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/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_internal.hrl b/lib/inets/test/ftp_internal.hrl new file mode 120000 index 0000000000..af57081f14 --- /dev/null +++ b/lib/inets/test/ftp_internal.hrl @@ -0,0 +1 @@ +../src/ftp/ftp_internal.hrl
\ No newline at end of file diff --git a/lib/inets/test/ftp_linux_ppc_test.erl b/lib/inets/test/ftp_linux_ppc_test.erl new file mode 100644 index 0000000000..ad38137678 --- /dev/null +++ b/lib/inets/test/ftp_linux_ppc_test.erl @@ -0,0 +1,151 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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("test_server.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(doc) -> + ["Test ftp client"]; + +all(suite) -> + [open, open_port, passive, active, api_missuse, + not_owner, progress_report]. + +%% 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). +passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). +active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/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). +progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/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 new file mode 100644 index 0000000000..b9c88d121a --- /dev/null +++ b/lib/inets/test/ftp_linux_x86_test.erl @@ -0,0 +1,160 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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("test_server.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(doc) -> + ["Test ftp client"]; + +all(suite) -> + [ + open, + open_port, + passive, + active, + api_missuse, + not_owner, + progress_report + ]. + +%% 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). +passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). +active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/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). +progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/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 new file mode 100644 index 0000000000..cf548a73c0 --- /dev/null +++ b/lib/inets/test/ftp_macosx_ppc_test.erl @@ -0,0 +1,152 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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("test_server.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(doc) -> + ["Test ftp client"]; + +all(suite) -> + [open, open_port, passive, active, api_missuse, + not_owner, progress_report]. + + +open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). +open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). +passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). +active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/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). +progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/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 new file mode 100644 index 0000000000..c59a992421 --- /dev/null +++ b/lib/inets/test/ftp_macosx_x86_test.erl @@ -0,0 +1,152 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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("test_server.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(doc) -> + ["Test ftp client"]; + +all(suite) -> + [open, open_port, passive, active, api_missuse, + not_owner, progress_report]. + +%% 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). +passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). +active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/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). +progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/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_netbsd_x86_test.erl b/lib/inets/test/ftp_netbsd_x86_test.erl new file mode 100644 index 0000000000..a5711b7bde --- /dev/null +++ b/lib/inets/test/ftp_netbsd_x86_test.erl @@ -0,0 +1,152 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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("test_server.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(doc) -> + ["Test ftp client"]; + +all(suite) -> + [open, open_port, passive, active, api_missuse, + not_owner, progress_report]. + +%% 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). +passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). +active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/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). +progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/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 new file mode 100644 index 0000000000..4833b6332b --- /dev/null +++ b/lib/inets/test/ftp_openbsd_x86_test.erl @@ -0,0 +1,151 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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("test_server.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(doc) -> + ["Test ftp client"]; + +all(suite) -> + [open, open_port, passive, active, api_missuse, + not_owner, progress_report]. + +%% 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). +passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). +active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/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). +progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/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 new file mode 100644 index 0000000000..6066195f9b --- /dev/null +++ b/lib/inets/test/ftp_solaris10_sparc_test.erl @@ -0,0 +1,154 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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("test_server.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(doc) -> + ["Test ftp client"]; + +all(suite) -> + [open, open_port, passive, active, api_missuse, + not_owner, progress_report]. + +%% 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). +passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). +active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/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). +progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/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 new file mode 100644 index 0000000000..3bd99fc3f2 --- /dev/null +++ b/lib/inets/test/ftp_solaris10_x86_test.erl @@ -0,0 +1,155 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009. 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("test_server.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(doc) -> + ["Test ftp client"]; + +all(suite) -> + [open, open_port, passive, active, api_missuse, + not_owner, progress_report]. + +%% 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). +passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). +active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/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). +progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/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 new file mode 100644 index 0000000000..9764071cd9 --- /dev/null +++ b/lib/inets/test/ftp_solaris8_sparc_test.erl @@ -0,0 +1,152 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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("test_server.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(doc) -> + ["Test ftp client"]; + +all(suite) -> + [open, open_port, passive, active, api_missuse, + not_owner, progress_report]. + +%% 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). +passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). +active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/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). +progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/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 new file mode 100644 index 0000000000..a9f77bbdac --- /dev/null +++ b/lib/inets/test/ftp_solaris9_sparc_test.erl @@ -0,0 +1,151 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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("test_server.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(doc) -> + ["Test ftp client"]; + +all(suite) -> + [open, open_port, passive, active, api_missuse, + not_owner, progress_report]. + +%% 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). +passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). +active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/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). +progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/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 new file mode 100644 index 0000000000..29339da6e5 --- /dev/null +++ b/lib/inets/test/ftp_suite_lib.erl @@ -0,0 +1,1531 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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("test_server.hrl"). +-include("test_server_line.hrl"). + +%% Test server specific exports +% -export([init_per_testcase/2, end_per_testcase/2]). + +-compile(export_all). + + +-record(progress, { + current = 0, + total + }). + + + +-define(FTP_HOSTS(X),ftp_hosts(X)). +-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, {timeout, timer:seconds(15)}] ++ Flags)). +-else. +-ifdef(ftp_trace_client). +-define(ftp_open(Host, Flags), + do_ftp_open(Host, [trace, {timeout, timer:seconds(15)}] ++ Flags)). +-else. +-define(ftp_open(Host, Flags), + do_ftp_open(Host, [verbose, {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 ?config(ftpd_hosts, Config) of + undefined -> + ?FTP_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({option_list, + [{host, Host}, {port, ?FTP_PORT}, + {timeout, 20000}]})) of + {ok, Pid} -> + (catch ftp:close(Pid)), + {ok, Host}; + _ -> + get_ftpd_host(Hosts) + 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) -> + 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), + inets:enable_trace(max, io, ftpc), + 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, [])) 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, [])) of + {ok, Pid} -> + ok = ftp:force_active(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 = [{host, Host}, + {port, ?FTP_PORT}, + {flags, [verbose]}, + {progress, {?MODULE, progress, #progress{}}}], + case ftp:open({option_list, 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), + Flags = + if + ((Case =:= passive_ip_v6_disabled) orelse + (Case =:= active_ip_v6_disabled)) -> + [ip_v6_disabled]; + true -> + [] + end, + case (catch ?ftp_open(Host, Flags)) of + {ok, Pid} -> + case string:tokens(atom_to_list(Case), [$_]) of + [_, "active"|_] -> + ok = ftp:force_active(Pid); + _ -> + ok + end, + 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) -> + {ok, Pid} = ?ftp_open(Host, []), + ok = ftp:close(Pid), + {ok, Pid1} = + ftp:open({option_list, [{host,Host}, + {port, ?FTP_PORT}, + {flags, [verbose]}, + {timeout, 30000}]}), + ok = ftp:close(Pid1), + {error, ehost} = ftp:open({option_list, [{port, ?FTP_PORT}, + {flags, [verbose]}]}), + {ok, Pid2} = ftp:open(Host), + ok = ftp:close(Pid2), + + {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. + {ok, Pid4} = + ftp:open({option_list, [{host, Host}, {port, badarg}, + {flags, [verbose]}, + {timeout, 30000}]}), + test_server:sleep(100), + ok = ftp:close(Pid4), + {ok, Pid5} = + ftp:open({option_list, [{host, Host}, {port, ?FTP_PORT}, + {flags, [verbose]}, + {timeout, -42}]}), + test_server:sleep(100), + ok = ftp:close(Pid5), + {ok, Pid6} = + ftp:open({option_list, [{host, Host}, {port, ?FTP_PORT}, + {flags, [verbose]}, + {mode, cool}]}), + test_server:sleep(100), + ok = ftp:close(Pid6), + 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, ?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), + io:format("Pid: ~p~n",[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), + do_nlist(Pid). + + +%%------------------------------------------------------------------------- + +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), + do_nlist(Pid). + + +%%------------------------------------------------------------------------- + +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) -> + Pid = ?config(ftp, Config), + Host = ftp_host(Config), + + %% Serious programming fault, connetion will be shut down + {error, {connection_terminated, 'API_violation'}} = + gen_server:call(Pid, {self(), foobar, 10}, infinity), + test_server:sleep(500), + undefined = process_info(Pid, status), + + {ok, Pid2} = ?ftp_open(Host, []), + %% Serious programming fault, connetion will be shut down + gen_server:cast(Pid2, {self(), foobar, 10}), + test_server:sleep(500), + undefined = process_info(Pid2, status), + + {ok, Pid3} = ?ftp_open(Host, []), + %% Could be an innocent misstake the connection lives. + Pid3 ! foobar, + test_server:sleep(500), + {status, _} = process_info(Pid3, status), + 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"]), + Pid = spawn(?MODULE,open_wait_6035,[self()]), + error_logger:logfile({open,LogFile}), + ok = kill_ftp_proc_6035(Pid,LogFile), + error_logger:logfile(close), + p("ticket_6035 -> done", []), + ok. + +kill_ftp_proc_6035(Pid, LogFile) -> + p("kill_ftp_proc_6035 -> entry"), + receive + open -> + p("kill_ftp_proc_6035 -> received open: send 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(From) -> + FtpServer = "elrond", + p("open_wait_6035 -> try connect to ~s", [FtpServer]), + case ftp:open(FtpServer, [{timeout, timer:seconds(15)}]) of + {ok, Pid} -> + p("open_wait_6035 -> connected, now login"), + 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} -> + p("is_error_report_6035 -> logfile read"), + read_log_6035(binary_to_list(Bin)); + _ -> + ok + end, + p("is_error_report_6035 -> logfile read result: " + "~n ~p", [Res]), + file:delete(LogFile), + Res. + +read_log_6035("=ERROR REPORT===="++_Rest) -> + error_report; +read_log_6035([_H|T]) -> + read_log_6035(T); +read_log_6035([]) -> + ok. + + +%%-------------------------------------------------------------------- +%% 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) -> + {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). + {ok, _} = ftp:nlist(Pid, "incom*"), +%% {error, epath} = ftp:nlist(Pid, ?BAD_DIR), + ok. + +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, <<Acc/binary, Bin/binary>>); + 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, Flags) -> + io:format("do_ftp_open -> entry with" + "~n Host: ~p" + "~n Flags: ~p", [Host, Flags]), + case ftp:open(Host, Flags) 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. + +ftp_hosts(Config) -> + DataDir = ?config(data_dir,Config), + FileName = filename:join([DataDir,"../ftp_SUITE_data/",inets_test_hosts]), + io:format("FileName: ~p~n",[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}); + _ -> 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 new file mode 100644 index 0000000000..96e443fab5 --- /dev/null +++ b/lib/inets/test/ftp_ticket_test.erl @@ -0,0 +1,52 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2009. 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(suite) -> + {conf,init,tickets(),fin}. + +init(Config) -> + [{ftp_remote_host, "elrond"}|Config]. +% ?LIB_MOD:ftpd_init(ticket_test,Config). + +tickets() -> + [ticket_6035]. + + +fin(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 new file mode 100644 index 0000000000..d24318d04f --- /dev/null +++ b/lib/inets/test/ftp_windows_2003_server_test.erl @@ -0,0 +1,152 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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("test_server.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(doc) -> + ["Test ftp client"]; + +all(suite) -> + [open, open_port, passive, active, api_missuse, + not_owner, progress_report]. + +%% 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). +passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). +active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/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). +progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/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 new file mode 100644 index 0000000000..bc161e4f6a --- /dev/null +++ b/lib/inets/test/ftp_windows_xp_test.erl @@ -0,0 +1,150 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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("test_server.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(doc) -> + ["Test ftp client"]; + +all(suite) -> + [open, open_port, passive, active, api_missuse, + not_owner, progress_report]. + +open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). +open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). +passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). +active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/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). +progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/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/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl new file mode 100644 index 0000000000..9559317640 --- /dev/null +++ b/lib/inets/test/http_format_SUITE.erl @@ -0,0 +1,585 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2009. 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(http_format_SUITE). +-author('[email protected]'). + +-include("test_server.hrl"). +-include("test_server_line.hrl"). +-include("http_internal.hrl"). + +%% Test server specific exports +-export([all/1, init_per_testcase/2, end_per_testcase/2]). + +%% Test cases must be exported. +-export([chunk/1, chunk_decode/1, chunk_encode/1, + chunk_extensions_otp_6005/1, chunk_decode_otp_6264/1, + chunk_decode_empty_chunk_otp_6511/1, + chunk_decode_trailer/1, + http_response/1, http_request/1, validate_request_line/1, script/1, + esi_parse_headers/1, cgi_parse_headers/1, + is_absolut_uri/1, convert_netscapecookie_date/1]). + +all(doc) -> + ["Test library functions to the http client and server."]; +all(suite) -> + [chunk, + http_response, http_request, validate_request_line, + script, is_absolut_uri, convert_netscapecookie_date]. + +init_per_testcase(_, Config) -> + Dog = test_server:timetrap(?t:minutes(1)), + NewConfig = lists:keydelete(watchdog, 1, Config), + [{watchdog, Dog} | NewConfig]. + +end_per_testcase(_, Config) -> + Dog = ?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- +script(doc) -> + ["Test header parsing in esi/cgi functionality."]; +script(suite) -> + [esi_parse_headers, cgi_parse_headers]. + +chunk(doc) -> + ["Test chunk encoding"]; +chunk(suite) -> + [chunk_decode, chunk_encode, chunk_extensions_otp_6005, + chunk_decode_otp_6264, chunk_decode_empty_chunk_otp_6511, + chunk_decode_trailer]. + +%%------------------------------------------------------------------------- +chunk_decode(doc) -> + ["Test http_chunk:decode/3"]; +chunk_decode(suite) -> + []; +chunk_decode(Config) when is_list(Config) -> + ReqHeaders = #http_request_h{'transfer-encoding' = "chunked"}, + ChunkedBody = "A" ++ ?CRLF ++ "1234567890" ++ ?CRLF ++ "4" ++ + ?CRLF ++ "HEJ!" ++ ?CRLF ++ "0" ++ ?CRLF ++ ?CRLF, + {ok, {Headers, Body}} = + http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), + "1234567890HEJ!" = binary_to_list(Body), + %% When the "chunked" is removed by the decoding the header + %% will become empty in this case i.e. undefined! + NewReqHeaders = http_chunk:handle_headers(ReqHeaders, Headers), + undefined = NewReqHeaders#http_request_h.'transfer-encoding', + + NewChunkedBody = ["A" ++ [?CR], [?LF] ++ "12345", "67890" ++ ?CRLF ++ "4" + ++ ?CRLF ++ "HEJ!" ++ ?CRLF ++ "0" ++ [?CR], + [?LF, ?CR, ?LF]], + + {Module, Function, Args} = + http_chunk:decode(list_to_binary(hd(NewChunkedBody)), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), + + {_, Body} = parse(Module, Function, Args, tl(NewChunkedBody)), + "1234567890HEJ!" = binary_to_list(Body), + + ok. + +%%------------------------------------------------------------------------- +chunk_extensions_otp_6005(doc) -> + ["Make sure so called extensions are ignored"]; +chunk_extensions_otp_6005(suite) -> + []; +chunk_extensions_otp_6005(Config) when is_list(Config)-> + ChunkedBody = "A;ignore this" ++ ?CRLF ++ "1234567890" ++ + ?CRLF ++ "4" ++ ?CRLF ++ "HEJ!"++ ?CRLF ++ "0" ++ + ";extensionname=extensionvalue;foo=bar" ++ ?CRLF ++ ?CRLF, + {ok, {["content-length:14"], Body}} = + http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), + "1234567890HEJ!" = binary_to_list(Body), + + ChunkedBody1 = ["A;", "ignore this" ++ [?CR], [?LF] ++ "1234567890" ++ + ?CRLF ++ "4" ++ ?CRLF ++ "HEJ!"++ ?CRLF ++ "0" ++ + ";extensionname=extensionvalue;foo=bar" ++ ?CRLF ++ ?CRLF], + + {Module1, Function1, Args1} = + http_chunk:decode(list_to_binary(hd(ChunkedBody1)), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), + + {_, NewBody} = parse(Module1, Function1, Args1, tl(ChunkedBody1)), + "1234567890HEJ!" = binary_to_list(NewBody), + ok. + +%%------------------------------------------------------------------------- +chunk_decode_otp_6264(doc) -> + ["Check that 0 in the body does not count as the last chunk"]; +chunk_decode_otp_6264(suite) -> + []; +chunk_decode_otp_6264(Config) when is_list(Config)-> + ChunkedBody = "A;ignore this" ++ ?CRLF ++ "1234567890" ++ + ?CRLF ++ "4" ++ ?CRLF ++ "0123"++ ?CRLF ++ "0" ++ + ";extensionname=extensionvalue;foo=bar" ++ ?CRLF ++ ?CRLF, + {ok, {["content-length:14"], Body}} = + http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), + "12345678900123" = binary_to_list(Body), + + NewChunkedBody = ["A" ++ [?CR], [?LF] ++ "12345", "67890" ++ ?CRLF ++ "1" + ++ ?CRLF ++ "0" ++ ?CRLF ++ "0" ++ [?CR], + [?LF, ?CR, ?LF]], + + {Module, Function, Args} = + http_chunk:decode(list_to_binary(hd(NewChunkedBody)), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), + + {_, NewBody} = parse(Module, Function, Args, tl(NewChunkedBody)), + "12345678900" = binary_to_list(NewBody), + + NewChunkedBody1 = ["A" ++ [?CR], [?LF] ++ "12345", "67890" ++ ?CRLF ++ "1" + ++ ?CRLF ++ "0", ?CRLF ++ "0", [?CR], [?LF], + [?CR], [?LF]], + + {Module1, Function1, Args1} = + http_chunk:decode(list_to_binary(hd(NewChunkedBody1)), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), + + {_, NewBody} = parse(Module1, Function1, Args1, tl(NewChunkedBody1)), + "12345678900" = binary_to_list(NewBody), + + ok. +%%------------------------------------------------------------------------- +chunk_decode_empty_chunk_otp_6511(doc) -> + [""]; +chunk_decode_empty_chunk_otp_6511(suite) -> + []; +chunk_decode_empty_chunk_otp_6511(Config) when is_list(Config) -> + ChunkedBody = "0" ++ ?CRLF ++ ?CRLF, + {ok,{["content-length:0"],<<>>}} = + http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), + ok. + +%%------------------------------------------------------------------------- +chunk_decode_trailer(doc) -> + ["Make sure trailers are handled correctly. Trailers should" + "become new headers"]; +chunk_decode_trailer(suite) -> + []; +chunk_decode_trailer(Config) when is_list(Config)-> + ChunkedBody = "1a; ignore-stuff-here" ++ ?CRLF ++ + "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF + ++ "1234567890abcdef" ++ ?CRLF ++ "0" ++ ?CRLF + ++ "some-footer:some-value" ++ ?CRLF + ++ "another-footer:another-value" ++ ?CRLF ++ ?CRLF, + + {ok, {Headers, Body}} = + http_chunk:decode(list_to_binary(ChunkedBody), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), + + %% There is no guaranteed order of headers. + true = lists:member("content-length:42", Headers), + true = lists:member("some-footer:some-value", Headers), + true = lists:member("another-footer:another-value", Headers), + "abcdefghijklmnopqrstuvwxyz1234567890abcdef" = binary_to_list(Body), + + ChunkedBody1 = "1a" ++ ?CRLF ++ + "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF + ++ "1234567890abcdef" ++ ?CRLF ++ "0" ++ ?CRLF + ++ "some-footer:some-value" ++ ?CRLF ++ ?CRLF, + + {ok, {Headers1, Body1}} = + http_chunk:decode(list_to_binary(ChunkedBody1), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), + + true = lists:member("content-length:42", Headers1), + true = lists:member("some-footer:some-value", Headers1), + false = lists:member("another-footer:another-value", Headers1), + "abcdefghijklmnopqrstuvwxyz1234567890abcdef" = binary_to_list(Body1), + + + ChunkedBody2 = ["1a", ?CRLF ++ + "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF + ++ "1234567890abcdef" ++ ?CRLF ++ "0", ";", + "ignore stuff here=foobar", ?CRLF ++ + "some-footer:some-value", ?CRLF, ?CRLF], + + {Module, Function, Args} = + http_chunk:decode(list_to_binary(hd(ChunkedBody2)), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), + + {_, NewBody} = parse(Module, Function, Args, tl(ChunkedBody2)), + "abcdefghijklmnopqrstuvwxyz1234567890abcdef" = binary_to_list(NewBody), + + ChunkedBody3 = ["1a", ?CRLF ++ + "abcdefghijklmnopqrstuvwxyz", ?CRLF ++ "10" ++ ?CRLF + ++ "1234567890abcdef" ++ ?CRLF ++ "0" ++ ?CRLF + ++ "some-footer:some-value", [?CR], [?LF] , ?CRLF], + + {Module1, Function1, Args1} = + http_chunk:decode(list_to_binary(hd(ChunkedBody3)), + ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE), + + {_, NewBody} = parse(Module1, Function1, Args1, tl(ChunkedBody3)), + "abcdefghijklmnopqrstuvwxyz1234567890abcdef" = binary_to_list(NewBody), + + ok. + +%%------------------------------------------------------------------------- +chunk_encode(doc) -> + ["Test http_chunk:encode/1 & http_chunk:encode_last/0"]; +chunk_encode(suite) -> + []; +chunk_encode(Config) when is_list(Config) -> + <<54, ?CR, ?LF, 102,111,111,98,97,114, ?CR, ?LF>> = + http_chunk:encode(list_to_binary("foobar")), + ["6", ?CR, ?LF,"foobar", ?CR, ?LF] = http_chunk:encode("foobar"), + <<$0, ?CR, ?LF, ?CR, ?LF >> = http_chunk:encode_last(), + ok. + + +%%------------------------------------------------------------------------- +http_response(doc) -> + ["Test httpc_response:parse*. This test case will simulate that the " + "message will be recived a little at the time on a socket and the " + "package may be broken up into smaller parts at arbitrary point."]; +http_response(suite) -> + []; +http_response(Config) when is_list(Config) -> + + HttpHead1 = ["HTTP", "/1.1 ", "20", "0 ", "ok", [?CR, ?LF], + "content-length:83" ++ ?CRLF ++ "content", "-type:", + "text/html" ++ ?CRLF ++ + "date:Thu, 28 Oct 2004 07:57:43 GMT" ++ + [?CR], [?LF, ?CR, ?LF]], + {"HTTP/1.1", + 200, + "ok", + #http_response_h{'content-length' = "83", + 'content-type' = "text/html", + date = "Thu, 28 Oct 2004 07:57:43 GMT"}, + <<>>} = + parse(httpc_response, parse, [?HTTP_MAX_HEADER_SIZE, false], + HttpHead1), + + HttpHead2 = ["HTTP/1.1 200", " ok", [?CR], [?LF] ++ + "content-length:83" ++ ?CRLF ++ "content-type:", + "text/html" ++ ?CRLF ++ + "date:" ++ "Thu, 28 Oct 2004 07:57:43 GMT" ++ + ?CRLF, ?CRLF], + {"HTTP/1.1", + 200, + "ok", + #http_response_h{'content-length' = "83", + 'content-type' = "text/html", + date = "Thu, 28 Oct 2004 07:57:43 GMT"}, + <<>>} = + parse(httpc_response, parse, [?HTTP_MAX_HEADER_SIZE, false], + HttpHead2), + + HttpHead3 = ["HTTP/1.1 200 ", "ok", ?CRLF ++ + "content-length:83" ++ ?CRLF ++ "content-type:", + "text/html" ++ ?CRLF ++ + "date:" ++ "Thu, 28 Oct 2004 07:57:43 GMT" ++ + [?CR, ?LF,?CR], [?LF]], + {"HTTP/1.1", + 200, + "ok", + #http_response_h{'content-length' = "83", + 'content-type' = "text/html", + date = "Thu, 28 Oct 2004 07:57:43 GMT"}, + <<>>} = + parse(httpc_response, parse, [?HTTP_MAX_HEADER_SIZE, false], + HttpHead3), + + HttpBody = ["<HTML>\n<HEAD>\n<TITLE> dummy </TITLE>\n</HEAD>\n<BODY>\n", + "<H1>dummy</H1>\n</BODY>\n</HTML>\n"], + + NewBody = lists:flatten(HttpBody), + Length = length(NewBody), + NewBody = + binary_to_list(parse + (httpc_response, whole_body, [<<>>,Length], + HttpBody)), + + HttpBody1 = ["<HTML", ">\n<HEAD>", "\n<TITLE> dummy </TITLE>\n</HEAD>\n", + "<BODY>\n", "<H1>du", "mmy</H1>\n</BODY>\n</HTML>\n"], + + NewBody1 = lists:flatten(HttpBody1), + Length1 = length(NewBody1), + NewBody1 = binary_to_list(parse + (httpc_response, whole_body, + [<<>>,Length1], HttpBody1)), + ok. +%%------------------------------------------------------------------------- +http_request(doc) -> + ["Test httpd_request:parse* This test case will simulate that the " + "message will be recived a little at the time on a socket and the " + "package may be broken up into smaller parts at arbitrary point."]; +http_request(suite) -> + []; +http_request(Config) when is_list(Config) -> + + HttpHead = ["GE", "T ", "http://www.erlang", ".org ", "HTTP", + "/1.1" ++ ?CRLF ++ "host:", + "www.erlang.org" ++ [?CR], + [?LF] ++ "te: " ++ ?CRLF, ?CRLF], + {"GET", + "http://www.erlang.org", + "HTTP/1.1", + {#http_request_h{host = "www.erlang.org", te = []}, + ["te: ","host:www.erlang.org"]}, <<>>} = + parse(httpd_request, parse, [?HTTP_MAX_HEADER_SIZE], HttpHead), + + HttpHead1 = ["GET http://www.erlang.org HTTP/1.1" ++ + [?CR], [?LF, ?CR, ?LF]], + {"GET", + "http://www.erlang.org", + "HTTP/1.1", + {#http_request_h{}, []}, <<>>} = + parse(httpd_request, parse, [?HTTP_MAX_HEADER_SIZE], HttpHead1), + + + HttpHead2 = ["GET http://www.erlang.org HTTP/1.1" ++ + [?CR, ?LF, ?CR], [?LF]], + {"GET", + "http://www.erlang.org", + "HTTP/1.1", + {#http_request_h{}, []}, <<>>} = + parse(httpd_request, parse, [?HTTP_MAX_HEADER_SIZE], HttpHead2), + + %% Note the following body is not related to the headers above + HttpBody = ["<HTML>\n<HEAD>\n<TITLE> dummy </TITLE>\n</HEAD>\n<BODY>\n", + "<H1>dummy</H1>\n</BODY>\n</HTML>\n"], + + NewBody = lists:flatten(HttpBody), + Length = length(NewBody), + NewBody = + binary_to_list(parse + (httpd_request, whole_body, [<<>>,Length], HttpBody)), + + HttpBody1 = ["<HTML", ">\n<HEAD>", "\n<TITLE> dummy </TITLE>\n</HEAD>\n", + "<BODY>\n", "<H1>du", "mmy</H1>\n</BODY>\n</HTML>\n"], + + NewBody1 = lists:flatten(HttpBody1), + Length1 = length(NewBody1), + NewBody1 = + binary_to_list(parse + (httpd_request, whole_body, + [<<>>, Length1], HttpBody1)), + ok. +%%------------------------------------------------------------------------- +validate_request_line(doc) -> + ["Test httpd_request:validate/3. Makes sure you can not get past" + " the server_root and that the request is recognized by the server" + " and protcol version." ]; +validate_request_line(suite) -> + []; +validate_request_line(Config) when is_list(Config) -> + + %% HTTP/0.9 only has GET requests + ok = + httpd_request:validate("GET", "http://www.erlang/org", "HTTP/0.9"), + {error, {not_supported, + {"HEAD", "http://www.erlang/org", "HTTP/0.9"}}} = + httpd_request:validate("HEAD", "http://www.erlang/org", "HTTP/0.9"), + {error, {not_supported, + {"TRACE", "http://www.erlang/org", "HTTP/0.9"}}} = + httpd_request:validate("TRACE", "http://www.erlang/org", "HTTP/0.9"), + {error, {not_supported, + {"POST", "http://www.erlang/org", "HTTP/0.9"}}} = + httpd_request:validate("POST", "http://www.erlang/org", "HTTP/0.9"), + + %% HTTP/1.* + ok = httpd_request:validate("HEAD", "http://www.erlang/org", + "HTTP/1.1"), + ok = httpd_request:validate("GET", "http://www.erlang/org", + "HTTP/1.1"), + ok = httpd_request:validate("POST","http://www.erlang/org", + "HTTP/1.1"), + ok = httpd_request:validate("TRACE","http://www.erlang/org", + "HTTP/1.1"), + {error, {not_supported, + {"FOOBAR", "http://www.erlang/org", "HTTP/1.1"}}} = + httpd_request:validate("FOOBAR", "http://www.erlang/org", + "HTTP/1.1"), + + %% Attempts to get outside of server_root directory by relative links + ForbiddenUri = "http://127.0.0.1:8888/../../../../../etc/passwd", + {error, {bad_request, {forbidden, ForbiddenUri}}} = + httpd_request:validate("GET", ForbiddenUri, "HTTP/1.1"), + + ForbiddenUri2 = + "http://127.0.0.1:8888/././././././../../../../../etc/passwd", + {error, {bad_request, {forbidden, ForbiddenUri2}}} = + httpd_request:validate("GET", ForbiddenUri2, "HTTP/1.1"), + + HexForbiddenUri = "http://127.0.0.1:8888/%2e%2e/%2e%2e/%2e%2e/" + "home/ingela/test.html", + {error, {bad_request, {forbidden, HexForbiddenUri}}} = + httpd_request:validate("GET", HexForbiddenUri, "HTTP/1.1"), + + NewForbiddenUri = + "http://127.0.0.1:8888/foobar/../../../home/ingela/test.html", + {error, {bad_request, {forbidden, NewForbiddenUri}}} = + httpd_request:validate("GET", NewForbiddenUri, "HTTP/1.1"), + + NewForbiddenUri1 = + "http://127.0.0.1:8888/../home/ingela/test.html", + {error, {bad_request, {forbidden, NewForbiddenUri1}}} = + httpd_request:validate("GET", NewForbiddenUri1, "HTTP/1.1"), + + ok. +%%------------------------------------------------------------------------- +esi_parse_headers(doc) -> + ["Test httpd_esi:*. All header values are received in the same" + " erlang message."]; +esi_parse_headers(suite) -> + []; +esi_parse_headers(Config) when is_list(Config) -> + + ESIResult = "content-type:text/html\r\ndate:Thu, 28 Oct 2004 07:57:43 " + "GMT\r\nstatus:200 OK\r\n\r\nFoobar", + + {"content-type:text/html\r\ndate:Thu, 28 Oct 2004 07:57:43 GMT\r\nst" + "atus:200 OK\r\n" = Headers, + "Foobar"} = httpd_esi:parse_headers(ESIResult), + + {ok,[{"date","Thu, 28 Oct 2004 07:57:43 GMT"}, + {"content-type","text/html"}], 200} = + httpd_esi:handle_headers(Headers), + + ESIResult2 = + "location:http://foo.bar.se\r\ndate:Thu, 28 Oct 2004 07:57:43 " + "GMT\r\n\r\n", + + {"location:http://foo.bar.se\r\ndate:Thu, 28 Oct 2004 07:57:43 GMT\r\n" = + Headers2,[]} + = httpd_esi:parse_headers(ESIResult2), + + {ok,[{"date","Thu, 28 Oct 2004 07:57:43 GMT"}, + {"location","http://foo.bar.se"}], 302} = + httpd_esi:handle_headers(Headers2), + + {proceed,"/foo/bar.html"} = + httpd_esi:handle_headers("location:/foo/bar.html\r\n"), + ok. + +%%-------------------------------------------------------------------- +cgi_parse_headers(doc) -> + ["Test httpd_cgi:*. This test case will simulate that the " + "message will be recived a little at the time on a socket and the " + "package may be broken up into smaller parts at arbitrary point."]; +cgi_parse_headers(suite) -> + []; +cgi_parse_headers(Config) when is_list(Config) -> + + CGIResult = ["content-type:text", "/html\ndate:Thu, 28 Oct 2004 07:57:43 " + "GMT\nst", "atus:200 OK\n", "\nFoobar"], + + {Headers, Body} = + parse(httpd_cgi, parse_headers, [<<>>, [], []], CGIResult), + + "Foobar" = binary_to_list(Body), + + {ok,[{"content-type","text/html"}, + {"date","Thu, 28 Oct 2004 07:57:43 GMT"}], {200,"OK"}} = + httpd_cgi:handle_headers(Headers), + + CGIResult2 = ["location:http://foo.bar.se\ndate:Thu, 28 Oct 2004" + " 07:57:43 GMT\n\n"], + {Headers2, _} = parse(httpd_cgi, parse_headers, + [<<>>, [], []], CGIResult2), + + {ok,[{"location","http://foo.bar.se"}, + {"date","Thu, 28 Oct 2004 07:57:43 GMT"}], {302,"Redirect"}} = + httpd_cgi:handle_headers(Headers2), + + {proceed,"/foo/bar.html"} = + httpd_cgi:handle_headers(["location:/foo/bar.html\n"]), + + CGIHTTPResult = ["Content-Type:text", "/html\n", "Connection:close\r\n", + "Content-Language:en\r\nAge:", "4711\r\n\r\n\nfoobar"], + + {Headers3, _} = parse(httpd_cgi, parse_headers, + [<<>>, [], []], CGIHTTPResult), + + {ok,[{"content-type","text/html"}, + {"connection","close"}, + {"content-language","en"}, + {"age","4711"}], {200,"ok"}} = httpd_cgi:handle_headers(Headers3), + + ok. + +%%------------------------------------------------------------------------- +is_absolut_uri(doc) -> + ["Test http_request:is_absolut_uri/1."]; +is_absolut_uri(suite) -> + []; +is_absolut_uri(Config) when is_list(Config) -> + true = http_request:is_absolut_uri("http://www.erlang.org"), + true = http_request:is_absolut_uri("https://www.erlang.org"), + false = http_request:is_absolut_uri("index.html"). + + +%%------------------------------------------------------------------------- +convert_netscapecookie_date(doc) -> + ["Test http_util:convert_netscapecookie_date/1."]; +convert_netscapecookie_date(suite) -> + []; +convert_netscapecookie_date(Config) when is_list(Config) -> + {{2006,1,6},{8,59,38}} = + http_util:convert_netscapecookie_date("Mon, 06-Jan-2006 08:59:38 GMT"), + {{2006,2, 7},{8,59,38}} = + http_util:convert_netscapecookie_date("Tue, 07-Feb-2006 08:59:38 GMT"), + {{2006,3,8},{8,59,38}} = + http_util:convert_netscapecookie_date("Wdy, 08-Mar-2006 08:59:38 GMT"), + {{2006,4,9},{8,59,38}} = + http_util:convert_netscapecookie_date("Thu, 09-Apr-2006 08:59:38 GMT"), + {{2006,5,10},{8,59,38}} = + http_util:convert_netscapecookie_date("Fri, 10-May-2006 08:59:38 GMT"), + {{2006,6,11},{8,59,38}} = + http_util:convert_netscapecookie_date("Sat, 11-Jun-2006 08:59:38 GMT"), + {{2006,7,12},{8,59,38}} = + http_util:convert_netscapecookie_date("Sun, 12-Jul-2006 08:59:38 GMT"), + {{2006,8,12},{8,59,38}} = + http_util:convert_netscapecookie_date("Sun, 12-Aug-2006 08:59:38 GMT"), + {{2006,9,12},{8,59,38}} = + http_util:convert_netscapecookie_date("Sun, 12-Sep-2006 08:59:38 GMT"), + {{2006,10,12},{8,59,38}} = + http_util:convert_netscapecookie_date("Sun, 12-Oct-2006 08:59:38 GMT"), + {{2006,11,12},{8,59,38}} = + http_util:convert_netscapecookie_date("Sun, 12-Nov-2006 08:59:38 GMT"), + {{2006,12,12},{8,59,38}} = + http_util:convert_netscapecookie_date("Sun, 12-Dec-2006 08:59:38 GMT"), + ok. + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- + +parse(Module, Function, Args, [Data | Rest]) -> + case Module:Function([list_to_binary(Data) | Args]) of + {ok, Result} -> + Result; + {NewModule, NewFunction, NewArgs} -> + parse(NewModule, NewFunction, NewArgs, Rest) + end. + + + diff --git a/lib/inets/test/http_internal.hrl b/lib/inets/test/http_internal.hrl new file mode 120000 index 0000000000..a4600a09f7 --- /dev/null +++ b/lib/inets/test/http_internal.hrl @@ -0,0 +1 @@ +../src/http_lib/http_internal.hrl
\ No newline at end of file diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl new file mode 100644 index 0000000000..3485966307 --- /dev/null +++ b/lib/inets/test/httpc_SUITE.erl @@ -0,0 +1,2532 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2009. 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(httpc_SUITE). +-author('[email protected]'). + +-include("test_server.hrl"). +-include("test_server_line.hrl"). + +-include_lib("kernel/include/file.hrl"). + +%% Note: This directive should only be used in test suites. +-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). +-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. +%%-------------------------------------------------------------------- +all(doc) -> + ["Test the http client in the intes application."]; +all(suite) -> + [ + proxy_options, + proxy_head, + proxy_get, + proxy_trace, + proxy_post, + proxy_put, + proxy_delete, + proxy_auth, + proxy_headers, + proxy_emulate_lower_versions, + http_options, + http_head, + http_get, + http_post, + http_dummy_pipe, + http_inets_pipe, + http_trace, + http_async, + http_save_to_file, + http_save_to_file_async, + http_headers, + http_headers_dummy, + http_bad_response, + ssl_head, + ssl_get, + ssl_trace, + http_redirect, + http_redirect_loop, + http_internal_server_error, + http_userinfo, + http_cookie, + http_server_does_not_exist, + http_invalid_http, + http_emulate_lower_versions, + http_relaxed, + page_does_not_exist, + proxy_page_does_not_exist, + proxy_https_not_supported, + http_stream, + http_stream_once, + proxy_stream, + parse_url, + options, + ipv6, + headers_as_is, + tickets + ]. + +%%-------------------------------------------------------------------- +%% 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) -> + 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}), + + [{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(Case, Config) -> + io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE,Case]), + PrivDir = ?config(priv_dir, Config), + application:stop(inets), + Dog = test_server:timetrap(inets_test_lib:minutes(10)), + TmpConfig = lists:keydelete(watchdog, 1, Config), + IpConfFile = integer_to_list(?IP_PORT) ++ ".conf", + SslConfFile = integer_to_list(?SSL_PORT) ++ ".conf", + + NewConfig = + case atom_to_list(Case) of + "ssl" ++ _ -> + application:stop(ssl), + TmpConfig2 = + lists:keydelete(local_ssl_server, 1, TmpConfig), + Server = + %% Will start inets + inets_test_lib:start_http_server( + filename:join(PrivDir, + SslConfFile)), + [{watchdog, Dog}, {local_ssl_server, Server} | TmpConfig2]; + "proxy" ++ Rest -> + case Rest of + "_https_not_supported" -> + inets:start(), + case (catch application:start(ssl)) of + ok -> + [{watchdog, Dog} | TmpConfig]; + _ -> + [{skip, + "SSL does not seem to be supported"} + | TmpConfig] + end; + _ -> + case is_proxy_available(?PROXY, ?PROXY_PORT) of + true -> + inets:start(), + [{watchdog, Dog} | TmpConfig]; + false -> + [{skip, "Failed to contact proxy"} | + TmpConfig] + end + end; + _ -> + TmpConfig2 = lists:keydelete(local_server, 1, TmpConfig), + Server = + %% Will start inets + inets_test_lib:start_http_server( + filename:join(PrivDir, IpConfFile)), + [{watchdog, Dog}, {local_server, Server} | TmpConfig2] + end, + + http:set_options([{proxy, {{?PROXY, ?PROXY_PORT}, + ["localhost", ?IPV6_LOCAL_HOST]}}]), + inets:enable_trace(max, io), + NewConfig. + +%%-------------------------------------------------------------------- +%% 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, Config) -> + PrivDir = ?config(priv_dir, Config), + FullPath = filename:join(PrivDir, "dummy.html"), + file:delete(FullPath), + finish(Config); + +end_per_testcase(_, Config) -> + finish(Config). + +finish(Config) -> + Dog = ?config(watchdog, Config), + case Dog of + undefined -> + ok; + _ -> + test_server:timetrap_cancel(Dog) + end. + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- + +tickets(doc) -> + ["."]; +tickets(suite) -> + [ + 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, + otp_7883, + otp_8154 + ]. + + +%%------------------------------------------------------------------------- + +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) -> + case ?config(local_server, Config) of + ok -> + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + case http:request(head, {URL, []}, [], []) of + {ok, {{_,200,_}, [_ | _], []}} -> + ok; + {ok, WrongReply} -> + test_server:fail({wrong_reply, WrongReply}); + Error -> + test_server:fail({failed, Error}) + end; + _ -> + {skip, "Failed to start local http-server"} + end. +%%------------------------------------------------------------------------- +http_get(doc) -> + ["Test http get request against local server"]; +http_get(suite) -> + []; +http_get(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", + Timeout = timer:seconds(1), + ConnTimeout = Timeout + timer:seconds(1), + {ok, {{_,200,_}, [_ | _], Body = [_ | _]}} = + http:request(get, {URL, []}, + [{timeout, Timeout}, {connect_timeout, ConnTimeout}], []), + %% eqvivivalent to http:request(get, {URL, []}, [], []), + inets_test_lib:check_body(Body), + {ok, {{_,200,_}, [_ | _], Bin}} = + http:request(get, {URL, []}, [], [{body_format, binary}]), + case Bin of + Bin when is_binary(Bin) -> + ok; + _ -> + test_server:fail(body_format_not_binary) + 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,_}, [_ | _], [_ | _]}} = + http:request(post, {URL, [{"expect","100-continue"}], + "text/plain", Body}, [], []), + + {ok, {{_,504,_}, [_ | _], []}} = + http:request(post, {URL, [{"expect","100-continue"}], + "text/plain", "foobar"}, [], []); + _ -> + {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} = + http:request(get, {URL, []}, [{version, "HTTP/0.9"}], []), + inets_test_lib:check_body(Body0), + {ok, {{"HTTP/1.0", 200, _}, [_ | _], Body1 = [_ | _]}} = + http:request(get, {URL, []}, [{version, "HTTP/1.0"}], []), + inets_test_lib:check_body(Body1), + {ok, {{"HTTP/1.1", 200, _}, [_ | _], Body2 = [_ | _]}} = + http: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 = http:set_options([{ipv6, disabled}]), % also test the old option + %% ok = http:set_options([{ipfamily, inet}]), + DummyServerPid = dummy_server(self(), ipv4), + + Port = receive + {port, ServerPort} -> + ServerPort + end, + + URL = ?URL_START ++ integer_to_list(Port) ++ + "/missing_reason_phrase.html", + + {error, Reason} = + http:request(get, {URL, []}, [{relaxed, false}], []), + + test_server:format("Not relaxed: ~p~n", [Reason]), + + {ok, {{_, 200, _}, [_ | _], [_ | _]}} = + http:request(get, {URL, []}, [{relaxed, true}], []), + + DummyServerPid ! stop, + ok = http:set_options([{ipv6, enabled}]), + %% ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok. + + +%%------------------------------------------------------------------------- +http_dummy_pipe(doc) -> + ["Test pipelining code."]; +http_dummy_pipe(suite) -> + []; +http_dummy_pipe(Config) when is_list(Config) -> + ok = http:set_options([{ipfamily, inet}]), + DummyServerPid = dummy_server(self(), ipv4), + + Port = receive + {port, ServerPort} -> + ServerPort + end, + + URL = ?URL_START ++ integer_to_list(Port) ++ "/foobar.html", + + test_pipeline(URL), + + DummyServerPid ! stop, + ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + 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) -> + + http:set_options([{pipeline_timeout, 50000}]), + + {ok, RequestId1} = + http:request(get, {URL, []}, [], [{sync, false}]), + test_server:format("RequestId1: ~p~n", [RequestId1]), + + %% Make sure pipeline is initiated + test_server:sleep(4000), + + {ok, RequestId2} = + http:request(get, {URL, []}, [], [{sync, false}]), + test_server:format("RequestId2: ~p~n", [RequestId2]), + + {ok, {{_,200,_}, [_ | _], [_ | _]}} + = http:request(get, {URL, []}, [], []), + receive + {http, {RequestId1, {{_, 200, _}, _, _}}} -> + receive + {http, {RequestId2, {{_, 200, _}, _, _}}} -> + ok; + {http, Msg1} -> + test_server:fail(Msg1) + end; + {http, {RequestId2, {{_, 200, _}, _, _}}} -> + receive + {http, {RequestId1, {{_, 200, _}, _, _}}} -> + ok; + {http, Msg2} -> + test_server:fail(Msg2) + end; + {http, Msg3} -> + test_server:fail(Msg3) + end, + + {ok, RequestId3} = + http:request(get, {URL, []}, [], [{sync, false}]), + test_server:format("RequestId3: ~p~n", [RequestId3]), + {ok, RequestId4} = + http:request(get, {URL, []}, [], [{sync, false}]), + test_server:format("RequestId4: ~p~n", [RequestId4]), + ok = http:cancel_request(RequestId3), + receive + {http, {RequestId3, _}} -> + test_server:fail(http_cancel_request_failed) + after 3000 -> + ok + end, + Body = + receive + Res = {http, {RequestId4, {{_, 200, _}, _, BinBody4}}} -> + test_server:format(" Receive : ~p~n", [Res]), + BinBody4; + {http, Msg4} -> + test_server:fail(Msg4) + end, + inets_test_lib:check_body(binary_to_list(Body)), + + receive + {http, Any} -> + test_server:fail({unexpected_message, Any}) + after 500 -> + ok + end. +%%------------------------------------------------------------------------- +http_trace(doc) -> + ["Perform a TRACE request that goes through a proxy."]; +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 http:request(trace, {URL, []}, [], []) of + {ok, {{_,200,_}, [_ | _], "TRACE /dummy.html" ++ _}} -> + ok; + {ok, {{_,200,_}, [_ | _], WrongBody}} -> + test_server:fail({wrong_body, WrongBody}); + {ok, WrongReply} -> + test_server:fail({wrong_reply, WrongReply}); + Error -> + test_server:fail({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} = + http:request(get, {URL, []}, [], [{sync, false}]), + + Body = + receive + {http, {RequestId, {{_, 200, _}, _, BinBody}}} -> + BinBody; + {http, Msg} -> + test_server:fail(Msg) + end, + + inets_test_lib:check_body(binary_to_list(Body)), + + {ok, NewRequestId} = + http:request(get, {URL, []}, [], [{sync, false}]), + ok = http:cancel_request(NewRequestId), + receive + {http, {NewRequestId, _NewResult}} -> + test_server:fail(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} + = http:request(get, {URL, []}, [], [{stream, FilePath}]), + {ok, Bin} = file:read_file(FilePath), + {ok, {{_,200,_}, [_ | _], Body}} = http: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} = http:request(get, {URL, []}, [], + [{stream, FilePath}, + {sync, false}]), + receive + {http, {RequestId, saved_to_file}} -> + ok; + {http, Msg} -> + test_server:fail(Msg) + end, + + {ok, Bin} = file:read_file(FilePath), + {ok, {{_,200,_}, [_ | _], Body}} = http: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,_}, [_ | _], [_ | _]}} = + http:request(get, {URL, [{"If-Modified-Since", + Mod}, + {"From","[email protected]"}, + {"Date", Date} + ]}, [], []), + + Mod1 = httpd_util:rfc1123_date( + calendar:gregorian_seconds_to_datetime( + CreatedSec+1)), + + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + http:request(get, {URL, [{"If-UnModified-Since", + Mod1} + ]}, [], []), + + Tag = httpd_util:create_etag(FileInfo), + + + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + http:request(get, {URL, [{"If-Match", + Tag} + ]}, [], []), + + {ok, {{_,200,_}, [_ | _], _}} = + http: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 = http:set_options([{ipfamily, inet}]), + DummyServerPid = dummy_server(self(), ipv4), + + Port = receive + {port, ServerPort} -> + ServerPort + end, + + 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,_}, [_ | _], [_|_]}} = + http: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 = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + 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 = http:set_options([{ipfamily, inet}]), + DummyServerPid = dummy_server(self(), ipv4), + + Port = receive + {port, ServerPort} -> + ServerPort + end, + + URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_crlf.html", + + URL1 = ?URL_START ++ integer_to_list(Port) ++ "/wrong_statusline.html", + + {error, timeout} = http:request(get, {URL, []}, [{timeout, 400}], []), + + {error, Reason} = http:request(URL1), + + test_server:format("Wrong Statusline: ~p~n", [Reason]), + + DummyServerPid ! stop, + ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok. + + +%%------------------------------------------------------------------------- +ssl_head(doc) -> + ["Same as http_head/1 but over ssl sockets."]; +ssl_head(suite) -> + []; +ssl_head(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}], + {ok, {{_,200, _}, [_ | _], []}} = + http:request(head, {URL, []}, [{ssl, SSLOptions}], []); + {ok, _} -> + {skip, "Failed to start local http-server"}; + _ -> + {skip, "Failed to start SSL"} + end. +%%------------------------------------------------------------------------- +ssl_get(doc) -> + ["Same as http_get/1 but over ssl sockets."]; +ssl_get(suite) -> + []; +ssl_get(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}], + {ok, {{_,200, _}, [_ | _], Body = [_ | _]}} = + http:request(get, {URL, []}, [{ssl, SSLOptions}], []), + inets_test_lib:check_body(Body); + {ok, _} -> + {skip, "Failed to start local http-server"}; + _ -> + {skip, "Failed to start SSL"} + end. +%%------------------------------------------------------------------------- +ssl_trace(doc) -> + ["Same as http_trace/1 but over ssl sockets."]; +ssl_trace(suite) -> + []; +ssl_trace(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}], + case http:request(trace, {URL, []}, [{ssl, SSLOptions}], []) of + {ok, {{_,200, _}, [_ | _], "TRACE /dummy.html" ++ _}} -> + ok; + {ok, {{_,200,_}, [_ | _], WrongBody}} -> + test_server:fail({wrong_body, WrongBody}); + {ok, WrongReply} -> + test_server:fail({wrong_reply, WrongReply}); + Error -> + test_server:fail({failed, Error}) + end; + {ok, _} -> + {skip, "Failed to start local http-server"}; + _ -> + {skip, "Failed to start SSL"} + 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) -> + case ?config(local_server, Config) of + ok -> + ok = http:set_options([{ipfamily, inet}]), + DummyServerPid = dummy_server(self(), ipv4), + + Port = receive + {port, ServerPort} -> + ServerPort + end, + + URL300 = ?URL_START ++ integer_to_list(Port) ++ "/300.html", + + {ok, {{_,200,_}, [_ | _], [_|_]}} + = http:request(get, {URL300, []}, [], []), + + {ok, {{_,300,_}, [_ | _], _}} + = http:request(get, {URL300, []}, [{autoredirect, false}], + []), + + URL301 = ?URL_START ++ integer_to_list(Port) ++ "/301.html", + + + {ok, {{_,200,_}, [_ | _], [_|_]}} + = http:request(get, {URL301, []}, [], []), + + {ok, {{_,200,_}, [_ | _], []}} + = http:request(head, {URL301, []}, [], []), + + {ok, {{_,301,_}, [_ | _], [_|_]}} + = http:request(post, {URL301, [],"text/plain", "foobar"}, + [], []), + + URL302 = ?URL_START ++ integer_to_list(Port) ++ "/302.html", + + {ok, {{_,200,_}, [_ | _], [_|_]}} + = http:request(get, {URL302, []}, [], []), + + {ok, {{_,200,_}, [_ | _], []}} + = http:request(head, {URL302, []}, [], []), + + {ok, {{_,302,_}, [_ | _], [_|_]}} + = http:request(post, {URL302, [],"text/plain", "foobar"}, + [], []), + + URL307 = ?URL_START ++ integer_to_list(Port) ++ "/307.html", + + {ok, {{_,200,_}, [_ | _], [_|_]}} + = http:request(get, {URL307, []}, [], []), + + {ok, {{_,200,_}, [_ | _], []}} + = http:request(head, {URL307, []}, [], []), + + {ok, {{_,307,_}, [_ | _], [_|_]}} + = http:request(post, {URL307, [],"text/plain", "foobar"}, + [], []), + + DummyServerPid ! stop, + ok = http:set_options([{ipfamily, inet6fb4}]); % ********** ipfamily = inet6 ************* + _ -> + {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 = http:set_options([{ipfamily, inet}]), + DummyServerPid = dummy_server(self(), ipv4), + + Port = receive + {port, ServerPort} -> + ServerPort + end, + + URL = ?URL_START ++ integer_to_list(Port) ++ "/redirectloop.html", + + {ok, {{_,300,_}, [_ | _], _}} + = http:request(get, {URL, []}, [], []), + DummyServerPid ! stop, + ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok. + +%%------------------------------------------------------------------------- +http_internal_server_error(doc) -> + ["Test 50X codes"]; +http_internal_server_error(suite) -> + []; +http_internal_server_error(Config) when is_list(Config) -> + ok = http:set_options([{ipfamily, inet}]), + DummyServerPid = dummy_server(self(), ipv4), + + Port = receive + {port, ServerPort} -> + ServerPort + end, + + URL500 = ?URL_START ++ integer_to_list(Port) ++ "/500.html", + + {ok, {{_,500,_}, [_ | _], _}} + = http: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, _}, [_ | _], [_|_]}} = + http:request(get, {URL503, []}, [], []), + + ets:insert(unavailable, {503, long_unavailable}), + + {ok, {{_,503, _}, [_ | _], [_|_]}} = + http:request(get, {URL503, []}, [], []), + + ets:delete(unavailable), + DummyServerPid ! stop, + ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + 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 = http:set_options([{ipfamily, inet}]), + + DummyServerPid = dummy_server(self(), ipv4), + + Port = receive + {port, ServerPort} -> + ServerPort + end, + + URLAuth = "http://alladin:sesame@localhost:" + ++ integer_to_list(Port) ++ "/userinfo.html", + + {ok, {{_,200,_}, [_ | _], _}} + = http:request(get, {URLAuth, []}, [], []), + + URLUnAuth = "http://alladin:foobar@localhost:" + ++ integer_to_list(Port) ++ "/userinfo.html", + + {ok, {{_,401, _}, [_ | _], _}} = + http:request(get, {URLUnAuth, []}, [], []), + + DummyServerPid ! stop, + ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok. + + +%%------------------------------------------------------------------------- +http_cookie(doc) -> + ["Test cookies."]; +http_cookie(suite) -> + []; +http_cookie(Config) when is_list(Config) -> + ok = http:set_options([{cookies, enabled}, {ipfamily, inet}]), + DummyServerPid = dummy_server(self(), ipv4), + + Port = receive + {port, ServerPort} -> + ServerPort + end, + + URLStart = ?URL_START + ++ integer_to_list(Port), + + URLCookie = URLStart ++ "/cookie.html", + + {ok, {{_,200,_}, [_ | _], [_|_]}} + = http:request(get, {URLCookie, []}, [], []), + + ets:new(cookie, [named_table, public, set]), + ets:insert(cookie, {cookies, true}), + + {ok, {{_,200,_}, [_ | _], [_|_]}} + = http:request(get, {URLStart ++ "/", []}, [], []), + + ets:delete(cookie), + + ok = http:set_options([{cookies, disabled}, {ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + DummyServerPid ! stop, + ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6************ + ok. + +%%------------------------------------------------------------------------- +proxy_options(doc) -> + ["Perform a OPTIONS request that goes through a proxy."]; +proxy_options(suite) -> + []; +proxy_options(Config) when is_list(Config) -> + case ?config(skip, Config) of + undefined -> + case http:request(options, {?PROXY_URL, []}, [], []) of + {ok, {{_,200,_}, Headers, _}} -> + case lists:keysearch("allow", 1, Headers) of + {value, {"allow", _}} -> + ok; + _ -> + test_server:fail(http_options_request_failed) + end; + Unexpected -> + test_server:fail({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) -> + case ?config(skip, Config) of + undefined -> + case http:request(head, {?PROXY_URL, []}, [], []) of + {ok, {{_,200, _}, [_ | _], []}} -> + ok; + Unexpected -> + test_server:fail({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 http:request(get, {?PROXY_URL, []}, [], []) of + {ok, {{_,200,_}, [_ | _], Body = [_ | _]}} -> + inets_test_lib:check_body(Body); + Unexpected -> + test_server:fail({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 -> + {ok, Body0 = [_| _]} = http:request(get, {?PROXY_URL, []}, + [{version, "HTTP/0.9"}], []), + inets_test_lib:check_body(Body0), + + %% We do not check the version here as many servers + %% do not behave according to the rfc and send + %% 1.1 in its response. + {ok,{{_, 200, _}, [_ | _], Body1 = [_ | _]}} = + http:request(get, {?PROXY_URL, []}, + [{version, "HTTP/1.0"}], []), + inets_test_lib:check_body(Body1), + + {ok, {{"HTTP/1.1", 200, _}, [_ | _], Body2 = [_ | _]}} = + http:request(get, {?PROXY_URL, []}, + [{version, "HTTP/1.1"}], []), + inets_test_lib:check_body(Body2); + Reason -> + {skip, Reason} + end. + +%%------------------------------------------------------------------------- +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 " ++ _}} = + %% http: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) -> + case ?config(skip, Config) of + undefined -> + case http:request(post, {?PROXY_URL, [], + "text/plain", "foobar"}, [],[]) of + {ok, {{_,405,_}, [_ | _], [_ | _]}} -> + ok; + Unexpected -> + test_server:fail({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) -> + case ?config(skip, Config) of + undefined -> + case http:request(put, {"http://www.erlang.org/foobar.html", [], + "html", "<html> <body><h1> foo </h1>" + "<p>bar</p> </body></html>"}, [], []) of + {ok, {{_,405,_}, [_ | _], [_ | _]}} -> + ok; + Unexpected -> + test_server:fail({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) -> + case ?config(skip, Config) of + undefined -> + URL = ?PROXY_URL ++ "/foobar.html", + case http:request(delete, {URL, []}, [], []) of + {ok, {{_,404,_}, [_ | _], [_ | _]}} -> + ok; + Unexpected -> + test_server:fail({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,_}, [_ | _], [_ | _]}} + = http: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 http:request(get, {?PROXY_URL, []}, + [{proxy_auth, {"foo", "bar"}}], []) of + {ok, {{_,200, _}, [_ | _], [_|_]}} -> + ok; + Unexpected -> + test_server:fail({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 " + "does note exist."]; +http_server_does_not_exist(suite) -> + []; +http_server_does_not_exist(Config) when is_list(Config) -> + {error, _} = + http: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,_}, [_ | _], [_ | _]}} + = http:request(get, {URL, []}, [], []), + 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,_}, [_ | _], [_ | _]}} = + http: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) -> + {error, https_through_proxy_is_not_currently_supported} + = http:request(get, {"https://login.yahoo.com", []}, [], []), + 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}} = + http:request(get, {URL, []}, [], []), + + {ok, RequestId} = + http:request(get, {URL, []}, [], [{sync, false}, + {stream, self}]), + + receive + {http, {RequestId, stream_start, _Headers}} -> + ok; + {http, Msg} -> + test_server:fail(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) -> + ok = http:set_options([{ipfamily, inet}]), + DummyServerPid = dummy_server(self(), ipv4), + Port = receive + {port, ServerPort} -> + ServerPort + end, + + PortStr = integer_to_list(Port), + once(?URL_START ++ PortStr ++ "/once.html"), + once(?URL_START ++ PortStr ++ "/once_chunked.html"), + once(?URL_START ++ PortStr ++ "/dummy.html"), + + DummyServerPid ! stop, + ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok. + +once(URL) -> + {ok, {{_,200,_}, [_ | _], Body}} = + http:request(get, {URL, []}, [], []), + + {ok, RequestId} = + http:request(get, {URL, []}, [], [{sync, false}, + {stream, {self, once}}]), + + NewPid = receive + {http, {RequestId, stream_start, _Headers, Pid}} -> + Pid; + {http, Msg} -> + test_server:fail(Msg) + end, + + test_server:format("Request handler: ~p~n", [NewPid]), + + BodyPart = + receive + {http, {RequestId, stream, BinBodyPart}} -> + BinBodyPart + end, + + test_server:format("First body part: ~p~n", + [binary_to_list(BodyPart)]), + + StreamedBody = receive_streamed_body(RequestId, BinBodyPart, NewPid), + + Body = binary_to_list(StreamedBody), + 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}} = + http:request(get, {?PROXY_URL, []}, [], []), + + {ok, RequestId} = + http:request(get, {?PROXY_URL, []}, [], + [{sync, false}, {stream, self}]), + + receive + {http, {RequestId, stream_start, _Headers}} -> + ok; + {http, Msg} -> + test_server:fail(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"]; +parse_url(suite) -> + []; +parse_url(Config) when is_list(Config) -> + %% ipv6 + {http,[],"2010:836B:4179::836B:4179",80,"/foobar.html",[]} + = http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html"), + {error, + {malformed_url,"http://2010:836B:4179::836B:4179/foobar.html"}} = + http_uri:parse("http://2010:836B:4179::836B:4179/foobar.html"), + + %% ipv4 + {http,[],"127.0.0.1",80,"/foobar.html",[]} = + http_uri:parse("http://127.0.0.1/foobar.html"), + + %% host + {http,[],"localhost",8888,"/foobar.html",[]} = + http_uri:parse("http://localhost:8888/foobar.html"), + + %% Userinfo + {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,{not_supported_scheme,localhost}} = + http_uri:parse("localhost:8888/foobar.html"), + + %% Query + {http,[],"localhost",8888,"/foobar.html","?foo=bar&foobar=42"} = + http_uri:parse("http://localhost:8888/foobar.html?foo=bar&foobar=42"), + + %% Esc chars + {http,[],"www.somedomain.com",80,"/%2Eabc",[]} = + http_uri:parse("http://www.somedomain.com/%2Eabc"), + {http,[],"www.somedomain.com",80,"/%252Eabc",[]} = + http_uri:parse("http://www.somedomain.com/%252Eabc"), + {http,[],"www.somedomain.com",80,"/%25abc",[]} = + http_uri:parse("http://www.somedomain.com/%25abc"), + {http,[],"www.somedomain.com",80,"/%25abc", "?foo=bar"} = + http_uri:parse("http://www.somedomain.com/%25abc?foo=bar"), + ok. + + +%%------------------------------------------------------------------------- +ipv6(doc) -> + ["Test ipv6."]; +ipv6(suite) -> + []; +ipv6(Config) when is_list(Config) -> + {ok, Hostname} = inet:gethostname(), + + case lists:member(list_to_atom(Hostname), + ?config(ipv6_hosts, Config)) of + true -> + DummyServerPid = dummy_server(self(), ipv6), + + Port = receive + {port, ServerPort} -> + ServerPort + end, + + URL = "http://[" ++ ?IPV6_LOCAL_HOST ++ "]:" ++ + integer_to_list(Port) ++ "/foobar.html", + {ok, {{_,200,_}, [_ | _], [_|_]}} = + http:request(get, {URL, []}, [], []), + + DummyServerPid ! stop, + ok; + false -> + {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,_}, [_|_], [_|_]}} = + http:request(get, {URL, [{"Host", "localhost"},{"Te", ""}]}, + [], [{headers_as_is, true}]), + + {ok, {{_,400,_}, [_|_], [_|_]}} = + http:request(get, {URL, [{"Te", ""}]},[], [{headers_as_is, true}]), + ok. + + +%%------------------------------------------------------------------------- +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}} + = http:request(get, {URL, []}, [{foo, bar}], + %% Ignore unknown options + [{body_format, binary}, {foo, bar}]), + + true = is_binary(Bin), + {ok, {200, [_|_]}} + = http: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 = http:set_options([{ipfamily, inet}]), + DummyServerPid = dummy_server(self(), ipv4), + + Port = receive + {port, ServerPort} -> + ServerPort + end, + + URL = ?URL_START ++ integer_to_list(Port) ++ "/invalid_http.html", + + {error, {could_not_parse_as_http, _} = Reason} = + http:request(get, {URL, []}, [], []), + + test_server:format("Parse error: ~p ~n", [Reason]), + DummyServerPid ! stop, + ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok. + + +%%------------------------------------------------------------------------- + +hexed_query_otp_6191(doc) -> + []; +hexed_query_otp_6191(suite) -> + []; +hexed_query_otp_6191(Config) when is_list(Config) -> + Google = "www.google.com", + 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, + + {http, [], Google, 80, "/search", _} = http_uri:parse(URI1), + {http, [], Google, 80, "/search", _} = http_uri:parse(URI2), + {http, [], Google, 80, "/search", _} = http_uri:parse(URI3), + ok. + + +%%------------------------------------------------------------------------- + +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,_}, [_ | _], []}} = + http: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 = http:set_options([{ipfamily, inet}]), + DummyServerPid = dummy_server(self(), ipv4), + + Port = receive + {port, ServerPort} -> + ServerPort + end, + + URL = ?URL_START ++ integer_to_list(Port) ++ + "/capital_transfer_encoding.html", + {ok, {{_,200,_}, [_|_], [_ | _]}} = http:request(URL), + DummyServerPid ! stop, + ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + 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) -> + ["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 = http:set_options([{ipfamily, inet}]), + DummyServerPid = dummy_server(self(), ipv4), + + Port = receive + {port, ServerPort} -> + ServerPort + end, + + URL = ?URL_START ++ integer_to_list(Port) ++ "/no_headers.html", + {ok, {{_,200,_}, [], [_ | _]}} = http:request(URL), + DummyServerPid ! stop, + ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + 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 = http:set_options([{ipfamily, inet}]), + DummyServerPid = dummy_server(self(), ipv4), + + Port = receive + {port, ServerPort} -> + ServerPort + end, + + URL = ?URL_START ++ integer_to_list(Port) ++ "/no_content.html", + {ok, {{_,204,_}, [], []}} = http:request(URL), + DummyServerPid ! stop, + ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + 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 = http:set_options([{ipfamily, inet}]), + DummyServerPid = dummy_server(self(), ipv4), + + Port = receive + {port, ServerPort} -> + ServerPort + end, + + URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_CR.html", + {ok, {{_,200,_}, _, [_ | _]}} = http:request(URL), + DummyServerPid ! stop, + ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok. + + +%%------------------------------------------------------------------------- + +otp_7883(suite) -> + [otp_7883_1, otp_7883_2]. + +otp_7883_1(doc) -> + ["OTP-7883-sync"]; +otp_7883_1(suite) -> + []; +otp_7883_1(Config) when is_list(Config) -> + ok = http:set_options([{ipfamily, inet}]), + + DummyServerPid = dummy_server(self(), ipv4), + + Port = receive + {port, ServerPort} -> + ServerPort + end, + + URL = ?URL_START ++ integer_to_list(Port) ++ "/just_close.html", + {error, socket_closed_remotely} = http:request(URL), + DummyServerPid ! stop, + + ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok. + +otp_7883_2(doc) -> + ["OTP-7883-async"]; +otp_7883_2(suite) -> + []; +otp_7883_2(Config) when is_list(Config) -> + ok = http:set_options([{ipfamily, inet}]), + + DummyServerPid = dummy_server(self(), ipv4), + + Port = receive + {port, ServerPort} -> + ServerPort + end, + + URL = ?URL_START ++ integer_to_list(Port) ++ "/just_close.html", + Method = get, + Request = {URL, []}, + HttpOptions = [], + Options = [{sync, false}], + Profile = http:default_profile(), + {ok, RequestId} = + http:request(Method, Request, HttpOptions, Options, Profile), + ok = + receive + {http, {RequestId, {error, socket_closed_remotely}}} -> + ok + end, + DummyServerPid ! stop, + + ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok. + + +%%------------------------------------------------------------------------- + +otp_8154(suite) -> + [otp_8154_1]. + +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(2)), + 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 http: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, <<Char>>) + 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)). + + + + +%%-------------------------------------------------------------------- +%% 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", + + HttpConfig = [ + 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"]). + +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}} -> + receive_streamed_body(RequestId, + <<Body/binary, BinBodyPart/binary>>); + {http, {RequestId, stream_end, _Headers}} -> + Body; + {http, Msg} -> + test_server:fail(Msg) + end. + +receive_streamed_body(RequestId, Body, Pid) -> + http:stream_next(Pid), + test_server:format("Requested next stream ~n", []), + receive + {http, {RequestId, stream, BinBodyPart}} -> + receive_streamed_body(RequestId, + <<Body/binary, BinBodyPart/binary>>, + Pid); + {http, {RequestId, stream_end, _Headers}} -> + Body; + {http, Msg} -> + test_server:fail(Msg) + end. + + + +dummy_server(Caller, IpV) -> + spawn(httpc_SUITE, dummy_server_init, [Caller, IpV]). + +dummy_server_init(Caller, IpV) -> + {ok, ListenSocket} = + case IpV of + ipv4 -> + gen_tcp:listen(0, [binary, inet, {packet, 0}, + {reuseaddr,true}, + {active, false}]); + ipv6 -> + gen_tcp:listen(0, [binary, inet6, {packet, 0}, + {reuseaddr,true}, + {active, false}]) + end, + {ok, Port} = inet:port(ListenSocket), + test_server:format("Port: ~p~n", [Port]), + Caller ! {port, Port}, + dummy_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]}, + [], ListenSocket). + +dummy_server_loop(MFA, Handlers, ListenSocket) -> + receive + stop -> + lists:foreach(fun(Handler) -> Handler ! stop end, + Handlers) + after 0 -> + {ok, Socket} = gen_tcp:accept(ListenSocket), + HandlerPid = dummy_request_handler(MFA, Socket), + gen_tcp:controlling_process(Socket, HandlerPid), + HandlerPid ! controller, + dummy_server_loop(MFA, [HandlerPid | Handlers], + ListenSocket) + end. + +dummy_request_handler(MFA, Socket) -> + spawn(httpc_SUITE, dummy_request_handler_init, [MFA, Socket]). + +dummy_request_handler_init(MFA, Socket) -> + receive + controller -> + inet:setopts(Socket, [{active, true}]) + end, + dummy_request_handler_loop(MFA, Socket). + +dummy_request_handler_loop({Module, Function, Args}, Socket) -> + receive + {tcp, _, Data} -> + test_server:format("dummy_request_handler_loop -> Data ~p~n", [Data]), + case handle_request(Module, Function, [Data | Args], Socket) of + stop -> + gen_tcp:close(Socket); + NewMFA -> + dummy_request_handler_loop(NewMFA, Socket) + end; + stop -> + gen_tcp:close(Socket) + end. + +handle_request(Module, Function, Args, Socket) -> + case Module:Function(Args) of + {ok, Result} -> + case handle_http_msg(Result, Socket) of + stop -> + stop; + <<>> -> + {httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]}; + Data -> + handle_request(httpd_request, parse, + [Data |[?HTTP_MAX_HEADER_SIZE]], + Socket) + end; + NewMFA -> + NewMFA + end. + +handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) -> + + 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, + + test_server:format("NextRequest: ~p~n", [NextRequest]), + + case (catch ets:lookup(cookie, cookies)) of + [{cookies, true}]-> + test_server:format("Headers ~p~n", [Headers]), + check_cookie(Headers); + _ -> + ok + end, + + DefaultResponse = "HTTP/1.1 200 ok\r\n" ++ + "Content-Length:32\r\n\r\n" + "<HTML><BODY>foobar</BODY></HTML>", + + 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"; + "/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" ++ + "<HTML><BODY><a href=" ++ NewUri ++ + ">New place</a></BODY></HTML>"; + "/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" ++ + "<HTML><BODY><a href=" ++ NewUri ++ + ">New place</a></BODY></HTML>"; + "/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" ++ + "<HTML><BODY><a href=" ++ NewUri ++ + ">New place</a></BODY></HTML>"; + "/500.html" -> + "HTTP/1.1 500 Internal Server Error\r\n" ++ + "Content-Length:47\r\n\r\n" ++ + "<HTML><BODY>Internal Server Error</BODY<</HTML>"; + "/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" ++ + "<HTML><BODY>Internal Server Error</BODY></HTML>"; + [{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" ++ + "<HTML><BODY>Internal Server Error</BODY></HTML>" + 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", + gen_tcp:send(Socket, Head), + gen_tcp:send(Socket, http_chunk:encode("<HTML><BODY>fo")), + gen_tcp:send(Socket, http_chunk:encode("obar</BODY></HTML>")), + http_chunk:encode_last(); + "/capital_transfer_encoding.html" -> + Head = "HTTP/1.1 200 ok\r\n" ++ + "Transfer-Encoding:Chunked\r\n\r\n", + gen_tcp:send(Socket, Head), + gen_tcp:send(Socket, http_chunk:encode("<HTML><BODY>fo")), + gen_tcp:send(Socket, http_chunk:encode("obar</BODY></HTML>")), + 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"++ + "<HTML><BODY>foobar</BODY></HTML>"; + "/missing_crlf.html" -> + "HTTP/1.1 200 ok" ++ + "Content-Length:32\r\n" ++ + "<HTML><BODY>foobar</BODY></HTML>"; + "/wrong_statusline.html" -> + "ok 200 HTTP/1.1\r\n\r\n" ++ + "Content-Length:32\r\n\r\n" ++ + "<HTML><BODY>foobar</BODY></HTML>"; + "/once_chunked.html" -> + Head = "HTTP/1.1 200 ok\r\n" ++ + "Transfer-Encoding:Chunked\r\n\r\n", + gen_tcp:send(Socket, Head), + gen_tcp:send(Socket, http_chunk:encode("<HTML><BODY>fo")), + gen_tcp:send(Socket, + http_chunk:encode("obar</BODY></HTML>")), + http_chunk:encode_last(); + "/once.html" -> + Head = "HTTP/1.1 200 ok\r\n" ++ + "Content-Length:32\r\n\r\n", + gen_tcp:send(Socket, Head), + gen_tcp:send(Socket, "<HTML><BODY>fo"), + test_server:sleep(1000), + gen_tcp:send(Socket, "ob"), + test_server:sleep(1000), + gen_tcp:send(Socket, "ar</BODY></HTML>"); + "/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" + "<HTML><BODY>foobar</BODY></HTML>"; + "/missing_CR.html" -> + "HTTP/1.1 200 ok\n" ++ + "Content-Length:32\r\n\n" + "<HTML><BODY>foobar</BODY></HTML>"; + _ -> + DefaultResponse + end, + + test_server:format("Msg: ~p~n", [Msg]), + case Msg of + close -> + %% Nothing to send, just close + gen_tcp:close(Socket); + _ -> + gen_tcp:send(Socket, Msg) + end, + NextRequest. + +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([]) -> + test_server:fail(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). + +provocate_not_modified_bug(Url) -> + Timeout = 15000, %% 15s should be plenty + + {ok, {{_, 200, _}, ReplyHeaders, _Body}} = + http:request(get, {Url, []}, [{timeout, Timeout}], []), + Etag = pick_header(ReplyHeaders, "ETag"), + Last = pick_header(ReplyHeaders, "last-modified"), + + case http: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. + + +%% p(F, A) -> +%% io:format("~p ~w:" ++ F ++ "~n", [self(), ?MODULE | A]). diff --git a/lib/inets/test/httpc_SUITE_data/Makefile.src b/lib/inets/test/httpc_SUITE_data/Makefile.src new file mode 120000 index 0000000000..ad6e7bf9c8 --- /dev/null +++ b/lib/inets/test/httpc_SUITE_data/Makefile.src @@ -0,0 +1 @@ +../httpd_SUITE_data/Makefile.src
\ No newline at end of file diff --git a/lib/inets/test/httpc_SUITE_data/cgi_echo.c b/lib/inets/test/httpc_SUITE_data/cgi_echo.c new file mode 120000 index 0000000000..38e06fe0d2 --- /dev/null +++ b/lib/inets/test/httpc_SUITE_data/cgi_echo.c @@ -0,0 +1 @@ +../httpd_SUITE_data/cgi_echo.c
\ No newline at end of file diff --git a/lib/inets/test/httpc_SUITE_data/dummy.html b/lib/inets/test/httpc_SUITE_data/dummy.html new file mode 100644 index 0000000000..9a960ecc8a --- /dev/null +++ b/lib/inets/test/httpc_SUITE_data/dummy.html @@ -0,0 +1,12 @@ +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> +<html> +<head> +<title>Dummy</title> +</head> + +<body> +<h1>Dummy</h1> + +<p>This is a dummy html file! </p> +</body> +</html> diff --git a/lib/inets/test/httpc_SUITE_data/empty.html b/lib/inets/test/httpc_SUITE_data/empty.html new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/inets/test/httpc_SUITE_data/empty.html diff --git a/lib/inets/test/httpc_SUITE_data/mime.types b/lib/inets/test/httpc_SUITE_data/mime.types new file mode 100644 index 0000000000..d2f81e4e5e --- /dev/null +++ b/lib/inets/test/httpc_SUITE_data/mime.types @@ -0,0 +1,465 @@ +# 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/httpc_SUITE_data/redirect.html b/lib/inets/test/httpc_SUITE_data/redirect.html new file mode 100644 index 0000000000..7034461ed6 --- /dev/null +++ b/lib/inets/test/httpc_SUITE_data/redirect.html @@ -0,0 +1,10 @@ +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> +<html> <head> +<meta http-eqviv="Refresh", content="10;URL=http://localhost:8888/dummy.html"> +<title>Redirect</title> + +</head> + +<body> +<p> You will be redirected</p> +</body> </html> diff --git a/lib/inets/test/httpc_SUITE_data/ssl_client_cert.pem b/lib/inets/test/httpc_SUITE_data/ssl_client_cert.pem new file mode 100644 index 0000000000..f274d2021d --- /dev/null +++ b/lib/inets/test/httpc_SUITE_data/ssl_client_cert.pem @@ -0,0 +1,22 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBANz7eFvORmJDi1XJMM2U3uHC5wmp/DXTLMw08XaEvtZ73wgVg84E +V0oyX3Kh1thRE3Hch9AyrHjgpizCj9/Ra38CAwEAAQJACzpz2SZYCTIpaEh6xFdm +I86FcsZCXHHIeu/NvRntoHQ+nfM7Np379+z6XNJWIcWh/QgG/jNJalR1BO+eyc6/ +YQIhAP3m8M0LDxJwSgHFtGAGatQqaqw9l48Kq5xdMFqvdpiHAiEA3s7lld6yCJYu +6q7fZjTH+eKUwgg0vpgJutP7Fsok60kCIHHesQBEhW3vjkFdOZgXSLH+k/jLZr1w +O6bU5GrHZpjhAiEAyTvGYcjDtTunXjDY9l+fadK6FlEBCk8ZIpNIiTnDhHkCIQDr +QxxLLuNHRj8iWNbuVVZ99SJy8zC33pMgPFaFKaZesQ== +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIB7jCCAZgCAQAwDQYJKoZIhvcNAQEEBQAwgYExCzAJBgNVBAYTAlNFMRIwEAYD +VQQHEwlTdG9ja2hvbG0xETAPBgNVBAoTCEVyaWNzc29uMQwwCgYDVQQLEwNFVFgx +FjAUBgNVBAMTDUhlbGVuIEFpcml5YW4xJTAjBgkqhkiG9w0BCQEWFmhlbGVuQGVy +aXguZXJpY3Nzb24uc2UwHhcNOTcwNzI4MDcxNDI1WhcNOTgxMjEwMDcxNDI1WjCB +gTELMAkGA1UEBhMCU0UxEjAQBgNVBAcTCVN0b2NraG9sbTERMA8GA1UEChMIRXJp +Y3Nzb24xDDAKBgNVBAsTA0VUWDEWMBQGA1UEAxMNSGVsZW4gQWlyaXlhbjElMCMG +CSqGSIb3DQEJARYWaGVsZW5AZXJpeC5lcmljc3Nvbi5zZTBcMA0GCSqGSIb3DQEB +AQUAA0sAMEgCQQDc+3hbzkZiQ4tVyTDNlN7hwucJqfw10yzMNPF2hL7We98IFYPO +BFdKMl9yodbYURNx3IfQMqx44KYswo/f0Wt/AgMBAAEwDQYJKoZIhvcNAQEEBQAD +QQC2++hLIaQJ4ChCjFE9UCfXO9cZ3Vq/FT9VjE+G4MRBDo4LQ5mBKNXcPF6EFZmi +7XrlvopXkVPlRguTi2SLRPkY +-----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 new file mode 100644 index 0000000000..f01b6c992b --- /dev/null +++ b/lib/inets/test/httpc_SUITE_data/ssl_server_cert.pem @@ -0,0 +1,22 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBOQIBAAJBAMe2WhP6s+JeKOwWPEjI9susfN4Vjn2dd1X4QUlOETcWVLoF916m +M4JU+ms7+ciMR8GRNCsIeqZGY8/GSqm74ccCAwEAAQJAF08YKlbLYfM9cXiS5qfV +7iWemUkIzW5wfC8yZ3zeE4Cp6R9ViUfs/dadQ/23Cw0Bpo2t8UdTUdCa4KpmqOem +cQIhAOnxTWZ5eo6h6PXDp7L5FZUACg8+wT3qf5f2is2mbSZPAiEA2orUY8JZDTSk +Rm7q9WxLiLNtORsXdTCmnCWhqBOYpwkCIErdowRxScxNekz0IT3AQqzdR1rbnWHg +IpcSGhd39CQ3AiA1XvQxjLP8wp9fyBS/bPwhXVhOOuyGpSP7PEF3b5m3KQIgGQWc +/a5wuWx3pc3mLx0ILwNoJr2ubFEuW1PJPsPJPv0= +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIB7jCCAZgCAQAwDQYJKoZIhvcNAQEEBQAwgYExCzAJBgNVBAYTAlNFMRIwEAYD +VQQHEwlTdG9ja2hvbG0xETAPBgNVBAoTCEVyaWNzc29uMQwwCgYDVQQLEwNFVFgx +FjAUBgNVBAMTDUhlbGVuIEFpcml5YW4xJTAjBgkqhkiG9w0BCQEWFmhlbGVuQGVy +aXguZXJpY3Nzb24uc2UwHhcNOTcwNzI4MDcyMTAwWhcNOTgxMjEwMDcyMTAwWjCB +gTELMAkGA1UEBhMCU0UxEjAQBgNVBAcTCVN0b2NraG9sbTERMA8GA1UEChMIRXJp +Y3Nzb24xDDAKBgNVBAsTA0VUWDEWMBQGA1UEAxMNSGVsZW4gQWlyaXlhbjElMCMG +CSqGSIb3DQEJARYWaGVsZW5AZXJpeC5lcmljc3Nvbi5zZTBcMA0GCSqGSIb3DQEB +AQUAA0sAMEgCQQDHtloT+rPiXijsFjxIyPbLrHzeFY59nXdV+EFJThE3FlS6Bfde +pjOCVPprO/nIjEfBkTQrCHqmRmPPxkqpu+HHAgMBAAEwDQYJKoZIhvcNAQEEBQAD +QQCnU1TkxmfbLdUwjdECb5x9QHCevAR7AmTms4Csn2oOEyPX+bgF2d94xhrV1sxO +Rs0yigk1PtN17Ci0Dey0LYkR +-----END CERTIFICATE----- diff --git a/lib/inets/test/httpc_cookie_SUITE.erl b/lib/inets/test/httpc_cookie_SUITE.erl new file mode 100644 index 0000000000..7d91631934 --- /dev/null +++ b/lib/inets/test/httpc_cookie_SUITE.erl @@ -0,0 +1,338 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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(httpc_cookie_SUITE). + +-include("test_server.hrl"). +-include_lib("stdlib/include/ms_transform.hrl"). + +%% Test server specific exports +-export([all/1, init_per_testcase/2, end_per_testcase/2]). + +%% Test cases must be exported. +-export([session_cookies_only/1, netscape_cookies/1, + cookie_cancel/1, cookie_expires/1, persistent_cookie/1, + domain_cookie/1, secure_cookie/1, update_cookie/1, + update_cookie_session/1, cookie_attributes/1]). + +-define(URL, "http://myhost.cookie.test.org"). +-define(URL_DOMAIN, "http://myhost2.cookie.test.org"). +-define(URL_SECURE, "https://myhost.cookie.test.org"). + +%% Test server callback functions + +%%-------------------------------------------------------------------- +%% 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(session_cookies_only, Config) -> + application:start(inets), + http:set_options([{cookies, verify}]), + watch_dog(Config); + +init_per_testcase(_, Config) -> + PrivDir = ?config(priv_dir, Config), + application:load(inets), + application:set_env(inets, services, [{httpc,{default, PrivDir}}]), + application:start(inets), + http:set_options([{cookies, verify}]), + watch_dog(Config). + +watch_dog(Config) -> + Dog = test_server:timetrap(inets_test_lib:minutes(10)), + NewConfig = lists:keydelete(watchdog, 1, Config), + [{watchdog, Dog} | NewConfig]. + +%%-------------------------------------------------------------------- +%% 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(_TestCase, Config) -> + application:stop(inets), + File = filename:join(?config(priv_dir, Config), "http_default_cookie_db"), + file:delete(File), + Dog = ?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +%%-------------------------------------------------------------------- +%% 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(doc) -> + ["Describe the main purpose of this suite"]; + +all(suite) -> + [session_cookies_only, netscape_cookies, cookie_cancel, + cookie_expires, persistent_cookie, domain_cookie, secure_cookie, + update_cookie, update_cookie_session, cookie_attributes]. + +%% Test cases starts here. +%%-------------------------------------------------------------------- +session_cookies_only(doc) -> + ["Test that all cookies are handled as session cookies if there" + "does not exist a directory to save presitent cookies in."]; +session_cookies_only(suite) -> + []; +session_cookies_only(Config) when is_list(Config) -> + SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" + ";max-age=60000"}], + http:verify_cookies(SetCookieHeaders, ?URL), + {"cookie","$Version=0; test_cookie=true; $Path=/"} + = http:cookie_header(?URL), + application:stop(inets), + application:start(inets), + {"cookie",""} + = http:cookie_header(?URL), + ok. + +netscape_cookies(doc) -> + ["Test that the old (original) format of cookies are accepted."]; +netscape_cookies(suite) -> + []; +netscape_cookies(Config) when is_list(Config) -> + Expires = future_netscape_date(), + SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/; " + "expires=" ++ Expires}], + http:verify_cookies(SetCookieHeaders, ?URL), + {"cookie","$Version=0; test_cookie=true; $Path=/"} + = http:cookie_header(?URL), + ok. + +cookie_cancel(doc) -> + ["A cookie can be canceld by sending the same cookie with max-age=0 " + "this test cheks that cookie is canceled."]; +cookie_cancel(suite) -> + []; +cookie_cancel(Config) when is_list(Config) -> + SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" + "max-age=60000"}], + http:verify_cookies(SetCookieHeaders, ?URL), + {"cookie","$Version=0; test_cookie=true; $Path=/"} + = http:cookie_header(?URL), + NewSetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" + "max-age=0"}], + http:verify_cookies(NewSetCookieHeaders, ?URL), + {"cookie", ""} = http:cookie_header(?URL), + ok. + +cookie_expires(doc) -> + ["Test that a cookie is not used when it has expired"]; +cookie_expires(suite) -> + []; +cookie_expires(Config) when is_list(Config) -> + SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" + "max-age=5"}], + http:verify_cookies(SetCookieHeaders, ?URL), + {"cookie","$Version=0; test_cookie=true; $Path=/"} + = http:cookie_header(?URL), + test_server:sleep(10000), + {"cookie", ""} = http:cookie_header(?URL), + ok. + +persistent_cookie(doc)-> + ["Test domian cookie attribute"]; +persistent_cookie(suite) -> + []; +persistent_cookie(Config) when is_list(Config)-> + SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" + "max-age=60000"}], + http:verify_cookies(SetCookieHeaders, ?URL), + {"cookie","$Version=0; test_cookie=true; $Path=/"} + = http:cookie_header(?URL), + PrivDir = ?config(priv_dir, Config), + application:stop(inets), + application:load(inets), + application:set_env(inets, services, [{httpc,{default, PrivDir}}]), + application:start(inets), + http:set_options([{cookies, enabled}]), + {"cookie","$Version=0; test_cookie=true; $Path=/"} + = http:cookie_header(?URL), + ok. + +domain_cookie(doc)-> + ["Test the domian cookie attribute"]; +domain_cookie(suite) -> + []; +domain_cookie(Config) when is_list(Config)-> + SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" + "domain=.cookie.test.org"}], + http:verify_cookies(SetCookieHeaders, ?URL), + {"cookie","$Version=0; test_cookie=true; $Path=/; " + "$Domain=.cookie.test.org"} + = http:cookie_header(?URL_DOMAIN), + ok. + +secure_cookie(doc)-> + ["Test the secure cookie attribute"]; +secure_cookie(suite) -> + []; +secure_cookie(Config) when is_list(Config)-> + SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/; secure"}], + http:verify_cookies(SetCookieHeaders, ?URL), + {"cookie","$Version=0; test_cookie=true; $Path=/"} + = http:cookie_header(?URL_SECURE), + {"cookie",""} + = http:cookie_header(?URL), + SetCookieHeaders1 = [{"set-cookie", "test1_cookie=true; path=/; secure"}], + http:verify_cookies(SetCookieHeaders1, ?URL), + {"cookie","$Version=0; test_cookie=true; $Path=/; " + "test1_cookie=true; $Path=/"} = http:cookie_header(?URL_SECURE), + ok. + +update_cookie(doc)-> + ["Test that a cookie can be updated."]; +update_cookie(suite) -> + []; +update_cookie(Config) when is_list(Config)-> + SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" + "max-age=6500"}, + {"set-cookie", "test_cookie2=true; path=/;" + "max-age=6500"}], + http:verify_cookies(SetCookieHeaders, ?URL), + {"cookie", "$Version=0; test_cookie2=true; $Path=/; " + "test_cookie=true; $Path=/"} = http:cookie_header(?URL), + NewSetCookieHeaders = [{"set-cookie", "test_cookie=false; " + "path=/;max-age=6500"}], + http:verify_cookies(NewSetCookieHeaders, ?URL), + {"cookie", "$Version=0; test_cookie2=true; $Path=/; " + "test_cookie=false; $Path=/"} = http:cookie_header(?URL). + +update_cookie_session(doc)-> + ["Test that a cookie can be updated."]; +update_cookie_session(suite) -> + []; +update_cookie_session(Config) when is_list(Config)-> + SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/"}, + {"set-cookie", "test_cookie2=true; path=/"}], + http:verify_cookies(SetCookieHeaders, ?URL), + {"cookie", "$Version=0; test_cookie2=true; $Path=/; " + "test_cookie=true; $Path=/"} = http:cookie_header(?URL), + NewSetCookieHeaders = [{"set-cookie", "test_cookie=false; path=/"}], + http:verify_cookies(NewSetCookieHeaders, ?URL), + {"cookie", "$Version=0; test_cookie2=true; $Path=/; " + "test_cookie=false; $Path=/"} = http:cookie_header(?URL). + + +cookie_attributes(doc) -> + ["Test attribute not covered by the other test cases"]; +cookie_attributes(suite) -> + []; +cookie_attributes(Config) when is_list(Config) -> + SetCookieHeaders = [{"set-cookie", "test_cookie=true;version=1;" + "comment=foobar; "%% Comment + "foo=bar;" %% Nonsense should be ignored + "max-age=60000"}], + http:verify_cookies(SetCookieHeaders, ?URL), + {"cookie","$Version=1; test_cookie=true"} + = http:cookie_header(?URL), + ok. + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +future_netscape_date() -> + [Day, DD, Mon, YYYY] = netscape_date(date()), + lists:flatten(io_lib:format("~s, ~s ~s ~s 12:30:00 GMT", + [Day, DD, Mon, YYYY])). + +netscape_date({Year, 12, _}) -> + NewYear = Year + 1, + NewMonth = 1, + NewDay = calendar:last_day_of_the_month(NewYear, NewMonth), + WeekDay = calendar:day_of_the_week(NewYear, NewMonth, NewDay), + WeekDayNrStr = day_nr_str(NewDay), + NewDayStr = week_day_str(WeekDay), + MonthStr = month_str(NewMonth), + [NewDayStr, WeekDayNrStr, MonthStr, integer_to_list(NewYear)]; + +netscape_date({Year, Month, _}) -> + NewMonth = Month + 1, + NewDay = calendar:last_day_of_the_month(Year, NewMonth), + WeekDay = calendar:day_of_the_week(Year, NewMonth, NewDay), + WeekDayNrStr = day_nr_str(NewDay), + NewDayStr = week_day_str(WeekDay), + MonthStr = month_str(NewMonth), + [NewDayStr, WeekDayNrStr, MonthStr, integer_to_list(Year)]. + +week_day_str(1) -> + "Mon"; +week_day_str(2) -> + "Tus"; +week_day_str(3) -> + "Wdy"; +week_day_str(4) -> + "Thu"; +week_day_str(5) -> + "Fri"; +week_day_str(6) -> + "Sat"; +week_day_str(7) -> + "Sun". + +day_nr_str(1) -> + "01"; +day_nr_str(2) -> + "02"; +day_nr_str(3) -> + "03"; +day_nr_str(4) -> + "04"; +day_nr_str(5) -> + "05"; +day_nr_str(6) -> + "06"; +day_nr_str(7) -> + "07"; +day_nr_str(8) -> + "08"; +day_nr_str(0) -> + "09"; +day_nr_str(N) -> + integer_to_list(N). + +month_str(1) ->"Jan"; +month_str(2) ->"Feb"; +month_str(3) ->"Mar"; +month_str(4) ->"Apr"; +month_str(5) ->"May"; +month_str(6) ->"Jun"; +month_str(7) ->"Jul"; +month_str(8) ->"Aug"; +month_str(9) ->"Sep"; +month_str(10) ->"Oct"; +month_str(11) ->"Nov"; +month_str(12) ->"Dec". diff --git a/lib/inets/test/httpc_internal.hrl b/lib/inets/test/httpc_internal.hrl new file mode 120000 index 0000000000..bef2c94879 --- /dev/null +++ b/lib/inets/test/httpc_internal.hrl @@ -0,0 +1 @@ +../src/http_client/httpc_internal.hrl
\ No newline at end of file diff --git a/lib/inets/test/httpd_1_1.erl b/lib/inets/test/httpd_1_1.erl new file mode 100644 index 0000000000..055d034bec --- /dev/null +++ b/lib/inets/test/httpd_1_1.erl @@ -0,0 +1,494 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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_1). +-author('[email protected]'). + +-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, + head/4, mod_cgi_chunked_encoding_test/5]). + +%% -define(all_keys_lower_case,true). +-ifndef(all_keys_lower_case). +-define(CONTENT_LENGTH, "Content-Length: "). +-define(CONTENT_RANGE, "Content-Range: "). +-define(CONTENT_TYPE, "Content-Type: "). +-else. +-define(CONTENT_LENGTH, "content-length:"). +-define(CONTENT_RANGE, "content-range:"). +-define(CONTENT_TYPE, "content-type:"). +-endif. + + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- +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", + [{statuscode, 400}]), + + %% If it is a fully qualified URL no host is needed + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET HTTP://"++ Host ++ ":" ++ + integer_to_list(Port)++ + "/ HTTP/1.1\r\n\r\n", + [{statuscode, 200}]), + + %% If both look at the url. + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET HTTP://"++ Host ++ ":"++ + integer_to_list(Port) ++ + "/ HTTP/1.1\r\nHost:"++ Host ++ + "\r\n\r\n",[{statuscode, 200}]), + + %% Allow the request if its a Host field + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET / HTTP/1.1\r\nHost:"++ + Host ++ "\r\n\r\n", + [{statuscode, 200}]), + ok. + +chunked(Type, Port, Host, Node)-> + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET / HTTP/1.1\r\n" + "Host:"++ Host ++"\r\n" + "Transfer-Encoding:chunked\r\n" + "\r\n" + "A\r\n" + "1234567890\r\n" + "4\r\n" + "HEJ!\r\n" + "0\r\n\r\n", + [{statuscode, 200}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET / HTTP/1.1\r\n" + "Host:"++ Host ++"\r\n" + "Transfer-Encoding:chunked\r\n" + "Trailer:Content-Type\r\n" + "\r\n" + "A\r\n" + "1234567890\r\n" + "4\r\n" + "HEJ!\r\n" + "0\r\n" + "Content-Type:text/plain\r\n\r\n", + [{statuscode, 200}]), + ok. + +expect(Type, Port, Host, Node)-> + Request="GET / HTTP/1.1\r\nHost:" ++ Host ++ + "\r\nContent-Length:22\r\nExpect:100-continue\r\n\r\n", + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + Request, + [{statuscode, 100}]). +range(Type, Port, Host, Node)-> + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /range.txt HTTP/1.1\r\nHost:" + ++ Host + ++ "\r\nRange:bytes=110-120\r\n\r\n", + [{statuscode,416}]), + %%The simples of all range request a range + Request1="GET /range.txt HTTP/1.1\r\nHost:" ++ Host ++ + "\r\nRange:bytes=0-9\r\n\r\n", + {ok, Socket1} = inets_test_lib:connect_byte(Type, Host, Port), + inets_test_lib:send(Type, Socket1,Request1), + ok = validateRangeRequest(Socket1,[],"1234567890",$2,$0,$6), + inets_test_lib:close(Type,Socket1), + + %% Request the end of the file + Request2 = + "GET /range.txt HTTP/1.1\r\nHost:" ++ Host ++ + "\r\nRange:bytes=90-\r\n\r\n", + + {ok, Socket2} = inets_test_lib:connect_byte(Type, Host, Port), + inets_test_lib:send(Type, Socket2, Request2), + ok = validateRangeRequest(Socket2,[],"1234567890",$2,$0,$6), + inets_test_lib:close(Type,Socket2), + + %% The last byte in the file + Request3 = + "GET /range.txt HTTP/1.1\r\nHost:"++ + Host ++ "\r\nRange:bytes=-1\r\n\r\n", + {ok, Socket3} = inets_test_lib:connect_byte(Type, Host, Port), + inets_test_lib:send(Type, Socket3,Request3), + ok = validateRangeRequest(Socket3,[],"0",$2,$0,$6), + inets_test_lib:close(Type, Socket3), + + %%Multi Range + Request4 = "GET /range.txt HTTP/1.1\r\nHost:" ++ Host ++ + "\r\nRange:bytes=0-0,2-2,-1\r\n\r\n", + {ok, Socket4} = inets_test_lib:connect_byte(Type, Host, Port), + inets_test_lib:send(Type, Socket4, Request4), + ok = validateRangeRequest(Socket4,[],"130",$2,$0,$6), + inets_test_lib:close(Type, Socket4). + +if_test(Type, Port, Host, Node, DocRoot)-> + {ok, FileInfo} = + file:read_file_info(filename:join([DocRoot,"index.html"])), + CreatedSec = + calendar:datetime_to_gregorian_seconds(FileInfo#file_info.mtime), + + Mod = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime( + CreatedSec-1)), + + %% Test that we get the data when the file is modified + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET / HTTP/1.1\r\nHost:" ++ Host ++ + "\r\nIf-Modified-Since:" ++ + Mod ++ "\r\n\r\n", + [{statuscode, 200}]), + Mod1 = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime( + CreatedSec+100)), + ok = httpd_test_lib:verify_request(Type,Host,Port,Node, + "GET / HTTP/1.1\r\nHost:" + ++ Host ++"\r\nIf-Modified-Since:" + ++ Mod1 ++"\r\n\r\n", + [{statuscode, 304}]), + + Mod2 = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime( + CreatedSec+1)), + %% Control that the If-Unmodified-Header lmits the response + ok = httpd_test_lib:verify_request(Type,Host,Port,Node, + "GET / HTTP/1.1\r\nHost:" + ++ Host ++ + "\r\nIf-Unmodified-Since:" ++ Mod2 + ++ "\r\n\r\n", + [{statuscode, 200}]), + Mod3 = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime( + CreatedSec-1)), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET / HTTP/1.1\r\nHost:" + ++ Host ++ + "\r\nIf-Unmodified-Since:"++ Mod3 + ++"\r\n\r\n", + [{statuscode, 412}]), + + %% Control that we get the body when the etag match + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET / HTTP/1.1\r\nHost:" ++ Host + ++"\r\n"++ + "If-Match:"++ + httpd_util:create_etag(FileInfo)++ + "\r\n\r\n", + [{statuscode, 200}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET / HTTP/1.1\r\nHost:" ++ + Host ++ "\r\n"++ + "If-Match:NotEtag\r\n\r\n", + [{statuscode, 412}]), + + %% Control the response when the if-none-match header is there + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET / HTTP/1.1\r\nHost:" + ++ Host ++"\r\n"++ + "If-None-Match:NoTaag," ++ + httpd_util:create_etag(FileInfo) ++ + "\r\n\r\n", + [{statuscode, 304}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET / HTTP/1.1\r\nHost:" + ++ Host ++ "\r\n"++ + "If-None-Match:NotEtag," + "NeihterEtag\r\n\r\n", + [{statuscode,200}]). + +http_trace(Type, Port, Host, Node)-> + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "TRACE / HTTP/1.1\r\n" ++ + "Host:" ++ Host ++ "\r\n" ++ + "Max-Forwards:2\r\n\r\n", + [{statuscode, 200}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "TRACE / HTTP/1.0\r\n\r\n", + [{statuscode, 501}, + {version, "HTTP/1.0"}]). +head(Type, Port, Host, Node)-> + %% mod_include + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "HEAD /fsize.shtml HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/1.0"}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "HEAD /fsize.shtml HTTP/1.1\r\nhost:" ++ + Host ++ "\r\n\r\n", [{statuscode, 200}]), + %% mod_esi + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "HEAD /cgi-bin/erl/httpd_example/newformat" + " HTTP/1.0\r\n\r\n", [{statuscode, 200}, + {version, "HTTP/1.0"}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "HEAD /cgi-bin/erl/httpd_example/newformat " + "HTTP/1.1\r\nhost:" ++ Host ++ "\r\n\r\n", + [{statuscode, 200}]), + %% mod_cgi + Script = + case test_server:os_type() of + {win32, _} -> + "printenv.bat"; + _ -> + "printenv.sh" + end, + ok = httpd_test_lib:verify_request(Type,Host,Port,Node,"HEAD /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, "HEAD /cgi-bin/" + ++ Script ++ " HTTP/1.1\r\nhost:" ++ + Host ++ "\r\n\r\n", + [{statuscode, 200}]). + +mod_cgi_chunked_encoding_test(_, _, _, _, [])-> + ok; +mod_cgi_chunked_encoding_test(Type, Port, Host, Node, [Request| Rest])-> + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, Request, + [{statuscode, 200}]), + mod_cgi_chunked_encoding_test(Type, Port, Host, Node, Rest). + +%%-------------------------------------------------------------------- +%% Internal functions +%%-------------------------------------------------------------------- +validateRangeRequest(Socket,Response,ValidBody,C,O,DE)-> + receive + {tcp,Socket,Data} -> + case string:str(Data,"\r\n") of + 0-> + validateRangeRequest(Socket, + Response ++ Data, + ValidBody, C, O, DE); + _N -> + case Response ++ Data of + [$H,$T,$T,$P,$/,$1,$.,$1,$ ,C,O,DE | _Rest]-> + case [C,O,DE] of + "206" -> + validateRangeRequest1(Socket, + Response ++ Data, + ValidBody); + _ -> + bad_code + end; + _-> + error + end + end; + _Error -> + error + end. + +validateRangeRequest1(Socket, Response, ValidBody) -> + case end_of_header(Response) of + false -> + receive + {tcp,Socket,Data} -> + validateRangeRequest1(Socket, Response ++ Data, + ValidBody); + _-> + error + end; + {true, Head1, Body, _Size} -> + %% In this case size will be 0 if it is a multipart so + %% don't use it. + validateRangeRequest2(Socket, Head1, Body, ValidBody, + getRangeSize(Head1)) + end. + +validateRangeRequest2(Socket, Head, Body, ValidBody, {multiPart,Boundary})-> + case endReached(Body,Boundary) of + true -> + validateMultiPartRangeRequest(Body, ValidBody, Boundary); + false-> + receive + {tcp, Socket, Data} -> + validateRangeRequest2(Socket, Head, Body ++ Data, + ValidBody, {multiPart, Boundary}); + {tcp_closed, Socket} -> + error; + _ -> + error + end + end; + +validateRangeRequest2(Socket, Head, Body, ValidBody, BodySize) + when is_integer(BodySize) -> + case length(Body) of + Size when Size =:= BodySize -> + case Body of + ValidBody -> + ok; + Body -> + error + end; + Size when Size < BodySize -> + receive + {tcp, Socket, Data} -> + validateRangeRequest2(Socket, Head, + Body ++ Data, ValidBody, BodySize); + _ -> + error + end; + _ -> + error + end. + + +validateMultiPartRangeRequest(Body, ValidBody, Boundary)-> + case inets_regexp:split(Body,"--"++Boundary++"--") of + %%Last is the epilogue and must be ignored + {ok,[First | _Last]}-> + %%First is now the actuall http request body. + case inets_regexp:split(First, "--" ++ Boundary) of + %%Parts is now a list of ranges and the heads for each range + %%Gues we try to split out the body + {ok,Parts}-> + case lists:flatten(lists:map(fun splitRange/1,Parts)) of + ValidBody-> + ok; + ParsedBody-> + error = ParsedBody + end + end; + _ -> + error + end. + + +splitRange(Part)-> + case inets_regexp:split(Part, "\r\n\r\n") of + {ok,[_, Body]} -> + string:substr(Body, 1, length(Body) - 2); + _ -> + [] + end. + +endReached(Body, Boundary)-> + EndBound = "--" ++ Boundary ++ "--", + case string:str(Body, EndBound) of + 0 -> + false; + _ -> + true + end. + +getRangeSize(Head)-> + case controlMimeType(Head) of + {multiPart, BoundaryString}-> + {multiPart, BoundaryString}; + _X1 -> + case inets_regexp:match(Head, ?CONTENT_RANGE "bytes=.*\r\n") of + {match, Start, Lenght} -> + %% Get the range data remove the fieldname and the + %% end of line. + RangeInfo = string:substr(Head, Start + 20, + Lenght - (20 - 2)), + rangeSize(RangeInfo); + _X2 -> + error + end + end. +%%RangeInfo is NNN1-NNN2/NNN3 +%%NNN1=RangeStartByte +%%NNN2=RangeEndByte +%%NNN3=total amount of bytes in file +rangeSize([$=|RangeInfo]) -> + rangeSize(RangeInfo); +rangeSize(RangeInfo) -> + StartByte = lists:takewhile(fun(X)-> + num(X, true) + end, RangeInfo), + RangeInfo2 = string:substr(RangeInfo, length(StartByte) + 2), + EndByte = lists:takewhile(fun(X)-> + num(X,true) + end, RangeInfo2), + case list_to_integer(EndByte) - list_to_integer(StartByte) of + Val when is_number(Val) -> + %%Add one since it is startByte to endbyte ie 0-0 is 1 + %%byte 0-99 is 100 bytes + Val + 1; + _Val -> + error + end. + +num(CharVal, RetVal) when (CharVal >= 48) andalso (CharVal =< 57) -> + RetVal; +num(_CharVal, true) -> + false; +num(_CharVal, false) -> + true. + +controlMimeType(Head)-> + case inets_regexp:match(Head,?CONTENT_TYPE "multipart/byteranges.*\r\n") of + {match,Start,Length}-> + FieldNameLen = length(?CONTENT_TYPE "multipart/byteranges"), + case clearBoundary(string:substr(Head, Start + FieldNameLen, + Length - (FieldNameLen+2))) of + error -> + error; + BoundaryStr -> + {multiPart,BoundaryStr} + end; + nomatch-> + 0; + _ -> + error + end. + +clearBoundary(Boundary)-> + case inets_regexp:match(Boundary, "boundary=.*\$") of + {match, Start1, Length1}-> + BoundLen = length("boundary="), + string:substr(Boundary, Start1 + BoundLen, Length1 - BoundLen); + _ -> + error + end. + + +end_of_header(HeaderPart) -> + case httpd_util:split(HeaderPart,"\r\n\r\n",2) of + {ok, [Head, Body]} -> + {true, Head, Body, get_body_size(Head)}; + _Pos -> + false + end. + +get_body_size(Head) -> + case inets_regexp:match(Head,?CONTENT_LENGTH ".*\r\n") of + {match, Start, Length} -> + %% 15 is length of Content-Length, + %% 17 Is length of Content-Length and \r\ + S = list_to_integer( + string:strip(string:substr(Head, Start + 15, Length-17))), + S; + _-> + 0 + end. diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl new file mode 100644 index 0000000000..b7e97aa3ec --- /dev/null +++ b/lib/inets/test/httpd_SUITE.erl @@ -0,0 +1,2081 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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_SUITE). + +-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/1]). +-export([init_per_testcase/2, end_per_testcase/2, + init_per_suite/1, end_per_suite/1]). + +%% Test cases must be exported. +-export([ip/1, ssl/1, http_1_1_ip/1, http_1_0_ip/1, http_0_9_ip/1, + ipv6/1, tickets/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([ssl_mod_alias/1, ssl_mod_actions/1, ssl_mod_security/1, + ssl_mod_auth/1, ssl_mod_auth_api/1, + ssl_mod_auth_mnesia_api/1, ssl_mod_htaccess/1, + ssl_mod_cgi/1, ssl_mod_esi/1, ssl_mod_get/1, ssl_mod_head/1, + ssl_mod_all/1, ssl_load_light/1, ssl_load_medium/1, + ssl_load_heavy/1, ssl_dos_hostname/1, ssl_time_test/1, + ssl_restart_no_block/1, ssl_restart_disturbing_block/1, + ssl_restart_non_disturbing_block/1, ssl_block_disturbing_idle/1, + ssl_block_non_disturbing_idle/1, ssl_block_503/1, + ssl_block_disturbing_active/1, ssl_block_non_disturbing_active/1, + ssl_block_disturbing_active_timeout_not_released/1, + ssl_block_disturbing_active_timeout_released/1, + ssl_block_non_disturbing_active_timeout_not_released/1, + ssl_block_non_disturbing_active_timeout_released/1, + ssl_block_disturbing_blocker_dies/1, + ssl_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]). + +%%% Misc +-export([ipv6_hostname/1, ipv6_address/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. +%%-------------------------------------------------------------------- +all(doc) -> + ["Test the http server in the intes application."]; +all(suite) -> + [ + ip, + ssl, + http_1_1_ip, + http_1_0_ip, + http_0_9_ip, + %% ipv6, + tickets + ]. + +%%-------------------------------------------------------------------- +%% 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]), + + 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, + + [{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) -> + + io:format(user, "~w:init_per_testcase2(~w) -> entry with" + "~n Config: ~p" + "~n", [?MODULE, Case, Config]), + + IpNormal = integer_to_list(?IP_PORT) ++ ".conf", + IpHtacess = integer_to_list(?IP_PORT) ++ "htacess.conf", + SslNormal = integer_to_list(?SSL_PORT) ++ ".conf", + SslHtacess = integer_to_list(?SSL_PORT) ++ "htacess.conf", + + DataDir = ?config(data_dir, Config), + SuiteTopDir = ?config(suite_top_dir, Config), + + io:format(user, "~w:init_per_testcase2(~w) -> " + "~n SuiteDir: ~p" + "~n DataDir: ~p" + "~n", [?MODULE, Case, SuiteTopDir, DataDir]), + + TcTopDir = filename:join(SuiteTopDir, Case), + ?line ok = file:make_dir(TcTopDir), + + io:format(user, "~w:init_per_testcase2(~w) -> " + "~n TcTopDir: ~p" + "~n", [?MODULE, Case, TcTopDir]), + + DataSrc = filename:join([DataDir, "server_root"]), + ServerRoot = filename:join([TcTopDir, "server_root"]), + + io:format(user, "~w:init_per_testcase2(~w) -> " + "~n DataSrc: ~p" + "~n ServerRoot: ~p" + "~n", [?MODULE, Case, DataSrc, ServerRoot]), + + ok = file:make_dir(ServerRoot), + ok = file:make_dir(filename:join([TcTopDir, "logs"])), + + NewConfig = [{tc_top_dir, TcTopDir}, {server_root, ServerRoot} | Config], + + io:format(user, "~w:init_per_testcase2(~w) -> " + "copy DataSrc to ServerRoot~n", + [?MODULE, Case]), + + inets_test_lib:copy_dirs(DataSrc, ServerRoot), + + io:format(user, "~w:init_per_testcase2(~w) -> fix cgi~n", + [?MODULE, Case]), + 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 + io:format(user, "~w:init_per_testcase2(~w) -> ip testcase setups~n", + [?MODULE, Case]), + create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig], + normal_acess, IpNormal), + create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig], + mod_htaccess, IpHtacess), + + %% To be used by SSL test cases + io:format(user, "~w:init_per_testcase2(~w) -> ssl testcase setups~n", + [?MODULE, Case]), + create_config([{port, ?SSL_PORT}, {sock_type, ssl} | NewConfig], + normal_acess, SslNormal), + create_config([{port, ?SSL_PORT}, {sock_type, ssl} | NewConfig], + mod_htaccess, SslHtacess), + + %% To be used by IPv6 test cases. Case-clause is so that + %% you can do ts:run(inets, httpd_SUITE, <test case>) + %% 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. + %% case (catch ?config(test_host_ipv6_only, Config)) of + %% {_,IPv6Host,IPv6Adress,_,_} -> + %% create_ipv6_config([{port, ?IP_PORT}, + %% {sock_type, ip_comm} | NewConfig], + %% "ipv6_hostname.conf", IPv6Host), + %% create_ipv6_config([{port, ?IP_PORT}, + %% {sock_type, ip_comm} | NewConfig], + %% "ipv6_address.conf", IPv6Adress); + %% _ -> + %% ok + %% end, + + io:format(user, "~w:init_per_testcase2(~w) -> done~n", + [?MODULE, Case]), + + NewConfig. + + +init_per_testcase3(Case, Config) -> + io:format(user, "~w:init_per_testcase3(~w) -> entry with" + "~n Config: ~p", [?MODULE, Case, Config]), + + %% Clean up (we do not want this clean up in end_per_testcase + %% if init_per_testcase crases 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(), + + %% TraceLevel = max, + TraceLevel = 70, + TraceDest = io, + inets:enable_trace(TraceLevel, TraceDest), + + %% Start initialization + io:format(user, "~w:init_per_testcase3(~w) -> start init", + [?MODULE, 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) ++ + "htacess.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; + "ssl_mod_htaccess" -> + case inets_test_lib:start_http_server_ssl( + filename:join(TcTopDir, + integer_to_list(?SSL_PORT) ++ + "htacess.conf")) of + ok -> + "mod_htaccess"; + Other -> + error_logger:info_report("Other: ~p~n", [Other]), + {skip, "SSL does not seem to be supported"} + end; + "ssl_" ++ Rest -> + case inets_test_lib:start_http_server_ssl( + filename:join(TcTopDir, + integer_to_list(?SSL_PORT) ++ + ".conf")) of + ok -> + Rest; + Other -> + error_logger:info_report("Other: ~p~n", [Other]), + {skip, "SSL does not seem to be supported"} + end; + "ipv6_" ++ _ = TestCaseStr -> + {ok, Hostname} = inet:gethostname(), + + case lists:member(list_to_atom(Hostname), + ?config(ipv6_hosts, Config)) of + true -> + inets_test_lib:start_http_server( + filename:join(TcTopDir, + TestCaseStr ++ ".conf")); + + false -> + {skip, "Host does not support IPv6"} + end + end, + + 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_htacess(Path), + create_htacess_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. + + +%%-------------------------------------------------------------------- +%% 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) -> + io:format(user, "~w:end_per_testcase2(~w) -> entry with" + "~n Config: ~p~n", + [?MODULE, Case, Config]), + application:unset_env(inets, services), + application:stop(inets), + application:stop(ssl), + cleanup_mnesia(), + io:format(user, "~w:end_per_testcase2(~w) -> done~n", + [?MODULE, Case]), + ok. + + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- +ip(doc) -> + ["HTTP tests using TCP/IP"]; +ip(suite) -> + [ +%% 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_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, +%% ip_restart_no_block, + ip_restart_disturbing_block, + ip_restart_non_disturbing_block + ]. + +%%------------------------------------------------------------------------- +ssl(doc) -> + ["HTTP test using SSL"]; +ssl(suite) -> + [ +%% ssl_mod_alias, +%% ssl_mod_actions, +%% ssl_mod_security, +%% ssl_mod_auth, +%% ssl_mod_auth_api, +%% ssl_mod_auth_mnesia_api, +%% ssl_mod_htaccess, +%% ssl_mod_cgi, +%% ssl_mod_esi, +%% ssl_mod_get, +%% ssl_mod_head, +%% ssl_mod_all, +%% ssl_load_light, +%% ssl_load_medium, +%% ssl_load_heavy, +%% ssl_dos_hostname, +%% ssl_time_test, +%% ssl_restart_no_block, + ssl_restart_disturbing_block, + ssl_restart_non_disturbing_block%% , +%% ssl_block_disturbing_idle, +%% ssl_block_non_disturbing_idle, +%% ssl_block_503, +%% ssl_block_disturbing_active, +%% ssl_block_non_disturbing_active, +%% ssl_block_disturbing_active_timeout_not_released, +%% ssl_block_disturbing_active_timeout_released, +%% ssl_block_non_disturbing_active_timeout_not_released, +%% ssl_block_non_disturbing_active_timeout_released, +%% ssl_block_disturbing_blocker_dies, +%% ssl_block_non_disturbing_blocker_dies + ]. + +%%------------------------------------------------------------------------- +http_1_1_ip(doc) -> + ["HTTP/1.1"]; +http_1_1_ip(suite) -> + [ + 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(doc) -> + ["HTTP/1.0"]; +http_1_0_ip(suite) -> + [ + ip_head_1_0, + ip_get_1_0, + ip_post_1_0 + ]. + +%%------------------------------------------------------------------------- +http_0_9_ip(doc) -> + ["HTTP/0.9"]; +http_0_9_ip(suite) -> + [ + ip_get_0_9 + ]. + +%%------------------------------------------------------------------------- +ipv6(doc) -> + ["Tests ipv6 functionality."]; +ipv6(suite) -> + [ + ipv6_hostname, + ipv6_address + ]. + +%%------------------------------------------------------------------------- +tickets(doc) -> + ["Test cases for reported bugs."]; +tickets(suite) -> + [ + ticket_5775, + ticket_5865, + ticket_5913, + ticket_6003, + ticket_7304 + ]. + +%%------------------------------------------------------------------------- +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) -> + %% <CONDITIONAL-SKIP> + Skippable = [win32], + Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, + ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% </CONDITIONAL-SKIP> + + 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) -> + %% <CONDITIONAL-SKIP> + 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), + %% </CONDITIONAL-SKIP> + + 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) -> + %% <CONDITIONAL-SKIP> + 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), + %% </CONDITIONAL-SKIP> + + httpd_block:restart_non_disturbing_block(ip_comm, ?IP_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + +%%------------------------------------------------------------------------- +ssl_mod_alias(doc) -> + ["Module test: mod_alias"]; +ssl_mod_alias(suite) -> + []; +ssl_mod_alias(Config) when is_list(Config) -> + httpd_mod:alias(ssl, ?SSL_PORT, + ?config(host, Config), ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ssl_mod_actions(doc) -> + ["Module test: mod_actions"]; +ssl_mod_actions(suite) -> + []; +ssl_mod_actions(Config) when is_list(Config) -> + httpd_mod:actions(ssl, ?SSL_PORT, + ?config(host, Config), ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ssl_mod_security(doc) -> + ["Module test: mod_security"]; +ssl_mod_security(suite) -> + []; +ssl_mod_security(Config) when is_list(Config) -> + ServerRoot = ?config(server_root, Config), + httpd_mod:security(ServerRoot, ssl, ?SSL_PORT, + ?config(host, Config), ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ssl_mod_auth(doc) -> + ["Module test: mod_auth"]; +ssl_mod_auth(suite) -> + []; +ssl_mod_auth(Config) when is_list(Config) -> + httpd_mod:auth(ssl, ?SSL_PORT, + ?config(host, Config), ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ssl_mod_auth_api(doc) -> + ["Module test: mod_auth"]; +ssl_mod_auth_api(suite) -> + []; +ssl_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, "", ssl, ?SSL_PORT, Host, Node), + httpd_mod:auth_api(ServerRoot, "dets_", ssl, ?SSL_PORT, Host, Node), + httpd_mod:auth_api(ServerRoot, "mnesia_", ssl, ?SSL_PORT, Host, Node), + ok. + +%%------------------------------------------------------------------------- +ssl_mod_auth_mnesia_api(doc) -> + ["Module test: mod_auth_mnesia_api"]; +ssl_mod_auth_mnesia_api(suite) -> + []; +ssl_mod_auth_mnesia_api(Config) when is_list(Config) -> + httpd_mod:auth_mnesia_api(ssl, ?SSL_PORT, + ?config(host, Config), ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ssl_mod_htaccess(doc) -> + ["Module test: mod_htaccess"]; +ssl_mod_htaccess(suite) -> + []; +ssl_mod_htaccess(Config) when is_list(Config) -> + httpd_mod:htaccess(ssl, ?SSL_PORT, + ?config(host, Config), ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ssl_mod_cgi(doc) -> + ["Module test: mod_cgi"]; +ssl_mod_cgi(suite) -> + []; +ssl_mod_cgi(Config) when is_list(Config) -> + case test_server:os_type() of + vxworks -> + {skip, cgi_not_supported_on_vxwoks}; + _ -> + httpd_mod:cgi(ssl, ?SSL_PORT, + ?config(host, Config), ?config(node, Config)), + ok + end. +%%------------------------------------------------------------------------- +ssl_mod_esi(doc) -> + ["Module test: mod_esi"]; +ssl_mod_esi(suite) -> + []; +ssl_mod_esi(Config) when is_list(Config) -> + httpd_mod:esi(ssl, ?SSL_PORT, + ?config(host, Config), ?config(node, Config)), + ok. + +%%------------------------------------------------------------------------- +ssl_mod_get(doc) -> + ["Module test: mod_get"]; +ssl_mod_get(suite) -> + []; +ssl_mod_get(Config) when is_list(Config) -> + httpd_mod:get(ssl, ?SSL_PORT, + ?config(host, Config), ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ssl_mod_head(doc) -> + ["Module test: mod_head"]; +ssl_mod_head(suite) -> + []; +ssl_mod_head(Config) when is_list(Config) -> + httpd_mod:head(ssl, ?SSL_PORT, + ?config(host, Config), ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ssl_mod_all(doc) -> + ["All modules test"]; +ssl_mod_all(suite) -> + []; +ssl_mod_all(Config) when is_list(Config) -> + httpd_mod:all(ssl, ?SSL_PORT, + ?config(host, Config), ?config(node, Config)), + ok. + +%%------------------------------------------------------------------------- +ssl_load_light(doc) -> + ["Test light load"]; +ssl_load_light(suite) -> + []; +ssl_load_light(Config) when is_list(Config) -> + httpd_load:load_test(ssl, ?SSL_PORT, ?config(host, Config), + ?config(node, Config), + get_nof_clients(ssl, light)), + ok. + +%%------------------------------------------------------------------------- +ssl_load_medium(doc) -> + ["Test medium load"]; +ssl_load_medium(suite) -> + []; +ssl_load_medium(Config) when is_list(Config) -> + %% <CONDITIONAL-SKIP> + Skippable = [win32], + Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, + ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% </CONDITIONAL-SKIP> + + httpd_load:load_test(ssl, ?SSL_PORT, ?config(host, Config), + ?config(node, Config), + get_nof_clients(ssl, medium)), + ok. + +%%------------------------------------------------------------------------- +ssl_load_heavy(doc) -> + ["Test heavy load"]; +ssl_load_heavy(suite) -> + []; +ssl_load_heavy(Config) when is_list(Config) -> + %% <CONDITIONAL-SKIP> + Skippable = [win32], + Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, + ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% </CONDITIONAL-SKIP> + + httpd_load:load_test(ssl, ?SSL_PORT, ?config(host, Config), + ?config(node, Config), + get_nof_clients(ssl, heavy)), + ok. + +%%------------------------------------------------------------------------- +ssl_dos_hostname(doc) -> + ["Denial Of Service (DOS) attack test case"]; +ssl_dos_hostname(suite) -> + []; +ssl_dos_hostname(Config) when is_list(Config) -> + dos_hostname(ssl, ?SSL_PORT, ?config(host, Config), + ?config(node, Config), ?MAX_HEADER_SIZE), + ok. +%%------------------------------------------------------------------------- +ssl_time_test(doc) -> + [""]; +ssl_time_test(suite) -> + []; +ssl_time_test(Config) when is_list(Config) -> + %% <CONDITIONAL-SKIP> + Condition = fun() -> true end, + ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% </CONDITIONAL-SKIP> + + httpd_time_test:t(ssl, ?config(host, Config), ?SSL_PORT), + ok. + +%%------------------------------------------------------------------------- +ssl_block_503(doc) -> + ["Check that you will receive status code 503 when the server" + " is blocked and 200 when its not blocked."]; +ssl_block_503(suite) -> + []; +ssl_block_503(Config) when is_list(Config) -> + httpd_block:block_503(ssl, ?SSL_PORT, ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ssl_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."]; +ssl_block_disturbing_idle(suite) -> + []; +ssl_block_disturbing_idle(Config) when is_list(Config) -> + httpd_block:block_disturbing_idle(ssl, ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ssl_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."]; +ssl_block_non_disturbing_idle(suite) -> + []; +ssl_block_non_disturbing_idle(Config) when is_list(Config) -> + httpd_block:block_non_disturbing_idle(ssl, ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ssl_block_disturbing_active(doc) -> + ["Check that you can block/unblock an active server. The strategy " + "distribing means ongoing requests should be terminated."]; +ssl_block_disturbing_active(suite) -> + []; +ssl_block_disturbing_active(Config) when is_list(Config) -> + httpd_block:block_disturbing_active(ssl, ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ssl_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."]; +ssl_block_non_disturbing_active(suite) -> + []; +ssl_block_non_disturbing_active(Config) when is_list(Config) -> + httpd_block:block_non_disturbing_idle(ssl, ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + +%%------------------------------------------------------------------------- +ssl_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."]; +ssl_block_disturbing_active_timeout_not_released(suite) -> + []; +ssl_block_disturbing_active_timeout_not_released(Config) + when is_list(Config) -> + httpd_block: + block_disturbing_active_timeout_not_released(ssl, + ?SSL_PORT, + ?config(host, + Config), + ?config(node, + Config)), + ok. +%%------------------------------------------------------------------------- +ssl_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."]; +ssl_block_disturbing_active_timeout_released(suite) -> + []; +ssl_block_disturbing_active_timeout_released(Config) + when is_list(Config) -> + httpd_block:block_disturbing_active_timeout_released(ssl, + ?SSL_PORT, + ?config(host, + Config), + ?config(node, + Config)), + ok. + +%%------------------------------------------------------------------------- +ssl_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."]; +ssl_block_non_disturbing_active_timeout_not_released(suite) -> + []; +ssl_block_non_disturbing_active_timeout_not_released(Config) + when is_list(Config) -> + httpd_block: + block_non_disturbing_active_timeout_not_released(ssl, + ?SSL_PORT, + ?config(host, + Config), + ?config(node, + Config)), + ok. +%%------------------------------------------------------------------------- +ssl_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." ]; +ssl_block_non_disturbing_active_timeout_released(suite) -> + []; +ssl_block_non_disturbing_active_timeout_released(Config) + when is_list(Config) -> + httpd_block: + block_non_disturbing_active_timeout_released(ssl, + ?SSL_PORT, + ?config(host, + Config), + ?config(node, + Config)), + ok. + +%%------------------------------------------------------------------------- +ssl_block_disturbing_blocker_dies(doc) -> + []; +ssl_block_disturbing_blocker_dies(suite) -> + []; +ssl_block_disturbing_blocker_dies(Config) when is_list(Config) -> + httpd_block:disturbing_blocker_dies(ssl, ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ssl_block_non_disturbing_blocker_dies(doc) -> + []; +ssl_block_non_disturbing_blocker_dies(suite) -> + []; +ssl_block_non_disturbing_blocker_dies(Config) when is_list(Config) -> + httpd_block:non_disturbing_blocker_dies(ssl, ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ssl_restart_no_block(doc) -> + [""]; +ssl_restart_no_block(suite) -> + []; +ssl_restart_no_block(Config) when is_list(Config) -> + httpd_block:restart_no_block(ssl, ?SSL_PORT, ?config(host, Config), + ?config(node, Config)), + ok. +%%------------------------------------------------------------------------- +ssl_restart_disturbing_block(doc) -> + [""]; +ssl_restart_disturbing_block(suite) -> + []; +ssl_restart_disturbing_block(Config) when is_list(Config) -> + %% <CONDITIONAL-SKIP> + 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), + %% </CONDITIONAL-SKIP> + + httpd_block:restart_disturbing_block(ssl, ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), + ok. + +%%------------------------------------------------------------------------- +ssl_restart_non_disturbing_block(doc) -> + [""]; +ssl_restart_non_disturbing_block(suite) -> + []; +ssl_restart_non_disturbing_block(Config) when is_list(Config) -> + %% <CONDITIONAL-SKIP> + 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), + %% </CONDITIONAL-SKIP> + + httpd_block:restart_non_disturbing_block(ssl, ?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(doc) -> + ["Test standard ipv6 address"]; +ipv6_hostname(suite)-> + []; +ipv6_hostname(Config) when is_list(Config) -> + Host = ?config(host, Config), + httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, node(), + "GET / HTTP/1.1\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/1.1"}]), + ok. + +%%------------------------------------------------------------------------- +ipv6_address(doc) -> + ["Test standard ipv6 address"]; +ipv6_address(suite)-> + []; +ipv6_address(Config) when is_list(Config) -> + httpd_test_lib:verify_request(ip_comm, ?IPV6_LOCAL_HOST, ?IP_PORT, + node(), "GET / HTTP/1.1\r\n\r\n", + [{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) -> + 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_last_modified, + "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]), + SSL = + case Type of + ssl -> + [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"])]; + _ -> + [] + end, + Mod_order = 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([Mod_order]), + %% cline(["LogFormat ", "erlang"]), + cline(["ServerAdmin [email protected]"]), + 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(["<Directory ", Dir, ">"]), + 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(["</Directory>\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 -> + test_server:fail({failed_to_cleanup_mnesia, Other}) + end, + case rpc:call(Node, ?MODULE, setup_mnesia, []) of + {atomic, ok} -> + ok; + Other2 -> + test_server: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. + +create_htacess_data(Path, IpAddress)-> + create_htacess_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_htacess_file(filename:join([Path,"ht/open/.htaccess"]), + Path, "user one Aladdin"), + create_htacess_file(filename:join([Path,"ht/secret/.htaccess"]), + Path, "group group1 group2"), + create_htacess_file(filename:join([Path, + "ht/secret/top_secret/.htaccess"]), + Path, "user four"), + create_htacess_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( + "<html><head><title>test</title></head> + <body>testar</body></html>")). + +create_htacess_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<Limit>\nrequire " ++ RequireData ++ + "\n</Limit>")). + +create_htacess_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<Limit GET>\n\tallow from " ++ + format_ip(IpAddress, + string:rchr(IpAddress,$.)) ++ + "\n</Limit>")). + +create_user_group_file(PathAndFileName, Data)-> + file:write_file(PathAndFileName, list_to_binary(Data)). + +create_htacess_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_htacess_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_htacess(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_htacess_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), +%% +%% 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", +%% +%% HttpConfig = [cline(["BindAddress ", "[" ++ Ipv6Address ++"]|inet6"]), +%% cline(["Port ", integer_to_list(Port)]), +%% cline(["ServerName ", "httpc_test"]), +%% 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"])], +%% ConfigFile = filename:join([TcTopDir,FileName]), +%% {ok, Fd} = file:open(ConfigFile, [write]), +%% ok = file:write(Fd, lists:flatten(HttpConfig)), +%% ok = file:close(Fd). diff --git a/lib/inets/test/httpd_SUITE_data/Makefile.src b/lib/inets/test/httpd_SUITE_data/Makefile.src new file mode 100644 index 0000000000..b0fdb43d8d --- /dev/null +++ b/lib/inets/test/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/httpd_SUITE_data/cgi_echo.c b/lib/inets/test/httpd_SUITE_data/cgi_echo.c new file mode 100644 index 0000000000..580f860e96 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/cgi_echo.c @@ -0,0 +1,97 @@ +#include <stdlib.h> +#include <stdio.h> + +#if defined __WIN32__ +#include <windows.h> +#include <fcntl.h> +#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/httpd_SUITE_data/server_root/auth/group b/lib/inets/test/httpd_SUITE_data/server_root/auth/group new file mode 100644 index 0000000000..b3da0ccbd3 --- /dev/null +++ b/lib/inets/test/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/httpd_SUITE_data/server_root/auth/passwd b/lib/inets/test/httpd_SUITE_data/server_root/auth/passwd new file mode 100644 index 0000000000..8c980ff547 --- /dev/null +++ b/lib/inets/test/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/httpd_SUITE_data/server_root/cgi-bin/printenv.bat b/lib/inets/test/httpd_SUITE_data/server_root/cgi-bin/printenv.bat new file mode 100644 index 0000000000..25a49a1536 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/cgi-bin/printenv.bat @@ -0,0 +1,9 @@ +@echo off +echo tomrad > c:\cygwin\tmp\hej +echo Content-type: text/html +echo. +echo ^<HTML^> ^<HEAD^> ^<TITLE^>OS Environment^</TITLE^> ^</HEAD^> ^<BODY^>^<PRE^> +set +echo ^</PRE^>^</BODY^>^</HTML^> + + diff --git a/lib/inets/test/httpd_SUITE_data/server_root/cgi-bin/printenv.sh b/lib/inets/test/httpd_SUITE_data/server_root/cgi-bin/printenv.sh new file mode 100755 index 0000000000..de81de9bde --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/cgi-bin/printenv.sh @@ -0,0 +1,6 @@ +#!/bin/sh +echo "Content-type: text/html" +echo "" +echo "<HTML> <HEAD> <TITLE>OS Environment</TITLE> </HEAD> <BODY><PRE>" +env +echo "</PRE></BODY></HTML>"
\ No newline at end of file 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 new file mode 100644 index 0000000000..48e66f0114 --- /dev/null +++ b/lib/inets/test/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 [email protected] +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 + +<Directory /var/tmp/server_root/htdocs/open> +AuthDBType plain +AuthName Open Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require user one Aladdin +</Directory> + +<Directory /var/tmp/server_root/htdocs/secret> +AuthDBType plain +AuthName Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group1 group2 +</Directory> + +<Directory /var/tmp/server_root/htdocs/secret/top_secret> +AuthDBType plain +AuthName Top Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group3 +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_open> +AuthDBType mnesia +AuthName Open Area +require user one Aladdin +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_secret> +AuthDBType mnesia +AuthName Secret Area +require group group1 group2 +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_secret/top_secret> +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 +</Directory> 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 new file mode 100644 index 0000000000..79bb7fcca4 --- /dev/null +++ b/lib/inets/test/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 [email protected] +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 + +<Directory /var/tmp/server_root/htdocs/open> +AuthName Open Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require user one Aladdin +</Directory> + +<Directory /var/tmp/server_root/htdocs/secret> +AuthName Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group1 group2 +</Directory> + +<Directory /var/tmp/server_root/htdocs/secret/top_secret> +AuthName Top Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group3 +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_open> +AuthName Open Area +AuthMnesiaDB On +require user one Aladdin +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_secret> +AuthName Secret Area +AuthMnesiaDB On +require group group1 group2 +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_secret/top_secret> +AuthName Top Secret Area +AuthMnesiaDB On +require group group3 +</Directory> 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 new file mode 100644 index 0000000000..8a74ed1afd --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf @@ -0,0 +1,268 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1997-2009. 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: <address>[|<ip-family>] +# +#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 [email protected] + +# 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). + +<Directory /var/tmp/server_root/htdocs/open> +AuthDBType plain +AuthName Open Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require user one Aladdin +</Directory> + +<Directory /var/tmp/server_root/htdocs/secret> +AuthDBType plain +AuthName Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group1 group2 +</Directory> + +<Directory /var/tmp/server_root/htdocs/secret/top_secret> +AuthDBType plain +AuthName Top Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group3 +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_open> +AuthDBType mnesia +AuthName Open Area +require user one Aladdin +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_secret> +AuthDBType mnesia +AuthName Secret Area +require group group1 group2 +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_secret/top_secret> +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 +</Directory> 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 new file mode 100644 index 0000000000..d2f81e4e5e --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/conf/mime.types @@ -0,0 +1,465 @@ +# 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 new file mode 100644 index 0000000000..8b8c57a98b --- /dev/null +++ b/lib/inets/test/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 [email protected] +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 + +<Directory /var/tmp/server_root/htdocs/open> +AuthName Open Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require user one Aladdin +</Directory> + +<Directory /var/tmp/server_root/htdocs/secret> +AuthName Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group1 group2 +</Directory> + +<Directory /var/tmp/server_root/htdocs/secret/top_secret> +AuthName Top Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group3 +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_open> +AuthName Open Area +AuthMnesiaDB On +require user one Aladdin +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_secret> +AuthName Secret Area +AuthMnesiaDB On +require group group1 group2 +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_secret/top_secret> +AuthName Top Secret Area +AuthMnesiaDB On +require group group3 +</Directory> diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/config.shtml b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/config.shtml new file mode 100644 index 0000000000..107e3ff610 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/config.shtml @@ -0,0 +1,70 @@ +<HTML> +<HEAD> +<TITLE>/ssi.html (17-Apr-1997)</TITLE> +</HEAD> +<BODY> +<H1>/ssi.html</H1> + +<!-- ************* CONFIG ************* --> + +<!--#config timefmt="%a %b %e %T %Z %Y" sizefmt="abbrev"--> +<!--#config errmsg="[an especially ugly error occurred while processing this directive]"--> + +<!-- ************* INCLUDE ************* --> + +<P>Include /misc/friedrich.html: +<!--#include virtual="/misc/friedrich.html"--> +<P>Include /misc/not_defined.html: <!--#include virtual="/misc/not_defined.html"--> +<P>Include misc/friedrich.html: +<!--#include file="misc/friedrich.html"--> +<P>Include not_defined.html: <!--#include file="not_defined.html"--> + +<P><HR> + +<!-- ************* ECHO ************* --> + +<P>DOCUMENT_NAME: <!--#echo var="DOCUMENT_NAME"--> +<P>DOCUMENT_URI: <!--#echo var="DOCUMENT_URI"--> +<P>QUERY_STRING_UNESCAPED: <!--#echo var="QUERY_STRING_UNESCAPED"--> +<P>DATE_LOCAL: <!--#echo var="DATE_LOCAL"--> +<P>DATE_GMT: <!--#echo var="DATE_GMT"--> +<P>LAST_MODIFIED: <!--#echo var="LAST_MODIFIED"--> +<P>NOT_DEFINED: <!--#echo var="NOT_DEFINED"--> + +<P><HR> + +<!-- ************* FSIZE ************* --> + +<P>Size of index.html: <!--#fsize file="index.html"--> +<P>Size of not_defined.html: <!--#fsize file="not_defined.html"--> +<!--#config sizefmt="bytes"--> +<P>Size of /misc/friedrich.html: <!--#fsize virtual="/misc/friedrich.html"--> +<P>Size of /misc/not_defined.html: <!--#fsize virtual="/misc/not_defined.html"--> + +<P><HR> + +<!-- ************* FLASTMOD ************* --> + +<P>Last modification of index.html: <!--#flastmod file="index.html"--> +<P>Last modification of not_defined.html: <!--#flastmod file="not_defined.html"--> +<P>Last modification of /misc/friedrich.html: <!--#flastmod virtual="/misc/friedrich.html"--> +<P>Last modification of /misc/not_defined.html: <!--#flastmod virtual="/misc/not_defined.html"--> + +<!--#exec cmd="ls"--> +<!--#exec cmd="printenv"--> +<!--#exec cmd="sunemaja"--> + +<!--#exec cgi="/cgi-bin/printenv.sh"--> + +</BODY> +</HTML> + + + + + + + + + + diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/dets_open/dummy.html b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/dets_open/dummy.html new file mode 100644 index 0000000000..a6e8a35a04 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/dets_open/dummy.html @@ -0,0 +1,10 @@ +<HTML> +<HEAD> +<TITLE>/open/dummy.html (17-Apr-1997)</TITLE> +<!-- Created by: Joakim Greben�, 17-Apr-1997 --> +<!-- Changed by: Joakim Greben�, 17-Apr-1997 --> +</HEAD> +<BODY> +<H1>/open/dummy.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/dets_secret/dummy.html b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/dets_secret/dummy.html new file mode 100644 index 0000000000..016b04e540 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/dets_secret/dummy.html @@ -0,0 +1,10 @@ +<HTML> +<HEAD> +<TITLE>/secret/dummy.html (17-Apr-1997)</TITLE> +<!-- Created by: Joakim Greben�, 17-Apr-1997 --> +<!-- Changed by: Joakim Greben�, 17-Apr-1997 --> +</HEAD> +<BODY> +<H1>/secret/dummy.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/dets_secret/top_secret/index.html b/lib/inets/test/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/httpd_SUITE_data/server_root/htdocs/dets_secret/top_secret/index.html @@ -0,0 +1,9 @@ +<HTML> +<HEAD> +<TITLE>/secret/top_secret/index.html (04-Feb-1998)</TITLE> +<!-- Created by: Mattias Nilsson, 04-Feb-1998 --> +</HEAD> +<BODY> +<H1>/secret/top_secret/index.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/echo.shtml b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/echo.shtml new file mode 100644 index 0000000000..141db5be59 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/echo.shtml @@ -0,0 +1,35 @@ +<HTML> +<HEAD> +<TITLE>/echo.shtml</TITLE> +</HEAD> +<BODY> +<H1>/echo.shtml</H1> + +<P>DOCUMENT_NAME: <!--#echo var="DOCUMENT_NAME"--> + +<P>DOCUMENT_URI: <!--#echo var="DOCUMENT_URI"--> + +<P>QUERY_STRING_UNESCAPED: <!--#echo var="QUERY_STRING_UNESCAPED"--> + +<P>DATE_LOCAL: <!--#echo var="DATE_LOCAL"--> + +<P>DATE_GMT: <!--#echo var="DATE_GMT"--> + +<P>LAST_MODIFIED: <!--#echo var="LAST_MODIFIED"--> + +<P>NOT_DEFINED: <!--#echo var="NOT_DEFINED"--> + +<P>[<A HREF="ssi.html">Back</A>] + +</BODY> +</HTML> + + + + + + + + + + diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/exec.shtml b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/exec.shtml new file mode 100644 index 0000000000..97333da898 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/exec.shtml @@ -0,0 +1,30 @@ +<HTML> +<HEAD> +<TITLE>/exec.shtml</TITLE> +</HEAD> +<BODY> +<H1>/exec.shtml</H1> +<PRE> +<!--#exec cmd="ls"--> +<HR> +<!--#exec cmd="printenv"--> +<HR> +<!--#exec cmd="sunemaja"--> +<HR> +<!--#exec cgi="/cgi-bin/printenv.sh"--> +</PRE> + +<P>[<A HREF="ssi.html">Back</A>] + +</BODY> +</HTML> + + + + + + + + + + diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/flastmod.shtml b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/flastmod.shtml new file mode 100644 index 0000000000..d54c36fe50 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/flastmod.shtml @@ -0,0 +1,29 @@ +<HTML> +<HEAD> +<TITLE>/flastmod.shtml</TITLE> +</HEAD> +<BODY> +<H1>/flastmod.shtml</H1> + +<P>Last modification of index.html: <!--#flastmod file="index.html"--> + +<P>Last modification of not_defined.html: <!--#flastmod file="not_defined.html"--> + +<P>Last modification of /misc/friedrich.html: <!--#flastmod virtual="/misc/friedrich.html"--> + +<P>Last modification of /misc/not_defined.html: <!--#flastmod virtual="/misc/not_defined.html"--> + +<P>[<A HREF="ssi.html">Back</A>] + +</BODY> +</HTML> + + + + + + + + + + diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/fsize.shtml b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/fsize.shtml new file mode 100644 index 0000000000..570ee9cf6d --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/fsize.shtml @@ -0,0 +1,29 @@ +<HTML> +<HEAD> +<TITLE>/fsize.shtml</TITLE> +</HEAD> +<BODY> +<H1>/fsize.shtml</H1> + +<P>Size of index.html: <!--#fsize file="index.html"--> + +<P>Size of not_defined.html: <!--#fsize file="not_defined.html"--> + +<P>Size of /misc/friedrich.html: <!--#fsize virtual="/misc/friedrich.html"--> + +<P>Size of /misc/not_defined.html: <!--#fsize virtual="/misc/not_defined.html"--> + +<P>[<A HREF="ssi.html">Back</A>] + +</BODY> +</HTML> + + + + + + + + + + diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/include.shtml b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/include.shtml new file mode 100644 index 0000000000..529aad0437 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/include.shtml @@ -0,0 +1,33 @@ +<HTML> +<HEAD> +<TITLE>/include.shtml</TITLE> +</HEAD> +<BODY> +<H1>/include.shtml</H1> + +<P>Include /misc/friedrich.html: +<!--#include virtual="/misc/friedrich.html"--> + +<P>Include /misc/not_defined.html: +<!--#include virtual="/misc/not_defined.html"--> + +<P>Include misc/friedrich.html: +<!--#include file="misc/friedrich.html"--> + +<P>Include not_defined.html: +<!--#include file="not_defined.html"--> + +<P>[<A HREF="ssi.html">Back</A>] + +</BODY> +</HTML> + + + + + + + + + + diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/index.html b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/index.html new file mode 100644 index 0000000000..cfdc9f9ab7 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/index.html @@ -0,0 +1,25 @@ +<HTML> +<HEAD> +<TITLE>/index.html</TITLE> +</HEAD> +<BODY> +<H1>/index.html</H1> + +<STRONG>Server-Side Include (SSI) commands:</STRONG><BR> +<A HREF="config.shtml">config</A><BR> +<A HREF="echo.shtml">echo</A><BR> +<A HREF="exec.shtml">exec</A><BR> +<A HREF="flastmod.shtml">flastmod</A><BR> +<A HREF="fsize.shtml">fsize</A><BR> +<A HREF="include.shtml">include</A><BR> + +<BR> +<BR> + +<STRONG>ESI callback:</STRING><BR> +<A HREF="cgi-bin/erl/httpd_example/get">cgi-bin/erl/httpd_example/get</A><BR> +<A HREF="cgi-bin/erl/httpd_example/yahoo">cgi-bin/erl/httpd_example/yahoo</A><BR> +<A HREF="cgi-bin/erl/httpd_example/test1">cgi-bin/erl/httpd_example/test1</A><BR> + +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/last_modified.html b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/last_modified.html new file mode 100644 index 0000000000..65c1790813 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/last_modified.html @@ -0,0 +1,22 @@ +<HTML> +<HEAD> +<TITLE>/last_modified.html</TITLE> +</HEAD> +<BODY> +<H1>/last_modified.html</H1> + +<P>This document is only used for test of illegal last-modified date.</P> + + +</BODY> +</HTML> + + + + + + + + + + diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/misc/friedrich.html b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/misc/friedrich.html new file mode 100644 index 0000000000..d7953d5df4 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/misc/friedrich.html @@ -0,0 +1,7 @@ +<P><CITE> +Talking much about oneself can also be a means to conceal oneself.<BR> +-- Friedrich Nietzsche +</CITE> + +<P>Nested Include: +<!--#include file="misc/oech.html"--> diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/misc/oech.html b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/misc/oech.html new file mode 100644 index 0000000000..506064bf04 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/misc/oech.html @@ -0,0 +1,4 @@ +<P><CITE> +What excuses stand in your way? How can you eliminate them?<BR> +-- Roger von Oech +</CITE> diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/misc/welcome.html b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/misc/welcome.html new file mode 100644 index 0000000000..8c17451f91 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/misc/welcome.html @@ -0,0 +1 @@ +<HTML></HTML> diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/mnesia_open/dummy.html b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/mnesia_open/dummy.html new file mode 100644 index 0000000000..a6e8a35a04 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/mnesia_open/dummy.html @@ -0,0 +1,10 @@ +<HTML> +<HEAD> +<TITLE>/open/dummy.html (17-Apr-1997)</TITLE> +<!-- Created by: Joakim Greben�, 17-Apr-1997 --> +<!-- Changed by: Joakim Greben�, 17-Apr-1997 --> +</HEAD> +<BODY> +<H1>/open/dummy.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/mnesia_secret/dummy.html b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/mnesia_secret/dummy.html new file mode 100644 index 0000000000..016b04e540 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/mnesia_secret/dummy.html @@ -0,0 +1,10 @@ +<HTML> +<HEAD> +<TITLE>/secret/dummy.html (17-Apr-1997)</TITLE> +<!-- Created by: Joakim Greben�, 17-Apr-1997 --> +<!-- Changed by: Joakim Greben�, 17-Apr-1997 --> +</HEAD> +<BODY> +<H1>/secret/dummy.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/mnesia_secret/top_secret/index.html b/lib/inets/test/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/httpd_SUITE_data/server_root/htdocs/mnesia_secret/top_secret/index.html @@ -0,0 +1,9 @@ +<HTML> +<HEAD> +<TITLE>/mnesia_secret/top_secret/index.html (04-Feb-1998)</TITLE> +<!-- Created by: Mattias Nilsson, 04-Feb-1998 --> +</HEAD> +<BODY> +<H1>/mnesia_secret/top_secret/index.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/open/dummy.html b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/open/dummy.html new file mode 100644 index 0000000000..a6e8a35a04 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/open/dummy.html @@ -0,0 +1,10 @@ +<HTML> +<HEAD> +<TITLE>/open/dummy.html (17-Apr-1997)</TITLE> +<!-- Created by: Joakim Greben�, 17-Apr-1997 --> +<!-- Changed by: Joakim Greben�, 17-Apr-1997 --> +</HEAD> +<BODY> +<H1>/open/dummy.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/secret/dummy.html b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/secret/dummy.html new file mode 100644 index 0000000000..016b04e540 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/secret/dummy.html @@ -0,0 +1,10 @@ +<HTML> +<HEAD> +<TITLE>/secret/dummy.html (17-Apr-1997)</TITLE> +<!-- Created by: Joakim Greben�, 17-Apr-1997 --> +<!-- Changed by: Joakim Greben�, 17-Apr-1997 --> +</HEAD> +<BODY> +<H1>/secret/dummy.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_SUITE_data/server_root/htdocs/secret/top_secret/index.html b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/secret/top_secret/index.html new file mode 100644 index 0000000000..34db3d5d1a --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/htdocs/secret/top_secret/index.html @@ -0,0 +1,9 @@ +<HTML> +<HEAD> +<TITLE>/secret/top_secret/index.html (04-Feb-1998)</TITLE> +<!-- Created by: Mattias Nilsson, 04-Feb-1998 --> +</HEAD> +<BODY> +<H1>/secret/top_secret/index.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/README b/lib/inets/test/httpd_SUITE_data/server_root/icons/README new file mode 100644 index 0000000000..a1fc5a5a9c --- /dev/null +++ b/lib/inets/test/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 ([email protected]). + + 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/icons/a.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/a.gif Binary files differnew file mode 100644 index 0000000000..bb23d971f4 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/a.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/alert.black.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/alert.black.gif Binary files differnew file mode 100644 index 0000000000..eaecd2172a --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/alert.black.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/alert.red.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/alert.red.gif Binary files differnew file mode 100644 index 0000000000..a423894043 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/alert.red.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/apache_pb.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/apache_pb.gif Binary files differnew file mode 100644 index 0000000000..3a1c139fc4 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/apache_pb.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/back.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/back.gif Binary files differnew file mode 100644 index 0000000000..a694ae1ec3 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/back.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/ball.gray.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/ball.gray.gif Binary files differnew file mode 100644 index 0000000000..eb84268c4c --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/ball.gray.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/ball.red.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/ball.red.gif Binary files differnew file mode 100644 index 0000000000..a8425cb574 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/ball.red.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/binary.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/binary.gif Binary files differnew file mode 100644 index 0000000000..9a15cbae04 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/binary.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/binhex.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/binhex.gif Binary files differnew file mode 100644 index 0000000000..62d0363108 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/binhex.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/blank.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/blank.gif Binary files differnew file mode 100644 index 0000000000..0ccf01e198 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/blank.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/bomb.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/bomb.gif Binary files differnew file mode 100644 index 0000000000..270fdb1c06 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/bomb.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/box1.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/box1.gif Binary files differnew file mode 100644 index 0000000000..65dcd002ea --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/box1.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/box2.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/box2.gif Binary files differnew file mode 100644 index 0000000000..c43bc4faec --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/box2.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/broken.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/broken.gif Binary files differnew file mode 100644 index 0000000000..9f8cbe9f76 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/broken.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/burst.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/burst.gif Binary files differnew file mode 100644 index 0000000000..fbdcf575f7 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/burst.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/button1.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/button1.gif Binary files differnew file mode 100644 index 0000000000..eb97cb7333 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button1.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/button10.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/button10.gif Binary files differnew file mode 100644 index 0000000000..fe0c97998c --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button10.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/button2.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/button2.gif Binary files differnew file mode 100644 index 0000000000..7698455bf9 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button2.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/button3.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/button3.gif Binary files differnew file mode 100644 index 0000000000..a8b8319232 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button3.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/button4.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/button4.gif Binary files differnew file mode 100644 index 0000000000..0fd15a0d7f --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button4.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/button5.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/button5.gif Binary files differnew file mode 100644 index 0000000000..64241e5c5d --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button5.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/button6.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/button6.gif Binary files differnew file mode 100644 index 0000000000..867cfd1212 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button6.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/button7.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/button7.gif Binary files differnew file mode 100644 index 0000000000..b3f5fb248f --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button7.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/button8.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/button8.gif Binary files differnew file mode 100644 index 0000000000..7a308be8f6 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button8.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/button9.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/button9.gif Binary files differnew file mode 100644 index 0000000000..9acba576c0 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button9.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/buttonl.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/buttonl.gif Binary files differnew file mode 100644 index 0000000000..3883088e7a --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/buttonl.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/buttonr.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/buttonr.gif Binary files differnew file mode 100644 index 0000000000..c4dc3887db --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/buttonr.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/c.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/c.gif Binary files differnew file mode 100644 index 0000000000..7555b6c164 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/c.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/comp.blue.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/comp.blue.gif Binary files differnew file mode 100644 index 0000000000..f8d76a8c23 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/comp.blue.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/comp.gray.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/comp.gray.gif Binary files differnew file mode 100644 index 0000000000..7664cd0364 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/comp.gray.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/compressed.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/compressed.gif Binary files differnew file mode 100644 index 0000000000..39e732739f --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/compressed.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/continued.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/continued.gif Binary files differnew file mode 100644 index 0000000000..b0ffb7e0cc --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/continued.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/dir.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/dir.gif Binary files differnew file mode 100644 index 0000000000..48264601ae --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/dir.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/down.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/down.gif Binary files differnew file mode 100644 index 0000000000..a354c871cd --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/down.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/dvi.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/dvi.gif Binary files differnew file mode 100644 index 0000000000..791be33105 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/dvi.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/f.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/f.gif Binary files differnew file mode 100644 index 0000000000..fbe353c282 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/f.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/folder.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/folder.gif Binary files differnew file mode 100644 index 0000000000..48264601ae --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/folder.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/folder.open.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/folder.open.gif Binary files differnew file mode 100644 index 0000000000..30979cb528 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/folder.open.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/folder.sec.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/folder.sec.gif Binary files differnew file mode 100644 index 0000000000..75332d9e59 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/folder.sec.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/forward.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/forward.gif Binary files differnew file mode 100644 index 0000000000..b2959b4c85 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/forward.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/generic.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/generic.gif Binary files differnew file mode 100644 index 0000000000..de60b2940f --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/generic.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/generic.red.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/generic.red.gif Binary files differnew file mode 100644 index 0000000000..94743981d9 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/generic.red.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/generic.sec.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/generic.sec.gif Binary files differnew file mode 100644 index 0000000000..88d5240c3c --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/generic.sec.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/hand.right.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/hand.right.gif Binary files differnew file mode 100644 index 0000000000..5cdbc7206d --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/hand.right.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/hand.up.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/hand.up.gif Binary files differnew file mode 100644 index 0000000000..85a5d68317 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/hand.up.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/htdig.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/htdig.gif Binary files differnew file mode 100644 index 0000000000..35443fb63a --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/htdig.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/icon.sheet.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/icon.sheet.gif Binary files differnew file mode 100644 index 0000000000..ad1686e448 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/icon.sheet.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/image1.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/image1.gif Binary files differnew file mode 100644 index 0000000000..01e442bfa9 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/image1.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/image2.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/image2.gif Binary files differnew file mode 100644 index 0000000000..751faeea36 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/image2.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/image3.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/image3.gif Binary files differnew file mode 100644 index 0000000000..4f30484ff6 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/image3.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/index.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/index.gif Binary files differnew file mode 100644 index 0000000000..162478fb3a --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/index.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/layout.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/layout.gif Binary files differnew file mode 100644 index 0000000000..c96338a152 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/layout.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/left.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/left.gif Binary files differnew file mode 100644 index 0000000000..279e6710d4 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/left.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/link.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/link.gif Binary files differnew file mode 100644 index 0000000000..c5b6889a76 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/link.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/movie.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/movie.gif Binary files differnew file mode 100644 index 0000000000..0035183774 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/movie.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/p.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/p.gif Binary files differnew file mode 100644 index 0000000000..7b917b4e91 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/p.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/patch.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/patch.gif Binary files differnew file mode 100644 index 0000000000..39bc90e795 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/patch.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/pdf.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/pdf.gif Binary files differnew file mode 100644 index 0000000000..c88fd777c4 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pdf.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/pie0.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie0.gif Binary files differnew file mode 100644 index 0000000000..6f7a0ae7a7 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie0.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/pie1.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie1.gif Binary files differnew file mode 100644 index 0000000000..03aa6be71e --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie1.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/pie2.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie2.gif Binary files differnew file mode 100644 index 0000000000..b04c5e0908 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie2.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/pie3.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie3.gif Binary files differnew file mode 100644 index 0000000000..4db9d023ed --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie3.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/pie4.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie4.gif Binary files differnew file mode 100644 index 0000000000..93471fdd88 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie4.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/pie5.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie5.gif Binary files differnew file mode 100644 index 0000000000..57aee93f07 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie5.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/pie6.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie6.gif Binary files differnew file mode 100644 index 0000000000..0dc327b569 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie6.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/pie7.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie7.gif Binary files differnew file mode 100644 index 0000000000..8661337f06 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie7.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/pie8.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie8.gif Binary files differnew file mode 100644 index 0000000000..59ddb34ce0 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie8.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/portal.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/portal.gif Binary files differnew file mode 100644 index 0000000000..0e6e506e00 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/portal.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/poweredby.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/poweredby.gif Binary files differnew file mode 100644 index 0000000000..d324ab80ea --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/poweredby.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/ps.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/ps.gif Binary files differnew file mode 100644 index 0000000000..0f565bc1db --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/ps.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/quill.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/quill.gif Binary files differnew file mode 100644 index 0000000000..818a5cdc7e --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/quill.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/right.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/right.gif Binary files differnew file mode 100644 index 0000000000..b256e5f75f --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/right.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/screw1.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/screw1.gif Binary files differnew file mode 100644 index 0000000000..af6ba2b097 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/screw1.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/screw2.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/screw2.gif Binary files differnew file mode 100644 index 0000000000..06dccb3e44 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/screw2.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/script.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/script.gif Binary files differnew file mode 100644 index 0000000000..d8a853bc58 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/script.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/sound1.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/sound1.gif Binary files differnew file mode 100644 index 0000000000..8efb49f55d --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/sound1.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/sound2.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/sound2.gif Binary files differnew file mode 100644 index 0000000000..48e6a7fb2f --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/sound2.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/sphere1.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/sphere1.gif Binary files differnew file mode 100644 index 0000000000..7067070da2 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/sphere1.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/sphere2.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/sphere2.gif Binary files differnew file mode 100644 index 0000000000..a9e462a377 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/sphere2.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/star.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/star.gif Binary files differnew file mode 100644 index 0000000000..4cfe0a5e0f --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/star.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/star_blank.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/star_blank.gif Binary files differnew file mode 100644 index 0000000000..a0c83cb85b --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/star_blank.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/tar.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/tar.gif Binary files differnew file mode 100644 index 0000000000..617e779efa --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/tar.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/tex.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/tex.gif Binary files differnew file mode 100644 index 0000000000..45e43233b8 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/tex.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/text.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/text.gif Binary files differnew file mode 100644 index 0000000000..4c623909fb --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/text.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/transfer.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/transfer.gif Binary files differnew file mode 100644 index 0000000000..33697dbb66 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/transfer.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/unknown.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/unknown.gif Binary files differnew file mode 100644 index 0000000000..32b1ea23fb --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/unknown.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/up.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/up.gif Binary files differnew file mode 100644 index 0000000000..6d6d6d1ebf --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/up.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/uu.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/uu.gif Binary files differnew file mode 100644 index 0000000000..4387d529f6 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/uu.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/uuencoded.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/uuencoded.gif Binary files differnew file mode 100644 index 0000000000..4387d529f6 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/uuencoded.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/world1.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/world1.gif Binary files differnew file mode 100644 index 0000000000..05b4ec2058 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/world1.gif diff --git a/lib/inets/test/httpd_SUITE_data/server_root/icons/world2.gif b/lib/inets/test/httpd_SUITE_data/server_root/icons/world2.gif Binary files differnew file mode 100644 index 0000000000..e3203f7a88 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/world2.gif 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 new file mode 100644 index 0000000000..8d1c8b69c3 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip @@ -0,0 +1 @@ + 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 new file mode 100644 index 0000000000..8221139eb4 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_client.pem @@ -0,0 +1,22 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBPAIBAAJBAL6Ym/bgUvhhnPkw08sggGg8Tnp759ThGMEjkmDzhuJ3w3PfnF65 +mgHcgunku4G6LxAQfEUougJWf9Phmjj3oRUCAwEAAQJBAKMjvVvzZxFzfAlP4flc +OI0AEayFokp04dtvtzuFN09f+aBo2dP18xHmKLCZvxrBOaRAROoQYscALiIVpN07 +GAECIQDfi+sSfAFaDlT3vzpL3xE5UEH6IzY8jWpaZfM1QaToJQIhANpEF50H4wGO +8Sbh7dUutNd+s+NYUjsMySW2DjLKMsoxAiEAzzb2ftrdsempD0F+O0gZwiPIFKLB +Kp33YLYyHEKuJtUCIDGi+pvDh2R7VWw6RRQOIyI+tjolg83aAoSI+oGiahqBAiEA +xzmNNajwoaokvWvlaz0na8rhxu45grOvDrflBT9XvSQ= +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIICDDCCAbYCAQAwDQYJKoZIhvcNAQEEBQAwgZAxCzAJBgNVBAYTAlNFMRIwEAYD +VQQIEwlTdG9ja2hvbG0xDzANBgNVBAcTBkFsdnNqbzEMMAoGA1UEChMDRVRYMQ4w +DAYDVQQLEwVETi9TUDEXMBUGA1UEAxMOSm9ha2ltIEdyZWJlbm8xJTAjBgkqhkiG +9w0BCQEWFmpvY2tlQGVyaXguZXJpY3Nzb24uc2UwHhcNOTcwNzE1MTUzNDM2WhcN +MDMwMjIyMTUzNDM2WjCBkDELMAkGA1UEBhMCU0UxEjAQBgNVBAgTCVN0b2NraG9s +bTEPMA0GA1UEBxMGQWx2c2pvMQwwCgYDVQQKEwNFVFgxDjAMBgNVBAsTBUROL1NQ +MRcwFQYDVQQDEw5Kb2FraW0gR3JlYmVubzElMCMGCSqGSIb3DQEJARYWam9ja2VA +ZXJpeC5lcmljc3Nvbi5zZTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC+mJv24FL4 +YZz5MNPLIIBoPE56e+fU4RjBI5Jg84bid8Nz35xeuZoB3ILp5LuBui8QEHxFKLoC +Vn/T4Zo496EVAgMBAAEwDQYJKoZIhvcNAQEEBQADQQBYxQVfTydyZCE0UXvZd7Ei +josNsAaWJk9fFIJaG9uyXCEfg2dVgoT2eBk3D9DI+7OB+78isM5CVlFbL7hilvP8 +-----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 new file mode 100644 index 0000000000..fe739c15f7 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_server.pem @@ -0,0 +1,22 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBAL9Bozj3BIjL5Cy8b3rjMT2kPZRychX4wz9bHoIIiKnKo1xXHYjw +g3N9zWM1f1ZzMADwVry1uAInA8q09+7hL20CAwEAAQJACwu2ao7RozjrV64WXimK +6X131P/7GMvCMwGHNIlbozqoOqmZcYrbKaF61l+XuwA2QvTo3ywW1Ivxcyr6TeAr +PQIhAOX+WXT6yiqqwjt08kjBCJyMgfZtdAO6pc/6pKjNWiZfAiEA1OH1iPW/OQe5 +tlQXpiRVdLyneNsPygPRJc4Bdwu3hbMCIQDbI5pA56QxOzqOREOGJsb5wrciAfAE +jZbnr72sSN2YqQIgAWFpvzagw9Tp/mWzNY+cwkIK7/yzsIKv04fveH8p9IMCIQCr +td4IiukeUwXmPSvYM4uCE/+J89wEL9qU8Mlc3gDLXA== +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIICDDCCAbYCAQAwDQYJKoZIhvcNAQEEBQAwgZAxCzAJBgNVBAYTAlNFMRIwEAYD +VQQIEwlTdG9ja2hvbG0xDzANBgNVBAcTBkFsdnNqbzEMMAoGA1UEChMDRVRYMQ4w +DAYDVQQLEwVETi9TUDEXMBUGA1UEAxMOSm9ha2ltIEdyZWJlbm8xJTAjBgkqhkiG +9w0BCQEWFmpvY2tlQGVyaXguZXJpY3Nzb24uc2UwHhcNOTcwNzE1MTUzMzQxWhcN +MDMwMjIyMTUzMzQxWjCBkDELMAkGA1UEBhMCU0UxEjAQBgNVBAgTCVN0b2NraG9s +bTEPMA0GA1UEBxMGQWx2c2pvMQwwCgYDVQQKEwNFVFgxDjAMBgNVBAsTBUROL1NQ +MRcwFQYDVQQDEw5Kb2FraW0gR3JlYmVubzElMCMGCSqGSIb3DQEJARYWam9ja2VA +ZXJpeC5lcmljc3Nvbi5zZTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC/QaM49wSI +y+QsvG964zE9pD2UcnIV+MM/Wx6CCIipyqNcVx2I8INzfc1jNX9WczAA8Fa8tbgC +JwPKtPfu4S9tAgMBAAEwDQYJKoZIhvcNAQEEBQADQQAmXDY1CyJjzvQZX442kkHG +ic9QFY1UuVfzokzNMwlHYl1Qx9zaodx0cJCrcH5GF9O9LJbhhV77LzoxT1Q5wZp5 +-----END CERTIFICATE----- diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl new file mode 100644 index 0000000000..f86c1fcb49 --- /dev/null +++ b/lib/inets/test/httpd_basic_SUITE.erl @@ -0,0 +1,136 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-2009. 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_basic_SUITE). + +-include("test_server.hrl"). +-include("test_server_line.hrl"). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +all(doc) -> + ["Basic test of httpd."]; + +all(suite) -> + [ + uri_too_long_414, + header_too_long_413 + ]. + +%%-------------------------------------------------------------------- +%% 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) -> + ok = inets:start(), + PrivDir = ?config(priv_dir, Config), + HttpdConf = [{port, 0}, {ipfamily, inet}, + {server_name, "httpd_test"}, {server_root, PrivDir}, + {document_root, PrivDir}, {bind_address, "localhost"}], + [{httpd_conf, HttpdConf} | 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. + +%%-------------------------------------------------------------------- +%% 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) -> + Config. + +%%-------------------------------------------------------------------- +%% 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) -> + Config. + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- +uri_too_long_414(doc) -> + ["Test that too long uri's get 414 HTTP code"]; +uri_too_long_414(suite) -> + []; +uri_too_long_414(Config) when is_list(Config) -> + HttpdConf = ?config(httpd_conf, Config), + {ok, Pid} = inets:start(httpd, [{port, 0}, {max_uri_size, 10} + | 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 /morethantenchars " + "HTTP/1.1\r\n\r\n", + [{statuscode, 414}, + %% Server will send lowest version + %% as it will not get to the + %% client version + %% before aborting + {version, "HTTP/0.9"}]), + inets:stop(httpd, Pid). + +header_too_long_413(doc) -> + ["Test that too long headers's get 413 HTTP code"]; +header_too_long_413(suite) -> + []; +header_too_long_413(Config) when is_list(Config) -> + HttpdConf = ?config(httpd_conf, Config), + {ok, Pid} = inets:start(httpd, [{port, 0}, {max_header_size, 10} + | 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 index.html " + "HTTP/1.1\r\n" + "Connection:close \r\n\r\n ", + [{statuscode, 413}, + {version, "HTTP/1.1"}]), + inets:stop(httpd, Pid). + + + + diff --git a/lib/inets/test/httpd_block.erl b/lib/inets/test/httpd_block.erl new file mode 100644 index 0000000000..f967d8172a --- /dev/null +++ b/lib/inets/test/httpd_block.erl @@ -0,0 +1,299 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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_block). + +-include("test_server.hrl"). +-include("test_server_line.hrl"). + +%% General testcases bodies called from httpd_SUITE +-export([block_disturbing_idle/4, block_non_disturbing_idle/4, + block_503/4, block_disturbing_active/4, + block_non_disturbing_active/4, + block_disturbing_active_timeout_not_released/4, + block_disturbing_active_timeout_released/4, + block_non_disturbing_active_timeout_not_released/4, + block_non_disturbing_active_timeout_released/4, + disturbing_blocker_dies/4, + non_disturbing_blocker_dies/4, restart_no_block/4, + restart_disturbing_block/4, restart_non_disturbing_block/4 + ]). + +%% Help functions +-export([do_block_server/4, do_block_nd_server/5, do_long_poll/6]). + +-define(report(Label, Content), + inets:report_event(20, Label, test_case, + [{module, ?MODULE}, {line, ?LINE} | Content])). + + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- +block_disturbing_idle(_Type, Port, Host, Node) -> + unblocked = get_admin_state(Node, Host, Port), + block_server(Node, Host, Port), + blocked = get_admin_state(Node, Host, Port), + unblock_server(Node, Host, Port), + unblocked = get_admin_state(Node, Host, Port). +%%-------------------------------------------------------------------- +block_non_disturbing_idle(_Type, Port, Host, Node) -> + unblocked = get_admin_state(Node, Host, Port), + block_nd_server(Node, Host, Port), + blocked = get_admin_state(Node, Host, Port), + unblock_server(Node, Host, Port), + unblocked = get_admin_state(Node, Host, Port). +%%-------------------------------------------------------------------- +block_503(Type, Port, Host, Node) -> + Req = "GET / HTTP/1.0\r\ndummy-host.ericsson.se:\r\n\r\n", + unblocked = get_admin_state(Node, Host, Port), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, Req, + [{statuscode, 200}, + {version, "HTTP/1.0"}]), + ok = block_server(Node, Host, Port), + blocked = get_admin_state(Node, Host, Port), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, Req, + [{statuscode, 503}, + {version, "HTTP/1.0"}]), + ok = unblock_server(Node, Host, Port), + unblocked = get_admin_state(Node, Host, Port), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, Req, + [{statuscode, 200}, + {version, "HTTP/1.0"}]). +%%-------------------------------------------------------------------- +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), + block_server(Node, Host, Port), + await_suite_failed_process_exit(Pid, "poller", 60000, + connection_closed), + blocked = get_admin_state(Node, Host, Port), + process_flag(trap_exit, false), + ok. +%%-------------------------------------------------------------------- +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), + ok = block_nd_server(Node, Host, Port), + await_normal_process_exit(Poller, "poller", 60000), + blocked = get_admin_state(Node, Host, Port), + process_flag(trap_exit, false), + ok. + +%%-------------------------------------------------------------------- +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), + Blocker = blocker(Node, Host, Port, 50000), + await_normal_process_exit(Blocker, "blocker", 50000), + await_normal_process_exit(Poller, "poller", 30000), + blocked = get_admin_state(Node, Host, Port), + process_flag(trap_exit, false), + ok. + +%%-------------------------------------------------------------------- +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), + Blocker = blocker(Node, Host, Port, 10000), + await_normal_process_exit(Blocker, "blocker", 15000), + await_suite_failed_process_exit(Poller, "poller", 40000, + connection_closed), + blocked = get_admin_state(Node, Host, Port), + process_flag(trap_exit, false), + ok. +%%-------------------------------------------------------------------- +block_non_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(5000), + ok = block_nd_server(Node, Host, Port, 40000), + await_normal_process_exit(Poller, "poller", 60000), + blocked = get_admin_state(Node, Host, Port), + process_flag(trap_exit, false), + ok. + +%%-------------------------------------------------------------------- +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), + Blocker = blocker_nd(Node, Host, Port ,10000, {error,timeout}), + await_normal_process_exit(Blocker, "blocker", 15000), + await_normal_process_exit(Poller, "poller", 50000), + unblocked = get_admin_state(Node, Host, Port), + process_flag(trap_exit, false), + ok. +%%-------------------------------------------------------------------- +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), + Blocker = blocker(Node, Host, Port, 10000), + test_server:sleep(5000), + exit(Blocker,simulate_blocker_crash), + await_normal_process_exit(Poller, "poller", 60000), + unblocked = get_admin_state(Node, Host, Port), + process_flag(trap_exit, false), + ok. + +%%-------------------------------------------------------------------- +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), + Blocker = blocker_nd(Node, Host, Port, 10000, ok), + test_server:sleep(5000), + exit(Blocker, simulate_blocker_crash), + await_normal_process_exit(Poller, "poller", 60000), + unblocked = get_admin_state(Node, Host, Port), + process_flag(trap_exit, false), + ok. +%%-------------------------------------------------------------------- +restart_no_block(_, Port, Host, Node) -> + {error,_Reason} = restart_server(Node, Host, Port). + +%%-------------------------------------------------------------------- +restart_disturbing_block(_, Port, Host, Node) -> + ?report("restart_disturbing_block - get_admin_state (unblocked)", []), + unblocked = get_admin_state(Node, Host, Port), + ?report("restart_disturbing_block - block_server", []), + ok = block_server(Node, Host, Port), + ?report("restart_disturbing_block - restart_server", []), + ok = restart_server(Node, Host, Port), + ?report("restart_disturbing_block - unblock_server", []), + ok = unblock_server(Node, Host, Port), + ?report("restart_disturbing_block - get_admin_state (unblocked)", []), + unblocked = get_admin_state(Node, Host, Port). + +%%-------------------------------------------------------------------- +restart_non_disturbing_block(_, Port, Host, Node) -> + ?report("restart_non_disturbing_block - get_admin_state (unblocked)", []), + unblocked = get_admin_state(Node, Host, Port), + ?report("restart_non_disturbing_block - block_nd_server", []), + ok = block_nd_server(Node, Host, Port), + ?report("restart_non_disturbing_block - restart_server", []), + ok = restart_server(Node, Host, Port), + ?report("restart_non_disturbing_block - unblock_server", []), + ok = unblock_server(Node, Host, Port), + ?report("restart_non_disturbing_block - get_admin_state (unblocked)", []), + unblocked = get_admin_state(Node, Host, Port). + +%%-------------------------------------------------------------------- +%% Internal functions +%%-------------------------------------------------------------------- +blocker(Node, Host, Port, Timeout) -> + spawn_link(?MODULE, do_block_server,[Node, Host, Port,Timeout]). + +do_block_server(Node, Host, Port, Timeout) -> + ok = block_server(Node, Host, Port, Timeout), + exit(normal). + +blocker_nd(Node, Host, Port, Timeout, Reply) -> + spawn_link(?MODULE, do_block_nd_server, + [Node, Host, Port, Timeout, Reply]). + +do_block_nd_server(Node, Host, Port, Timeout, Reply) -> + Reply = block_nd_server(Node, Host, Port, Timeout), + exit(normal). + +restart_server(Node, _Host, Port) -> + Addr = undefined, + rpc:call(Node, httpd, restart, [Addr, Port]). + +block_server(Node, _Host, Port) -> + Addr = undefined, + rpc:call(Node, httpd, block, [Addr, Port]). + +block_server(Node, _Host, Port, Timeout) -> + Addr = undefined, + rpc:call(Node, httpd, block, [Addr, Port, disturbing, Timeout]). + +block_nd_server(Node, _Host, Port) -> + Addr = undefined, + rpc:call(Node, httpd, block, [Addr, Port, non_disturbing]). + +block_nd_server(Node, _Host, Port, Timeout) -> + Addr = undefined, + rpc:call(Node, httpd, block, [Addr, Port, non_disturbing, Timeout]). + +unblock_server(Node, _Host, Port) -> + Addr = undefined, + rpc:call(Node, httpd, unblock, [Addr, Port]). + +get_admin_state(Node,_Host,Port) -> + Addr = undefined, + rpc:call(Node, httpd, get_admin_state, [Addr, Port]). + +await_normal_process_exit(Pid, Name, Timeout) -> + receive + {'EXIT', Pid, normal} -> + ok; + {'EXIT', Pid, Reason} -> + Err = + lists:flatten( + io_lib:format("expected normal exit, " + "unexpected exit of ~s process: ~p", + [Name, Reason])), + test_server:fail(Err) + after Timeout -> + test_server:fail("timeout while waiting for " ++ Name) + end. + +await_suite_failed_process_exit(Pid, Name, Timeout, Why) -> + receive + {'EXIT', Pid, {suite_failed, Why}} -> + ok; + {'EXIT', Pid, Reason} -> + Err = + lists:flatten( + io_lib:format("expected connection_closed, " + "unexpected exit of ~s process: ~p", + [Name, Reason])), + test_server:fail(Err) + after Timeout -> + test_server:fail("timeout while waiting for " ++ Name) + end. + +long_poll(Type, Host, Port, Node, StatusCode, Timeout) -> + spawn_link(?MODULE, do_long_poll, [Type, Host, Port, Node, + StatusCode, Timeout]). + +do_long_poll(Type, Host, Port, Node, StatusCode, Timeout) -> + Mod = "httpd_example", + Func = "delay", + Req = lists:flatten(io_lib:format("GET /eval?" ++ Mod ++ ":" ++ Func ++ + "(~p) HTTP/1.0\r\n\r\n",[30000])), + case httpd_test_lib:verify_request(Type, Host, Port, Node, Req, + [{statuscode, StatusCode}, + {version, "HTTP/1.0"}], Timeout) of + ok -> + exit(normal); + Reason -> + test_server:fail(Reason) + end. + + + + + diff --git a/lib/inets/test/httpd_load.erl b/lib/inets/test/httpd_load.erl new file mode 100644 index 0000000000..9bb9f9f94e --- /dev/null +++ b/lib/inets/test/httpd_load.erl @@ -0,0 +1,99 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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_load). + +-include("test_server.hrl"). +-include("test_server_line.hrl"). + +%% General testcases bodies called from httpd_SUITE +-export([load_test/5]). + +%% Help functions +-export([load_test_client/8]). + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- +load_test(Type, Port, Host, Node, NofTesters) -> + URIs = + [ + "/index.html", + "/echo.shtml", + "/", + "/flastmod.shtml", + "/misc/" + ], + Fun = fun(Mod, Host1, Port1, Node1, Req, Exp) -> + ok = httpd_test_lib:verify_request(Mod, Host1, Port1, + Node1, Req, Exp) + end, + load_test(Fun, URIs ++ URIs, Type, Host, Port, Node, NofTesters, []). +%%-------------------------------------------------------------------- +%% Internal functions +%%-------------------------------------------------------------------- + +load_test(_, _, _, _, _, _, 0, []) -> + ok; +load_test(Fun, URIs, Type, Host, Port, Node, 0, List) -> + receive + {Pid, done} -> + load_test(Fun, URIs, Type, Host, Port, Node, 0, + lists:delete(Pid, List)); + {'EXIT', Pid, normal} -> + load_test(Fun, URIs, Type, Host, Port, Node, 0, + lists:delete(Pid, List)); + {'EXIT', Pid, Reason} -> + Str = lists:flatten(io_lib:format("client ~p exited: ~p", + [Pid,Reason])), + test_server:fail(Str); + _ -> + load_test(Fun, URIs, Type, Host, Port, Node, 0, List) + end; + +load_test(Fun, URIs, Type, Host, Port, Node, X, List) -> + Pid = spawn_link(?MODULE, load_test_client, + [Fun, URIs, Type, Host, Port, Node, self(), 100]), + load_test(Fun, lists:reverse(URIs), Type, Host, Port, Node, X-1, + [Pid | List]). + +load_test_client(_Fun, [], _Type, _Host, _Port, _Node, Boss, _Timeout) -> + load_test_client_done(Boss); +load_test_client(Fun, [URI|URIs], Type, Host, Port, Node, Boss, Timeout) -> + Req = "GET "++URI++" HTTP/1.0\r\nConnection: Close\r\n" + "From: m@erix\r\nReferer: http://www.ericsson.se/\r\n\r\n", + Timeout1 = + case (catch Fun(Type, Host, Port, Node, Req, + [{statuscode, 200}, {statuscode, 500}, + {statuscode, 503}, {version, "HTTP/1.0"}])) of + {'EXIT', {suite_failed, connection_closed, _, _}} -> + %% Some platforms seems to handle heavy load badly. + %% So, back off and see if this helps + %%?LOG("load_test_client->requestfailed:connection_closed"[]), + 2 * Timeout; + _ -> + Timeout + end, + test_server:sleep(Timeout1), + load_test_client(Fun, URIs, Type, Host, Port, Node, Boss, Timeout1). + +load_test_client_done(Boss) -> + Boss ! {self(), done}. + diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl new file mode 100644 index 0000000000..b03f842e7c --- /dev/null +++ b/lib/inets/test/httpd_mod.erl @@ -0,0 +1,947 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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_mod). +-author('[email protected]'). + +-include("test_server.hrl"). +-include("test_server_line.hrl"). + +%% General testcases bodies called from httpd_SUITE +-export([alias/4, actions/4, security/5, auth/4, auth_api/6, + auth_mnesia_api/4, htaccess/4, + cgi/4, esi/4, get/4, head/4, all/4]). + +%% Help functions +-export([event/4, ssl_password_cb/0]). + +%% Seconds before successful auths timeout. +-define(AUTH_TIMEOUT,5). + + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- +alias(Type, Port, Host, Node) -> + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /pics/icon.sheet.gif " + "HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {header, "Content-Type","image/gif"}, + {header, "Server"}, + {header, "Date"}, + {version, "HTTP/1.0"}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET / HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {header, "Content-Type","text/html"}, + {header, "Server"}, + {header, "Date"}, + {version, "HTTP/1.0"}]), + + ok = httpd_test_lib:verify_request(Type,Host,Port,Node, + "GET /misc/ HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {header, "Content-Type","text/html"}, + {header, "Server"}, + {header, "Date"}, + {version, "HTTP/1.0"}]), + + %% Check redirection if trailing slash is missing. + ok = httpd_test_lib:verify_request(Type,Host,Port,Node, + "GET /misc HTTP/1.0\r\n\r\n", + [{statuscode, 301}, + {header, "Location"}, + {header, "Content-Type","text/html"}, + {version, "HTTP/1.0"}]). + +%%------------------------------------------------------------------------- +actions(Type, Port, Host, Node) -> + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "HEAD / HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/1.0"}]). + +%%------------------------------------------------------------------------- +security(ServerRoot, Type, Port, Host, Node) -> + io:format(user, "~w:security -> entry with" + "~n ServerRoot: ~p" + "~n Type: ~p" + "~n Port: ~p" + "~n Host: ~p" + "~n Node: ~p" + "~n", [?MODULE, ServerRoot, Type, Port, Host, Node]), + + global:register_name(mod_security_test, self()), % Receive events + + test_server:sleep(5000), + + OpenDir = filename:join([ServerRoot, "htdocs", "open"]), + + %% Test blocking / unblocking of users. + + %% /open, require user one Aladdin + remove_users(Node, ServerRoot, Host, Port, "open"), + + auth_request(Type, Host, Port, Node, "/open/", "one", "onePassword", + [{statuscode, 401}]), + receive_security_event({event, auth_fail, Port, OpenDir, + [{user, "one"}, {password, "onePassword"}]}, + Node, Port), + + auth_request(Type,Host,Port,Node,"/open/", "two", "twoPassword", + [{statuscode, 401}]), + receive_security_event({event, auth_fail, Port, OpenDir, + [{user, "two"}, {password, "twoPassword"}]}, + Node, Port), + + auth_request(Type, Host, Port, Node,"/open/", "Aladdin", + "AladdinPassword", [{statuscode, 401}]), + receive_security_event({event, auth_fail, Port, OpenDir, + [{user, "Aladdin"}, + {password, "AladdinPassword"}]}, + Node, Port), + + add_user(Node, ServerRoot, Port, "open", "one", "onePassword", []), + add_user(Node, ServerRoot, Port, "open", "two", "twoPassword", []), + + auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", + [{statuscode, 401}]), + receive_security_event({event, auth_fail, Port, OpenDir, + [{user, "one"}, {password, "WrongPassword"}]}, + Node, Port), + + auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", + [{statuscode, 401}]), + receive_security_event({event, auth_fail, Port, OpenDir, + [{user, "one"}, {password, "WrongPassword"}]}, + Node, Port), + + receive_security_event({event, user_block, Port, OpenDir, + [{user, "one"}]}, Node, Port), + + global:unregister_name(mod_security_test), % No more events. + + auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", + [{statuscode, 401}]), + auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword", + [{statuscode, 403}]), + + %% User "one" should be blocked now.. + %% [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node,Port), + case list_blocked_users(Node, Port) of + [{"one",_, Port, OpenDir,_}] -> + ok; + Blocked -> + io:format(user, "~w:security -> Blocked: ~p" + "~n", [?MODULE, Blocked]), + exit({unexpected_blocked, Blocked}) + end, + + [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node,Port,OpenDir), + + true = unblock_user(Node, "one", Port, OpenDir), + %% User "one" should not be blocked any more.. + [] = list_blocked_users(Node, Port), + [] = list_blocked_users(Node, Port, OpenDir), + auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword", + [{statuscode, 200}]), + + %% Test list_auth_users & auth_timeout + ["one"] = list_auth_users(Node, Port), + ["one"] = list_auth_users(Node, Port, OpenDir), + auth_request(Type, Host, Port, Node,"/open/", "two", "onePassword", + [{statuscode, 401}]), + ["one"] = list_auth_users(Node, Port), + ["one"] = list_auth_users(Node, Port, OpenDir), + auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword", + [{statuscode, 401}]), + ["one"] = list_auth_users(Node, Port), + ["one"] = list_auth_users(Node, Port, OpenDir), + %% Wait for successful auth to timeout. + test_server:sleep(?AUTH_TIMEOUT*1001), + [] = list_auth_users(Node, Port), + [] = list_auth_users(Node, Port, OpenDir), + %% "two" is blocked. + true = unblock_user(Node, "two", Port, OpenDir), + %% Test explicit blocking. Block user 'two'. + [] = list_blocked_users(Node,Port,OpenDir), + true = block_user(Node, "two", Port, OpenDir, 10), + auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword", + [{statuscode, 401}]). + +%%------------------------------------------------------------------------- +auth(Type, Port, Host, Node) -> + %% Authentication required! + ok = httpd_test_lib:verify_request(Type,Host,Port,Node, + "GET /open/ HTTP/1.0\r\n\r\n", + [{statuscode, 401}, + {version, "HTTP/1.0"}, + {header, "WWW-Authenticate"}]), + ok = httpd_test_lib:verify_request(Type,Host,Port,Node, + "GET /secret/ HTTP/1.0\r\n\r\n", + [{statuscode, 401}, + {version, "HTTP/1.0"}, + {header, "WWW-Authenticate"}]), + ok = httpd_test_lib:verify_request(Type,Host,Port,Node, + "GET /secret/top_secret/" + " HTTP/1.0\r\n\r\n", + [{statuscode, 401}, + {version, "HTTP/1.0"}, + {header, "WWW-Authenticate"}]), + + %% Authentication OK! ["one:OnePassword" user first in user list] + auth_request(Type, Host, Port, Node, "/open/dummy.html", "one", + "onePassword", [{statuscode, 200}]), + %% Authentication OK and a directory listing is supplied! + %% ["Aladdin:open sesame" user second in user list] + auth_request(Type, Host, Port, Node, "/open/","Aladdin", + "AladdinPassword", [{statuscode, 200}]), + + %% User correct but wrong password! ["one:one" user first in user list] + auth_request(Type, Host, Port, Node, "/open/", "one", "one", + [{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! + auth_request(Type, Host, Port, Node, "/open/", "one", "one", + [{statuscode, 401},{header, "WWW-Authenticate"}]), + + %% Neither user or password correct! ["dummy:dummy"] + auth_request(Type, Host, Port, Node, "/open/", "dummy", "dummy", + [{statuscode, 401}]), + + %% Authentication OK! ["two:TwoPassword" user in first group] + auth_request(Type, Host, Port, Node, "/secret/dummy.html", "two", + "twoPassword", [{statuscode, 200}]), + %% Authentication OK and a directory listing is supplied! + %% ["three:ThreePassword" user in second group] + auth_request(Type, Host, Port, Node,"/secret/", "three", + "threePassword", [{statuscode, 200}]), + + %% User correct but wrong password! ["two:two" user in first group] + auth_request(Type, Host, Port, Node, "/secret/", "two", "two", + [{statuscode, 401}]), + %% Neither user or password correct! ["dummy:dummy"] + auth_request(Type, Host, Port, Node,"/secret/", "dummy", "dummy", + [{statuscode, 401}]), + + %% Nested secret/top_secret OK! ["Aladdin:open sesame"] + auth_request(Type, Host, Port, Node, "/secret/top_secret/", "Aladdin", + "AladdinPassword", [{statuscode, 200}]), + %% Authentication still required! + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /open/ " + "HTTP/1.0\r\n\r\n", + [{statuscode, 401}, + {version, "HTTP/1.0"}, + {header, "WWW-Authenticate"}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /secret/ " + "HTTP/1.0\r\n\r\n", + [{statuscode, 401}, + {version, "HTTP/1.0"}, + {header, "WWW-Authenticate"}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /secret/top_secret/ " + "HTTP/1.0\r\n\r\n", + [{statuscode, 401}, + {version, "HTTP/1.0"}, + {header, "WWW-Authenticate"}]). + + +%%------------------------------------------------------------------------- +%% What to test here: +%% +%% /open - plain, require user one Aladdin +%% /secret - plain, require group group1 group2 +%% /secret/top_secret - plain, require group group3 +%% /dets_open - dets, require user one Aladdin +%% /dets_secret - dets, require group group1 group2 +%% /dets_secret/top_secret - dets, require group group3 +%% /mnesia_open/ - mnesia, require user one Aladdin +%% /mnesia_secret/ - mnesia, require group group1 group2 +%% /mnesia_secret/top_secret/ - mnesia, require group group3 +auth_api(ServerRoot, AuthStoreType, Type, Port, Host, Node) -> + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET / HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/1.0"}]), + auth_request(Type, Host, Port, Node, "/", "one", "WrongPassword", + [{statuscode, 200}]), + + %% Make sure Authenticate header is received even the second time + %% we try a incorrect password! Otherwise a browser client will hang! + auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/", + "dummy", "WrongPassword", [{statuscode, 401}, + {header, "WWW-Authenticate"}]), + auth_request(Type, Host, Port, Node,"/" ++ AuthStoreType ++ "open/", + "dummy", "WrongPassword", [{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}]). + +%%-------------------------------------------------------------------------- +auth_mnesia_api(_Type, Port, _Host, _Node) -> + %% Create three groups: + %% group1 : one Aladdin + %% group2 : two + %% group3 : three + mod_auth_mnesia:store_user("one", "onePassword", Port, + "/mnesia_open", ""), + mod_auth_mnesia:store_user("Aladdin", "AladdinPassword", Port, + "/mnesia_open", ""), + mod_auth_mnesia:store_user("two", "twoPassword", Port, + "/mnesia_open", ""), + mod_auth_mnesia:store_user("three", "threePassword", Port, + "/mnesia_open", ""), + Users = mod_auth_mnesia:list_users(Port, "/mnesia_open"), + + ok = check_lists_members(Users,["Aladdin","one","two","three"]), + + true = mod_auth_mnesia:store_group_member("group1", "one", Port, + "/mnesia_open", ""), + true = mod_auth_mnesia:store_group_member("group1","Aladdin", Port, + "/mnesia_open", ""), + true = mod_auth_mnesia:store_group_member("group2","two", Port, + "/mnesia_open", ""), + true = mod_auth_mnesia:store_group_member("group3","three", Port, + "/mnesia_open", ""), + %% Check that all three created groups exist. + Groups = mod_auth_mnesia:list_groups(Port, "/mnesia_open"), + ok = check_lists_members(Groups, ["group1","group2","group3"]), + + %% Check that the members of all groups are correct. + Group1 = mod_auth_mnesia:list_group_members("group1", Port, + "/mnesia_open"), + ok = check_lists_members(Group1,["one","Aladdin"]), + {ok,["two"]} = mod_auth_mnesia:list_group_members("group2", Port, + "/mnesia_open"), + + {ok,["three"]} = mod_auth_mnesia:list_group_members("group3", Port, + "/mnesia_open"), + + %% Delete user 'one' from group one and check that he was removed + %% correctly. + true = mod_auth_mnesia:remove_group_member("group1", "one", Port, + "/mnesia_open", ""), + {ok,["Aladdin"]} = mod_auth_mnesia:list_group_members("group1", Port, + "/mnesia_open"), + + %% Remove group1 and check that the group was removed correctly. + true = mod_auth_mnesia:remove_group("group1", Port, "/mnesia_open", ""), + Groups_1 = mod_auth_mnesia:list_groups(Port, "/mnesia_open"), + ok = check_lists_members(Groups_1,["group2","group3"]), + + %% Check that the other users still exist in their groups. + Users_1 = mod_auth_mnesia:list_users(Port, "/mnesia_open"), + ok = check_lists_members(Users_1,["Aladdin","one","two","three"]), + {ok,["two"]} = mod_auth_mnesia:list_group_members("group2", Port, + "/mnesia_open"), + {ok,["three"]} = mod_auth_mnesia:list_group_members("group3", Port, + "/mnesia_open"), + + %% Remove the remaining groups/users and check that all + %% users/groups are removed. + true = mod_auth_mnesia:remove_group("group2", Port, "/mnesia_open", ""), + true = mod_auth_mnesia:remove_group("group3", Port, "/mnesia_open", ""), + {ok, []} = mod_auth_mnesia:list_groups(Port, "/mnesia_open"), + true = mod_auth_mnesia:remove_user("one", Port, "/mnesia_open", ""), + true = mod_auth_mnesia:remove_user("Aladdin", Port, "/mnesia_open", ""), + true = mod_auth_mnesia:remove_user("two", Port, "/mnesia_open", ""), + true = mod_auth_mnesia:remove_user("three", Port, "/mnesia_open", ""), + {ok, []} = mod_auth_mnesia:list_users(Port, "/mnesia_open"), + ok. +%%-------------------------------------------------------------------------- +htaccess(Type, Port, Host, Node) -> + %% Control that authentication required! + %% Control that the pages that shall be + %% authenticated really need authenticatin + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /ht/open/ HTTP/1.0\r\n\r\n", + [{statuscode, 401}, + {version, "HTTP/1.0"}, + {header, "WWW-Authenticate"}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /ht/secret/ HTTP/1.0\r\n\r\n", + [{statuscode, 401}, + {version, "HTTP/1.0"}, + {header, "WWW-Authenticate"}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /ht/secret/top_secret/ " + "HTTP/1.0\r\n\r\n", + [{statuscode, 401}, + {version, "HTTP/1.0"}, + {header, "WWW-Authenticate"}]), + + %% Make sure Authenticate header is received even the second time + %% we try a incorrect password! Otherwise a browser client will hang! + auth_request(Type, Host, Port, Node,"/ht/open/", + "dummy", "WrongPassword", [{statuscode, 401}, + {header, "WWW-Authenticate"}]), + auth_request(Type, Host, Port, Node,"/ht/open/", + "dummy", "WrongPassword", [{statuscode, 401}, + {header, "WWW-Authenticate"}]), + + %% Control that not just the first user in the list is valid + %% Control the first user + %% Authennticating ["one:OnePassword" user first in user list] + auth_request(Type, Host, Port, Node, "/ht/open/dummy.html", "one", + "OnePassword", [{statuscode, 200}]), + + %% Control the second user + %% Authentication OK and a directory listing is supplied! + %% ["Aladdin:open sesame" user second in user list] + auth_request(Type, Host, Port, Node, "/ht/open/","Aladdin", + "AladdinPassword", [{statuscode, 200}]), + + %% Contro that bad passwords and userids get a good denial + %% User correct but wrong password! ["one:one" user first in user list] + auth_request(Type, Host, Port, Node, "/ht/open/", "one", "one", + [{statuscode, 401}]), + %% Neither user or password correct! ["dummy:dummy"] + auth_request(Type, Host, Port, Node, "/ht/open/", "dummy", "dummy", + [{statuscode, 401}]), + + %% Control that authetication still works, even if its a member in a group + %% Authentication OK! ["two:TwoPassword" user in first group] + auth_request(Type, Host, Port, Node, "/ht/secret/dummy.html", "two", + "TwoPassword", [{statuscode, 200}]), + + %% Authentication OK and a directory listing is supplied! + %% ["three:ThreePassword" user in second group] + auth_request(Type, Host, Port, Node,"/ht/secret/", "three", + "ThreePassword", [{statuscode, 200}]), + + %% Deny users with bad passwords even if the user is a group member + %% User correct but wrong password! ["two:two" user in first group] + auth_request(Type, Host, Port, Node, "/ht/secret/", "two", "two", + [{statuscode, 401}]), + %% Neither user or password correct! ["dummy:dummy"] + auth_request(Type, Host, Port, Node,"/ht/secret/", "dummy", "dummy", + [{statuscode, 401}]), + + %% control that we deny the users that are in subnet above the allowed + auth_request(Type, Host, Port, Node,"/ht/blocknet/dummy.html", "four", + "FourPassword", [{statuscode, 403}]), + %% Control that we only applies the rules to the right methods + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "HEAD /ht/blocknet/dummy.html" + " HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/1.0"}]), + + %% Control that the rerquire directive can be overrideen + auth_request(Type, Host, Port, Node, + "/ht/secret/top_secret/", "Aladdin", "AladdinPassword", + [{statuscode, 401}]), + + %% Authentication still required! + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /ht/open/ " + "HTTP/1.0\r\n\r\n", + [{statuscode, 401}, + {version, "HTTP/1.0"}, + {header, "WWW-Authenticate"}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /ht/secret/ HTTP/1.0\r\n\r\n", + [{statuscode, 401}, + {version, "HTTP/1.0"}, + {header, "WWW-Authenticate"}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /ht/secret/top_secret/ " + "HTTP/1.0\r\n\r\n", + [{statuscode, 401}, + {version, "HTTP/1.0"}, + {header, "WWW-Authenticate"}]). +%%-------------------------------------------------------------------- +cgi(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 ++ + " HTTP/1.0\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, "HTTP/1.0"}, + {header, "content-type", "text/plain"}]), + + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /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 /cgi-bin/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 /cgi-bin/"++ 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 /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"}]), + ok. + +%%-------------------------------------------------------------------- +esi(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!\")" + " HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/1.0"}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /eval?not_allowed:print(\"Hi!\")" + " HTTP/1.0\r\n\r\n", + [{statuscode, 403}, + {version, "HTTP/1.0"}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /eval?httpd_example:undef(\"Hi!\")" + " HTTP/1.0\r\n\r\n", + [{statuscode, 500}, + {version, "HTTP/1.0"}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/erl/httpd_example " + "HTTP/1.0\r\n\r\n", + [{statuscode, 400}, + {version, "HTTP/1.0"}]), + 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}, + {version, "HTTP/1.0"}]), + 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 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/not_allowed:post " + "HTTP/1.0\r\n\r\n", + [{statuscode, 403}, + {version, "HTTP/1.0"}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/erl/httpd_example:undef " + "HTTP/1.0\r\n\r\n", + [{statuscode, 404}, + {version, "HTTP/1.0"}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/erl/httpd_example/yahoo" + " HTTP/1.0\r\n\r\n", + [{statuscode, 302}, + {version, "HTTP/1.0"}]), + ok. +%%-------------------------------------------------------------------- +get(Type, Port, Host, Node) -> + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /index.html HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {header, "Content-Type", "text/html"}, + {header, "Date"}, + {header, "Server"}, + {version, "HTTP/1.0"}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /fsize.shtml HTTP/1.1\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 /fsize.shtml HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {header, "Content-Type"}, + {header, "Server"}, + {header, "Date"}, + {version, "HTTP/1.0"}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /secret/dummy.html " + "HTTP/1.0\r\n\r\n", + [{statuscode, 401}, + {header, "WWW-Authenticate"}, + {version, "HTTP/1.0"}]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /index.html HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {header, "Server"}, + {header, "Date"}, + {header, "Content-Type", + "text/html"}, + {version, "HTTP/1.0"}]), + ok. + +%%-------------------------------------------------------------------- +head(Type, Port, Host, Node) -> + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "HEAD /index.html HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {version, "HTTP/1.0"}]), + ok. +%%-------------------------------------------------------------------- +all(Type, Port, Host, Node) -> + actions(Type, Port, Host, Node), + alias(Type, Port, Host, Node), + auth(Type, Port, Host, Node), + cgi(Type, Port, Host, Node), + esi(Type, Port, Host, Node), + get(Type, Port, Host, Node), + head(Type, Port, Host, Node), + ok. + +%%-------------------------------------------------------------------- +%% Internal functions +%%-------------------------------------------------------------------- +auth_request(Type, Host, Port, Node, URI, User, Passwd, Expect) -> + Req = ["GET ", URI, " HTTP/1.0\r\n", + "Authorization: Basic ", + base64:encode_to_string(User++":"++Passwd), + "\r\n\r\n"], + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + lists:flatten(Req), + [{version, "HTTP/1.0"} | Expect]). + +remove_users(Node, ServerRoot, Host, Port, Dir) -> + %% List users, delete them, and make sure they are gone. + case list_users(Node, ServerRoot, Host, Port, Dir) of + {ok, Users} -> + lists:foreach(fun(User) -> + delete_user(Node, ServerRoot, Host, + Port, Dir, User) + end, + Users), + {ok, []} = list_users(Node, ServerRoot, Host, Port, Dir); + _ -> + ok + end. + +add_user(Node, Root, Port, Dir, User, Password, UserData) -> + Addr = undefined, + Directory = filename:join([Root, "htdocs", Dir]), + rpc:call(Node, mod_auth, add_user, + [User, Password, UserData, Addr, Port, Directory]). + +delete_user(Node, Root, _Host, Port, Dir, User) -> + Addr = undefined, + Directory = filename:join([Root, "htdocs", Dir]), + rpc:call(Node, mod_auth, delete_user, [User, Addr, Port, Directory]). + +list_users(Node, Root, _Host, Port, Dir) -> + Addr = undefined, + Directory = filename:join([Root, "htdocs", Dir]), + rpc:call(Node, mod_auth, list_users, [Addr, Port, Directory]). + +receive_security_event(Event, Node, Port) -> + io:format(user, "~w:receive_security_event -> entry with" + "~n Event: ~p" + "~n Node: ~p" + "~n Port: ~p" + "~n", [?MODULE, Event, Node, Port]), + receive + Event -> + ok; + {'EXIT', _, _} -> + receive_security_event(Event, Node, Port); + Other -> + test_server:fail({unexpected_event, + {expected, Event}, {received, Other}}) + after 5000 -> + test_server:fail(no_event_recived) + + end. + +list_blocked_users(Node,Port) -> + Addr = undefined, % Assumed to be on the same host + rpc:call(Node, mod_security, list_blocked_users, [Addr,Port]). + +list_blocked_users(Node,Port,Dir) -> + Addr = undefined, % Assumed to be on the same host + rpc:call(Node, mod_security, list_blocked_users, [Addr,Port,Dir]). + +block_user(Node,User,Port,Dir,Sec) -> + Addr = undefined, % Assumed to be on the same host + rpc:call(Node, mod_security, block_user, [User, Addr, Port, Dir, Sec]). + +unblock_user(Node,User,Port,Dir) -> + Addr = undefined, % Assumed to be on the same host + rpc:call(Node, mod_security, unblock_user, [User, Addr, Port, Dir]). + +list_auth_users(Node,Port) -> + Addr = undefined, % Assumed to be on the same host + rpc:call(Node, mod_security, list_auth_users, [Addr,Port]). + +list_auth_users(Node,Port,Dir) -> + Addr = undefined, % Assumed to be on the same host + rpc:call(Node, mod_security, list_auth_users, [Addr,Port,Dir]). + +update_password(Node, ServerRoot, _Address, Port, Dir, Old, New)-> + Directory = filename:join([ServerRoot, "htdocs", Dir]), + rpc:call(Node, mod_auth, update_password, + [undefined, Port, Directory, Old, New, New]). + +remove_groups(Node, ServerRoot, Host, Port, Dir) -> + Directory = filename:join([ServerRoot, "htdocs", Dir]), + {ok, Groups} = list_groups(Node, ServerRoot, Host, Port, Directory), + lists:foreach(fun(Group) -> + delete_group(Node, Group, Port, Directory) + end, + Groups), + {ok, []} = list_groups(Node, ServerRoot, Host, Port, Directory), + ok. + +delete_group(Node, Group, Port, Dir) -> + Addr = undefined, + rpc:call(Node, mod_auth, delete_group, [Group, Addr, Port, Dir]). + +list_groups(Node, _, _, Port, Dir) -> + Addr = undefined, + rpc:call(Node, mod_auth, list_groups, [Addr, Port, Dir]). + +add_group_member(Node, ServerRoot, Port, Dir, User, Group) -> + Addr = undefined, + rpc:call(Node, mod_auth, add_group_member, [Group, User, Addr, Port, + filename:join( + [ServerRoot, + "htdocs",Dir])]). +event(What, Port, Dir, Data) -> + Msg = {event, What, Port, Dir, Data}, + case global:whereis_name(mod_security_test) of + undefined -> + ok; + _Pid -> + global:send(mod_security_test, Msg) + end. + +ssl_password_cb() -> + "dummy-ssl-password". + +check_lists_members({ok,L},L) -> + ok; +check_lists_members({ok,L1},L2) -> + check_lists_members1(lists:sort(L1),lists:sort(L2)); +check_lists_members(Error,_L) -> + Error. + +check_lists_members1(L,L) -> + ok; +check_lists_members1(L1,L2) -> + {error,{lists_not_equal,L1,L2}}. diff --git a/lib/inets/test/httpd_poll.erl b/lib/inets/test/httpd_poll.erl new file mode 100644 index 0000000000..1cc10365a7 --- /dev/null +++ b/lib/inets/test/httpd_poll.erl @@ -0,0 +1,496 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2000-2009. 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_poll). +-behaviour(gen_server). + + +%% External API +-export([start/0, start_appup/2, start/3,stop/0,verbosity/1,poll_time/1]). + +%% gen_server exports +-export([init/1, + handle_call/3, handle_cast/2, handle_info/2, terminate/2]). + + +-define(default_verbosity,error). +-define(default_poll_time,60000). %% 60 seconds + + +-record(state,{host = "", port = -1, ptime = -1, tref = none, uris = []}). + + +%% start/0 +%% +%% Description: Start polling HTTPD with default values +%% +start() -> + Options = default_options(otp), + start("gandalf", 8000, Options). + +start_appup(Host, Port) -> + Options = default_options(top), + start(Host, Port, Options). + +%% start/3 +%% +%% Description: Start polling HTTPD +%% +%% Parameters: +%% Host = string() +%% Host name of HTTPD +%% Port = integer() +%% Port number of HTTPD +%% Options = [Option] +%% Option = {poll_time,integer()} | {verbosity,verbosity()} | +%% {log_file,string()} | {uris,[uri()]} +%% verbosity() = silence | error | log | debug | trace +%% uri() = {string(),string} +%% First part is a descriptive string and the second +%% part is the actual URI. +%% +start(Host,Port,Options) -> + gen_server:start({local,httpd_tester},?MODULE,[Host,Port,Options],[]). + +stop() -> + gen_server:call(httpd_tester,stop). + + +default_options(UriDesc) -> + Verbosity = {verbosity,?default_verbosity}, + Uris = {uris,uris(UriDesc)}, + PollTime = {poll_time,?default_poll_time}, + Logging = {log_file,"httpd_poll.log"}, + [Verbosity, Uris, PollTime, Logging]. + + +options(Options) -> + options(Options, default_options(otp), []). + +options([], Defaults, Options) -> + Options ++ Defaults; +options([{Key,Val} = Opt|Opts], Defaults, Options) -> + options(Opts, lists:keydelete(Key, 1, Defaults), [Opt|Options]). + + +verbosity(silence) -> + set_verbosity(silence); +verbosity(error) -> + set_verbosity(error); +verbosity(log) -> + set_verbosity(log); +verbosity(debug) -> + set_verbosity(debug); +verbosity(trace) -> + set_verbosity(trace). + +set_verbosity(Verbosity) -> + gen_server:cast(httpd_tester,{verbosity,Verbosity}). + +poll_time(NewTime) -> + gen_server:call(httpd_tester,{poll_time,NewTime}). + + +%% ---------------------------------------------------------------------- + + +init([Host, Port, Options0]) -> + process_flag(trap_exit,true), + Options = options(Options0), + put(verbosity,get_verbosity(Options)), + log_open(get_log_file(Options)), + tstart(), + PollTime = get_poll_time(Options), + Ref = tcreate(PollTime), + log("created"), + {ok,#state{host = Host, + port = Port, + ptime = PollTime, + tref = Ref, + uris = get_uris(Options)}}. + +uris(top) -> + [uri_top_index()]; + +uris(otp) -> + [ + uri_top_index(), + uri_internal_product1(), + uri_internal_product2(), + uri_p7a_test_results(), + uri_bjorn1(), + uri_bjorn2(), + uri_top_ronja() + ]. + +uri_top_index() -> + {"top page","/"}. + +uri_internal_product1() -> + {"product internal page (1)","/product/internal/"}. + +uri_internal_product2() -> + {"product internal page (2)","/product/internal"}. + +uri_p7a_test_results() -> + {"test summery index page", + "/product/internal/test/test_results/progress_P7A/index.html"}. + +uri_bjorn1() -> + {"bjorns home page (1)","/~bjorn/"}. + +uri_bjorn2() -> + {"bjorns home page (2)","/~bjorn"}. + +uri_top_ronja() -> + {"ronja top page","/ronja/"}. + + +handle_call(stop, _From, State) -> + vlog("stop request"), + {stop, normal, ok, State}; + +handle_call({poll_time,NewTime}, _From, State) -> + vlog("set new poll time: ~p",[NewTime]), + OldTime = State#state.ptime, + {stop, normal, OldTime, State#state{ptime = NewTime}}; + +handle_call(Request, _From, State) -> + vlog("unexpected request(call): ~p",[Request]), + {reply, ok, State}. + + +handle_cast({verbosity,Verbosity}, State) -> + vlog("set (new) verbosity to: ~p",[Verbosity]), + put(verbosity,Verbosity), + {noreply, State}; + +handle_cast(Message, State) -> + vlog("unexpected message(call): ~p",[Message]), + {noreply, State}. + + +handle_info(poll_time,State) -> + {{Description,Uri},Uris} = get_uri(State#state.uris), + vlog("poll time for ~s",[Description]), + do_poll(State#state.host,State#state.port,Uri), + Ref = tcreate(State#state.ptime), + {noreply, State#state{tref = Ref, uris = Uris}}; + +handle_info(Info, State) -> + vlog("unexpected message(info): ~p",[Info]), + {noreply, State}. + + +terminate(Reason,State) -> + tcancel(State#state.tref), + log_close(get(log_file)), + ok. + + +get_uri([Uri|Uris]) -> + {Uri,Uris++[Uri]}. + + +do_poll(Host,Port,Uri) -> + (catch poll(create(Host,Port),Uri,"200")). + +poll({ok,Socket},Uri,ExpStatus) -> + vtrace("poll -> entry with Socket: ~p",[Socket]), + put(latest_requested_uri,Uri), + Req = "GET " ++ Uri ++ " HTTP/1.0\r\n\r\n", + await_poll_response(send(Socket,Req),Socket,ExpStatus); +poll({error,Reason},_Req,_ExpStatus) -> + verror("failed creating socket: ~p",[Reason]), + log("failed creating socket: ~p",[Reason]), + exit({error,Reason}); +poll(O,_Req,_ExpStatus) -> + verror("unexpected result from socket create: ~p",[O]), + log("unexpected result from socket create: ~p",[O]), + exit({unexpected_result,O}). + +await_poll_response(ok,Socket,ExpStatusCode) -> + vtrace("await_poll_response -> awaiting response with status ~s", + [ExpStatusCode]), + receive + {tcp_closed,Socket} -> + verror("connection closed when awaiting poll response"), + log("connection closed when awaiting reply to GET of '~s'", + [get(latest_requested_uri)]), + exit(connection_closed); + {tcp,Socket,Response} -> + vdebug("received response"), + validate(ExpStatusCode,Socket,Response) + after 10000 -> + verror("connection timeout waiting for poll response",[]), + log("connection timeout waiting for reply to GET of '~s'", + [get(latest_requested_uri)]), + exit(connection_timed_out) + end; +await_poll_response(Error,_Socket,_ExpStatusCode) -> + verror("failed sending GET request for '~s' for reason: ~p", + [get(latest_requested_uri),Error]), + log("failed sending GET request for '~s' for reason: ~p", + [get(latest_requested_uri),Error]), + exit(Error). + + +validate(ExpStatusCode,Socket,Response) -> + Sz = sz(Response), + vtrace("validate -> Entry with ~p bytes response",[Sz]), + Size = trash_the_rest(Socket,Sz), + close(Socket), + case inets_regexp:split(Response," ") of + {ok,["HTTP/1.0",ExpStatusCode|_]} -> + vlog("response (~p bytes) was ok",[Size]), + ok; + {ok,["HTTP/1.0",StatusCode|_]} -> + verror("unexpected response status received: ~s => ~s", + [StatusCode,status_to_message(StatusCode)]), + log("unexpected result to GET of '~s': ~s => ~s", + [get(latest_requested_uri),StatusCode, + status_to_message(StatusCode)]), + exit({unexpected_response_code,StatusCode,ExpStatusCode}) + end. + + +%% ------------------------------------------------------------------ + +trash_the_rest(Socket,N) -> + receive + {tcp, Socket, Trash} -> + vtrace("trash_the_rest -> trash ~p bytes",[sz(Trash)]), + trash_the_rest(Socket,add(N,sz(Trash))); + {tcp_closed, Socket} -> + vdebug("socket closed after receiving ~p bytes",[N]), + N + after 10000 -> + verror("connection timeout waiting for message"), + exit(connection_timed_out) + end. + + +add(N1,N2) when integer(N1),integer(N2) -> + N1 + N2; +add(N1,N2) when integer(N1) -> + N1; +add(N1,N2) when integer(N2) -> + N2. + +sz(L) when list(L) -> + length(lists:flatten(L)); +sz(B) when binary(B) -> + size(B); +sz(O) -> + {unknown_size,O}. + + +%% -------------------------------------------------------------- +%% +%% Status code to printable string +%% + +status_to_message(L) when list(L) -> + case (catch list_to_integer(L)) of + I when integer(I) -> + status_to_message(I); + _ -> + io_lib:format("UNKNOWN STATUS CODE: '~p'",[L]) + end; +status_to_message(100) -> "Section 10.1.1: Continue"; +status_to_message(101) -> "Section 10.1.2: Switching Protocols"; +status_to_message(200) -> "Section 10.2.1: OK"; +status_to_message(201) -> "Section 10.2.2: Created"; +status_to_message(202) -> "Section 10.2.3: Accepted"; +status_to_message(203) -> "Section 10.2.4: Non-Authoritative Information"; +status_to_message(204) -> "Section 10.2.5: No Content"; +status_to_message(205) -> "Section 10.2.6: Reset Content"; +status_to_message(206) -> "Section 10.2.7: Partial Content"; +status_to_message(300) -> "Section 10.3.1: Multiple Choices"; +status_to_message(301) -> "Section 10.3.2: Moved Permanently"; +status_to_message(302) -> "Section 10.3.3: Found"; +status_to_message(303) -> "Section 10.3.4: See Other"; +status_to_message(304) -> "Section 10.3.5: Not Modified"; +status_to_message(305) -> "Section 10.3.6: Use Proxy"; +status_to_message(307) -> "Section 10.3.8: Temporary Redirect"; +status_to_message(400) -> "Section 10.4.1: Bad Request"; +status_to_message(401) -> "Section 10.4.2: Unauthorized"; +status_to_message(402) -> "Section 10.4.3: Peyment Required"; +status_to_message(403) -> "Section 10.4.4: Forbidden"; +status_to_message(404) -> "Section 10.4.5: Not Found"; +status_to_message(405) -> "Section 10.4.6: Method Not Allowed"; +status_to_message(406) -> "Section 10.4.7: Not Acceptable"; +status_to_message(407) -> "Section 10.4.8: Proxy Authentication Required"; +status_to_message(408) -> "Section 10.4.9: Request Time-Out"; +status_to_message(409) -> "Section 10.4.10: Conflict"; +status_to_message(410) -> "Section 10.4.11: Gone"; +status_to_message(411) -> "Section 10.4.12: Length Required"; +status_to_message(412) -> "Section 10.4.13: Precondition Failed"; +status_to_message(413) -> "Section 10.4.14: Request Entity Too Large"; +status_to_message(414) -> "Section 10.4.15: Request-URI Too Large"; +status_to_message(415) -> "Section 10.4.16: Unsupported Media Type"; +status_to_message(416) -> "Section 10.4.17: Requested range not satisfiable"; +status_to_message(417) -> "Section 10.4.18: Expectation Failed"; +status_to_message(500) -> "Section 10.5.1: Internal Server Error"; +status_to_message(501) -> "Section 10.5.2: Not Implemented"; +status_to_message(502) -> "Section 10.5.3: Bad Gatteway"; +status_to_message(503) -> "Section 10.5.4: Service Unavailable"; +status_to_message(504) -> "Section 10.5.5: Gateway Time-out"; +status_to_message(505) -> "Section 10.5.6: HTTP Version not supported"; +status_to_message(Code) -> io_lib:format("Unknown status code: ~p",[Code]). + + +%% ---------------------------------------------------------------- + +create(Host,Port) -> + vtrace("create -> ~n\tHost: ~s~n\tPort: ~p",[Host,Port]), + case gen_tcp:connect(Host,Port,[{packet,0},{reuseaddr,true}]) of + {ok,Socket} -> + {ok,Socket}; + {error,{enfile,_}} -> + {error,enfile}; + Error -> + Error + end. + +close(Socket) -> + gen_tcp:close(Socket). + + +send(Socket,Data) -> + vtrace("send -> send ~p bytes of data",[length(Data)]), + gen_tcp:send(Socket,Data). + + +%% ---------------------------------------------------------------- + +tstart() -> + timer:start(). + +tcreate(Time) -> + {ok,Ref} = timer:send_after(Time,poll_time), + Ref. + +tcancel(Ref) -> + timer:cancel(Ref). + +%% ---------------------------------------------------------------- + +log_open(undefined) -> + ok; +log_open(FileName) -> + put(log_file,fopen(FileName)). + +log_close(undefined) -> + ok; +log_close(Fd) -> + fclose(Fd). + +log(F) -> + log(F,[]). + +log(F,A) -> + {{Year,Month,Day},{Hour,Min,Sec}} = local_time(), + fwrite(get(log_file), + "~w.~w.~w ~w.~w.~w " ++ F ++ "~n", + [Year,Month,Day,Hour,Min,Sec] ++ A). + +%% ---------------------------------------------------------------- + +fopen(Name) -> + {ok,Fd} = file:open(Name,[write]), + Fd. + +fclose(Fd) -> + file:close(Fd). + +fwrite(undefined,_F,_A) -> + ok; +fwrite(Fd,F,A) -> + io:format(Fd,F,A). + + +%% ---------------------------------------------------------------- + +get_poll_time(Opts) -> + get_option(poll_time,Opts,?default_poll_time). + +get_log_file(Opts) -> + get_option(log_file,Opts). + +get_uris(Opts) -> + get_option(uris,Opts,[]). + +get_verbosity(Opts) -> + get_option(verbosity,Opts,?default_verbosity). + +get_option(Opt,Opts) -> + get_option(Opt,Opts,undefined). + +get_option(Opt,Opts,Default) -> + case lists:keysearch(Opt,1,Opts) of + {value,{Opt,Value}} -> + Value; + false -> + Default + end. + +%% ---------------------------------------------------------------- + +%% sleep(T) -> receive after T -> ok end. + +%% ---------------------------------------------------------------- + +%% vtrace(F) -> vprint(get(verbosity),trace,F,[]). +vtrace(F,A) -> vprint(get(verbosity),trace,F,A). + +vdebug(F) -> vprint(get(verbosity),debug,F,[]). +vdebug(F,A) -> vprint(get(verbosity),debug,F,A). + +vlog(F) -> vprint(get(verbosity),log,F,[]). +vlog(F,A) -> vprint(get(verbosity),log,F,A). + +verror(F) -> vprint(get(verbosity),error,F,[]). +verror(F,A) -> vprint(get(verbosity),error,F,A). + +vprint(trace,Severity,F,A) -> vprint(Severity,F,A); +vprint(debug,trace,F,A) -> ok; +vprint(debug,Severity,F,A) -> vprint(Severity,F,A); +vprint(log,log,F,A) -> vprint(log,F,A); +vprint(log,error,F,A) -> vprint(log,F,A); +vprint(error,error,F,A) -> vprint(error,F,A); +vprint(_Verbosity,_Severity,_F,_A) -> ok. + +vprint(Severity,F,A) -> + {{Year,Month,Day},{Hour,Min,Sec}} = local_time(), + io:format("~w.~w.~w ~w.~w.~w " ++ image_of(Severity) ++ F ++ "~n", + [Year,Month,Day,Hour,Min,Sec] ++ A). + +image_of(error) -> "ERR: "; +image_of(log) -> "LOG: "; +image_of(debug) -> "DBG: "; +image_of(trace) -> "TRC: ". + +local_time() -> calendar:local_time(). + + + + + diff --git a/lib/inets/test/httpd_test_data/server_root/auth/group b/lib/inets/test/httpd_test_data/server_root/auth/group new file mode 100644 index 0000000000..b3da0ccbd3 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/auth/group @@ -0,0 +1,3 @@ +group1: one two +group2: two three +group3: three Aladdin diff --git a/lib/inets/test/httpd_test_data/server_root/auth/passwd b/lib/inets/test/httpd_test_data/server_root/auth/passwd new file mode 100644 index 0000000000..8c980ff547 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/auth/passwd @@ -0,0 +1,4 @@ +one:onePassword +two:twoPassword +three:threePassword +Aladdin:AladdinPassword diff --git a/lib/inets/test/httpd_test_data/server_root/cgi-bin/printenv.bat b/lib/inets/test/httpd_test_data/server_root/cgi-bin/printenv.bat new file mode 100644 index 0000000000..25a49a1536 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/cgi-bin/printenv.bat @@ -0,0 +1,9 @@ +@echo off +echo tomrad > c:\cygwin\tmp\hej +echo Content-type: text/html +echo. +echo ^<HTML^> ^<HEAD^> ^<TITLE^>OS Environment^</TITLE^> ^</HEAD^> ^<BODY^>^<PRE^> +set +echo ^</PRE^>^</BODY^>^</HTML^> + + diff --git a/lib/inets/test/httpd_test_data/server_root/cgi-bin/printenv.sh b/lib/inets/test/httpd_test_data/server_root/cgi-bin/printenv.sh new file mode 100755 index 0000000000..de81de9bde --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/cgi-bin/printenv.sh @@ -0,0 +1,6 @@ +#!/bin/sh +echo "Content-type: text/html" +echo "" +echo "<HTML> <HEAD> <TITLE>OS Environment</TITLE> </HEAD> <BODY><PRE>" +env +echo "</PRE></BODY></HTML>"
\ No newline at end of file diff --git a/lib/inets/test/httpd_test_data/server_root/conf/8080.conf b/lib/inets/test/httpd_test_data/server_root/conf/8080.conf new file mode 100644 index 0000000000..48e66f0114 --- /dev/null +++ b/lib/inets/test/httpd_test_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 [email protected] +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 + +<Directory /var/tmp/server_root/htdocs/open> +AuthDBType plain +AuthName Open Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require user one Aladdin +</Directory> + +<Directory /var/tmp/server_root/htdocs/secret> +AuthDBType plain +AuthName Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group1 group2 +</Directory> + +<Directory /var/tmp/server_root/htdocs/secret/top_secret> +AuthDBType plain +AuthName Top Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group3 +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_open> +AuthDBType mnesia +AuthName Open Area +require user one Aladdin +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_secret> +AuthDBType mnesia +AuthName Secret Area +require group group1 group2 +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_secret/top_secret> +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 +</Directory> diff --git a/lib/inets/test/httpd_test_data/server_root/conf/8888.conf b/lib/inets/test/httpd_test_data/server_root/conf/8888.conf new file mode 100644 index 0000000000..79bb7fcca4 --- /dev/null +++ b/lib/inets/test/httpd_test_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 [email protected] +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 + +<Directory /var/tmp/server_root/htdocs/open> +AuthName Open Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require user one Aladdin +</Directory> + +<Directory /var/tmp/server_root/htdocs/secret> +AuthName Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group1 group2 +</Directory> + +<Directory /var/tmp/server_root/htdocs/secret/top_secret> +AuthName Top Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group3 +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_open> +AuthName Open Area +AuthMnesiaDB On +require user one Aladdin +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_secret> +AuthName Secret Area +AuthMnesiaDB On +require group group1 group2 +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_secret/top_secret> +AuthName Top Secret Area +AuthMnesiaDB On +require group group3 +</Directory> diff --git a/lib/inets/test/httpd_test_data/server_root/conf/httpd.conf b/lib/inets/test/httpd_test_data/server_root/conf/httpd.conf new file mode 100644 index 0000000000..8a74ed1afd --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/conf/httpd.conf @@ -0,0 +1,268 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1997-2009. 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: <address>[|<ip-family>] +# +#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 [email protected] + +# 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). + +<Directory /var/tmp/server_root/htdocs/open> +AuthDBType plain +AuthName Open Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require user one Aladdin +</Directory> + +<Directory /var/tmp/server_root/htdocs/secret> +AuthDBType plain +AuthName Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group1 group2 +</Directory> + +<Directory /var/tmp/server_root/htdocs/secret/top_secret> +AuthDBType plain +AuthName Top Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group3 +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_open> +AuthDBType mnesia +AuthName Open Area +require user one Aladdin +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_secret> +AuthDBType mnesia +AuthName Secret Area +require group group1 group2 +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_secret/top_secret> +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 +</Directory> diff --git a/lib/inets/test/httpd_test_data/server_root/conf/mime.types b/lib/inets/test/httpd_test_data/server_root/conf/mime.types new file mode 100644 index 0000000000..d2f81e4e5e --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/conf/mime.types @@ -0,0 +1,465 @@ +# 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_test_data/server_root/conf/ssl.conf b/lib/inets/test/httpd_test_data/server_root/conf/ssl.conf new file mode 100644 index 0000000000..8b8c57a98b --- /dev/null +++ b/lib/inets/test/httpd_test_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 [email protected] +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 + +<Directory /var/tmp/server_root/htdocs/open> +AuthName Open Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require user one Aladdin +</Directory> + +<Directory /var/tmp/server_root/htdocs/secret> +AuthName Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group1 group2 +</Directory> + +<Directory /var/tmp/server_root/htdocs/secret/top_secret> +AuthName Top Secret Area +AuthUserFile /var/tmp/server_root/auth/passwd +AuthGroupFile /var/tmp/server_root/auth/group +require group group3 +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_open> +AuthName Open Area +AuthMnesiaDB On +require user one Aladdin +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_secret> +AuthName Secret Area +AuthMnesiaDB On +require group group1 group2 +</Directory> + +<Directory /var/tmp/server_root/htdocs/mnesia_secret/top_secret> +AuthName Top Secret Area +AuthMnesiaDB On +require group group3 +</Directory> diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/config.shtml b/lib/inets/test/httpd_test_data/server_root/htdocs/config.shtml new file mode 100644 index 0000000000..107e3ff610 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/config.shtml @@ -0,0 +1,70 @@ +<HTML> +<HEAD> +<TITLE>/ssi.html (17-Apr-1997)</TITLE> +</HEAD> +<BODY> +<H1>/ssi.html</H1> + +<!-- ************* CONFIG ************* --> + +<!--#config timefmt="%a %b %e %T %Z %Y" sizefmt="abbrev"--> +<!--#config errmsg="[an especially ugly error occurred while processing this directive]"--> + +<!-- ************* INCLUDE ************* --> + +<P>Include /misc/friedrich.html: +<!--#include virtual="/misc/friedrich.html"--> +<P>Include /misc/not_defined.html: <!--#include virtual="/misc/not_defined.html"--> +<P>Include misc/friedrich.html: +<!--#include file="misc/friedrich.html"--> +<P>Include not_defined.html: <!--#include file="not_defined.html"--> + +<P><HR> + +<!-- ************* ECHO ************* --> + +<P>DOCUMENT_NAME: <!--#echo var="DOCUMENT_NAME"--> +<P>DOCUMENT_URI: <!--#echo var="DOCUMENT_URI"--> +<P>QUERY_STRING_UNESCAPED: <!--#echo var="QUERY_STRING_UNESCAPED"--> +<P>DATE_LOCAL: <!--#echo var="DATE_LOCAL"--> +<P>DATE_GMT: <!--#echo var="DATE_GMT"--> +<P>LAST_MODIFIED: <!--#echo var="LAST_MODIFIED"--> +<P>NOT_DEFINED: <!--#echo var="NOT_DEFINED"--> + +<P><HR> + +<!-- ************* FSIZE ************* --> + +<P>Size of index.html: <!--#fsize file="index.html"--> +<P>Size of not_defined.html: <!--#fsize file="not_defined.html"--> +<!--#config sizefmt="bytes"--> +<P>Size of /misc/friedrich.html: <!--#fsize virtual="/misc/friedrich.html"--> +<P>Size of /misc/not_defined.html: <!--#fsize virtual="/misc/not_defined.html"--> + +<P><HR> + +<!-- ************* FLASTMOD ************* --> + +<P>Last modification of index.html: <!--#flastmod file="index.html"--> +<P>Last modification of not_defined.html: <!--#flastmod file="not_defined.html"--> +<P>Last modification of /misc/friedrich.html: <!--#flastmod virtual="/misc/friedrich.html"--> +<P>Last modification of /misc/not_defined.html: <!--#flastmod virtual="/misc/not_defined.html"--> + +<!--#exec cmd="ls"--> +<!--#exec cmd="printenv"--> +<!--#exec cmd="sunemaja"--> + +<!--#exec cgi="/cgi-bin/printenv.sh"--> + +</BODY> +</HTML> + + + + + + + + + + diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/dets_open/dummy.html b/lib/inets/test/httpd_test_data/server_root/htdocs/dets_open/dummy.html new file mode 100644 index 0000000000..a6e8a35a04 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/dets_open/dummy.html @@ -0,0 +1,10 @@ +<HTML> +<HEAD> +<TITLE>/open/dummy.html (17-Apr-1997)</TITLE> +<!-- Created by: Joakim Greben�, 17-Apr-1997 --> +<!-- Changed by: Joakim Greben�, 17-Apr-1997 --> +</HEAD> +<BODY> +<H1>/open/dummy.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/dets_secret/dummy.html b/lib/inets/test/httpd_test_data/server_root/htdocs/dets_secret/dummy.html new file mode 100644 index 0000000000..016b04e540 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/dets_secret/dummy.html @@ -0,0 +1,10 @@ +<HTML> +<HEAD> +<TITLE>/secret/dummy.html (17-Apr-1997)</TITLE> +<!-- Created by: Joakim Greben�, 17-Apr-1997 --> +<!-- Changed by: Joakim Greben�, 17-Apr-1997 --> +</HEAD> +<BODY> +<H1>/secret/dummy.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/dets_secret/top_secret/index.html b/lib/inets/test/httpd_test_data/server_root/htdocs/dets_secret/top_secret/index.html new file mode 100644 index 0000000000..34db3d5d1a --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/dets_secret/top_secret/index.html @@ -0,0 +1,9 @@ +<HTML> +<HEAD> +<TITLE>/secret/top_secret/index.html (04-Feb-1998)</TITLE> +<!-- Created by: Mattias Nilsson, 04-Feb-1998 --> +</HEAD> +<BODY> +<H1>/secret/top_secret/index.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/echo.shtml b/lib/inets/test/httpd_test_data/server_root/htdocs/echo.shtml new file mode 100644 index 0000000000..141db5be59 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/echo.shtml @@ -0,0 +1,35 @@ +<HTML> +<HEAD> +<TITLE>/echo.shtml</TITLE> +</HEAD> +<BODY> +<H1>/echo.shtml</H1> + +<P>DOCUMENT_NAME: <!--#echo var="DOCUMENT_NAME"--> + +<P>DOCUMENT_URI: <!--#echo var="DOCUMENT_URI"--> + +<P>QUERY_STRING_UNESCAPED: <!--#echo var="QUERY_STRING_UNESCAPED"--> + +<P>DATE_LOCAL: <!--#echo var="DATE_LOCAL"--> + +<P>DATE_GMT: <!--#echo var="DATE_GMT"--> + +<P>LAST_MODIFIED: <!--#echo var="LAST_MODIFIED"--> + +<P>NOT_DEFINED: <!--#echo var="NOT_DEFINED"--> + +<P>[<A HREF="ssi.html">Back</A>] + +</BODY> +</HTML> + + + + + + + + + + diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/exec.shtml b/lib/inets/test/httpd_test_data/server_root/htdocs/exec.shtml new file mode 100644 index 0000000000..97333da898 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/exec.shtml @@ -0,0 +1,30 @@ +<HTML> +<HEAD> +<TITLE>/exec.shtml</TITLE> +</HEAD> +<BODY> +<H1>/exec.shtml</H1> +<PRE> +<!--#exec cmd="ls"--> +<HR> +<!--#exec cmd="printenv"--> +<HR> +<!--#exec cmd="sunemaja"--> +<HR> +<!--#exec cgi="/cgi-bin/printenv.sh"--> +</PRE> + +<P>[<A HREF="ssi.html">Back</A>] + +</BODY> +</HTML> + + + + + + + + + + diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/flastmod.shtml b/lib/inets/test/httpd_test_data/server_root/htdocs/flastmod.shtml new file mode 100644 index 0000000000..d54c36fe50 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/flastmod.shtml @@ -0,0 +1,29 @@ +<HTML> +<HEAD> +<TITLE>/flastmod.shtml</TITLE> +</HEAD> +<BODY> +<H1>/flastmod.shtml</H1> + +<P>Last modification of index.html: <!--#flastmod file="index.html"--> + +<P>Last modification of not_defined.html: <!--#flastmod file="not_defined.html"--> + +<P>Last modification of /misc/friedrich.html: <!--#flastmod virtual="/misc/friedrich.html"--> + +<P>Last modification of /misc/not_defined.html: <!--#flastmod virtual="/misc/not_defined.html"--> + +<P>[<A HREF="ssi.html">Back</A>] + +</BODY> +</HTML> + + + + + + + + + + diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/fsize.shtml b/lib/inets/test/httpd_test_data/server_root/htdocs/fsize.shtml new file mode 100644 index 0000000000..570ee9cf6d --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/fsize.shtml @@ -0,0 +1,29 @@ +<HTML> +<HEAD> +<TITLE>/fsize.shtml</TITLE> +</HEAD> +<BODY> +<H1>/fsize.shtml</H1> + +<P>Size of index.html: <!--#fsize file="index.html"--> + +<P>Size of not_defined.html: <!--#fsize file="not_defined.html"--> + +<P>Size of /misc/friedrich.html: <!--#fsize virtual="/misc/friedrich.html"--> + +<P>Size of /misc/not_defined.html: <!--#fsize virtual="/misc/not_defined.html"--> + +<P>[<A HREF="ssi.html">Back</A>] + +</BODY> +</HTML> + + + + + + + + + + diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/include.shtml b/lib/inets/test/httpd_test_data/server_root/htdocs/include.shtml new file mode 100644 index 0000000000..529aad0437 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/include.shtml @@ -0,0 +1,33 @@ +<HTML> +<HEAD> +<TITLE>/include.shtml</TITLE> +</HEAD> +<BODY> +<H1>/include.shtml</H1> + +<P>Include /misc/friedrich.html: +<!--#include virtual="/misc/friedrich.html"--> + +<P>Include /misc/not_defined.html: +<!--#include virtual="/misc/not_defined.html"--> + +<P>Include misc/friedrich.html: +<!--#include file="misc/friedrich.html"--> + +<P>Include not_defined.html: +<!--#include file="not_defined.html"--> + +<P>[<A HREF="ssi.html">Back</A>] + +</BODY> +</HTML> + + + + + + + + + + diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/index.html b/lib/inets/test/httpd_test_data/server_root/htdocs/index.html new file mode 100644 index 0000000000..cfdc9f9ab7 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/index.html @@ -0,0 +1,25 @@ +<HTML> +<HEAD> +<TITLE>/index.html</TITLE> +</HEAD> +<BODY> +<H1>/index.html</H1> + +<STRONG>Server-Side Include (SSI) commands:</STRONG><BR> +<A HREF="config.shtml">config</A><BR> +<A HREF="echo.shtml">echo</A><BR> +<A HREF="exec.shtml">exec</A><BR> +<A HREF="flastmod.shtml">flastmod</A><BR> +<A HREF="fsize.shtml">fsize</A><BR> +<A HREF="include.shtml">include</A><BR> + +<BR> +<BR> + +<STRONG>ESI callback:</STRING><BR> +<A HREF="cgi-bin/erl/httpd_example/get">cgi-bin/erl/httpd_example/get</A><BR> +<A HREF="cgi-bin/erl/httpd_example/yahoo">cgi-bin/erl/httpd_example/yahoo</A><BR> +<A HREF="cgi-bin/erl/httpd_example/test1">cgi-bin/erl/httpd_example/test1</A><BR> + +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/last_modified.html b/lib/inets/test/httpd_test_data/server_root/htdocs/last_modified.html new file mode 100644 index 0000000000..65c1790813 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/last_modified.html @@ -0,0 +1,22 @@ +<HTML> +<HEAD> +<TITLE>/last_modified.html</TITLE> +</HEAD> +<BODY> +<H1>/last_modified.html</H1> + +<P>This document is only used for test of illegal last-modified date.</P> + + +</BODY> +</HTML> + + + + + + + + + + diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/misc/friedrich.html b/lib/inets/test/httpd_test_data/server_root/htdocs/misc/friedrich.html new file mode 100644 index 0000000000..d7953d5df4 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/misc/friedrich.html @@ -0,0 +1,7 @@ +<P><CITE> +Talking much about oneself can also be a means to conceal oneself.<BR> +-- Friedrich Nietzsche +</CITE> + +<P>Nested Include: +<!--#include file="misc/oech.html"--> diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/misc/oech.html b/lib/inets/test/httpd_test_data/server_root/htdocs/misc/oech.html new file mode 100644 index 0000000000..506064bf04 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/misc/oech.html @@ -0,0 +1,4 @@ +<P><CITE> +What excuses stand in your way? How can you eliminate them?<BR> +-- Roger von Oech +</CITE> diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/misc/welcome.html b/lib/inets/test/httpd_test_data/server_root/htdocs/misc/welcome.html new file mode 100644 index 0000000000..8c17451f91 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/misc/welcome.html @@ -0,0 +1 @@ +<HTML></HTML> diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/mnesia_open/dummy.html b/lib/inets/test/httpd_test_data/server_root/htdocs/mnesia_open/dummy.html new file mode 100644 index 0000000000..a6e8a35a04 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/mnesia_open/dummy.html @@ -0,0 +1,10 @@ +<HTML> +<HEAD> +<TITLE>/open/dummy.html (17-Apr-1997)</TITLE> +<!-- Created by: Joakim Greben�, 17-Apr-1997 --> +<!-- Changed by: Joakim Greben�, 17-Apr-1997 --> +</HEAD> +<BODY> +<H1>/open/dummy.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/mnesia_secret/dummy.html b/lib/inets/test/httpd_test_data/server_root/htdocs/mnesia_secret/dummy.html new file mode 100644 index 0000000000..016b04e540 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/mnesia_secret/dummy.html @@ -0,0 +1,10 @@ +<HTML> +<HEAD> +<TITLE>/secret/dummy.html (17-Apr-1997)</TITLE> +<!-- Created by: Joakim Greben�, 17-Apr-1997 --> +<!-- Changed by: Joakim Greben�, 17-Apr-1997 --> +</HEAD> +<BODY> +<H1>/secret/dummy.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/mnesia_secret/top_secret/index.html b/lib/inets/test/httpd_test_data/server_root/htdocs/mnesia_secret/top_secret/index.html new file mode 100644 index 0000000000..2d17e8b596 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/mnesia_secret/top_secret/index.html @@ -0,0 +1,9 @@ +<HTML> +<HEAD> +<TITLE>/mnesia_secret/top_secret/index.html (04-Feb-1998)</TITLE> +<!-- Created by: Mattias Nilsson, 04-Feb-1998 --> +</HEAD> +<BODY> +<H1>/mnesia_secret/top_secret/index.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/open/dummy.html b/lib/inets/test/httpd_test_data/server_root/htdocs/open/dummy.html new file mode 100644 index 0000000000..a6e8a35a04 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/open/dummy.html @@ -0,0 +1,10 @@ +<HTML> +<HEAD> +<TITLE>/open/dummy.html (17-Apr-1997)</TITLE> +<!-- Created by: Joakim Greben�, 17-Apr-1997 --> +<!-- Changed by: Joakim Greben�, 17-Apr-1997 --> +</HEAD> +<BODY> +<H1>/open/dummy.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/secret/dummy.html b/lib/inets/test/httpd_test_data/server_root/htdocs/secret/dummy.html new file mode 100644 index 0000000000..016b04e540 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/secret/dummy.html @@ -0,0 +1,10 @@ +<HTML> +<HEAD> +<TITLE>/secret/dummy.html (17-Apr-1997)</TITLE> +<!-- Created by: Joakim Greben�, 17-Apr-1997 --> +<!-- Changed by: Joakim Greben�, 17-Apr-1997 --> +</HEAD> +<BODY> +<H1>/secret/dummy.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_test_data/server_root/htdocs/secret/top_secret/index.html b/lib/inets/test/httpd_test_data/server_root/htdocs/secret/top_secret/index.html new file mode 100644 index 0000000000..34db3d5d1a --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/htdocs/secret/top_secret/index.html @@ -0,0 +1,9 @@ +<HTML> +<HEAD> +<TITLE>/secret/top_secret/index.html (04-Feb-1998)</TITLE> +<!-- Created by: Mattias Nilsson, 04-Feb-1998 --> +</HEAD> +<BODY> +<H1>/secret/top_secret/index.html</H1> +</BODY> +</HTML> diff --git a/lib/inets/test/httpd_test_data/server_root/icons/README b/lib/inets/test/httpd_test_data/server_root/icons/README new file mode 100644 index 0000000000..a1fc5a5a9c --- /dev/null +++ b/lib/inets/test/httpd_test_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 ([email protected]). + + 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_test_data/server_root/icons/a.gif b/lib/inets/test/httpd_test_data/server_root/icons/a.gif Binary files differnew file mode 100644 index 0000000000..bb23d971f4 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/a.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/alert.black.gif b/lib/inets/test/httpd_test_data/server_root/icons/alert.black.gif Binary files differnew file mode 100644 index 0000000000..eaecd2172a --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/alert.black.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/alert.red.gif b/lib/inets/test/httpd_test_data/server_root/icons/alert.red.gif Binary files differnew file mode 100644 index 0000000000..a423894043 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/alert.red.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/apache_pb.gif b/lib/inets/test/httpd_test_data/server_root/icons/apache_pb.gif Binary files differnew file mode 100644 index 0000000000..3a1c139fc4 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/apache_pb.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/back.gif b/lib/inets/test/httpd_test_data/server_root/icons/back.gif Binary files differnew file mode 100644 index 0000000000..a694ae1ec3 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/back.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/ball.gray.gif b/lib/inets/test/httpd_test_data/server_root/icons/ball.gray.gif Binary files differnew file mode 100644 index 0000000000..eb84268c4c --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/ball.gray.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/ball.red.gif b/lib/inets/test/httpd_test_data/server_root/icons/ball.red.gif Binary files differnew file mode 100644 index 0000000000..a8425cb574 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/ball.red.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/binary.gif b/lib/inets/test/httpd_test_data/server_root/icons/binary.gif Binary files differnew file mode 100644 index 0000000000..9a15cbae04 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/binary.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/binhex.gif b/lib/inets/test/httpd_test_data/server_root/icons/binhex.gif Binary files differnew file mode 100644 index 0000000000..62d0363108 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/binhex.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/blank.gif b/lib/inets/test/httpd_test_data/server_root/icons/blank.gif Binary files differnew file mode 100644 index 0000000000..0ccf01e198 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/blank.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/bomb.gif b/lib/inets/test/httpd_test_data/server_root/icons/bomb.gif Binary files differnew file mode 100644 index 0000000000..270fdb1c06 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/bomb.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/box1.gif b/lib/inets/test/httpd_test_data/server_root/icons/box1.gif Binary files differnew file mode 100644 index 0000000000..65dcd002ea --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/box1.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/box2.gif b/lib/inets/test/httpd_test_data/server_root/icons/box2.gif Binary files differnew file mode 100644 index 0000000000..c43bc4faec --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/box2.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/broken.gif b/lib/inets/test/httpd_test_data/server_root/icons/broken.gif Binary files differnew file mode 100644 index 0000000000..9f8cbe9f76 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/broken.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/burst.gif b/lib/inets/test/httpd_test_data/server_root/icons/burst.gif Binary files differnew file mode 100644 index 0000000000..fbdcf575f7 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/burst.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/button1.gif b/lib/inets/test/httpd_test_data/server_root/icons/button1.gif Binary files differnew file mode 100644 index 0000000000..eb97cb7333 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/button1.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/button10.gif b/lib/inets/test/httpd_test_data/server_root/icons/button10.gif Binary files differnew file mode 100644 index 0000000000..fe0c97998c --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/button10.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/button2.gif b/lib/inets/test/httpd_test_data/server_root/icons/button2.gif Binary files differnew file mode 100644 index 0000000000..7698455bf9 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/button2.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/button3.gif b/lib/inets/test/httpd_test_data/server_root/icons/button3.gif Binary files differnew file mode 100644 index 0000000000..a8b8319232 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/button3.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/button4.gif b/lib/inets/test/httpd_test_data/server_root/icons/button4.gif Binary files differnew file mode 100644 index 0000000000..0fd15a0d7f --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/button4.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/button5.gif b/lib/inets/test/httpd_test_data/server_root/icons/button5.gif Binary files differnew file mode 100644 index 0000000000..64241e5c5d --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/button5.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/button6.gif b/lib/inets/test/httpd_test_data/server_root/icons/button6.gif Binary files differnew file mode 100644 index 0000000000..867cfd1212 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/button6.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/button7.gif b/lib/inets/test/httpd_test_data/server_root/icons/button7.gif Binary files differnew file mode 100644 index 0000000000..b3f5fb248f --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/button7.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/button8.gif b/lib/inets/test/httpd_test_data/server_root/icons/button8.gif Binary files differnew file mode 100644 index 0000000000..7a308be8f6 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/button8.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/button9.gif b/lib/inets/test/httpd_test_data/server_root/icons/button9.gif Binary files differnew file mode 100644 index 0000000000..9acba576c0 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/button9.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/buttonl.gif b/lib/inets/test/httpd_test_data/server_root/icons/buttonl.gif Binary files differnew file mode 100644 index 0000000000..3883088e7a --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/buttonl.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/buttonr.gif b/lib/inets/test/httpd_test_data/server_root/icons/buttonr.gif Binary files differnew file mode 100644 index 0000000000..c4dc3887db --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/buttonr.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/c.gif b/lib/inets/test/httpd_test_data/server_root/icons/c.gif Binary files differnew file mode 100644 index 0000000000..7555b6c164 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/c.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/comp.blue.gif b/lib/inets/test/httpd_test_data/server_root/icons/comp.blue.gif Binary files differnew file mode 100644 index 0000000000..f8d76a8c23 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/comp.blue.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/comp.gray.gif b/lib/inets/test/httpd_test_data/server_root/icons/comp.gray.gif Binary files differnew file mode 100644 index 0000000000..7664cd0364 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/comp.gray.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/compressed.gif b/lib/inets/test/httpd_test_data/server_root/icons/compressed.gif Binary files differnew file mode 100644 index 0000000000..39e732739f --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/compressed.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/continued.gif b/lib/inets/test/httpd_test_data/server_root/icons/continued.gif Binary files differnew file mode 100644 index 0000000000..b0ffb7e0cc --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/continued.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/dir.gif b/lib/inets/test/httpd_test_data/server_root/icons/dir.gif Binary files differnew file mode 100644 index 0000000000..48264601ae --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/dir.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/down.gif b/lib/inets/test/httpd_test_data/server_root/icons/down.gif Binary files differnew file mode 100644 index 0000000000..a354c871cd --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/down.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/dvi.gif b/lib/inets/test/httpd_test_data/server_root/icons/dvi.gif Binary files differnew file mode 100644 index 0000000000..791be33105 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/dvi.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/f.gif b/lib/inets/test/httpd_test_data/server_root/icons/f.gif Binary files differnew file mode 100644 index 0000000000..fbe353c282 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/f.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/folder.gif b/lib/inets/test/httpd_test_data/server_root/icons/folder.gif Binary files differnew file mode 100644 index 0000000000..48264601ae --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/folder.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/folder.open.gif b/lib/inets/test/httpd_test_data/server_root/icons/folder.open.gif Binary files differnew file mode 100644 index 0000000000..30979cb528 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/folder.open.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/folder.sec.gif b/lib/inets/test/httpd_test_data/server_root/icons/folder.sec.gif Binary files differnew file mode 100644 index 0000000000..75332d9e59 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/folder.sec.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/forward.gif b/lib/inets/test/httpd_test_data/server_root/icons/forward.gif Binary files differnew file mode 100644 index 0000000000..b2959b4c85 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/forward.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/generic.gif b/lib/inets/test/httpd_test_data/server_root/icons/generic.gif Binary files differnew file mode 100644 index 0000000000..de60b2940f --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/generic.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/generic.red.gif b/lib/inets/test/httpd_test_data/server_root/icons/generic.red.gif Binary files differnew file mode 100644 index 0000000000..94743981d9 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/generic.red.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/generic.sec.gif b/lib/inets/test/httpd_test_data/server_root/icons/generic.sec.gif Binary files differnew file mode 100644 index 0000000000..88d5240c3c --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/generic.sec.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/hand.right.gif b/lib/inets/test/httpd_test_data/server_root/icons/hand.right.gif Binary files differnew file mode 100644 index 0000000000..5cdbc7206d --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/hand.right.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/hand.up.gif b/lib/inets/test/httpd_test_data/server_root/icons/hand.up.gif Binary files differnew file mode 100644 index 0000000000..85a5d68317 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/hand.up.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/htdig.gif b/lib/inets/test/httpd_test_data/server_root/icons/htdig.gif Binary files differnew file mode 100644 index 0000000000..35443fb63a --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/htdig.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/icon.sheet.gif b/lib/inets/test/httpd_test_data/server_root/icons/icon.sheet.gif Binary files differnew file mode 100644 index 0000000000..ad1686e448 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/icon.sheet.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/image1.gif b/lib/inets/test/httpd_test_data/server_root/icons/image1.gif Binary files differnew file mode 100644 index 0000000000..01e442bfa9 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/image1.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/image2.gif b/lib/inets/test/httpd_test_data/server_root/icons/image2.gif Binary files differnew file mode 100644 index 0000000000..751faeea36 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/image2.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/image3.gif b/lib/inets/test/httpd_test_data/server_root/icons/image3.gif Binary files differnew file mode 100644 index 0000000000..4f30484ff6 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/image3.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/index.gif b/lib/inets/test/httpd_test_data/server_root/icons/index.gif Binary files differnew file mode 100644 index 0000000000..162478fb3a --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/index.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/layout.gif b/lib/inets/test/httpd_test_data/server_root/icons/layout.gif Binary files differnew file mode 100644 index 0000000000..c96338a152 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/layout.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/left.gif b/lib/inets/test/httpd_test_data/server_root/icons/left.gif Binary files differnew file mode 100644 index 0000000000..279e6710d4 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/left.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/link.gif b/lib/inets/test/httpd_test_data/server_root/icons/link.gif Binary files differnew file mode 100644 index 0000000000..c5b6889a76 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/link.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/movie.gif b/lib/inets/test/httpd_test_data/server_root/icons/movie.gif Binary files differnew file mode 100644 index 0000000000..0035183774 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/movie.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/p.gif b/lib/inets/test/httpd_test_data/server_root/icons/p.gif Binary files differnew file mode 100644 index 0000000000..7b917b4e91 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/p.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/patch.gif b/lib/inets/test/httpd_test_data/server_root/icons/patch.gif Binary files differnew file mode 100644 index 0000000000..39bc90e795 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/patch.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/pdf.gif b/lib/inets/test/httpd_test_data/server_root/icons/pdf.gif Binary files differnew file mode 100644 index 0000000000..c88fd777c4 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/pdf.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/pie0.gif b/lib/inets/test/httpd_test_data/server_root/icons/pie0.gif Binary files differnew file mode 100644 index 0000000000..6f7a0ae7a7 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/pie0.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/pie1.gif b/lib/inets/test/httpd_test_data/server_root/icons/pie1.gif Binary files differnew file mode 100644 index 0000000000..03aa6be71e --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/pie1.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/pie2.gif b/lib/inets/test/httpd_test_data/server_root/icons/pie2.gif Binary files differnew file mode 100644 index 0000000000..b04c5e0908 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/pie2.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/pie3.gif b/lib/inets/test/httpd_test_data/server_root/icons/pie3.gif Binary files differnew file mode 100644 index 0000000000..4db9d023ed --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/pie3.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/pie4.gif b/lib/inets/test/httpd_test_data/server_root/icons/pie4.gif Binary files differnew file mode 100644 index 0000000000..93471fdd88 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/pie4.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/pie5.gif b/lib/inets/test/httpd_test_data/server_root/icons/pie5.gif Binary files differnew file mode 100644 index 0000000000..57aee93f07 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/pie5.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/pie6.gif b/lib/inets/test/httpd_test_data/server_root/icons/pie6.gif Binary files differnew file mode 100644 index 0000000000..0dc327b569 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/pie6.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/pie7.gif b/lib/inets/test/httpd_test_data/server_root/icons/pie7.gif Binary files differnew file mode 100644 index 0000000000..8661337f06 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/pie7.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/pie8.gif b/lib/inets/test/httpd_test_data/server_root/icons/pie8.gif Binary files differnew file mode 100644 index 0000000000..59ddb34ce0 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/pie8.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/portal.gif b/lib/inets/test/httpd_test_data/server_root/icons/portal.gif Binary files differnew file mode 100644 index 0000000000..0e6e506e00 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/portal.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/poweredby.gif b/lib/inets/test/httpd_test_data/server_root/icons/poweredby.gif Binary files differnew file mode 100644 index 0000000000..d324ab80ea --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/poweredby.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/ps.gif b/lib/inets/test/httpd_test_data/server_root/icons/ps.gif Binary files differnew file mode 100644 index 0000000000..0f565bc1db --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/ps.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/quill.gif b/lib/inets/test/httpd_test_data/server_root/icons/quill.gif Binary files differnew file mode 100644 index 0000000000..818a5cdc7e --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/quill.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/right.gif b/lib/inets/test/httpd_test_data/server_root/icons/right.gif Binary files differnew file mode 100644 index 0000000000..b256e5f75f --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/right.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/screw1.gif b/lib/inets/test/httpd_test_data/server_root/icons/screw1.gif Binary files differnew file mode 100644 index 0000000000..af6ba2b097 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/screw1.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/screw2.gif b/lib/inets/test/httpd_test_data/server_root/icons/screw2.gif Binary files differnew file mode 100644 index 0000000000..06dccb3e44 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/screw2.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/script.gif b/lib/inets/test/httpd_test_data/server_root/icons/script.gif Binary files differnew file mode 100644 index 0000000000..d8a853bc58 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/script.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/sound1.gif b/lib/inets/test/httpd_test_data/server_root/icons/sound1.gif Binary files differnew file mode 100644 index 0000000000..8efb49f55d --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/sound1.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/sound2.gif b/lib/inets/test/httpd_test_data/server_root/icons/sound2.gif Binary files differnew file mode 100644 index 0000000000..48e6a7fb2f --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/sound2.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/sphere1.gif b/lib/inets/test/httpd_test_data/server_root/icons/sphere1.gif Binary files differnew file mode 100644 index 0000000000..7067070da2 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/sphere1.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/sphere2.gif b/lib/inets/test/httpd_test_data/server_root/icons/sphere2.gif Binary files differnew file mode 100644 index 0000000000..a9e462a377 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/sphere2.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/star.gif b/lib/inets/test/httpd_test_data/server_root/icons/star.gif Binary files differnew file mode 100644 index 0000000000..4cfe0a5e0f --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/star.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/star_blank.gif b/lib/inets/test/httpd_test_data/server_root/icons/star_blank.gif Binary files differnew file mode 100644 index 0000000000..a0c83cb85b --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/star_blank.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/tar.gif b/lib/inets/test/httpd_test_data/server_root/icons/tar.gif Binary files differnew file mode 100644 index 0000000000..617e779efa --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/tar.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/tex.gif b/lib/inets/test/httpd_test_data/server_root/icons/tex.gif Binary files differnew file mode 100644 index 0000000000..45e43233b8 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/tex.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/text.gif b/lib/inets/test/httpd_test_data/server_root/icons/text.gif Binary files differnew file mode 100644 index 0000000000..4c623909fb --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/text.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/transfer.gif b/lib/inets/test/httpd_test_data/server_root/icons/transfer.gif Binary files differnew file mode 100644 index 0000000000..33697dbb66 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/transfer.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/unknown.gif b/lib/inets/test/httpd_test_data/server_root/icons/unknown.gif Binary files differnew file mode 100644 index 0000000000..32b1ea23fb --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/unknown.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/up.gif b/lib/inets/test/httpd_test_data/server_root/icons/up.gif Binary files differnew file mode 100644 index 0000000000..6d6d6d1ebf --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/up.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/uu.gif b/lib/inets/test/httpd_test_data/server_root/icons/uu.gif Binary files differnew file mode 100644 index 0000000000..4387d529f6 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/uu.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/uuencoded.gif b/lib/inets/test/httpd_test_data/server_root/icons/uuencoded.gif Binary files differnew file mode 100644 index 0000000000..4387d529f6 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/uuencoded.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/world1.gif b/lib/inets/test/httpd_test_data/server_root/icons/world1.gif Binary files differnew file mode 100644 index 0000000000..05b4ec2058 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/world1.gif diff --git a/lib/inets/test/httpd_test_data/server_root/icons/world2.gif b/lib/inets/test/httpd_test_data/server_root/icons/world2.gif Binary files differnew file mode 100644 index 0000000000..e3203f7a88 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/icons/world2.gif diff --git a/lib/inets/test/httpd_test_data/server_root/logs/Dummy_File_Needed_By_WinZip b/lib/inets/test/httpd_test_data/server_root/logs/Dummy_File_Needed_By_WinZip new file mode 100644 index 0000000000..8d1c8b69c3 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/logs/Dummy_File_Needed_By_WinZip @@ -0,0 +1 @@ + diff --git a/lib/inets/test/httpd_test_data/server_root/ssl/ssl_client.pem b/lib/inets/test/httpd_test_data/server_root/ssl/ssl_client.pem new file mode 100644 index 0000000000..8221139eb4 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/ssl/ssl_client.pem @@ -0,0 +1,22 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBPAIBAAJBAL6Ym/bgUvhhnPkw08sggGg8Tnp759ThGMEjkmDzhuJ3w3PfnF65 +mgHcgunku4G6LxAQfEUougJWf9Phmjj3oRUCAwEAAQJBAKMjvVvzZxFzfAlP4flc +OI0AEayFokp04dtvtzuFN09f+aBo2dP18xHmKLCZvxrBOaRAROoQYscALiIVpN07 +GAECIQDfi+sSfAFaDlT3vzpL3xE5UEH6IzY8jWpaZfM1QaToJQIhANpEF50H4wGO +8Sbh7dUutNd+s+NYUjsMySW2DjLKMsoxAiEAzzb2ftrdsempD0F+O0gZwiPIFKLB +Kp33YLYyHEKuJtUCIDGi+pvDh2R7VWw6RRQOIyI+tjolg83aAoSI+oGiahqBAiEA +xzmNNajwoaokvWvlaz0na8rhxu45grOvDrflBT9XvSQ= +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIICDDCCAbYCAQAwDQYJKoZIhvcNAQEEBQAwgZAxCzAJBgNVBAYTAlNFMRIwEAYD +VQQIEwlTdG9ja2hvbG0xDzANBgNVBAcTBkFsdnNqbzEMMAoGA1UEChMDRVRYMQ4w +DAYDVQQLEwVETi9TUDEXMBUGA1UEAxMOSm9ha2ltIEdyZWJlbm8xJTAjBgkqhkiG +9w0BCQEWFmpvY2tlQGVyaXguZXJpY3Nzb24uc2UwHhcNOTcwNzE1MTUzNDM2WhcN +MDMwMjIyMTUzNDM2WjCBkDELMAkGA1UEBhMCU0UxEjAQBgNVBAgTCVN0b2NraG9s +bTEPMA0GA1UEBxMGQWx2c2pvMQwwCgYDVQQKEwNFVFgxDjAMBgNVBAsTBUROL1NQ +MRcwFQYDVQQDEw5Kb2FraW0gR3JlYmVubzElMCMGCSqGSIb3DQEJARYWam9ja2VA +ZXJpeC5lcmljc3Nvbi5zZTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC+mJv24FL4 +YZz5MNPLIIBoPE56e+fU4RjBI5Jg84bid8Nz35xeuZoB3ILp5LuBui8QEHxFKLoC +Vn/T4Zo496EVAgMBAAEwDQYJKoZIhvcNAQEEBQADQQBYxQVfTydyZCE0UXvZd7Ei +josNsAaWJk9fFIJaG9uyXCEfg2dVgoT2eBk3D9DI+7OB+78isM5CVlFbL7hilvP8 +-----END CERTIFICATE----- diff --git a/lib/inets/test/httpd_test_data/server_root/ssl/ssl_server.pem b/lib/inets/test/httpd_test_data/server_root/ssl/ssl_server.pem new file mode 100644 index 0000000000..fe739c15f7 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/ssl/ssl_server.pem @@ -0,0 +1,22 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBAL9Bozj3BIjL5Cy8b3rjMT2kPZRychX4wz9bHoIIiKnKo1xXHYjw +g3N9zWM1f1ZzMADwVry1uAInA8q09+7hL20CAwEAAQJACwu2ao7RozjrV64WXimK +6X131P/7GMvCMwGHNIlbozqoOqmZcYrbKaF61l+XuwA2QvTo3ywW1Ivxcyr6TeAr +PQIhAOX+WXT6yiqqwjt08kjBCJyMgfZtdAO6pc/6pKjNWiZfAiEA1OH1iPW/OQe5 +tlQXpiRVdLyneNsPygPRJc4Bdwu3hbMCIQDbI5pA56QxOzqOREOGJsb5wrciAfAE +jZbnr72sSN2YqQIgAWFpvzagw9Tp/mWzNY+cwkIK7/yzsIKv04fveH8p9IMCIQCr +td4IiukeUwXmPSvYM4uCE/+J89wEL9qU8Mlc3gDLXA== +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIICDDCCAbYCAQAwDQYJKoZIhvcNAQEEBQAwgZAxCzAJBgNVBAYTAlNFMRIwEAYD +VQQIEwlTdG9ja2hvbG0xDzANBgNVBAcTBkFsdnNqbzEMMAoGA1UEChMDRVRYMQ4w +DAYDVQQLEwVETi9TUDEXMBUGA1UEAxMOSm9ha2ltIEdyZWJlbm8xJTAjBgkqhkiG +9w0BCQEWFmpvY2tlQGVyaXguZXJpY3Nzb24uc2UwHhcNOTcwNzE1MTUzMzQxWhcN +MDMwMjIyMTUzMzQxWjCBkDELMAkGA1UEBhMCU0UxEjAQBgNVBAgTCVN0b2NraG9s +bTEPMA0GA1UEBxMGQWx2c2pvMQwwCgYDVQQKEwNFVFgxDjAMBgNVBAsTBUROL1NQ +MRcwFQYDVQQDEw5Kb2FraW0gR3JlYmVubzElMCMGCSqGSIb3DQEJARYWam9ja2VA +ZXJpeC5lcmljc3Nvbi5zZTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC/QaM49wSI +y+QsvG964zE9pD2UcnIV+MM/Wx6CCIipyqNcVx2I8INzfc1jNX9WczAA8Fa8tbgC +JwPKtPfu4S9tAgMBAAEwDQYJKoZIhvcNAQEEBQADQQAmXDY1CyJjzvQZX442kkHG +ic9QFY1UuVfzokzNMwlHYl1Qx9zaodx0cJCrcH5GF9O9LJbhhV77LzoxT1Q5wZp5 +-----END CERTIFICATE----- diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl new file mode 100644 index 0000000000..6abee5be2c --- /dev/null +++ b/lib/inets/test/httpd_test_lib.erl @@ -0,0 +1,332 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-2009. 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_test_lib). + +-include("inets_test_lib.hrl"). + +%% Poll functions +-export([verify_request/6, verify_request/7, is_expect/1]). + +-record(state, {request, % string() + socket, % socket() + status_line, % {Version, StatusCode, ReasonPharse} + headers, % #http_response_h{} + body, % binary() + mfa = {httpc_response, parse, [nolimit, false]}, + canceled = [], % [RequestId] + max_header_size = nolimit, % nolimit | integer() + max_body_size = nolimit, % nolimit | integer() + print = false + }). + +%%% Part of http.hrl - Temporary solution %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Response headers +-record(http_response_h,{ +%%% --- Standard "General" headers + 'cache-control', + connection, + date, + pragma, + trailer, + 'transfer-encoding', + upgrade, + via, + warning, +%%% --- Standard "Response" headers + 'accept-ranges', + age, + etag, + location, + 'proxy-authenticate', + 'retry-after', + server, + vary, + 'www-authenticate', +%%% --- Standard "Entity" headers + allow, + 'content-encoding', + 'content-language', + 'content-length' = "0", + 'content-location', + 'content-md5', + 'content-range', + 'content-type', + expires, + 'last-modified', + other=[] % list() - Key/Value list with other headers + }). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%-------------------------------------------------------------------- +%% API +%%------------------------------------------------------------------ +verify_request(SocketType, Host, Port, Node, RequestStr, Options) -> + verify_request(SocketType, Host, Port, Node, RequestStr, Options, 30000). +verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut) -> + {ok, Socket} = inets_test_lib:connect_bin(SocketType, Host, Port), + inets_test_lib:send(SocketType, Socket, RequestStr), + + State = case inets_regexp:match(RequestStr, "printenv") of + nomatch -> + #state{}; + _ -> + #state{print = true} + end, + + case request(State#state{request = RequestStr, socket = Socket}, TimeOut) of + {error, Reson} -> + {error, Reson}; + NewState -> + ValidateResult = validate(RequestStr, NewState, Options, + Node, Port), + inets_test_lib:close(SocketType, Socket), + ValidateResult + end. + +request(#state{mfa = {Module, Function, Args}, + request = RequestStr, socket = Socket} = State, TimeOut) -> + HeadRequest = lists:sublist(RequestStr, 1, 4), + receive + {tcp, Socket, Data} -> + print(tcp, Data, State), + case Module:Function([Data | Args]) of + {ok, Parsed} -> + handle_http_msg(Parsed, State); + {_, whole_body, _} when HeadRequest == "HEAD" -> + State#state{body = <<>>}; + NewMFA -> + request(State#state{mfa = NewMFA}, TimeOut) + end; + {tcp_closed, Socket} when Function == whole_body -> + print(tcp, "closed", State), + State#state{body = hd(Args)}; + {tcp_closed, Socket} -> + test_server:fail(connection_closed); + {tcp_error, Socket, Reason} -> + test_server:fail({tcp_error, Reason}); + {ssl, Socket, Data} -> + print(ssl, Data, State), + case Module:Function([Data | Args]) of + {ok, Parsed} -> + handle_http_msg(Parsed, State); + {_, whole_body, _} when HeadRequest == "HEAD" -> + State#state{body = <<>>}; + NewMFA -> + request(State#state{mfa = NewMFA}, TimeOut) + end; + {ssl_closed, Socket} when Function == whole_body -> + print(ssl, "closed", State), + State#state{body = hd(Args)}; + {ssl_closed, Socket} -> + test_server:fail(connection_closed); + {ssl_error, Socket, Reason} -> + test_server:fail({ssl_error, Reason}) + after TimeOut -> + test_server:fail(connection_timed_out) + end. + +handle_http_msg({Version, StatusCode, ReasonPharse, Headers, Body}, + State = #state{request = RequestStr}) -> + case is_expect(RequestStr) of + true -> + State#state{status_line = {Version, + StatusCode, + ReasonPharse}, + headers = Headers}; + false -> + handle_http_body(Body, + State#state{status_line = {Version, + StatusCode, + ReasonPharse}, + headers = Headers}) + end; + +handle_http_msg({ChunkedHeaders, Body}, + State = #state{headers = Headers}) -> + NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders), + State#state{headers = NewHeaders, body = Body}; + +handle_http_msg(Body, State) -> + State#state{body = Body}. + +handle_http_body(<<>>, State = #state{request = "HEAD" ++ _}) -> + State#state{body = <<>>}; + +handle_http_body(Body, State = #state{headers = Headers, + max_body_size = MaxBodySize}) -> + case Headers#http_response_h.'transfer-encoding' of + "chunked" -> + case http_chunk:decode(Body, State#state.max_body_size, + State#state.max_header_size) of + {Module, Function, Args} -> + request(State#state{mfa = {Module, Function, Args}}, + 30000); + {ok, {ChunkedHeaders, NewBody}} -> + NewHeaders = http_chunk:handle_headers(Headers, + ChunkedHeaders), + State#state{headers = NewHeaders, body = NewBody} + end; + _ -> + Length = + list_to_integer(Headers#http_response_h.'content-length'), + case ((Length =< MaxBodySize) or (MaxBodySize == nolimit)) of + true -> + case httpc_response:whole_body(Body, Length) of + {ok, NewBody} -> + State#state{body = NewBody}; + MFA -> + request(State#state{mfa = MFA}, 5000) + end; + false -> + test_server:fail(body_too_big) + end + end. + +validate(RequestStr, #state{status_line = {Version, StatusCode, _}, + headers = Headers, + body = Body}, Options, N, P) -> + + %io:format("Status~p: H:~p B:~p~n", [StatusCode, Headers, Body]), + check_version(Version, Options), + case lists:keysearch(statuscode, 1, Options) of + {value, _} -> + check_status_code(StatusCode, Options, Options); + _ -> + ok + end, + do_validate(http_response:header_list(Headers), Options, N, P), + check_body(RequestStr, StatusCode, + Headers#http_response_h.'content-type', + list_to_integer(Headers#http_response_h.'content-length'), + Body). + +%%-------------------------------------------------------------------- +%% Internal functions +%%------------------------------------------------------------------ +check_version(Version, Options) -> + case lists:keysearch(version, 1, Options) of + {value, {version, Version}} -> + ok; + {value, {version, Ver}} -> + test_server:fail({wrong_version, [{got, Version}, + {expected, Ver}]}); + _ -> + case Version of + "HTTP/1.1" -> + ok; + _ -> + test_server:fail({wrong_version, [{got, Version}, + {expected, "HTTP/1.1"}]}) + end + end. + +check_status_code(StatusCode, [], Options) -> + test_server:fail({wrong_status_code, [{got, StatusCode}, + {expected, Options}]}); +check_status_code(StatusCode, Current = [_ | Rest], Options) -> + case lists:keysearch(statuscode, 1, Current) of + {value, {statuscode, StatusCode}} -> + ok; + {value, {statuscode, _OtherStatus}} -> + check_status_code(StatusCode, Rest, Options); + false -> + test_server:fail({wrong_status_code, [{got, StatusCode}, + {expected, Options}]}) + end. + +do_validate(_, [], _, _) -> + ok; +do_validate(Header, [{statuscode, _Code} | Rest], N, P) -> + do_validate(Header, Rest, N, P); +do_validate(Header, [{header, HeaderField}|Rest], N, P) -> + LowerHeaderField = http_util:to_lower(HeaderField), + case lists:keysearch(LowerHeaderField, 1, Header) of + {value, {LowerHeaderField, _Value}} -> + ok; + false -> + test_server:fail({missing_header_field, LowerHeaderField, Header}); + _ -> + test_server:fail({missing_header_field, LowerHeaderField, Header}) + end, + do_validate(Header, Rest, N, P); +do_validate(Header, [{header, HeaderField, Value}|Rest],N,P) -> + LowerHeaderField = http_util:to_lower(HeaderField), + case lists:keysearch(LowerHeaderField, 1, Header) of + {value, {LowerHeaderField, Value}} -> + ok; + false -> + test_server:fail({wrong_header_field_value, LowerHeaderField, + Header}); + _ -> + test_server:fail({wrong_header_field_value, LowerHeaderField, + Header}) + end, + do_validate(Header, Rest, N, P); +do_validate(Header,[{no_last_modified,HeaderField}|Rest],N,P) -> +% io:format("Header: ~p~nHeaderField: ~p~n",[Header,HeaderField]), + case lists:keysearch(HeaderField,1,Header) of + {value,_} -> + test_server:fail({wrong_header_field_value, HeaderField, + Header}); + _ -> + ok + end, + do_validate(Header, Rest, N, P); +do_validate(Header, [_Unknown | Rest], N, P) -> + do_validate(Header, Rest, N, P). + +is_expect(RequestStr) -> + + case inets_regexp:match(RequestStr, "xpect:100-continue") of + {match, _, _}-> + true; + _ -> + false + end. + +%% OTP-5775, content-length +check_body("GET /cgi-bin/erl/httpd_example:get_bin HTTP/1.0\r\n\r\n", 200, "text/html", Length, _Body) when Length /= 274-> + test_server:fail(content_length_error); +check_body("GET /cgi-bin/cgi_echo HTTP/1.0\r\n\r\n", 200, "text/plain", + _, Body) -> + case size(Body) of + 100 -> + ok; + _ -> + test_server:fail(content_length_error) + end; + +check_body(RequestStr, 200, "text/html", _, Body) -> + HeadRequest = lists:sublist(RequestStr, 1, 3), + case HeadRequest of + "GET" -> + inets_test_lib:check_body(binary_to_list(Body)); + _ -> + ok + end; + +check_body(_, _, _, _,_) -> + ok. + +print(Proto, Data, #state{print = true}) -> + test_server:format("Received ~p: ~p~n", [Proto, Data]); +print(_, _, #state{print = false}) -> + ok. + diff --git a/lib/inets/test/httpd_time_test.erl b/lib/inets/test/httpd_time_test.erl new file mode 100644 index 0000000000..7d6aa08542 --- /dev/null +++ b/lib/inets/test/httpd_time_test.erl @@ -0,0 +1,500 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-2009. 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_time_test). + +-export([t/3, t1/2, t2/2]). + +-export([do/1, do/2, do/3, do/4, do/5]). + +-export([main/5, poller_main/4, poller_loop/4]). + +-include("inets_test_lib.hrl"). + +-record(stat, {pid, time = undefined, count = undefined, res}). + + +%%% ----------------------------------------------------------------- +%%% Test suite interface +%%% + +t1(Host, Port) -> + t(ip_comm, Host, Port). + + +t2(Host, Port) -> + t(ssl, Host, Port). + + +t(SocketType, Host, Port) -> + %% put(dbg,true), + main(1, SocketType, Host, Port, 60000). + + + +%%% ----------------------------------------------------------------- +%%% Public interface when running the time test manually... +%%% + +do(Port) -> + do(ip_comm, hostname(), Port). + +do(Port, Time) when is_integer(Port) andalso is_integer(Time) -> + do(ip_comm, hostname(), Port, Time); + +do(Host, Port) -> + do(ip_comm, Host, Port). + +do(Host, Port, Time) when is_integer(Port) andalso is_integer(Time) -> + do(1, ip_comm, Host, Port, Time); + +do(SocketType, Host, Port) when is_integer(Port) -> + do(1, SocketType, Host, Port, 60000). + +do(N, SocketType, Host, Port) when is_integer(N) andalso is_integer(Port) -> + do(N, SocketType, Host, Port, 60000); + +do(SocketType, Host, Port, Time) + when is_integer(Port) andalso is_integer(Time) -> + do(1, SocketType, Host, Port, Time). + +do(N, SocketType, Host, Port, Time) + when is_integer(N) andalso is_integer(Port) andalso is_integer(Time) -> + do_it(N, SocketType, Host, Port, Time). + +do_it(N, SocketType, Host, Port, Time) -> + d("do_it -> entry with" + "~n N: ~p" + "~n SocketType: ~p" + "~n Host: ~p" + "~n Port: ~p" + "~n Time: ~p", [N, SocketType, Host, Port, Time]), + proc_lib:spawn(?MODULE, main, [N, SocketType, Host, Port, Time]). + + + +%%% ----------------------------------------------------------------- +%%% Controller (main) process +%%% + +main(N, SocketType, Host, Port, Time) + when is_integer(N) andalso + is_atom(SocketType) andalso + is_integer(Port) andalso + is_integer(Time) -> + process_flag(trap_exit,true), + put(sname,ctrl), + %% put(dbg,true), + d("main -> entry"), + Pollers = start_pollers(N, [self(), SocketType, Host, Port]), + d("main -> Pollers: ~p", [Pollers]), + loop(Pollers, Time). + +loop(Pollers, Timeout) -> + d("loop -> entry when" + "~n Timeout: ~p", [Timeout]), + Start = t(), + receive + {'EXIT', Pid, {poller_stat_failure, Time, Reason}} -> + case is_poller(Pid, Pollers) of + true -> + error_msg("received unexpected exit from poller ~p~n" + "befor completion of test " + "(after ~p micro sec):~n" + "~p~n", [Pid,Time,Reason]), + exit({fail, {poller_exit, Pid, Reason}}); + false -> + error_msg("received unexpected ~p from ~p" + "befor completion of test", [Reason, Pid]), + loop(Pollers, to(Timeout, Start)) + end; + + {poller_stat_failure, Pid, {Time, Reason}} -> + error_msg("received stat failure ~p from poller ~p after ~p " + "befor completion of test", [Reason, Pid, Time]), + exit({fail, {poller_failure, Pid, Reason}}); + + {poller_stat_failure, Pid, Reason} -> + error_msg("received stat failure ~p from poller ~p " + "befor completion of test", [Reason, Pid]), + exit({fail, {poller_failure, Pid, Reason}}); + + Any -> + error_msg("received unexpected message befor completion of test: " + "~n ~p", [Any]), + exit({fail, Any}) + + after Timeout -> + d("loop -> timeout: stop pollers"), + stop_pollers(Pollers), + d("loop -> collect poller statistics"), + Stats = collect_poller_stat(Pollers, []), + d("loop -> Stats: ~p", [Stats]), + display_poller_stat(Stats, Timeout), + ok + end. + +collect_poller_stat([], PollersStat) -> + PollersStat; +collect_poller_stat(Pollers, PollersStat) -> + d("collect_poller_stat -> entry with" + "~n Pollers: ~p" + "~n PollersStat: ~p", [Pollers, PollersStat]), + receive + {poller_statistics, Poller, {Time, Count}} -> + d("collect_poller_stat -> got statistics from ~p", [Poller]), + case lists:keysearch(Poller, 2, Pollers) of + {value, PollerStat} -> + d("collect_poller_stat -> current statistic record: ~p", + [PollerStat]), + P = lists:keydelete(Poller, 2, Pollers), + d("collect_poller_stat -> P: ~p", [P]), + S = PollerStat#stat{time = Time, count = Count, res = ok}, + d("collect_poller_stat -> S: ~p", [S]), + collect_poller_stat(P, [S | PollersStat]); + false -> + error_msg("statistics already received for ~p", [Poller]), + collect_poller_stat(Pollers, PollersStat) + end; + {poller_stat_failure, Poller, Else} -> + error_msg("poller statistics failure for ~p with ~p", + [Poller, Else]), + case lists:keysearch(Poller, 2, Pollers) of + {value, PollerStat} -> + P = lists:keydelete(Poller, 2, Pollers), + S = PollerStat#stat{res = {error, Else}}, + collect_poller_stat(P, [S | PollerStat]); + false -> + error_msg("statistics already received for ~p", [Poller]), + collect_poller_stat(Pollers, PollersStat) + end + end. + + +display_poller_stat(Stats, T) -> + display_poller_stat(Stats, 1, T, 0). + +display_poller_stat([], _, TestTime, AccCount) -> + io:format("Total statistics:~n" + " Accumulated count: ~w~n" + " Average access time: ~w milli sec~n", + [AccCount, (TestTime/AccCount)]); +display_poller_stat([#stat{res = ok} = Stat | Stats], N, TestTime, AccCount) -> + #stat{pid = Pid, time = Time, count = Count} = Stat, + io:format("Statistics for poller ~p (~p):~n" + " time: ~w seconds~n" + " count: ~w~n" + " Average access time: ~w milli sec~n", + [Pid, N, Time/(1000*1000), Count, (TestTime/Count)]), + display_poller_stat(Stats, N + 1, TestTime, AccCount+Count); +display_poller_stat([Stat | Stats], N, TestTime, AccCount) -> + #stat{pid = Pid, res = Error} = Stat, + io:format("Statistics failed for poller ~p (~p):~n" + " ~p~n", [Pid, N, Error]), + display_poller_stat(Stats, N + 1, TestTime, AccCount). + + + +%%% ----------------------------------------------------------------- +%%% Poller process +%%% + +start_pollers(N, Args) -> + start_pollers(N, Args, []). + +start_pollers(0, _Args, Pollers) -> + Pollers; +start_pollers(N, Args, Pollers) -> + Pid = proc_lib:spawn_link(?MODULE, poller_main, Args), + start_pollers(N-1, Args, [#stat{pid = Pid} | Pollers]). + +stop_pollers(Pollers) -> + [Pid ! stop || #stat{pid = Pid} <- Pollers], + await_stop_pollers(Pollers). + +await_stop_pollers([]) -> + ok; +await_stop_pollers(Pollers0) -> + receive + {'EXIT', Pid, _Reason} -> + Pollers = lists:keydelete(Pid, 2, Pollers0), + await_stop_pollers(Pollers) + after 5000 -> + [Pid ! shutdown || #stat{pid = Pid} <- Pollers0] + end. + + +is_poller(_, []) -> + false; +is_poller(Pid, [#stat{pid = Pid}|_]) -> + true; +is_poller(Pid, [_|Rest]) -> + is_poller(Pid, Rest). + + +poller_main(Parent, SocketType, Host, Port) -> + process_flag(trap_exit,true), + put(sname,poller), + case timer:tc(?MODULE, poller_loop, [SocketType, Host, Port, uris()]) of + {Time, Count} when is_integer(Time) andalso is_integer(Count) -> + Parent ! {poller_statistics, self(), {Time, Count}}; + {Time, {'EXIT', Reason}} when is_integer(Time) -> + exit({poller_stat_failure, Time, Reason}); + {Time, Other} when is_integer(Time) -> + Parent ! {poller_stat_failure, self(), {Time, Other}}; + Else -> + Parent ! {poller_stat_failure, self(), Else} + end. + + +uris() -> + uris(get(uris)). + +uris(L) when is_list(L) -> + L; +uris(_) -> + ["/", + "/index.html"]. + + +poller_loop(SocketType, Host, Port, URIs) -> + poller_loop(SocketType, Host, Port, URIs, 0). + +poller_loop(SocketType, Host, Port, URIs, Count) -> + receive + stop -> + Count + after 0 -> + case poller_loop1(SocketType, Host, Port, URIs) of + done -> + poller_loop(SocketType, Host, Port, URIs, + Count + length(URIs)); + {error, Reason, FailURI, FailURIs} -> + SuccessCount = + Count + (length(URIs) - (length(FailURIs) + 1)), + exit({Reason, FailURI, SuccessCount}) + end + end. + + +poller_loop1(_SocketType, _Host, _Port, []) -> + done; +poller_loop1(SocketType, Host, Port, [URI | URIs]) -> + Res = inets_test_lib:connect_byte(SocketType, Host, Port), + case (catch poll(Res, SocketType, URI, "200")) of + ok -> + poller_loop1(SocketType, Host, Port, URIs); + {'EXIT', Reason} -> + {error, Reason, URI, URIs} + end. + +poll({ok, Socket}, SocketType, URI, ExpRes) -> + Req = "GET " ++ URI ++ " HTTP/1.0\r\n\r\n", + Res = inets_test_lib:send(SocketType, Socket, Req), + await_poll_response(Res, SocketType, Socket, ExpRes); +poll({error, Reason}, _SocketType, _URI, _ExpRes) -> + exit({failed_creating_socket, Reason}); +poll(Error, _SocketType, _URI, _ExpRes) -> + exit({failed_creating_socket, Error}). + +await_poll_response(ok, SocketType, Socket, ExpStatusCode) -> + receive + %% SSL receives + {ssl, Socket, Data} -> + validate(ExpStatusCode, SocketType, Socket, Data); + {ssl_closed, Socket} -> + exit(connection_closed); + {ssl_error, Socket, Error} -> + exit({connection_error, Error}); + + %% TCP receives + {tcp, Socket, Response} -> + validate(ExpStatusCode, SocketType, Socket, Response); + {tcp_closed, Socket} -> + exit(connection_closed); + {tcp_error, Socket, Error} -> + exit({connection_error, Error}) + + after 10000 -> + exit(response_timed_out) + end; +await_poll_response(Error, _SocketType, _Socket, _ExpStatusCode) -> + exit(Error). + + +validate(ExpStatusCode, SocketType, Socket, Response) -> + Sz = sz(Response), + trash_the_rest(Socket, Sz), + inets_test_lib:close(SocketType, Socket), + case inets_regexp:split(Response," ") of + {ok,["HTTP/1.0", ExpStatusCode|_]} -> + ok; + {ok,["HTTP/1.0", StatusCode|_]} -> + error_msg("Unexpected status code: ~p (~s). " + "Expected status code: ~p (~s)", + [StatusCode, status_to_message(StatusCode), + ExpStatusCode, status_to_message(ExpStatusCode)]), + exit({unexpected_response_code, StatusCode, ExpStatusCode}); + {ok,["HTTP/1.1", ExpStatusCode|_]} -> + ok; + {ok,["HTTP/1.1", StatusCode|_]} -> + error_msg("Unexpected status code: ~p (~s). " + "Expected status code: ~p (~s)", + [StatusCode, status_to_message(StatusCode), + ExpStatusCode, status_to_message(ExpStatusCode)]), + exit({unexpected_response_code, StatusCode, ExpStatusCode}) + end. + + +trash_the_rest(Socket, N) -> + receive + {ssl, Socket, Trash} -> + trash_the_rest(Socket, add(N,sz(Trash))); + {ssl_closed, Socket} -> + N; + {ssl_error, Socket, Error} -> + exit({connection_error, Error}); + + {tcp, Socket, Trash} -> + trash_the_rest(Socket, add(N,sz(Trash))); + {tcp_closed, Socket} -> + N; + {tcp_error, Socket, Error} -> + exit({connection_error, Error}) + + after 10000 -> + exit({connection_timed_out, N}) + end. + + +add(N1,N2) when is_integer(N1) andalso is_integer(N2) -> + N1 + N2; +add(N1,_) when is_integer(N1) -> + N1; +add(_,N2) when is_integer(N2) -> + N2. + + +sz(L) when is_list(L) -> + length(lists:flatten(L)); +sz(B) when is_binary(B) -> + size(B); +sz(O) -> + {unknown_size,O}. + + +%% -------------------------------------------------------------- +%% +%% Status code to printable string +%% + +status_to_message(L) when is_list(L) -> + case (catch list_to_integer(L)) of + I when is_integer(I) -> + status_to_message(I); + _ -> + io_lib:format("UNKNOWN STATUS CODE: '~p'",[L]) + end; +status_to_message(100) -> "Section 10.1.1: Continue"; +status_to_message(101) -> "Section 10.1.2: Switching Protocols"; +status_to_message(200) -> "Section 10.2.1: OK"; +status_to_message(201) -> "Section 10.2.2: Created"; +status_to_message(202) -> "Section 10.2.3: Accepted"; +status_to_message(203) -> "Section 10.2.4: Non-Authoritative Information"; +status_to_message(204) -> "Section 10.2.5: No Content"; +status_to_message(205) -> "Section 10.2.6: Reset Content"; +status_to_message(206) -> "Section 10.2.7: Partial Content"; +status_to_message(300) -> "Section 10.3.1: Multiple Choices"; +status_to_message(301) -> "Section 10.3.2: Moved Permanently"; +status_to_message(302) -> "Section 10.3.3: Found"; +status_to_message(303) -> "Section 10.3.4: See Other"; +status_to_message(304) -> "Section 10.3.5: Not Modified"; +status_to_message(305) -> "Section 10.3.6: Use Proxy"; +status_to_message(307) -> "Section 10.3.8: Temporary Redirect"; +status_to_message(400) -> "Section 10.4.1: Bad Request"; +status_to_message(401) -> "Section 10.4.2: Unauthorized"; +status_to_message(402) -> "Section 10.4.3: Peyment Required"; +status_to_message(403) -> "Section 10.4.4: Forbidden"; +status_to_message(404) -> "Section 10.4.5: Not Found"; +status_to_message(405) -> "Section 10.4.6: Method Not Allowed"; +status_to_message(406) -> "Section 10.4.7: Not Acceptable"; +status_to_message(407) -> "Section 10.4.8: Proxy Authentication Required"; +status_to_message(408) -> "Section 10.4.9: Request Time-Out"; +status_to_message(409) -> "Section 10.4.10: Conflict"; +status_to_message(410) -> "Section 10.4.11: Gone"; +status_to_message(411) -> "Section 10.4.12: Length Required"; +status_to_message(412) -> "Section 10.4.13: Precondition Failed"; +status_to_message(413) -> "Section 10.4.14: Request Entity Too Large"; +status_to_message(414) -> "Section 10.4.15: Request-URI Too Large"; +status_to_message(415) -> "Section 10.4.16: Unsupported Media Type"; +status_to_message(416) -> "Section 10.4.17: Requested range not satisfiable"; +status_to_message(417) -> "Section 10.4.18: Expectation Failed"; +status_to_message(500) -> "Section 10.5.1: Internal Server Error"; +status_to_message(501) -> "Section 10.5.2: Not Implemented"; +status_to_message(502) -> "Section 10.5.3: Bad Gatteway"; +status_to_message(503) -> "Section 10.5.4: Service Unavailable"; +status_to_message(504) -> "Section 10.5.5: Gateway Time-out"; +status_to_message(505) -> "Section 10.5.6: HTTP Version not supported"; +status_to_message(Code) -> io_lib:format("Unknown status code: ~p",[Code]). + +%% ---------------------------------------------------------------- + +to(To, Start) -> + To - (t() - Start). + +%% Time in milli seconds +t() -> + {A,B,C} = erlang:now(), + A*1000000000+B*1000+(C div 1000). + + +%% ---------------------------------------------------------------- + + + +% close(Socket) -> +% gen_tcp:close(Socket). + +% send(Socket, Data) -> +% gen_tcp:send(Socket, Data). + + +hostname() -> + {ok, Hostname} = inet:gethostname(), + hostname(Hostname). + +hostname(Hostname) when is_list(Hostname) -> + list_to_atom(Hostname); +hostname(Hostname) -> + Hostname. + +%% ---------------------------------------------------------------- + +error_msg(F,A) -> error_logger:error_msg(F ++ "~n",A). + +d(F) -> + d(get(dbg),F,[]). + +d(F,A) -> + d(get(dbg),F,A). + +d(true, F, A) -> + io:format("DBG ~p ~p " ++ F ++ "~n", [self(),get(sname)]++A); +d(_,_,_) -> + ok. diff --git a/lib/inets/test/inets.config b/lib/inets/test/inets.config new file mode 100644 index 0000000000..6c9077594d --- /dev/null +++ b/lib/inets/test/inets.config @@ -0,0 +1 @@ +[{inets,[{services,[{httpd,"/ldisk/tests/bmk/inets/priv_dir/8099.conf"}]}]}]. diff --git a/lib/inets/test/inets.spec b/lib/inets/test/inets.spec new file mode 100644 index 0000000000..a9b4524295 --- /dev/null +++ b/lib/inets/test/inets.spec @@ -0,0 +1,2 @@ +{topcase, {dir, "../inets_test"}}. +{hosts, ["tuor"]}. diff --git a/lib/inets/test/inets.spec.vxworks b/lib/inets/test/inets.spec.vxworks new file mode 100644 index 0000000000..6886299226 --- /dev/null +++ b/lib/inets/test/inets.spec.vxworks @@ -0,0 +1,5 @@ +{topcase, {dir, "../inets_test"}}. +{skip, {inets_SUITE, ip_mod_cgi, "Requires processes"}}. +{skip, {inets_SUITE, ip_mod_all_modules, "Requires processes"}}. +{skip, {inets_SUITE, ssl, "Requires SSL"}}. + diff --git a/lib/inets/test/inets_SUITE.erl b/lib/inets/test/inets_SUITE.erl new file mode 100644 index 0000000000..c249bdf81b --- /dev/null +++ b/lib/inets/test/inets_SUITE.erl @@ -0,0 +1,498 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. 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(inets_SUITE). + +-include("test_server.hrl"). +-include("test_server_line.hrl"). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-define(NUM_DEFAULT_SERVICES, 1). +-define(FTP_HOST,"tuor"). + +all(doc) -> + ["Test suites for the inets application."]; + +all(suite) -> + [ + app_test, + appup_test, + start_inets, + start_httpc, + start_httpd, + start_ftpc, + start_tftpd, + httpd_reload + ]. + +%%-------------------------------------------------------------------- +%% 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) -> + 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) -> + 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) -> + inets:stop(), + Config. + +%%-------------------------------------------------------------------- +%% 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) -> + Config. + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- +app_test(suite) -> + [{inets_app_test, all}]. + +appup_test(suite) -> + [{inets_appup_test, all}]. + + +%%------------------------------------------------------------------------- + +start_inets(doc) -> + ["Test inets API functions"]; +start_inets(suite) -> + []; +start_inets(Config) when is_list(Config) -> + [_|_] = inets:service_names(), + + {error,inets_not_started} = inets:services(), + {error,inets_not_started} = inets:services_info(), + + ok = inets:start(), + + %% httpc default profile always started + [_|_] = inets:services(), + [_|_] = inets:services_info(), + + {error,{already_started,inets}} = inets:start(), + + ok = inets:stop(), + {error,{not_started,inets}} = inets:stop(), + + ok = inets:start(transient), + ok = inets:stop(), + + ok = inets:start(permanent), + ok = inets:stop(). + + +%%------------------------------------------------------------------------- + +start_httpc(doc) -> + ["Start/stop of httpc service"]; +start_httpc(suite) -> + []; +start_httpc(Config) when is_list(Config) -> + process_flag(trap_exit, true), + PrivDir = ?config(priv_dir, Config), + ok = inets:start(), + {ok, Pid0} = inets:start(httpc, [{profile, foo}]), + Pids0 = [ServicePid || {_, ServicePid} <- inets:services()], + true = lists:member(Pid0, Pids0), + [_|_] = inets:services_info(), + inets:stop(httpc, Pid0), + test_server:sleep(100), + Pids1 = [ServicePid || {_, ServicePid} <- inets:services()], + false = lists:member(Pid0, Pids1), + {ok, Pid1} = inets:start(httpc, [{profile, bar}], stand_alone), + Pids2 = [ServicePid || {_, ServicePid} <- inets:services()], + false = lists:member(Pid1, Pids2), + ok = inets:stop(stand_alone, Pid1), + receive + {'EXIT', Pid1, shutdown} -> + ok + after 100 -> + test_server:fail(stand_alone_not_shutdown) + end, + ok = inets:stop(), + application:load(inets), + application:set_env(inets, services, [{httpc,[{profile, foo}, + {data_dir, PrivDir}]}]), + ok = inets:start(), + (?NUM_DEFAULT_SERVICES + 1) = length(inets:services()), + application:unset_env(inets, services), + ok = inets:stop(), + ok = inets:start(), + {ok, Pid3} = inets:start(httpc, [{profile, foo}]), + ok = inets:stop(httpc, foo), + Pids3 = [ServicePid || {_, ServicePid} <- inets:services()], + false = lists:member(Pid3, Pids3), + ok = inets:stop(). + + +%%------------------------------------------------------------------------- + +start_httpd(doc) -> + ["Start/stop of httpd service"]; +start_httpd(suite) -> + []; +start_httpd(Config) when is_list(Config) -> + process_flag(trap_exit, true), + i("start_httpd -> entry with" + "~n Config: ~p", [Config]), + PrivDir = ?config(priv_dir, Config), + HttpdConf = [{server_name, "httpd_test"}, {server_root, PrivDir}, + {document_root, PrivDir}, {bind_address, "localhost"}], + + i("start_httpd -> start inets"), + ok = inets:start(), + i("start_httpd -> start httpd service"), + {ok, Pid0} = inets:start(httpd, [{port, 0}, {ipfamily, inet} | HttpdConf]), + Pids0 = [ServicePid || {_, ServicePid} <- inets:services()], + true = lists:member(Pid0, Pids0), + [_|_] = inets:services_info(), + i("start_httpd -> stop httpd service"), + inets:stop(httpd, Pid0), + test_server:sleep(500), + Pids1 = [ServicePid || {_, ServicePid} <- inets:services()], + false = lists:member(Pid0, Pids1), + i("start_httpd -> start (stand-alone) httpd service"), + {ok, Pid1} = + inets:start(httpd, [{port, 0}, {ipfamily, inet} | HttpdConf], + stand_alone), + Pids2 = [ServicePid || {_, ServicePid} <- inets:services()], + false = lists:member(Pid1, Pids2), + i("start_httpd -> stop (stand-alone) httpd service"), + ok = inets:stop(stand_alone, Pid1), + receive + {'EXIT', Pid1, shutdown} -> + ok + after 100 -> + test_server:fail(stand_alone_not_shutdown) + end, + i("start_httpd -> stop inets"), + ok = inets:stop(), + File0 = filename:join(PrivDir, "httpd.conf"), + {ok, Fd0} = file:open(File0, [write]), + Str = io_lib:format("~p.~n", [[{port, 0}, {ipfamily, inet} | HttpdConf]]), + ok = file:write(Fd0, Str), + file:close(Fd0), + + i("start_httpd -> [application] load inets"), + application:load(inets), + i("start_httpd -> [application] set httpd services env with proplist-file"), + application:set_env(inets, + services, [{httpd, [{proplist_file, File0}]}]), + i("start_httpd -> start inets"), + ok = inets:start(), + (?NUM_DEFAULT_SERVICES + 1) = length(inets:services()), + i("start_httpd -> [application] unset services env"), + application:unset_env(inets, services), + i("start_httpd -> stop inets"), + ok = inets:stop(), + + File1 = filename:join(PrivDir, "httpd_apache.conf"), + + {ok, Fd1} = file:open(File1, [write]), + file:write(Fd1, "ServerName httpd_test\r\n"), + file:write(Fd1, "ServerRoot " ++ PrivDir ++ "\r\n"), + file:write(Fd1, "DocumentRoot " ++ PrivDir ++" \r\n"), + file:write(Fd1, "BindAddress *|inet\r\n"), + file:write(Fd1, "Port 0\r\n"), + file:close(Fd1), + + i("start_httpd -> [application] load inets"), + application:load(inets), + i("start_httpd -> [application] set httpd services env with file"), + application:set_env(inets, + services, [{httpd, [{file, File1}]}]), + i("start_httpd -> start inets"), + ok = inets:start(), + (?NUM_DEFAULT_SERVICES + 1) = length(inets:services()), + i("start_httpd -> [application] unset services env"), + application:unset_env(inets, services), + i("start_httpd -> stop inets"), + ok = inets:stop(), + + %% OLD format + i("start_httpd -> [application] load inets"), + application:load(inets), + i("start_httpd -> [application] set httpd services OLD env"), + application:set_env(inets, + services, [{httpd, File1}]), + i("start_httpd -> start inets"), + ok = inets:start(), + (?NUM_DEFAULT_SERVICES + 1) = length(inets:services()), + i("start_httpd -> [application] unset services enc"), + application:unset_env(inets, services), + i("start_httpd -> stop inets"), + ok = inets:stop(), + + i("start_httpd -> start inets"), + ok = inets:start(), + i("start_httpd -> try (and fail) start httpd service - server_name"), + {error, {missing_property, server_name}} = + inets:start(httpd, [{port, 0}, + {server_root, PrivDir}, + {document_root, PrivDir}, + {bind_address, "localhost"}]), + i("start_httpd -> try (and fail) start httpd service - missing document_root"), + {error, {missing_property, document_root}} = + inets:start(httpd, [{port, 0}, + {server_name, "httpd_test"}, + {server_root, PrivDir}, + {bind_address, "localhost"}]), + i("start_httpd -> try (and fail) start httpd service - missing server_root"), + {error, {missing_property, server_root}} = + inets:start(httpd, [{port, 0}, + {server_name, "httpd_test"}, + {document_root, PrivDir}, + {bind_address, "localhost"}]), + i("start_httpd -> try (and fail) start httpd service - missing port"), + {error, {missing_property, port}} = + inets:start(httpd, HttpdConf), + i("start_httpd -> stop inets"), + ok = inets:stop(), + i("start_httpd -> done"), + ok. + + +%%------------------------------------------------------------------------- + +start_ftpc(doc) -> + ["Start/stop of ftpc service"]; +start_ftpc(suite) -> + []; +start_ftpc(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ok = inets:start(), + case inets:start(ftpc, [{host, ?FTP_HOST}]) of + {ok, Pid0} -> + Pids0 = [ServicePid || {_, ServicePid} <- inets:services()], + true = lists:member(Pid0, Pids0), + [_|_] = inets:services_info(), + inets:stop(ftpc, Pid0), + test_server:sleep(100), + Pids1 = [ServicePid || {_, ServicePid} <- inets:services()], + false = lists:member(Pid0, Pids1), + {ok, Pid1} = inets:start(ftpc, [{host, ?FTP_HOST}], stand_alone), + Pids2 = [ServicePid || {_, ServicePid} <- inets:services()], + false = lists:member(Pid1, Pids2), + ok = inets:stop(stand_alone, Pid1), + receive + {'EXIT', Pid1, shutdown} -> + ok + after 100 -> + test_server:fail(stand_alone_not_shutdown) + end, + ok = inets:stop(); + _ -> + {skip, "Unable to reach test FTP server " ++ ?FTP_HOST} + end. + + +%%------------------------------------------------------------------------- + +start_tftpd(doc) -> + ["Start/stop of tfpd service"]; +start_tftpd(suite) -> + []; +start_tftpd(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ok = inets:start(), + {ok, Pid0} = inets:start(tftpd, [{host, "localhost"}, {port, 0}]), + Pids0 = [ServicePid || {_, ServicePid} <- inets:services()], + true = lists:member(Pid0, Pids0), + [_|_] = inets:services_info(), + inets:stop(tftpd, Pid0), + test_server:sleep(100), + Pids1 = [ServicePid || {_, ServicePid} <- inets:services()], + false = lists:member(Pid0, Pids1), + {ok, Pid1} = + inets:start(tftpd, [{host, "localhost"}, {port, 0}], stand_alone), + Pids2 = [ServicePid || {_, ServicePid} <- inets:services()], + false = lists:member(Pid1, Pids2), + ok = inets:stop(stand_alone, Pid1), + receive + {'EXIT', Pid1, shutdown} -> + ok + after 100 -> + test_server:fail(stand_alone_not_shutdown) + end, + ok = inets:stop(), + application:load(inets), + application:set_env(inets, services, [{tftpd,[{host, "localhost"}, + {port, 0}]}]), + ok = inets:start(), + (?NUM_DEFAULT_SERVICES + 1) = length(inets:services()), + application:unset_env(inets, services), + ok = inets:stop(). + + +%%------------------------------------------------------------------------- + +httpd_reload(doc) -> + ["Reload httpd configuration without restarting service"]; +httpd_reload(suite) -> + []; +httpd_reload(Config) when is_list(Config) -> + process_flag(trap_exit, true), + i("httpd_reload -> starting"), + PrivDir = ?config(priv_dir, Config), + DataDir = ?config(data_dir, Config), + HttpdConf = [{server_name, "httpd_test"}, + {server_root, PrivDir}, + {document_root, PrivDir}, + {bind_address, "localhost"}], + + inets:enable_trace(max, io), + + i("httpd_reload -> start inets"), + + ok = inets:start(), + test_server:sleep(5000), + i("httpd_reload -> inets started - start httpd service"), + + {ok, Pid0} = inets:start(httpd, [{port, 0}, {ipfamily, inet} | HttpdConf]), + test_server:sleep(5000), + i("httpd_reload -> httpd service started (~p) - get port", [Pid0]), + + [{port, Port0}] = httpd:info(Pid0, [port]), + test_server:sleep(5000), + i("httpd_reload -> Port: ~p - get document root", [Port0]), + + [{document_root, PrivDir}] = httpd:info(Pid0, [document_root]), + test_server:sleep(5000), + i("httpd_reload -> document root: ~p - reload config", [PrivDir]), + + ok = httpd:reload_config([{port, Port0}, {ipfamily, inet}, + {server_name, "httpd_test"}, + {server_root, PrivDir}, + {document_root, DataDir}, + {bind_address, "localhost"}], non_disturbing), + test_server:sleep(5000), + io:format("~w:~w:httpd_reload - reloaded - get document root~n", [?MODULE, ?LINE]), + + [{document_root, DataDir}] = httpd:info(Pid0, [document_root]), + test_server:sleep(5000), + i("httpd_reload -> document root: ~p - reload config", [DataDir]), + + ok = httpd:reload_config([{port, Port0}, {ipfamily, inet}, + {server_name, "httpd_test"}, + {server_root, PrivDir}, + {document_root, PrivDir}, + {bind_address, "localhost"}], disturbing), + + [{document_root, PrivDir}] = httpd:info(Pid0, [document_root]), + ok = inets:stop(httpd, Pid0), + ok = inets:stop(), + + File = filename:join(PrivDir, "httpd_apache.conf"), + + {ok, Fd0} = file:open(File, [write]), + file:write(Fd0, "ServerName httpd_test\r\n"), + file:write(Fd0, "ServerRoot " ++ PrivDir ++ "\r\n"), + file:write(Fd0, "DocumentRoot " ++ PrivDir ++" \r\n"), + file:write(Fd0, "BindAddress *\r\n"), + file:write(Fd0, "Port 0\r\n"), + file:close(Fd0), + + application:load(inets), + application:set_env(inets, + services, [{httpd, [{file, File}]}]), + + ok = inets:start(), + [Pid1] = [HttpdPid || {httpd, HttpdPid} <- inets:services()], + [{server_name, "httpd_test"}] = httpd:info(Pid1, [server_name]), + [{port, Port1}] = httpd:info(Pid1, [port]), + {ok, Fd1} = file:open(File, [write]), + file:write(Fd1, "ServerName httpd_test2\r\n"), + file:write(Fd1, "ServerRoot " ++ PrivDir ++ "\r\n"), + file:write(Fd1, "DocumentRoot " ++ PrivDir ++" \r\n"), + file:write(Fd1, "BindAddress *\r\n"), + file:write(Fd1, "Port " ++ integer_to_list(Port1) ++ "\r\n"), + file:close(Fd1), + + ok = httpd:reload_config(File, non_disturbing), + [{server_name, "httpd_test2"}] = httpd:info(Pid1, [server_name]), + + {ok, Fd2} = file:open(File, [write]), + file:write(Fd2, "ServerName httpd_test\r\n"), + file:write(Fd2, "ServerRoot " ++ PrivDir ++ "\r\n"), + file:write(Fd2, "DocumentRoot " ++ PrivDir ++" \r\n"), + file:write(Fd2, "BindAddress *\r\n"), + file:write(Fd2, "Port " ++ integer_to_list(Port1) ++ "\r\n"), + file:close(Fd2), + ok = httpd:reload_config(File, disturbing), + [{server_name, "httpd_test"}] = httpd:info(Pid1, [server_name]), + + ok = inets:stop(httpd, Pid1), + application:unset_env(inets, services), + ok = inets:stop(), + i("httpd_reload -> starting"), + ok. + + +i(F) -> + i(F, []). + +i(F, A) -> + Timestamp = formated_timestamp(), + io:format("*** ~s ~w:~w:" ++ F ++ "~n", [Timestamp, ?MODULE, ?LINE | A]). + +formated_timestamp() -> + format_timestamp( now() ). + +format_timestamp({_N1, _N2, N3} = Now) -> + {Date, Time} = calendar:now_to_datetime(Now), + {YYYY,MM,DD} = Date, + {Hour,Min,Sec} = Time, + FormatDate = + io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w", + [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), + lists:flatten(FormatDate). diff --git a/lib/inets/test/inets_SUITE_data/.gitignore b/lib/inets/test/inets_SUITE_data/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/inets/test/inets_SUITE_data/.gitignore diff --git a/lib/inets/test/inets_app_test.erl b/lib/inets/test/inets_app_test.erl new file mode 100644 index 0000000000..6bdb9bb308 --- /dev/null +++ b/lib/inets/test/inets_app_test.erl @@ -0,0 +1,296 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-2009. 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: Verify the application specifics of the inets application +%%---------------------------------------------------------------------- +-module(inets_app_test). + +-compile(export_all). + +-include("inets_test_lib.hrl"). + + +% t() -> megaco_test_lib:t(?MODULE). +% t(Case) -> megaco_test_lib:t({?MODULE, Case}). + + +%% Test server callbacks +init_per_testcase(undef_funcs, Config) -> + NewConfig = lists:keydelete(watchdog, 1, Config), + Dog = test_server:timetrap(inets_test_lib:minutes(10)), + [{watchdog, Dog}| NewConfig]; +init_per_testcase(_, Config) -> + Config. + +fin_per_testcase(_Case, Config) -> + Config. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +all(suite) -> + Cases = + [ + fields, + modules, + exportall, + app_depend, + undef_funcs + ], + {req, [], {conf, app_init, Cases, app_fin}}. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +app_init(suite) -> []; +app_init(doc) -> []; +app_init(Config) when is_list(Config) -> + case is_app(inets) of + {ok, AppFile} -> + io:format("AppFile: ~n~p~n", [AppFile]), + inets:print_version_info(), + [{app_file, AppFile}|Config]; + {error, Reason} -> + fail(Reason) + end. + +is_app(App) -> + LibDir = code:lib_dir(App), + File = filename:join([LibDir, "ebin", atom_to_list(App) ++ ".app"]), + case file:consult(File) of + {ok, [{application, App, AppFile}]} -> + {ok, AppFile}; + Error -> + {error, {invalid_format, Error}} + end. + + +app_fin(suite) -> []; +app_fin(doc) -> []; +app_fin(Config) when is_list(Config) -> + Config. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +fields(suite) -> + []; +fields(doc) -> + []; +fields(Config) when is_list(Config) -> + AppFile = key1search(app_file, Config), + Fields = [vsn, description, modules, registered, applications], + case check_fields(Fields, AppFile, []) of + [] -> + ok; + Missing -> + fail({missing_fields, Missing}) + end. + +check_fields([], _AppFile, Missing) -> + Missing; +check_fields([Field|Fields], AppFile, Missing) -> + check_fields(Fields, AppFile, check_field(Field, AppFile, Missing)). + +check_field(Name, AppFile, Missing) -> + io:format("checking field: ~p~n", [Name]), + case lists:keymember(Name, 1, AppFile) of + true -> + Missing; + false -> + [Name|Missing] + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +modules(suite) -> + []; +modules(doc) -> + []; +modules(Config) when is_list(Config) -> + AppFile = key1search(app_file, Config), + Mods = key1search(modules, AppFile), + EbinList = get_ebin_mods(inets), + case missing_modules(Mods, EbinList, []) of + [] -> + ok; + Missing -> + throw({error, {missing_modules, Missing}}) + end, + case extra_modules(Mods, EbinList, []) of + [] -> + ok; + Extra -> + throw({error, {extra_modules, Extra}}) + end, + {ok, Mods}. + +get_ebin_mods(App) -> + LibDir = code:lib_dir(App), + EbinDir = filename:join([LibDir,"ebin"]), + {ok, Files0} = file:list_dir(EbinDir), + Files1 = [lists:reverse(File) || File <- Files0], + [list_to_atom(lists:reverse(Name)) || [$m,$a,$e,$b,$.|Name] <- Files1]. + + +missing_modules([], _Ebins, Missing) -> + Missing; +missing_modules([Mod|Mods], Ebins, Missing) -> + case lists:member(Mod, Ebins) of + true -> + missing_modules(Mods, Ebins, Missing); + false -> + io:format("missing module: ~p~n", [Mod]), + missing_modules(Mods, Ebins, [Mod|Missing]) + end. + + +extra_modules(_Mods, [], Extra) -> + Extra; +extra_modules(Mods, [Mod|Ebins], Extra) -> + case lists:member(Mod, Mods) of + true -> + extra_modules(Mods, Ebins, Extra); + false -> + io:format("supefluous module: ~p~n", [Mod]), + extra_modules(Mods, Ebins, [Mod|Extra]) + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +exportall(suite) -> + []; +exportall(doc) -> + []; +exportall(Config) when is_list(Config) -> + AppFile = key1search(app_file, Config), + Mods = key1search(modules, AppFile), + check_export_all(Mods). + + +check_export_all([]) -> + ok; +check_export_all([Mod|Mods]) -> + case (catch apply(Mod, module_info, [compile])) of + {'EXIT', {undef, _}} -> + check_export_all(Mods); + O -> + case lists:keysearch(options, 1, O) of + false -> + check_export_all(Mods); + {value, {options, List}} -> + case lists:member(export_all, List) of + true -> + throw({error, {export_all, Mod}}); + false -> + check_export_all(Mods) + end + end + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +app_depend(suite) -> + []; +app_depend(doc) -> + []; +app_depend(Config) when is_list(Config) -> + AppFile = key1search(app_file, Config), + Apps = key1search(applications, AppFile), + check_apps(Apps). + + +check_apps([]) -> + ok; +check_apps([App|Apps]) -> + case is_app(App) of + {ok, _} -> + check_apps(Apps); + Error -> + throw({error, {missing_app, {App, Error}}}) + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +undef_funcs(suite) -> + []; +undef_funcs(doc) -> + []; +undef_funcs(Config) when is_list(Config) -> + App = inets, + AppFile = key1search(app_file, Config), + Mods = key1search(modules, AppFile), + Root = code:root_dir(), + LibDir = code:lib_dir(App), + EbinDir = filename:join([LibDir,"ebin"]), + XRefTestName = undef_funcs_make_name(App, xref_test_name), + {ok, XRef} = xref:start(XRefTestName), + ok = xref:set_default(XRef, + [{verbose,false},{warnings,false}]), + XRefName = undef_funcs_make_name(App, xref_name), + {ok, XRefName} = xref:add_release(XRef, Root, {name,XRefName}), + {ok, App} = xref:replace_application(XRef, App, EbinDir), + {ok, Undefs} = xref:analyze(XRef, undefined_function_calls), + xref:stop(XRef), + analyze_undefined_function_calls(Undefs, Mods, []). + +analyze_undefined_function_calls([], _, []) -> + ok; +analyze_undefined_function_calls([], _, AppUndefs) -> + exit({suite_failed, {undefined_function_calls, AppUndefs}}); +analyze_undefined_function_calls([{{Mod, _F, _A}, _C} = AppUndef|Undefs], + AppModules, AppUndefs) -> + %% Check that this module is our's + case lists:member(Mod,AppModules) of + true -> + {Calling,Called} = AppUndef, + {Mod1,Func1,Ar1} = Calling, + {Mod2,Func2,Ar2} = Called, + io:format("undefined function call: " + "~n ~w:~w/~w calls ~w:~w/~w~n", + [Mod1,Func1,Ar1,Mod2,Func2,Ar2]), + analyze_undefined_function_calls(Undefs, AppModules, + [AppUndef|AppUndefs]); + false -> + io:format("dropping ~p~n", [Mod]), + analyze_undefined_function_calls(Undefs, AppModules, AppUndefs) + end. + +%% This function is used simply to avoid cut-and-paste errors later... +undef_funcs_make_name(App, PostFix) -> + list_to_atom(atom_to_list(App) ++ "_" ++ atom_to_list(PostFix)). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +fail(Reason) -> + exit({suite_failed, Reason}). + +key1search(Key, L) -> + case lists:keysearch(Key, 1, L) of + undefined -> + fail({not_found, Key, L}); + {value, {Key, Value}} -> + Value + end. diff --git a/lib/inets/test/inets_appup_test.erl b/lib/inets/test/inets_appup_test.erl new file mode 100644 index 0000000000..d580c6c4c5 --- /dev/null +++ b/lib/inets/test/inets_appup_test.erl @@ -0,0 +1,336 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-2009. 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: Verify the application specifics of the Megaco application +%%---------------------------------------------------------------------- +-module(inets_appup_test). + +-compile(export_all). + +-include("inets_test_lib.hrl"). + + +% t() -> megaco_test_lib:t(?MODULE). +% t(Case) -> megaco_test_lib:t({?MODULE, Case}). + + +%% Test server callbacks +init_per_testcase(_Case, Config) -> + Config. + +fin_per_testcase(_Case, Config) -> + Config. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +all(suite) -> + Cases = + [ + appup + ], + {req, [], {conf, appup_init, Cases, appup_fin}}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +appup_init(suite) -> []; +appup_init(doc) -> []; +appup_init(Config) when is_list(Config) -> + AppFile = file_name(inets, ".app"), + AppupFile = file_name(inets, ".appup"), + [{app_file, AppFile}, {appup_file, AppupFile}|Config]. + + +file_name(App, Ext) -> + LibDir = code:lib_dir(App), + filename:join([LibDir, "ebin", atom_to_list(App) ++ Ext]). + + +appup_fin(suite) -> []; +appup_fin(doc) -> []; +appup_fin(Config) when is_list(Config) -> + Config. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +appup(suite) -> + []; +appup(doc) -> + "perform a simple check of the appup file"; +appup(Config) when is_list(Config) -> + AppupFile = key1search(appup_file, Config), + AppFile = key1search(app_file, Config), + Modules = modules(AppFile), + check_appup(AppupFile, Modules). + +modules(File) -> + case file:consult(File) of + {ok, [{application,inets,Info}]} -> + case lists:keysearch(modules,1,Info) of + {value, {modules, Modules}} -> + Modules; + false -> + fail({bad_appinfo, Info}) + end; + Error -> + fail({bad_appfile, Error}) + end. + + +check_appup(AppupFile, Modules) -> + case file:consult(AppupFile) of + {ok, [{V, UpFrom, DownTo}]} -> +% io:format("~p => " +% "~n ~p" +% "~n ~p" +% "~n", [V, UpFrom, DownTo]), + check_appup(V, UpFrom, DownTo, Modules); + Else -> + fail({bad_appupfile, Else}) + end. + + +check_appup(V, UpFrom, DownTo, Modules) -> + check_version(V), + check_depends(up, UpFrom, Modules), + check_depends(down, DownTo, Modules), + ok. + + +check_depends(_, [], _) -> + ok; +check_depends(UpDown, [Dep|Deps], Modules) -> + check_depend(UpDown, Dep, Modules), + check_depends(UpDown, Deps, Modules). + + +check_depend(UpDown, {V, Instructions}, Modules) -> + check_version(V), + case check_instructions(UpDown, + Instructions, Instructions, [], [], Modules) of + {_Good, []} -> + ok; + {_, Bad} -> + fail({bad_instructions, Bad, UpDown}) + end. + + +check_instructions(_, [], _, Good, Bad, _) -> + {lists:reverse(Good), lists:reverse(Bad)}; +check_instructions(UpDown, [Instr|Instrs], AllInstr, Good, Bad, Modules) -> + case (catch check_instruction(UpDown, Instr, AllInstr, Modules)) of + ok -> + check_instructions(UpDown, Instrs, AllInstr, + [Instr|Good], Bad, Modules); + {error, Reason} -> + check_instructions(UpDown, Instrs, AllInstr, Good, + [{Instr, Reason}|Bad], Modules) + end; +check_instructions(UpDown, Instructions, _, _, _, _) -> + fail({bad_instructions, {UpDown, Instructions}}). + +%% A new module is added +check_instruction(up, {add_module, Module}, _, Modules) + when is_atom(Module) -> + check_module(Module, Modules); + +%% An old module is re-added +check_instruction(down, {add_module, Module}, _, Modules) + when is_atom(Module) -> + case (catch check_module(Module, Modules)) of + {error, {unknown_module, Module, Modules}} -> + ok; + ok -> + error({existing_readded_module, Module}) + end; + +%% Removing a module on upgrade: +%% - the module has been removed from the app-file. +%% - check that no module depends on this (removed) module +check_instruction(up, {remove, {Module, Pre, Post}}, _, Modules) + when is_atom(Module), is_atom(Pre), is_atom(Post) -> + case (catch check_module(Module, Modules)) of + {error, {unknown_module, Module, Modules}} -> + check_purge(Pre), + check_purge(Post); + ok -> + error({existing_removed_module, Module}) + end; + +%% Removing a module on downgrade: the module exist +%% in the app-file. +check_instruction(down, {remove, {Module, Pre, Post}}, AllInstr, Modules) + when is_atom(Module), is_atom(Pre), is_atom(Post) -> + case (catch check_module(Module, Modules)) of + ok -> + check_purge(Pre), + check_purge(Post), + check_no_remove_depends(Module, AllInstr); + {error, {unknown_module, Module, Modules}} -> + error({nonexisting_removed_module, Module}) + end; + +check_instruction(up, {load_module, Module, Pre, Post, Depend}, _, Modules) + when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) -> + check_module(Module, Modules), + check_module_depend(Module, Depend, Modules), + check_purge(Pre), + check_purge(Post); + +check_instruction(down, {load_module, Module, Pre, Post, Depend}, _, Modules) + when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) -> + check_module(Module, Modules), + % Can not be sure that the the dependent module exists in the new appfile + %%check_module_depend(Module, Depend, Modules), + check_purge(Pre), + check_purge(Post); + + + +check_instruction(up, {delete_module, Module}, _, Modules) + when is_atom(Module) -> + case (catch check_module(Module, Modules)) of + {error, {unknown_module, Module, Modules}} -> + ok; + ok -> + error({existing_module_deleted, Module}) + end; + +check_instruction(down, {delete_module, Module}, _, Modules) + when is_atom(Module) -> + check_module(Module, Modules); + + +check_instruction(_, {apply, {Module, Function, Args}}, _, _) when is_atom(Module), is_atom(Function), is_list(Args) -> + ok; + +check_instruction(_, {update, Module, supervisor}, _, Modules) when is_atom(Module) -> + check_module(Module, Modules); + +check_instruction(_, {update, Module, {advanced, _}, DepMods}, _, Modules) when is_atom(Module), is_list(DepMods) -> + check_module(Module, Modules), + check_module_depend(Module, DepMods, Modules); + +check_instruction(_, {update, Module, Change, Pre, Post, Depend}, _, Modules) + when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) -> + check_module(Module, Modules), + check_module_depend(Module, Depend, Modules), + check_change(Change), + check_purge(Pre), + check_purge(Post); + +check_instruction(_, {restart_application, inets}, _AllInstr, _Modules) -> + ok; + +check_instruction(_, {update, Module, {advanced, _}}, _, Modules) -> + check_module(Module, Modules); + +check_instruction(_, Instr, _AllInstr, _Modules) -> + error({error, {unknown_instruction, Instr}}). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +check_version(V) when is_list(V) -> + ok; +check_version(V) -> + error({bad_version, V}). + + +check_module(M, Modules) when is_atom(M) -> + case lists:member(M,Modules) of + true -> + ok; + false -> + error({unknown_module, M, Modules}) + end; +check_module(M, _) -> + error({bad_module, M}). + + +check_module_depend(M, [], _) when is_atom(M) -> + ok; +check_module_depend(M, Deps, Modules) when is_atom(M), is_list(Deps) -> + case [Dep || Dep <- Deps, lists:member(Dep, Modules) == false] of + [] -> + ok; + Unknown -> + error({unknown_depend_modules, Unknown}) + end; +check_module_depend(_M, D, _Modules) -> + error({bad_depend, D}). + + +check_no_remove_depends(_Module, []) -> + ok; +check_no_remove_depends(Module, [Instr|Instrs]) -> + check_no_remove_depend(Module, Instr), + check_no_remove_depends(Module, Instrs). + +check_no_remove_depend(Module, {load_module, Mod, _Pre, _Post, Depend}) -> + case lists:member(Module, Depend) of + true -> + error({removed_module_in_depend, load_module, Mod, Module}); + false -> + ok + end; +check_no_remove_depend(Module, {update, Mod, _Change, _Pre, _Post, Depend}) -> + case lists:member(Module, Depend) of + true -> + error({removed_module_in_depend, update, Mod, Module}); + false -> + ok + end; +check_no_remove_depend(_, _) -> + ok. + + +check_change(soft) -> + ok; +check_change({advanced, _Something}) -> + ok; +check_change(Change) -> + error({bad_change, Change}). + + +check_purge(soft_purge) -> + ok; +check_purge(brutal_purge) -> + ok; +check_purge(Purge) -> + error({bad_purge, Purge}). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +error(Reason) -> + throw({error, Reason}). + +fail(Reason) -> + exit({suite_failed, Reason}). + +key1search(Key, L) -> + case lists:keysearch(Key, 1, L) of + undefined -> + fail({not_found, Key, L}); + {value, {Key, Value}} -> + Value + end. diff --git a/lib/inets/test/inets_internal.hrl b/lib/inets/test/inets_internal.hrl new file mode 120000 index 0000000000..3228d7ef6a --- /dev/null +++ b/lib/inets/test/inets_internal.hrl @@ -0,0 +1 @@ +../src/inets_app/inets_internal.hrl
\ No newline at end of file diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl new file mode 100644 index 0000000000..755ab594ed --- /dev/null +++ b/lib/inets/test/inets_sup_SUITE.erl @@ -0,0 +1,386 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2009. 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(inets_sup_SUITE). + +-include("test_server.hrl"). +-include("test_server_line.hrl"). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-define(FTP_HOST, "tuor"). + +all(doc) -> + ["Test that the inets supervisorstructur is the expected one."]; +all(suite) -> + [ + default_tree, + ftpc_worker, + tftpd_worker, + httpd_subtree, + httpc_subtree + ]. + +%%-------------------------------------------------------------------- +%% 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) -> + 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(_) -> + inets:stop(), + 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(httpd_subtree, Config) -> + io:format("init_per_testcase(httpd_subtree) -> entry with" + "~n Config: ~p" + "~n", [Config]), + Dog = test_server:timetrap(?t:minutes(1)), + NewConfig = lists:keydelete(watchdog, 1, Config), + + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + ServerROOT = filename:join(PrivDir, "server_root"), + DocROOT = filename:join(PrivDir, "htdocs"), + ConfDir = filename:join(ServerROOT, "conf"), + + io:format("init_per_testcase(httpd_subtree) -> create dir(s)" + "~n", []), + file:make_dir(ServerROOT), %% until http_test is cleaned up! + ok = file:make_dir(DocROOT), + ok = file:make_dir(ConfDir), + + io:format("init_per_testcase(httpd_subtree) -> copy file(s)" + "~n", []), + {ok, _} = inets_test_lib:copy_file("simple.conf", DataDir, PrivDir), + {ok, _} = inets_test_lib:copy_file("mime.types", DataDir, ConfDir), + + io:format("init_per_testcase(httpd_subtree) -> write file(s)" + "~n", []), + ConfFile = filename:join(PrivDir, "simple.conf"), + {ok, Fd} = file:open(ConfFile, [append]), + ok = file:write(Fd, "ServerRoot " ++ ServerROOT ++ "\n"), + ok = file:write(Fd, "DocumentRoot " ++ DocROOT ++ "\n"), + ok = file:close(Fd), + + %% To make sure application:set_env is not overwritten by any + %% app-file settings. + io:format("init_per_testcase(httpd_subtree) -> load inets app" + "~n", []), + application:load(inets), + io:format("init_per_testcase(httpd_subtree) -> update inets env" + "~n", []), + ok = application:set_env(inets, services, [{httpd, ConfFile}]), + + try + io:format("init_per_testcase(httpd_subtree) -> start inets app" + "~n", []), + ok = inets:start(), + io:format("init_per_testcase(httpd_subtree) -> done" + "~n", []), + [{watchdog, Dog}, {server_root, ServerROOT}, {doc_root, DocROOT}, + {conf_dir, ConfDir}| NewConfig] + catch + _:Reason -> + io:format("init_per_testcase(httpd_subtree) -> " + "failed starting inets - cleanup" + "~n Reason: ~p" + "~n", [Reason]), + application:unset_env(inets, services), + application:unload(inets), + exit({failed_starting_inets, Reason}) + end; + + +init_per_testcase(Case, Config) -> + io:format("init_per_testcase(~p) -> entry with" + "~n Config: ~p" + "~n", [Case, Config]), + Dog = test_server:timetrap(?t:minutes(5)), + NewConfig = lists:keydelete(watchdog, 1, Config), + Stop = inets:stop(), + io:format("init_per_testcase(~p) -> Stop: ~p" + "~n", [Case, Stop]), + ok = inets:start(), + [{watchdog, Dog} | NewConfig]. + + +%%-------------------------------------------------------------------- +%% 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(httpd_subtree, Config) -> + Dog = ?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + PrivDir = ?config(priv_dir, Config), + inets_test_lib:del_dirs(PrivDir), + ok; + +end_per_testcase(_, Config) -> + Dog = ?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + inets:stop(), + ok. + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- + + +%%------------------------------------------------------------------------- +%% default_tree +%%------------------------------------------------------------------------- +default_tree(doc) -> + ["Makes sure the correct processes are started and linked," + "in the default case."]; +default_tree(suite) -> + []; +default_tree(Config) when is_list(Config) -> + TopSupChildren = supervisor:which_children(inets_sup), + 4 = length(TopSupChildren), + {value, {httpd_sup, _, supervisor,[httpd_sup]}} = + lists:keysearch(httpd_sup, 1, TopSupChildren), + {value, {httpc_sup, _,supervisor,[httpc_sup]}} = + lists:keysearch(httpc_sup, 1, TopSupChildren), + {value, {ftp_sup,_,supervisor,[ftp_sup]}} = + lists:keysearch(ftp_sup, 1, TopSupChildren), + {value, {tftp_sup,_,supervisor,[tftp_sup]}} = + lists:keysearch(tftp_sup, 1, TopSupChildren), + + HttpcSupChildren = supervisor:which_children(httpc_sup), + {value, {httpc_profile_sup,_, supervisor, [httpc_profile_sup]}} = + lists:keysearch(httpc_profile_sup, 1, HttpcSupChildren), + {value, {httpc_handler_sup,_, supervisor, [httpc_handler_sup]}} = + lists:keysearch(httpc_handler_sup, 1, HttpcSupChildren), + + [] = supervisor:which_children(ftp_sup), + + [] = supervisor:which_children(httpd_sup), + + %% Default profile + [{httpc_manager, _, worker,[httpc_manager]}] + = supervisor:which_children(httpc_profile_sup), + + [] = supervisor:which_children(httpc_handler_sup), + + [] = supervisor:which_children(tftp_sup), + + ok. + + +%%------------------------------------------------------------------------- +%% ftpc_worker +%%------------------------------------------------------------------------- +ftpc_worker(doc) -> + ["Makes sure the ftp worker processes are added and removed " + "appropriatly to/from the supervison tree."]; +ftpc_worker(suite) -> + []; +ftpc_worker(Config) when is_list(Config) -> + [] = supervisor:which_children(ftp_sup), + case inets:start(ftpc, [{host, ?FTP_HOST}]) of + {ok, Pid} -> + case supervisor:which_children(ftp_sup) of + [{_,_, worker, [ftp]}] -> + inets:stop(ftpc, Pid), + test_server:sleep(5000), + [] = supervisor:which_children(ftp_sup), + ok; + Children -> + exit({unexpected_children, Children}) + end; + _ -> + {skip, "Unable to reach test FTP server"} + end. + + +%%------------------------------------------------------------------------- +%% tftpd_worker +%%------------------------------------------------------------------------- +tftpd_worker(doc) -> + ["Makes sure the tftp sub tree is correct."]; +tftpd_worker(suite) -> + []; +tftpd_worker(Config) when is_list(Config) -> + [] = supervisor:which_children(tftp_sup), + {ok, Pid0} = inets:start(tftpd, [{host, "localhost"}, + {port, inet_port()}]), + {ok, _Pid1} = inets:start(tftpd, [{host, "localhost"}, + {port, inet_port()}], stand_alone), + + [{_,Pid0, worker, _}] = supervisor:which_children(tftp_sup), + inets:stop(tftpd, Pid0), + test_server:sleep(5000), + [] = supervisor:which_children(tftp_sup), + ok. + + +%%------------------------------------------------------------------------- +%% httpd_subtree +%%------------------------------------------------------------------------- +httpd_subtree(doc) -> + ["Makes sure the httpd sub tree is correct."]; +httpd_subtree(suite) -> + []; +httpd_subtree(Config) when is_list(Config) -> + io:format("httpd_subtree -> entry with" + "~n Config: ~p" + "~n", [Config]), + + %% Check that we have the httpd top supervisor + io:format("httpd_subtree -> verify inets~n", []), + {ok, _} = verify_child(inets_sup, httpd_sup, supervisor), + + %% Check that we have the httpd instance supervisor + io:format("httpd_subtree -> verify httpd~n", []), + {ok, Id} = verify_child(httpd_sup, httpd_instance_sup, supervisor), + {httpd_instance_sup, Addr, Port} = Id, + Instance = httpd_util:make_name("httpd_instance_sup", Addr, Port), + + %% 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_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), + case supervisor:which_children(InstanceAcc) of + [_ | _] -> + ok; + InstanceAccUnexpectedChildren -> + exit({unexpected_children, + InstanceAcc, InstanceAccUnexpectedChildren}) + end, + + %% Check that the httpd instance misc supervisor has no children + io:format("httpd_subtree -> verify misc~n", []), + InstanceMisc = httpd_util:make_name("httpd_misc_sup", Addr, Port), + case supervisor:which_children(InstanceMisc) of + [] -> + ok; + InstanceMiscUnexpectedChildren -> + exit({unexpected_children, + InstanceMisc, InstanceMiscUnexpectedChildren}) + end, + io:format("httpd_subtree -> done~n", []), + ok. + + +verify_child(Parent, Child, Type) -> +%% io:format("verify_child -> entry with" +%% "~n Parent: ~p" +%% "~n Child: ~p" +%% "~n Type: ~p" +%% "~n", [Parent, Child, Type]), + Children = supervisor:which_children(Parent), +%% io:format("verify_child -> which children" +%% "~n Children: ~p" +%% "~n", [Children]), + verify_child(Children, Parent, Child, Type). + +verify_child([], Parent, Child, _Type) -> + {error, {child_not_found, Child, Parent}}; +verify_child([{Id, _Pid, Type2, Mods}|Children], Parent, Child, Type) -> + case lists:member(Child, Mods) of + true when (Type2 =:= Type) -> +%% io:format("verify_child -> found with expected type" +%% "~n Id: ~p" +%% "~n", [Id]), + {ok, Id}; + true when (Type2 =/= Type) -> +%% io:format("verify_child -> found with unexpected type" +%% "~n Type2: ~p" +%% "~n Id: ~p" +%% "~n", [Type2, Id]), + {error, {wrong_type, Type2, Child, Parent}}; + false -> + verify_child(Children, Parent, Child, Type) + end. + + + +%%------------------------------------------------------------------------- +%% httpc_subtree +%%------------------------------------------------------------------------- +httpc_subtree(doc) -> + ["Makes sure the httpc sub tree is correct."]; +httpc_subtree(suite) -> + []; +httpc_subtree(Config) when is_list(Config) -> + io:format("httpd_subtree -> entry with" + "~n Config: ~p" + "~n", [Config]), + + {ok, Foo} = inets:start(httpc, [{profile, foo}]), + io:format("httpc_subtree -> foo started~n", []), + {ok, Bar} = inets:start(httpc, [{profile, bar}], stand_alone), + io:format("httpc_subtree -> bar started~n", []), + + HttpcChildren = supervisor:which_children(httpc_profile_sup), + io:format("httpc_subtree -> HttpcChildren: ~p~n", [HttpcChildren]), + + {value, {httpc_manager, _, worker,[httpc_manager]}} = + lists:keysearch(httpc_manager, 1, HttpcChildren), + {value,{{http,foo}, Pid, worker,[httpc_manager]}} = + lists:keysearch({http, foo}, 1, HttpcChildren), + false = lists:keysearch({http, bar}, 1, HttpcChildren), + + inets:stop(httpc, Pid), + ok. + +inet_port() -> + {ok, Socket} = gen_tcp:listen(0, [{reuseaddr, true}]), + {ok, Port} = inet:port(Socket), + gen_tcp:close(Socket), + Port. + + diff --git a/lib/inets/test/inets_sup_SUITE_data/mime.types b/lib/inets/test/inets_sup_SUITE_data/mime.types new file mode 100644 index 0000000000..e52d345ff7 --- /dev/null +++ b/lib/inets/test/inets_sup_SUITE_data/mime.types @@ -0,0 +1,3 @@ +# MIME type Extension +text/html html htm +text/plain asc txt diff --git a/lib/inets/test/inets_sup_SUITE_data/simple.conf b/lib/inets/test/inets_sup_SUITE_data/simple.conf new file mode 100644 index 0000000000..e1429b4a28 --- /dev/null +++ b/lib/inets/test/inets_sup_SUITE_data/simple.conf @@ -0,0 +1,6 @@ +Port 8888 +ServerName www.test +SocketType ip_comm +Modules mod_get +ServerAdmin [email protected] + diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl new file mode 100644 index 0000000000..6af2ad32f7 --- /dev/null +++ b/lib/inets/test/inets_test_lib.erl @@ -0,0 +1,302 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-2009. 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(inets_test_lib). + +-include("inets_test_lib.hrl"). + +%% Various small utility functions +-export([start_http_server/1, start_http_server_ssl/1]). +-export([hostname/0]). +-export([connect_bin/3, connect_byte/3, 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([check_body/1]). +-export([millis/0, millis_diff/2, hours/1, minutes/1, seconds/1, sleep/1]). +-export([non_pc_tc_maybe_skip/4, os_based_skip/1]). + +start_http_server(Conf) -> + application:load(inets), + ok = application:set_env(inets, services, [{httpd, Conf}]), + ok = application:start(inets). + +start_http_server_ssl(FileName) -> + application:start(ssl), + catch start_http_server(FileName). + +%% ---------------------------------------------------------------------- +%% print functions +%% + +info(F, A, Mod, Line) -> + print("INF ", F, A, Mod, Line). + +log(F, A, Mod, Line) -> + print("LOG ", F, A, Mod, Line). + +debug(F, A, Mod, Line) -> + print("DBG ", F, A, Mod, Line). + +print(P, F, A, Mod, Line) -> + io:format("~s[~p:~p:~p] : " ++ F ++ "~n", [P, self(), Mod, Line| A]). + +print(F, A, Mod, Line) -> + print("", F, A, Mod, Line). + +hostname() -> + from($@, atom_to_list(node())). +from(H, [H | T]) -> T; +from(H, [_ | T]) -> from(H, T); +from(_, []) -> []. + + +copy_file(File, From, To) -> + file:copy(filename:join(From, File), filename:join(To, File)). + +copy_files(FromDir, ToDir) -> + {ok, Files} = file:list_dir(FromDir), + lists:foreach(fun(File) -> + FullPath = filename:join(FromDir, File), + case filelib:is_file(FullPath) of + true -> + file:copy(FullPath, + filename:join(ToDir, File)); + false -> + ok + end + end, Files). + + +copy_dirs(FromDirRoot, ToDirRoot) -> +%% io:format("~w:copy_dirs -> entry with" +%% "~n FromDirRoot: ~p" +%% "~n ToDirRoot: ~p" +%% "~n", [?MODULE, FromDirRoot, ToDirRoot]), + {ok, Files} = file:list_dir(FromDirRoot), + lists:foreach( + fun(FileOrDir) -> + %% Check if it's a directory or a file +%% io:format("~w:copy_dirs -> check ~p" +%% "~n", [?MODULE, FileOrDir]), + case filelib:is_dir(filename:join(FromDirRoot, FileOrDir)) of + true -> +%% io:format("~w:copy_dirs -> ~p is a directory" +%% "~n", [?MODULE, FileOrDir]), + FromDir = filename:join([FromDirRoot, FileOrDir]), + ToDir = filename:join([ToDirRoot, FileOrDir]), + ok = file:make_dir(ToDir), + copy_dirs(FromDir, ToDir); + false -> +%% io:format("~w:copy_dirs -> ~p is a file" +%% "~n", [?MODULE, FileOrDir]), + copy_file(FileOrDir, FromDirRoot, ToDirRoot) + end + end, Files). + +del_dirs(Dir) -> + case file:list_dir(Dir) of + {ok, []} -> + file:del_dir(Dir); + {ok, Files} -> + lists:foreach(fun(File) -> + FullPath = filename:join(Dir,File), + case filelib:is_dir(FullPath) of + true -> + del_dirs(FullPath), + file:del_dir(FullPath); + false -> + file:delete(FullPath) + end + end, Files); + _ -> + ok + end. + +check_body(Body) -> + case string:rstr(Body, "</html>") of + 0 -> + case string:rstr(Body, "</HTML>") of + 0 -> + test_server:format("Body ~p~n", [Body]), + test_server:fail(did_not_receive_whole_body); + _ -> + ok + end; + _ -> + ok + end. + +%% ---------------------------------------------------------------- +%% Conditional skip of testcases +%% + +non_pc_tc_maybe_skip(Config, Condition, File, Line) + when is_list(Config) andalso is_function(Condition) -> + %% Check if we shall skip the skip + case os:getenv("TS_OS_BASED_SKIP") of + "false" -> + ok; + _ -> + case lists:keysearch(ts, 1, Config) of + {value, {ts, inets}} -> + %% Always run the testcase if we are using our own + %% test-server... + ok; + _ -> + case (catch Condition()) of + true -> + skip(non_pc_testcase, File, Line); + _ -> + ok + end + end + end. + + +os_based_skip(any) -> + true; +os_based_skip(Skippable) when is_list(Skippable) -> + {OsFam, OsName} = + case os:type() of + {_Fam, _Name} = FamAndName -> + FamAndName; + Fam -> + {Fam, undefined} + end, + case lists:member(OsFam, Skippable) of + true -> + true; + false -> + case lists:keysearch(OsFam, 1, Skippable) of + {value, {OsFam, OsName}} -> + true; + {value, {OsFam, OsNames}} when is_list(OsNames) -> + lists:member(OsName, OsNames); + _ -> + false + end + end; +os_based_skip(_) -> + false. + + +%% ---------------------------------------------------------------------- +%% Socket functions: +%% open(SocketType, Host, Port) -> {ok, Socket} | {error, Reason} +%% SocketType -> ssl | ip_comm +%% Host -> atom() | string() | {A, B, C, D} +%% Port -> integer() + +connect_bin(ssl, Host, Port) -> + ssl:start(), + %% Does not support ipv6 in old ssl + case ssl:connect(Host, Port, [binary, {packet,0}]) of + {ok, Socket} -> + {ok, Socket}; + {error, Reason} -> + {error, Reason}; + Error -> + Error + end; +connect_bin(ip_comm, Host, Port) -> + Opts = [inet6, binary, {packet,0}], + connect(ip_comm, Host, Port, Opts). + + +connect(ip_comm, Host, Port, Opts) -> + test_server:format("gen_tcp:connect(~p, ~p, ~p) ~n", [Host, Port, Opts]), + case gen_tcp:connect(Host,Port, Opts) of + {ok, Socket} -> + test_server:format("connect success~n", []), + {ok, Socket}; + {error, nxdomain} -> + test_server:format("nxdomain opts: ~p~n", [Opts]), + connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); + {error, eafnosupport} -> + test_server:format("eafnosupport opts: ~p~n", [Opts]), + connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); + {error, {enfile,_}} -> + test_server:format("Error enfile~n", []), + {error, enfile}; + Error -> + test_server:format("Unexpected error: " + "~n Error: ~p" + "~nwhen" + "~n Host: ~p" + "~n Port: ~p" + "~n Opts: ~p" + "~n", [Error, Host, Port, Opts]), + Error + end. + +connect_byte(ip_comm, Host, Port) -> + Opts = [inet6, {packet,0}], + connect(ip_comm, Host, Port, Opts); + +connect_byte(ssl, Host, Port) -> + ssl:start(), + %% Does not support ipv6 in old ssl + case ssl:connect(Host,Port,[{packet,0}]) of + {ok,Socket} -> + {ok,Socket}; + {error,{enfile,_}} -> + {error, enfile}; + Error -> + Error + end. + +send(ssl, Socket, Data) -> + ssl:send(Socket, Data); +send(ip_comm,Socket,Data) -> + gen_tcp:send(Socket,Data). + + +close(ssl,Socket) -> + catch ssl:close(Socket); +close(ip_comm,Socket) -> + catch gen_tcp:close(Socket). + +millis() -> + erlang:now(). + +millis_diff(A,B) -> + T1 = (element(1,A)*1000000) + element(2,A) + (element(3,A)/1000000), + T2 = (element(1,B)*1000000) + element(2,B) + (element(3,B)/1000000), + T1 - T2. + +hours(N) -> trunc(N * 1000 * 60 * 60). +minutes(N) -> trunc(N * 1000 * 60). +seconds(N) -> trunc(N * 1000). + + +sleep(infinity) -> + receive + after infinity -> + ok + end; +sleep(MSecs) -> + receive + after trunc(MSecs) -> + ok + end, + ok. + + +skip(Reason, File, Line) -> + exit({skipped, {Reason, File, Line}}). diff --git a/lib/inets/test/inets_test_lib.hrl b/lib/inets/test/inets_test_lib.hrl new file mode 100644 index 0000000000..12a43fa136 --- /dev/null +++ b/lib/inets/test/inets_test_lib.hrl @@ -0,0 +1,104 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-2009. 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: Define common macros for testing +%%---------------------------------------------------------------------- + +%% - Print macros - + +-ifdef(inets_debug). +-define(DEBUG(F,A), inets_test_lib:debug(F, A, ?MODULE, ?LINE)). +-else. +-define(DEBUG(F,A),ok). +-endif. + +-ifdef(inets_log). +-define(LOG(F,A), inets_test_lib:log(F, A, ?MODULE, ?LINE)). +-else. +-define(LOG(F,A),ok). +-endif. + +-define(INFO(F,A), inets_test_lib:info(F, A, ?MODULE, ?LINE)). +-define(PRINT(F,A), inets_test_lib:print(F, A, ?MODULE, ?LINE)). + + +%% - Macros stolen from the test server - + +-ifndef(line). +-define(line,put(test_server_loc,{?MODULE,?LINE}),). +-endif. + + +%% - Test case macros - + +-define(EXPANDABLE(I, C, F), inets_test_lib:expandable(I, C, F)). +-define(OS_BASED_SKIP(Skippable), + inets_test_lib:os_based_skip(Skippable)). + +-define(NON_PC_TC_MAYBE_SKIP(Config, Condition), + inets_test_lib:non_pc_tc_maybe_skip(Config, Condition, ?MODULE, ?LINE)). + + + +%% - Misc macros - + +-define(UPDATE(K,V,C), inets_test_lib:update_config(K,V,C)). +-define(CONFIG(K,C), inets_test_lib:get_config(K,C)). +-define(HOSTNAME(), inets_test_lib:hostname()). +-define(SZ(X), inets_test_lib:sz(X)). + + +%% - Test case macros - + +-define(SKIP(Reason), inets_test_lib:skip(Reason)). +-define(FAIL(Reason), inets_test_lib:fail(Reason, ?MODULE, ?LINE)). + + +%% - Socket macros - + +-define(CONNECT(M,H,P), inets_test_lib:connect(M,H,P)). +-define(SEND(M,S,D), inets_test_lib:send(M,S,D)). +-define(CSEND(M,S,D,C,T), inets_test_lib:csend(M,S,D,C,T)). +-define(CLOSE(M,S), inets_test_lib:close(M,S)). + + +%% - Time macros - + +-define(HOURS(N), inets_test_lib:hours(N)). +-define(MINS(N), inets_test_lib:minutes(N)). +-define(SECS(N), inets_test_lib:seconds(N)). + +-define(WD_START(T), inets_test_lib:watchdog_start(T)). +-define(WD_STOP(P), inets_test_lib:watchdog_stop(P)). + +-define(SLEEP(MSEC), inets_test_lib:sleep(MSEC)). +-define(M(), inets_test_lib:millis()). +-define(MDIFF(A,B), inets_test_lib:millis_diff(A,B)). + + +%% - Process utility macros - + +-define(FLUSH(), inets_test_lib:flush_mqueue()). +-define(ETRAP_GET(), inets_test_lib:trap_exit()). +-define(ETRAP_SET(O), inets_test_lib:trap_exit(O)). + + + + diff --git a/lib/inets/test/rules.mk b/lib/inets/test/rules.mk new file mode 100644 index 0000000000..047c03b267 --- /dev/null +++ b/lib/inets/test/rules.mk @@ -0,0 +1,59 @@ +#-*-makefile-*- ; force emacs to enter makefile-mode +# ---------------------------------------------------- +# Make include file for otp +# +# Copyright (C) 1996, Ericsson Telecommunications +# Author: Lars Thorsen +# ---------------------------------------------------- +.SUFFIXES: .hrl .erl .jam .beam + + +# ---------------------------------------------------- +# Common macros +# ---------------------------------------------------- +DEFAULT_TARGETS = opt debug instr release release_docs clean docs + +# ---------------------------------------------------- +# Erlang language section +# ---------------------------------------------------- +EMULATOR = beam +ifeq ($(findstring vxworks,$(TARGET)),vxworks) +# VxWorks object files should be compressed. +# Other object files should have debug_info. +ERL_COMPILE_FLAGS += +compressed +else +ifdef BOOTSTRAP +ERL_COMPILE_FLAGS += +slim +else +ERL_COMPILE_FLAGS += +debug_info +endif +endif +ERLC_WFLAGS = -W +ERLC = erlc $(ERLC_WFLAGS) $(ERLC_FLAGS) +ERL.beam = erl.beam -boot start_clean +ERL.jam = erl -boot start_clean +ERL = $(ERL.$(EMULATOR)) + +ifeq ($(EBIN),) +EBIN = . +endif + +ESRC = . + + +$(EBIN)/%.jam: $(ESRC)/%.erl + $(ERLC) -bjam $(ERL_COMPILE_FLAGS) -o$(EBIN) $< + +$(EBIN)/%.beam: $(ESRC)/%.erl + $(ERLC) -bbeam $(ERL_COMPILE_FLAGS) -o$(EBIN) $< + +.erl.jam: + $(ERLC) -bjam $(ERL_COMPILE_FLAGS) -o$(dir $@) $< + +.erl.beam: + $(ERLC) -bbeam $(ERL_COMPILE_FLAGS) -o$(dir $@) $< + + + + + diff --git a/lib/inets/test/tftp_SUITE.erl b/lib/inets/test/tftp_SUITE.erl new file mode 100644 index 0000000000..5768fff88b --- /dev/null +++ b/lib/inets/test/tftp_SUITE.erl @@ -0,0 +1,903 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2009. 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(tftp_SUITE). + +-compile(export_all). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Includes and defines +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-include("tftp_test_lib.hrl"). + +-define(START_DAEMON(PortX, OptionsX), + fun(Port, Options) -> + {ok, Pid} = ?VERIFY({ok, _Pid}, tftp:start([{port, Port} | Options])), + if + Port == 0 -> + {ok, ActualOptions} = ?IGNORE(tftp:info(Pid)), + {value, {port, ActualPort}} = + lists:keysearch(port, 1, ActualOptions), + {ActualPort, Pid}; + true -> + {Port, Pid} + end + end(PortX, OptionsX)). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% API +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +t() -> + tftp_test_lib:t([{?MODULE, all}]). + +t(Cases) -> + tftp_test_lib:t(Cases, default_config()). + +t(Cases, Config) -> + tftp_test_lib:t(Cases, Config). + +default_config() -> + []. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test server callbacks +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init_per_testcase(Case, Config) -> + tftp_test_lib:init_per_testcase(Case, Config). + +fin_per_testcase(Case, Config) when is_list(Config) -> + tftp_test_lib:fin_per_testcase(Case, Config). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Top test case +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +all(doc) -> + ["Test suites for TFTP."]; + +all(suite) -> + [ + simple, + extra, + reuse_connection, + resend_client, + resend_server + ]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Simple +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +simple(doc) -> + ["Start the daemon and perform simple a read and write."]; +simple(suite) -> + []; +simple(Config) when is_list(Config) -> + ?VERIFY(ok, application:start(inets)), + + {Port, DaemonPid} = ?IGNORE(?START_DAEMON(0, [{debug, brief}])), + + %% Read fail + RemoteFilename = "tftp_temporary_remote_test_file.txt", + LocalFilename = "tftp_temporary_local_test_file.txt", + Blob = list_to_binary(lists:duplicate(2000, $1)), + %% Blob = <<"Some file contents\n">>, + Size = size(Blob), + ?IGNORE(file:delete(RemoteFilename)), + ?VERIFY({error, {client_open, enoent, _}}, + tftp:read_file(RemoteFilename, binary, [{port, Port}])), + + %% Write and read + ?VERIFY({ok, Size}, tftp:write_file(RemoteFilename, Blob, [{port, Port}])), + ?VERIFY({ok, Blob}, tftp:read_file(RemoteFilename, binary, [{port, Port}])), + ?IGNORE(file:delete(LocalFilename)), + ?VERIFY({ok, Size}, tftp:read_file(RemoteFilename, LocalFilename, [{port, Port}])), + + %% Cleanup + unlink(DaemonPid), + exit(DaemonPid, kill), + ?VERIFY(ok, file:delete(LocalFilename)), + ?VERIFY(ok, file:delete(RemoteFilename)), + ?VERIFY(ok, application:stop(inets)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Extra +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +extra(doc) -> + ["Verify new stuff for IS 1.2."]; +extra(suite) -> + []; +extra(Config) when is_list(Config) -> + ?VERIFY({'EXIT', {badarg,{fake_key, fake_flag}}}, + tftp:start([{port, 0}, {fake_key, fake_flag}])), + + {Port, DaemonPid} = ?IGNORE(?START_DAEMON(0, [{debug, brief}])), + + RemoteFilename = "tftp_extra_temporary_remote_test_file.txt", + LocalFilename = "tftp_extra_temporary_local_test_file.txt", + Blob = <<"Some file contents\n">>, + Size = size(Blob), + Host = "127.0.0.1", + Peer = {inet, Host, Port}, + Generic = + [ + {state, []}, + {prepare, fun extra_prepare/6}, + {open, fun extra_open/6}, + {read, fun extra_read/1}, + {write, fun extra_write/2}, + {abort, fun extra_abort/3 } + ], + Options = [{host, Host}, + {port, Port}, + %%{ debug,all}, + {callback, {".*", tftp_test_lib, Generic}}], + ?VERIFY(ok, file:write_file(LocalFilename, Blob)), + ?VERIFY({ok, [{count, Size}, Peer]}, + tftp:write_file(RemoteFilename, LocalFilename, Options)), + ?VERIFY(ok, file:delete(LocalFilename)), + + ?VERIFY({ok,[{bin, Blob}, Peer]}, + tftp:read_file(RemoteFilename, LocalFilename, Options)), + + %% Cleanup + unlink(DaemonPid), + exit(DaemonPid, kill), + ?VERIFY(ok, file:delete(LocalFilename)), + ?VERIFY(ok, file:delete(RemoteFilename)), + ok. + +-record(extra_state, {file, blksize, count, acc, peer}). + +%%------------------------------------------------------------------- +%% Prepare +%%------------------------------------------------------------------- + +extra_prepare(Peer, Access, LocalFilename, Mode, SuggestedOptions, []) -> + %% Client side + BlkSize = list_to_integer(tftp_test_lib:lookup_option("blksize", "512", SuggestedOptions)), + State = #extra_state{blksize = BlkSize, peer = Peer}, + extra_open(Peer, Access, LocalFilename, Mode, SuggestedOptions, State), + {ok, SuggestedOptions, State}; +extra_prepare(_Peer, _Access, _Bin, _Mode, _SuggestedOptions, _Initial) -> + {error, {undef, "Illegal callback options."}}. + +%%------------------------------------------------------------------- +%% Open +%%------------------------------------------------------------------- + +extra_open(Peer, Access, LocalFilename, Mode, SuggestedOptions, []) -> + %% Server side + case extra_prepare(Peer, Access, LocalFilename, Mode, SuggestedOptions, []) of + {ok, AcceptedOptions, []} -> + BlkSize = list_to_integer(tftp_test_lib:lookup_option("blksize", "512", AcceptedOptions)), + State = #extra_state{blksize = BlkSize, peer = Peer}, + extra_open(Peer, Access, LocalFilename, Mode, AcceptedOptions, State); + {error, {Code, Text}} -> + {error, {Code, Text}} + end; +extra_open(_Peer, Access, LocalFilename, _Mode, NegotiatedOptions, #extra_state{} = State) -> + {File, Acc} = + case Access of + read -> + if + is_binary(LocalFilename) -> + {undefined, LocalFilename}; + is_list(LocalFilename) -> + {ok, Bin} = file:read_file(LocalFilename), + {LocalFilename, Bin} + end; + write -> + {LocalFilename, []} + end, + %% Both sides + State2 = State#extra_state{file = File, acc = Acc, count = 0}, + {ok, NegotiatedOptions, State2}. + +%%------------------------------------------------------------------- +%% Read +%%------------------------------------------------------------------- + +extra_read(#extra_state{acc = Bin} = State) when is_binary(Bin) -> + BlkSize = State#extra_state.blksize, + Count = State#extra_state.count + size(Bin), + if + size(Bin) >= BlkSize -> + <<Block:BlkSize/binary, Bin2/binary>> = Bin, + State2 = State#extra_state{acc = Bin2, count = Count}, + {more, Block, State2}; + size(Bin) < BlkSize -> + Res = [{count, Count}, State#extra_state.peer], + {last, Bin, Res} + end. + +%%------------------------------------------------------------------- +%% Write +%%------------------------------------------------------------------- + +extra_write(Bin, #extra_state{acc = List} = State) when is_binary(Bin), is_list(List) -> + Size = size(Bin), + BlkSize = State#extra_state.blksize, + if + Size == BlkSize -> + {more, State#extra_state{acc = [Bin | List]}}; + Size < BlkSize -> + Bin2 = list_to_binary(lists:reverse([Bin | List])), + Res = [{bin, Bin2}, State#extra_state.peer], + file:write_file(State#extra_state.file, Bin2), + {last, Res} + end. + +%%------------------------------------------------------------------- +%% Abort +%%------------------------------------------------------------------- + +extra_abort(_Code, _Text, #extra_state{}) -> + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Re-send client +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +resend_client(doc) -> + ["Verify that the server behaves correctly when the client re-sends packets."]; +resend_client(suite) -> + []; +resend_client(Config) when is_list(Config) -> + Host = {127, 0, 0, 1}, + {Port, DaemonPid} = ?IGNORE(?START_DAEMON(0, [{debug, all}])), + + ?VERIFY(ok, resend_read_client(Host, Port, 10)), + ?VERIFY(ok, resend_read_client(Host, Port, 512)), + ?VERIFY(ok, resend_read_client(Host, Port, 1025)), + + ?VERIFY(ok, resend_write_client(Host, Port, 10)), + ?VERIFY(ok, resend_write_client(Host, Port, 512)), + ?VERIFY(ok, resend_write_client(Host, Port, 1025)), + + %% Cleanup + unlink(DaemonPid), + exit(DaemonPid, kill), + ok. + +resend_read_client(Host, Port, BlkSize) -> + RemoteFilename = "tftp_resend_read_client.tmp", + Block1 = lists:duplicate(BlkSize, $1), + Block2 = lists:duplicate(BlkSize, $2), + Block3 = lists:duplicate(BlkSize, $3), + Block4 = lists:duplicate(BlkSize, $4), + Block5 = lists:duplicate(BlkSize, $5), + Blocks = [Block1, Block2, Block3, Block4, Block5], + Blob = list_to_binary(Blocks), + ?VERIFY(ok, file:write_file(RemoteFilename, Blob)), + + Timeout = timer:seconds(3), + ?VERIFY(timeout, recv(0)), + + %% Open socket + {ok, Socket} = ?VERIFY({ok, _}, gen_udp:open(0, [binary, {reuseaddr, true}, {active, true}])), + + ReadList = [0, 1, RemoteFilename, 0, "octet", 0], + Data1Bin = list_to_binary([0, 3, 0, 1 | Block1]), + NewPort = + if + BlkSize =:= 512 -> + %% Send READ + ReadBin = list_to_binary(ReadList), + ?VERIFY(ok, gen_udp:send(Socket, Host, Port, ReadBin)), + + %% Sleep a while in order to provoke the server to re-send the packet + timer:sleep(Timeout + timer:seconds(1)), + + %% Recv DATA #1 (the packet that the server think that we have lost) + {udp, _, _, NewPort0, _} = ?VERIFY({udp, Socket, Host, _, Data1Bin}, recv(Timeout)), + NewPort0; + true -> + %% Send READ + BlkSizeList = integer_to_list(BlkSize), + Options = ["blksize", 0, BlkSizeList, 0], + ReadBin = list_to_binary([ReadList | Options]), + ?VERIFY(ok, gen_udp:send(Socket, Host, Port, ReadBin)), + + %% Recv OACK + OptionAckBin = list_to_binary([0, 6 | Options]), + {udp, _, _, NewPort0, _} = ?VERIFY({udp, Socket, Host, _, OptionAckBin}, recv(Timeout)), + + %% Send ACK #0 + Ack0Bin = <<0, 4, 0, 0>>, + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort0, Ack0Bin)), + + %% Send ACK #0 AGAIN (pretend that we timed out) + timer:sleep(timer:seconds(1)), + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort0, Ack0Bin)), + + %% Recv DATA #1 (the packet that the server think that we have lost) + ?VERIFY({udp, Socket, Host, NewPort0, Data1Bin}, recv(Timeout)), + NewPort0 + end, + + %% Recv DATA #1 AGAIN (the re-sent package) + ?VERIFY({udp, Socket, Host, NewPort, Data1Bin}, recv(Timeout)), + + %% Send ACK #1 + Ack1Bin = <<0, 4, 0, 1>>, + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort, Ack1Bin)), + + %% Recv DATA #2 + Data2Bin = list_to_binary([0, 3, 0, 2 | Block2]), + ?VERIFY({udp, Socket, Host, NewPort, Data2Bin}, recv(Timeout)), + + %% Send ACK #2 + Ack2Bin = <<0, 4, 0, 2>>, + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort, Ack2Bin)), + + %% Recv DATA #3 + Data3Bin = list_to_binary([0, 3, 0, 3 | Block3]), + ?VERIFY({udp, Socket, Host, NewPort, Data3Bin}, recv(Timeout)), + + %% Send ACK #3 + Ack3Bin = <<0, 4, 0, 3>>, + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort, Ack3Bin)), + + %% Send ACK #3 AGAIN (pretend that we timed out) + timer:sleep(timer:seconds(1)), + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort, Ack3Bin)), + + %% Recv DATA #4 (the packet that the server think that we have lost) + Data4Bin = list_to_binary([0, 3, 0, 4 | Block4]), + ?VERIFY({udp, Socket, Host, NewPort, Data4Bin}, recv(Timeout)), + + %% Recv DATA #4 AGAIN (the re-sent package) + ?VERIFY({udp, Socket, Host, NewPort, Data4Bin}, recv(Timeout)), + + %% Send ACK #2 which is out of range + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort, Ack2Bin)), + + %% Send ACK #4 + Ack4Bin = <<0, 4, 0, 4>>, + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort, Ack4Bin)), + + %% Recv DATA #5 + Data5Bin = list_to_binary([0, 3, 0, 5 | Block5]), + ?VERIFY({udp, Socket, Host, NewPort, Data5Bin}, recv(Timeout)), + + %% Send ACK #5 + Ack5Bin = <<0, 4, 0, 5>>, + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort, Ack5Bin)), + + %% Close socket + ?VERIFY(ok, gen_udp:close(Socket)), + + ?VERIFY(timeout, recv(Timeout)), + ?VERIFY(ok, file:delete(RemoteFilename)), + ok. + +resend_write_client(Host, Port, BlkSize) -> + RemoteFilename = "tftp_resend_write_client.tmp", + Block1 = lists:duplicate(BlkSize, $1), + Block2 = lists:duplicate(BlkSize, $2), + Block3 = lists:duplicate(BlkSize, $3), + Block4 = lists:duplicate(BlkSize, $4), + Block5 = lists:duplicate(BlkSize, $5), + Blocks = [Block1, Block2, Block3, Block4, Block5], + Blob = list_to_binary(Blocks), + ?IGNORE(file:delete(RemoteFilename)), + ?VERIFY({error, enoent}, file:read_file(RemoteFilename)), + + Timeout = timer:seconds(3), + ?VERIFY(timeout, recv(0)), + + %% Open socket + {ok, Socket} = ?VERIFY({ok, _}, gen_udp:open(0, [binary, {reuseaddr, true}, {active, true}])), + + WriteList = [0, 2, RemoteFilename, 0, "octet", 0], + NewPort = + if + BlkSize =:= 512 -> + %% Send WRITE + WriteBin = list_to_binary(WriteList), + ?VERIFY(ok, gen_udp:send(Socket, Host, Port, WriteBin)), + + %% Sleep a while in order to provoke the server to re-send the packet + timer:sleep(Timeout + timer:seconds(1)), + + %% Recv ACK #0 (the packet that the server think that we have lost) + Ack0Bin = <<0, 4, 0, 0>>, + ?VERIFY({udp, Socket, Host, _, Ack0Bin}, recv(Timeout)), + + %% Recv ACK #0 AGAIN (the re-sent package) + {udp, _, _, NewPort0, _} = ?VERIFY({udp, Socket, Host, _, Ack0Bin}, recv(Timeout)), + NewPort0; + true -> + %% Send WRITE + BlkSizeList = integer_to_list(BlkSize), + WriteBin = list_to_binary([WriteList, "blksize", 0, BlkSizeList, 0]), + ?VERIFY(ok, gen_udp:send(Socket, Host, Port, WriteBin)), + + %% Sleep a while in order to provoke the server to re-send the packet + timer:sleep(timer:seconds(1)), + + %% Recv OACK (the packet that the server think that we have lost) + OptionAckBin = list_to_binary([0, 6, "blksize",0, BlkSizeList, 0]), + ?VERIFY({udp, Socket, Host, _, OptionAckBin}, recv(Timeout)), + + %% Recv OACK AGAIN (the re-sent package) + {udp, _, _, NewPort0, _} = ?VERIFY({udp, Socket, Host, _, OptionAckBin}, recv(Timeout)), + NewPort0 + end, + + %% Send DATA #1 + Data1Bin = list_to_binary([0, 3, 0, 1 | Block1]), + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort, Data1Bin)), + + %% Recv ACK #1 + Ack1Bin = <<0, 4, 0, 1>>, + ?VERIFY({udp, Socket, Host, NewPort, Ack1Bin}, recv(Timeout)), + + %% Send DATA #2 + Data2Bin = list_to_binary([0, 3, 0, 2 | Block2]), + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort, Data2Bin)), + + %% Recv ACK #2 + Ack2Bin = <<0, 4, 0, 2>>, + ?VERIFY({udp, Socket, Host, NewPort, Ack2Bin}, recv(Timeout)), + + %% Send DATA #3 + Data3Bin = list_to_binary([0, 3, 0, 3 | Block3]), + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort, Data3Bin)), + + %% Recv ACK #3 + Ack3Bin = <<0, 4, 0, 3>>, + ?VERIFY({udp, Socket, Host, NewPort, Ack3Bin}, recv(Timeout)), + + %% Send DATA #3 AGAIN (pretend that we timed out) + timer:sleep(timer:seconds(1)), + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort, Data3Bin)), + + %% Recv ACK #3 AGAIN (the packet that the server think that we have lost) + ?VERIFY({udp, Socket, Host, NewPort, Ack3Bin}, recv(Timeout)), + + %% Send DATA #2 which is out of range + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort, Data2Bin)), + + %% Send DATA #4 + Data4Bin = list_to_binary([0, 3, 0, 4 | Block4]), + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort, Data4Bin)), + + %% Recv ACK #4 + Ack4Bin = <<0, 4, 0, 4>>, + ?VERIFY({udp, Socket, Host, NewPort, Ack4Bin}, recv(Timeout)), + + %% Send DATA #5 + Data5Bin = list_to_binary([0, 3, 0, 5 | Block5]), + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort, Data5Bin)), + + %% Recv ACK #5 + Ack5Bin = <<0, 4, 0, 5>>, + ?VERIFY({udp, Socket, Host, NewPort, Ack5Bin}, recv(Timeout)), + + %% Close socket + ?VERIFY(ok, gen_udp:close(Socket)), + + ?VERIFY(timeout, recv(Timeout)), + ?VERIFY({ok, Blob}, file:read_file(RemoteFilename)), + ?VERIFY(ok, file:delete(RemoteFilename)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Re-send server +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +resend_server(doc) -> + ["Verify that the server behaves correctly when the server re-sends packets."]; +resend_server(suite) -> + []; +resend_server(Config) when is_list(Config) -> + Host = {127, 0, 0, 1}, + + ?VERIFY(ok, resend_read_server(Host, 10)), + ?VERIFY(ok, resend_read_server(Host, 512)), + ?VERIFY(ok, resend_read_server(Host, 1025)), + + ?VERIFY(ok, resend_write_server(Host, 10)), + ?VERIFY(ok, resend_write_server(Host, 512)), + ?VERIFY(ok, resend_write_server(Host, 1025)), + ok. + +resend_read_server(Host, BlkSize) -> + RemoteFilename = "tftp_resend_read_server.tmp", + Block1 = lists:duplicate(BlkSize, $1), + Block2 = lists:duplicate(BlkSize, $2), + Block3 = lists:duplicate(BlkSize, $3), + Block4 = lists:duplicate(BlkSize, $4), + Block5 = lists:duplicate(BlkSize, $5), + Block6 = [], + Blocks = [Block1, Block2, Block3, Block4, Block5, Block6], + Blob = list_to_binary(Blocks), + + Timeout = timer:seconds(3), + ?VERIFY(timeout, recv(0)), + + %% Open daemon socket + {ok, DaemonSocket} = ?VERIFY({ok, _}, gen_udp:open(0, [binary, {reuseaddr, true}, {active, true}])), + {ok, DaemonPort} = ?IGNORE(inet:port(DaemonSocket)), + + %% Open server socket + {ok, ServerSocket} = ?VERIFY({ok, _}, gen_udp:open(0, [binary, {reuseaddr, true}, {active, true}])), + ?IGNORE(inet:port(ServerSocket)), + + %% Prepare client process + ReplyTo = self(), + ClientFun = + fun(Extra) -> + Options = [{port, DaemonPort}, {debug, brief}] ++ Extra, + Res = ?VERIFY({ok, Blob}, tftp:read_file(RemoteFilename, binary, Options)), + ReplyTo ! {self(), {tftp_client_reply, Res}}, + exit(normal) + end, + + ReadList = [0, 1, RemoteFilename, 0, "octet", 0], + Data1Bin = list_to_binary([0, 3, 0, 1 | Block1]), + Ack1Bin = <<0, 4, 0, 1>>, + {ClientPort, ClientPid} = + if + BlkSize =:= 512 -> + %% Start client process + ClientPid0 = spawn_link(fun() -> ClientFun([]) end), + + %% Recv READ + ReadBin = list_to_binary(ReadList), + {udp, _, _, ClientPort0, _} = ?VERIFY({udp, DaemonSocket, Host, _, ReadBin}, recv(Timeout)), + + %% Send DATA #1 + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort0, Data1Bin)), + + %% Sleep a while in order to provoke the client to re-send the packet + timer:sleep(Timeout + timer:seconds(1)), + + %% Recv ACK #1 (the packet that the server think that we have lost) + ?VERIFY({udp, ServerSocket, Host, ClientPort0, Ack1Bin}, recv(Timeout)), + + %% Recv ACK #1 AGAIN (the re-sent package) + ?VERIFY({udp, ServerSocket, Host, _, Ack1Bin}, recv(Timeout)), + {ClientPort0, ClientPid0}; + true -> + %% Start client process + BlkSizeList = integer_to_list(BlkSize), + ClientPid0 = spawn_link(fun() -> ClientFun([{"blksize", BlkSizeList}]) end), + + %% Recv READ + Options = ["blksize", 0, BlkSizeList, 0], + ReadBin = list_to_binary([ReadList | Options]), + {udp, _, _, ClientPort0, _} = ?VERIFY({udp, DaemonSocket, Host, _, ReadBin}, recv(Timeout)), + + %% Send OACK + BlkSizeList = integer_to_list(BlkSize), + OptionAckBin = list_to_binary([0, 6, "blksize",0, BlkSizeList, 0]), + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort0, OptionAckBin)), + + %% Sleep a while in order to provoke the client to re-send the packet + timer:sleep(Timeout + timer:seconds(1)), + + %% Recv ACK #0 (the packet that the server think that we have lost) + Ack0Bin = <<0, 4, 0, 0>>, + ?VERIFY({udp, ServerSocket, Host, ClientPort0, Ack0Bin}, recv(Timeout)), + + %% Recv ACK #0 AGAIN (the re-sent package) + ?VERIFY({udp, ServerSocket, Host, ClientPort0, Ack0Bin}, recv(Timeout)), + + %% Send DATA #1 + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort0, Data1Bin)), + + %% Recv ACK #1 + ?VERIFY({udp, ServerSocket, Host, _, Ack1Bin}, recv(Timeout)), + {ClientPort0, ClientPid0} + end, + + %% Send DATA #2 + Data2Bin = list_to_binary([0, 3, 0, 2 | Block2]), + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort, Data2Bin)), + + %% Recv ACK #2 + Ack2Bin = <<0, 4, 0, 2>>, + ?VERIFY({udp, ServerSocket, Host, ClientPort, Ack2Bin}, recv(Timeout)), + + %% Send DATA #3 + Data3Bin = list_to_binary([0, 3, 0, 3 | Block3]), + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort, Data3Bin)), + + %% Recv ACK #3 + Ack3Bin = <<0, 4, 0, 3>>, + ?VERIFY({udp, ServerSocket, Host, ClientPort, Ack3Bin}, recv(Timeout)), + + %% Send DATA #3 AGAIN (pretend that we timed out) + timer:sleep(timer:seconds(1)), + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort, Data3Bin)), + + %% Recv ACK #3 AGAIN (the packet that the server think that we have lost) + ?VERIFY({udp, ServerSocket, Host, ClientPort, Ack3Bin}, recv(Timeout)), + + %% Send DATA #4 + Data4Bin = list_to_binary([0, 3, 0, 4 | Block4]), + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort, Data4Bin)), + + %% Recv ACK #4 + Ack4Bin = <<0, 4, 0, 4>>, + ?VERIFY({udp, ServerSocket, Host, ClientPort, Ack4Bin}, recv(Timeout)), + + %% Send DATA #3 which is out of range + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort, Data3Bin)), + + %% Send DATA #5 + Data5Bin = list_to_binary([0, 3, 0, 5 | Block5]), + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort, Data5Bin)), + + %% Recv ACK #5 + Ack5Bin = <<0, 4, 0, 5>>, + ?VERIFY({udp, ServerSocket, Host, ClientPort, Ack5Bin}, recv(Timeout)), + + %% Send DATA #6 + Data6Bin = list_to_binary([0, 3, 0, 6 | Block6]), + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort, Data6Bin)), + + %% Close daemon and server sockets + ?VERIFY(ok, gen_udp:close(ServerSocket)), + ?VERIFY(ok, gen_udp:close(DaemonSocket)), + + ?VERIFY({ClientPid, {tftp_client_reply, {ok, Blob}}}, recv(Timeout)), + + ?VERIFY(timeout, recv(Timeout)), + ok. + +resend_write_server(Host, BlkSize) -> + RemoteFilename = "tftp_resend_write_server.tmp", + Block1 = lists:duplicate(BlkSize, $1), + Block2 = lists:duplicate(BlkSize, $2), + Block3 = lists:duplicate(BlkSize, $3), + Block4 = lists:duplicate(BlkSize, $4), + Block5 = lists:duplicate(BlkSize, $5), + Block6 = [], + Blocks = [Block1, Block2, Block3, Block4, Block5, Block6], + Blob = list_to_binary(Blocks), + Size = size(Blob), + + Timeout = timer:seconds(3), + ?VERIFY(timeout, recv(0)), + + %% Open daemon socket + {ok, DaemonSocket} = ?VERIFY({ok, _}, gen_udp:open(0, [binary, {reuseaddr, true}, {active, true}])), + {ok, DaemonPort} = ?IGNORE(inet:port(DaemonSocket)), + + %% Open server socket + {ok, ServerSocket} = ?VERIFY({ok, _}, gen_udp:open(0, [binary, {reuseaddr, true}, {active, true}])), + ?IGNORE(inet:port(ServerSocket)), + + %% Prepare client process + ReplyTo = self(), + ClientFun = + fun(Extra) -> + Options = [{port, DaemonPort}, {debug, brief}] ++ Extra, + Res = ?VERIFY({ok, Size}, tftp:write_file(RemoteFilename, Blob, Options)), + ReplyTo ! {self(), {tftp_client_reply, Res}}, + exit(normal) + end, + + WriteList = [0, 2, RemoteFilename, 0, "octet", 0], + Data1Bin = list_to_binary([0, 3, 0, 1 | Block1]), + {ClientPort, ClientPid} = + if + BlkSize =:= 512 -> + %% Start client process + ClientPid0 = spawn_link(fun() -> ClientFun([]) end), + + %% Recv WRITE + WriteBin = list_to_binary(WriteList), + io:format("WriteBin ~p\n", [WriteBin]), + {udp, _, _, ClientPort0, _} = ?VERIFY({udp, DaemonSocket, Host, _, WriteBin}, recv(Timeout)), + + %% Send ACK #1 + Ack0Bin = <<0, 4, 0, 0>>, + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort0, Ack0Bin)), + + %% Sleep a while in order to provoke the client to re-send the packet + timer:sleep(Timeout + timer:seconds(1)), + + %% Recv DATA #1 (the packet that the server think that we have lost) + ?VERIFY({udp, ServerSocket, Host, ClientPort0, Data1Bin}, recv(Timeout)), + + %% Recv DATA #1 AGAIN (the re-sent package) + ?VERIFY({udp, ServerSocket, Host, _, Data1Bin}, recv(Timeout)), + {ClientPort0, ClientPid0}; + true -> + %% Start client process + BlkSizeList = integer_to_list(BlkSize), + ClientPid0 = spawn_link(fun() -> ClientFun([{"blksize", BlkSizeList}]) end), + + %% Recv WRITE + Options = ["blksize", 0, BlkSizeList, 0], + WriteBin = list_to_binary([WriteList | Options]), + {udp, _, _, ClientPort0, _} = ?VERIFY({udp, DaemonSocket, Host, _, WriteBin}, recv(Timeout)), + + %% Send OACK + BlkSizeList = integer_to_list(BlkSize), + OptionAckBin = list_to_binary([0, 6, "blksize",0, BlkSizeList, 0]), + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort0, OptionAckBin)), + + %% Sleep a while in order to provoke the client to re-send the packet + timer:sleep(Timeout + timer:seconds(1)), + + %% Recv DATA #1 (the packet that the server think that we have lost) + ?VERIFY({udp, ServerSocket, Host, ClientPort0, Data1Bin}, recv(Timeout)), + + %% Recv DATA #1 AGAIN (the re-sent package) + ?VERIFY({udp, ServerSocket, Host, ClientPort0, Data1Bin}, recv(Timeout)), + {ClientPort0, ClientPid0} + end, + + %% Send ACK #1 + Ack1Bin = <<0, 4, 0, 1>>, + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort, Ack1Bin)), + + %% Recv DATA #2 + Data2Bin = list_to_binary([0, 3, 0, 2 | Block2]), + ?VERIFY({udp, ServerSocket, Host, ClientPort, Data2Bin}, recv(Timeout)), + + %% Send ACK #2 + Ack2Bin = <<0, 4, 0, 2>>, + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort, Ack2Bin)), + + %% Recv DATA #3 + Data3Bin = list_to_binary([0, 3, 0, 3 | Block3]), + ?VERIFY({udp, ServerSocket, Host, ClientPort, Data3Bin}, recv(Timeout)), + + %% Send ACK #3 + Ack3Bin = <<0, 4, 0, 3>>, + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort, Ack3Bin)), + + %% Send ACK #3 AGAIN (pretend that we timed out) + timer:sleep(timer:seconds(1)), + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort, Ack3Bin)), + + %% Recv DATA #4 (the packet that the server think that we have lost) + Data4Bin = list_to_binary([0, 3, 0, 4 | Block4]), + ?VERIFY({udp, ServerSocket, Host, ClientPort, Data4Bin}, recv(Timeout)), + + %% Recv DATA #4 AGAIN (the re-sent package) + ?VERIFY({udp, ServerSocket, Host, ClientPort, Data4Bin}, recv(Timeout)), + + %% Send ACK #4 + Ack4Bin = <<0, 4, 0, 4>>, + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort, Ack4Bin)), + + %% Recv DATA #5 + Data5Bin = list_to_binary([0, 3, 0, 5 | Block5]), + ?VERIFY({udp, ServerSocket, Host, ClientPort, Data5Bin}, recv(Timeout)), + + %% Send ACK #3 which is out of range + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort, Ack3Bin)), + + %% Send ACK #5 + Ack5Bin = <<0, 4, 0, 5>>, + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort, Ack5Bin)), + + %% Recv DATA #6 + Data6Bin = list_to_binary([0, 3, 0, 6 | Block6]), + ?VERIFY({udp, ServerSocket, Host, ClientPort, Data6Bin}, recv(Timeout)), + + %% Send ACK #6 + Ack6Bin = <<0, 4, 0, 6>>, + ?VERIFY(ok, gen_udp:send(ServerSocket, Host, ClientPort, Ack6Bin)), + + %% Close daemon and server sockets + ?VERIFY(ok, gen_udp:close(ServerSocket)), + ?VERIFY(ok, gen_udp:close(DaemonSocket)), + + ?VERIFY({ClientPid, {tftp_client_reply, {ok, Size}}}, recv(Timeout)), + + ?VERIFY(timeout, recv(Timeout)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +reuse_connection(doc) -> + ["Verify that the server can reuse an ongiong connection when same client resends request."]; +reuse_connection(suite) -> + []; +reuse_connection(Config) when is_list(Config) -> + Host = {127, 0, 0, 1}, + {Port, DaemonPid} = ?IGNORE(?START_DAEMON(0, [{debug, all}])), + + RemoteFilename = "reuse_connection.tmp", + BlkSize = 512, + Block1 = lists:duplicate(BlkSize, $1), + Block2 = lists:duplicate(BlkSize div 2, $2), + Blocks = [Block1, Block2], + Blob = list_to_binary(Blocks), + ?VERIFY(ok, file:write_file(RemoteFilename, Blob)), + + Seconds = 3, + Timeout = timer:seconds(Seconds), + ?VERIFY(timeout, recv(0)), + + %% Open socket + {ok, Socket} = ?VERIFY({ok, _}, gen_udp:open(0, [binary, {reuseaddr, true}, {active, true}])), + + ReadList = [0, 1, RemoteFilename, 0, "octet", 0], + Data1Bin = list_to_binary([0, 3, 0, 1 | Block1]), + + %% Send READ + TimeoutList = integer_to_list(Seconds), + Options = ["timeout", 0, TimeoutList, 0], + ReadBin = list_to_binary([ReadList | Options]), + ?VERIFY(ok, gen_udp:send(Socket, Host, Port, ReadBin)), + + %% Send yet another READ for same file + ?VERIFY(ok, gen_udp:send(Socket, Host, Port, ReadBin)), + + %% Recv OACK + OptionAckBin = list_to_binary([0, 6 | Options]), + {udp, _, _, NewPort, _} = ?VERIFY({udp, Socket, Host, _, OptionAckBin}, recv(Timeout)), + + %% Send ACK #0 + Ack0Bin = <<0, 4, 0, 0>>, + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort, Ack0Bin)), + + %% Recv DATA #1 + ?VERIFY({udp, Socket, Host, NewPort, Data1Bin}, recv(Timeout)), + + %% Send ACK #1 + Ack1Bin = <<0, 4, 0, 1>>, + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort, Ack1Bin)), + + %% Recv DATA #2 + Data2Bin = list_to_binary([0, 3, 0, 2 | Block2]), + ?VERIFY({udp, Socket, Host, NewPort, Data2Bin}, recv(Timeout)), + + %% Send ACK #2 + Ack2Bin = <<0, 4, 0, 2>>, + ?VERIFY(ok, gen_udp:send(Socket, Host, NewPort, Ack2Bin)), + + %% Close socket + ?VERIFY(ok, gen_udp:close(Socket)), + + ?VERIFY(timeout, recv(Timeout)), + ?VERIFY(ok, file:delete(RemoteFilename)), + + %% Cleanup + unlink(DaemonPid), + exit(DaemonPid, kill), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Goodies +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +recv(Timeout) -> + receive + Msg -> + Msg + after Timeout -> + timeout + end. diff --git a/lib/inets/test/tftp_test_lib.erl b/lib/inets/test/tftp_test_lib.erl new file mode 100644 index 0000000000..3729309b0e --- /dev/null +++ b/lib/inets/test/tftp_test_lib.erl @@ -0,0 +1,307 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-2009. 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(tftp_test_lib). + +-compile(export_all). + +-include("tftp_test_lib.hrl"). + +%% +%% ----- +%% + +init_per_testcase(_Case, Config) when is_list(Config) -> + io:format("\n ", []), + ?IGNORE(application:stop(inets)), + Config. + +fin_per_testcase(_Case, Config) when is_list(Config) -> + ?IGNORE(application:stop(inets)), + Config. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Infrastructure for test suite +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +error(Actual, Mod, Line) -> + (catch global:send(tftp_global_logger, {failed, Mod, Line})), + log("<ERROR> Bad result: ~p\n", [Actual], Mod, Line), + Label = lists:concat([Mod, "(", Line, ") unexpected result"]), + et:report_event(60, Mod, Mod, Label, + [{line, Mod, Line}, {error, Actual}]), + case global:whereis_name(tftp_test_case_sup) of + undefined -> + ignore; + Pid -> + Fail = #'REASON'{mod = Mod, line = Line, desc = Actual}, + Pid ! {fail, self(), Fail} + end, + Actual. + +log(Format, Args, Mod, Line) -> + case global:whereis_name(tftp_global_logger) of + undefined -> + io:format(user, "~p(~p): " ++ Format, + [Mod, Line] ++ Args); + Pid -> + io:format(Pid, "~p(~p): " ++ Format, + [Mod, Line] ++ Args) + end. + +default_config() -> + []. + +t() -> + t([{?MODULE, all}]). + +t(Cases) -> + t(Cases, default_config()). + +t(Cases, Config) -> + process_flag(trap_exit, true), + Res = lists:flatten(do_test(Cases, Config)), + io:format("Res: ~p\n", [Res]), + display_result(Res), + Res. + +do_test({Mod, Fun}, Config) when is_atom(Mod), is_atom(Fun) -> + case catch apply(Mod, Fun, [suite]) of + [] -> + io:format("Eval: ~p:", [{Mod, Fun}]), + Res = eval(Mod, Fun, Config), + {R, _, _} = Res, + io:format(" ~p\n", [R]), + Res; + + Cases when is_list(Cases) -> + io:format("Expand: ~p ...\n", [{Mod, Fun}]), + Map = fun(Case) when is_atom(Case)-> {Mod, Case}; + (Case) -> Case + end, + do_test(lists:map(Map, Cases), Config); + + {req, _, {conf, Init, Cases, Finish}} -> + case (catch apply(Mod, Init, [Config])) of + Conf when is_list(Conf) -> + io:format("Expand: ~p ...\n", [{Mod, Fun}]), + Map = fun(Case) when is_atom(Case)-> {Mod, Case}; + (Case) -> Case + end, + Res = do_test(lists:map(Map, Cases), Conf), + (catch apply(Mod, Finish, [Conf])), + Res; + + {'EXIT', {skipped, Reason}} -> + io:format(" => skipping: ~p\n", [Reason]), + [{skipped, {Mod, Fun}, Reason}]; + + Error -> + io:format(" => failed: ~p\n", [Error]), + [{failed, {Mod, Fun}, Error}] + end; + + {'EXIT', {undef, _}} -> + io:format("Undefined: ~p\n", [{Mod, Fun}]), + [{nyi, {Mod, Fun}, ok}]; + + Error -> + io:format("Ignoring: ~p: ~p\n", [{Mod, Fun}, Error]), + [{failed, {Mod, Fun}, Error}] + end; +do_test(Mod, Config) when is_atom(Mod) -> + Res = do_test({Mod, all}, Config), + Res; +do_test(Cases, Config) when is_list(Cases) -> + [do_test(Case, Config) || Case <- Cases]; +do_test(Bad, _Config) -> + [{badarg, Bad, ok}]. + +eval(Mod, Fun, Config) -> + TestCase = {?MODULE, Mod, Fun}, + Label = lists:concat(["TEST CASE: ", Fun]), + et:report_event(40, ?MODULE, Mod, Label ++ " started", + [TestCase, Config]), + global:register_name(tftp_test_case_sup, self()), + Flag = process_flag(trap_exit, true), + Config2 = Mod:init_per_testcase(Fun, Config), + Pid = spawn_link(?MODULE, do_eval, [self(), Mod, Fun, Config2]), + R = wait_for_evaluator(Pid, Mod, Fun, Config2, []), + Mod:fin_per_testcase(Fun, Config2), + global:unregister_name(tftp_test_case_sup), + process_flag(trap_exit, Flag), + R. + +wait_for_evaluator(Pid, Mod, Fun, Config, Errors) -> + TestCase = {?MODULE, Mod, Fun}, + Label = lists:concat(["TEST CASE: ", Fun]), + receive + {done, Pid, ok} when Errors == [] -> + et:report_event(40, Mod, ?MODULE, Label ++ " ok", + [TestCase, Config]), + {ok, {Mod, Fun}, Errors}; + {done, Pid, {ok, _}} when Errors == [] -> + et:report_event(40, Mod, ?MODULE, Label ++ " ok", + [TestCase, Config]), + {ok, {Mod, Fun}, Errors}; + {done, Pid, Fail} -> + et:report_event(20, Mod, ?MODULE, Label ++ " failed", + [TestCase, Config, {return, Fail}, Errors]), + {failed, {Mod,Fun}, Fail}; + {'EXIT', Pid, {skipped, Reason}} -> + et:report_event(20, Mod, ?MODULE, Label ++ " skipped", + [TestCase, Config, {skipped, Reason}]), + {skipped, {Mod, Fun}, Errors}; + {'EXIT', Pid, Reason} -> + et:report_event(20, Mod, ?MODULE, Label ++ " crashed", + [TestCase, Config, {'EXIT', Reason}]), + {crashed, {Mod, Fun}, [{'EXIT', Reason} | Errors]}; + {fail, Pid, Reason} -> + wait_for_evaluator(Pid, Mod, Fun, Config, Errors ++ [Reason]) + end. + +do_eval(ReplyTo, Mod, Fun, Config) -> + case (catch apply(Mod, Fun, [Config])) of + {'EXIT', {skipped, Reason}} -> + ReplyTo ! {'EXIT', self(), {skipped, Reason}}; + Other -> + ReplyTo ! {done, self(), Other} + end, + unlink(ReplyTo), + exit(shutdown). + +display_result([]) -> + io:format("OK\n", []); +display_result(Res) when is_list(Res) -> + Ok = [MF || {ok, MF, _} <- Res], + Nyi = [MF || {nyi, MF, _} <- Res], + Skipped = [{MF, Reason} || {skipped, MF, Reason} <- Res], + Failed = [{MF, Reason} || {failed, MF, Reason} <- Res], + Crashed = [{MF, Reason} || {crashed, MF, Reason} <- Res], + display_summary(Ok, Nyi, Skipped, Failed, Crashed), + display_skipped(Skipped), + display_failed(Failed), + display_crashed(Crashed). + +display_summary(Ok, Nyi, Skipped, Failed, Crashed) -> + io:format("\nTest case summary:\n", []), + display_summary(Ok, "successful"), + display_summary(Nyi, "not yet implemented"), + display_summary(Skipped, "skipped"), + display_summary(Failed, "failed"), + display_summary(Crashed, "crashed"), + io:format("\n", []). + +display_summary(Res, Info) -> + io:format(" ~w test cases ~s\n", [length(Res), Info]). + +display_skipped([]) -> + ok; +display_skipped(Skipped) -> + io:format("Skipped test cases:\n", []), + F = fun({MF, Reason}) -> io:format(" ~p => ~p\n", [MF, Reason]) end, + lists:foreach(F, Skipped), + io:format("\n", []). + + +display_failed([]) -> + ok; +display_failed(Failed) -> + io:format("Failed test cases:\n", []), + F = fun({MF, Reason}) -> io:format(" ~p => ~p\n", [MF, Reason]) end, + lists:foreach(F, Failed), + io:format("\n", []). + +display_crashed([]) -> + ok; +display_crashed(Crashed) -> + io:format("Crashed test cases:\n", []), + F = fun({MF, Reason}) -> io:format(" ~p => ~p\n", [MF, Reason]) end, + lists:foreach(F, Crashed), + io:format("\n", []). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% generic callback +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-record(generic_state, {state, prepare, open, read, write, abort}). + +prepare(Peer, Access, LocalFilename, Mode, SuggestedOptions, Initial) when is_list(Initial) -> + State = lookup_option(state, mandatory, Initial), + Prepare = lookup_option(prepare, mandatory, Initial), + Open = lookup_option(open, mandatory, Initial), + Read = lookup_option(read, mandatory, Initial), + Write = lookup_option(write, mandatory, Initial), + Abort = lookup_option(abort, mandatory, Initial), + case Prepare(Peer, Access, LocalFilename, Mode, SuggestedOptions, State) of + {ok, AcceptedOptions, NewState} -> + {ok, + AcceptedOptions, + #generic_state{state = NewState, + prepare = Prepare, + open = Open, + read = Read, + write = Write, + abort = Abort}}; + Other -> + Other + end. + +open(Peer, Access, LocalFilename, Mode, SuggestedOptions, Initial) when is_list(Initial) -> + case prepare(Peer, Access, LocalFilename, Mode, SuggestedOptions, Initial) of + {ok, SuggestedOptions2, GenericState} -> + open(Peer, Access, LocalFilename, Mode, SuggestedOptions2, GenericState); + Other -> + Other + end; +open(Peer, Access, LocalFilename, Mode, SuggestedOptions, #generic_state{state = State, open = Open} = GenericState) -> + case Open(Peer, Access, LocalFilename, Mode, SuggestedOptions, State) of + {ok, SuggestedOptions2, NewState} -> + {ok, SuggestedOptions2, GenericState#generic_state{state = NewState}}; + Other -> + Other + end. + +read(#generic_state{state = State, read = Read} = GenericState) -> + case Read(State) of + {more, DataBlock, NewState} -> + {more, DataBlock, GenericState#generic_state{state = NewState}}; + Other -> + Other + end. + +write(DataBlock, #generic_state{state = State, write = Write} = GenericState) -> + case Write(DataBlock, State) of + {more, NewState} -> + {more, GenericState#generic_state{state = NewState}}; + Other -> + Other + end. + +abort(Code, Text, #generic_state{state = State, abort = Abort}) -> + Abort(Code, Text, State). + +lookup_option(Key, Default, Options) -> + case lists:keysearch(Key, 1, Options) of + {value, {_, Val}} -> + Val; + false -> + Default + end. + diff --git a/lib/inets/test/tftp_test_lib.hrl b/lib/inets/test/tftp_test_lib.hrl new file mode 100644 index 0000000000..da4b065976 --- /dev/null +++ b/lib/inets/test/tftp_test_lib.hrl @@ -0,0 +1,43 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-2009. 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% +%% + +-record('REASON', {mod, line, desc}). + +-define(LOG(Format, Args), + tftp_test_lib:log(Format, Args, ?MODULE, ?LINE)). + +-define(ERROR(Reason), + tftp_test_lib:error(Reason, ?MODULE, ?LINE)). + +-define(VERIFY(Expected, Expr), + fun() -> + AcTuAlReS = (catch (Expr)), + case AcTuAlReS of + Expected -> ?LOG("Ok, ~p\n", [AcTuAlReS]); + _ -> ?ERROR(AcTuAlReS) + end, + AcTuAlReS + end()). + +-define(IGNORE(Expr), + fun() -> + AcTuAlReS = (catch (Expr)), + ?LOG("Ok, ~p\n", [AcTuAlReS]), + AcTuAlReS + end()). |