aboutsummaryrefslogtreecommitdiffstats
path: root/lib/inets/test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/inets/test')
-rw-r--r--lib/inets/test/Makefile343
-rw-r--r--lib/inets/test/ftp_SUITE.erl143
-rw-r--r--lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel18
-rw-r--r--lib/inets/test/ftp_format_SUITE.erl341
-rw-r--r--lib/inets/test/ftp_freebsd_x86_test.erl153
l---------lib/inets/test/ftp_internal.hrl1
-rw-r--r--lib/inets/test/ftp_linux_ppc_test.erl151
-rw-r--r--lib/inets/test/ftp_linux_x86_test.erl160
-rw-r--r--lib/inets/test/ftp_macosx_ppc_test.erl152
-rw-r--r--lib/inets/test/ftp_macosx_x86_test.erl152
-rw-r--r--lib/inets/test/ftp_netbsd_x86_test.erl152
-rw-r--r--lib/inets/test/ftp_openbsd_x86_test.erl151
-rw-r--r--lib/inets/test/ftp_solaris10_sparc_test.erl154
-rw-r--r--lib/inets/test/ftp_solaris10_x86_test.erl155
-rw-r--r--lib/inets/test/ftp_solaris8_sparc_test.erl152
-rw-r--r--lib/inets/test/ftp_solaris9_sparc_test.erl151
-rw-r--r--lib/inets/test/ftp_suite_lib.erl1599
-rw-r--r--lib/inets/test/ftp_ticket_test.erl51
-rw-r--r--lib/inets/test/ftp_windows_2003_server_test.erl152
-rw-r--r--lib/inets/test/ftp_windows_xp_test.erl150
-rw-r--r--lib/inets/test/http_format_SUITE.erl591
l---------lib/inets/test/http_internal.hrl1
-rw-r--r--lib/inets/test/httpc_SUITE.erl3163
l---------lib/inets/test/httpc_SUITE_data/Makefile.src1
l---------lib/inets/test/httpc_SUITE_data/cgi_echo.c1
-rw-r--r--lib/inets/test/httpc_SUITE_data/dummy.html12
-rw-r--r--lib/inets/test/httpc_SUITE_data/empty.html0
-rw-r--r--lib/inets/test/httpc_SUITE_data/mime.types465
-rw-r--r--lib/inets/test/httpc_SUITE_data/redirect.html10
-rw-r--r--lib/inets/test/httpc_SUITE_data/ssl_client_cert.pem22
-rw-r--r--lib/inets/test/httpc_SUITE_data/ssl_server_cert.pem22
-rw-r--r--lib/inets/test/httpc_cookie_SUITE.erl451
l---------lib/inets/test/httpc_internal.hrl1
-rw-r--r--lib/inets/test/httpd_1_1.erl502
-rw-r--r--lib/inets/test/httpd_SUITE.erl2081
-rw-r--r--lib/inets/test/httpd_SUITE_data/Makefile.src14
-rw-r--r--lib/inets/test/httpd_SUITE_data/cgi_echo.c97
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/auth/group3
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/auth/passwd4
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/cgi-bin/printenv.bat9
-rwxr-xr-xlib/inets/test/httpd_SUITE_data/server_root/cgi-bin/printenv.sh6
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/conf/8080.conf79
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/conf/8888.conf63
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf268
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/conf/mime.types465
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/conf/ssl.conf66
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/config.shtml70
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/dets_open/dummy.html10
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/dets_secret/dummy.html10
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/dets_secret/top_secret/index.html9
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/echo.shtml35
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/exec.shtml30
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/flastmod.shtml29
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/fsize.shtml29
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/include.shtml33
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/index.html25
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/last_modified.html22
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/misc/friedrich.html7
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/misc/oech.html4
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/misc/welcome.html1
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/mnesia_open/dummy.html10
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/mnesia_secret/dummy.html10
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/mnesia_secret/top_secret/index.html9
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/open/dummy.html10
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/secret/dummy.html10
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/htdocs/secret/top_secret/index.html9
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/README161
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/a.gifbin0 -> 246 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/alert.black.gifbin0 -> 242 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/alert.red.gifbin0 -> 247 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/apache_pb.gifbin0 -> 2326 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/back.gifbin0 -> 216 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/ball.gray.gifbin0 -> 233 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/ball.red.gifbin0 -> 205 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/binary.gifbin0 -> 246 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/binhex.gifbin0 -> 246 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/blank.gifbin0 -> 148 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/bomb.gifbin0 -> 308 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/box1.gifbin0 -> 251 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/box2.gifbin0 -> 268 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/broken.gifbin0 -> 247 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/burst.gifbin0 -> 235 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/button1.gifbin0 -> 755 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/button10.gifbin0 -> 781 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/button2.gifbin0 -> 785 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/button3.gifbin0 -> 745 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/button4.gifbin0 -> 786 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/button5.gifbin0 -> 780 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/button6.gifbin0 -> 791 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/button7.gifbin0 -> 796 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/button8.gifbin0 -> 784 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/button9.gifbin0 -> 784 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/buttonl.gifbin0 -> 587 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/buttonr.gifbin0 -> 576 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/c.gifbin0 -> 242 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/comp.blue.gifbin0 -> 251 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/comp.gray.gifbin0 -> 246 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/compressed.gifbin0 -> 1038 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/continued.gifbin0 -> 214 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/dir.gifbin0 -> 225 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/down.gifbin0 -> 163 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/dvi.gifbin0 -> 238 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/f.gifbin0 -> 236 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/folder.gifbin0 -> 225 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/folder.open.gifbin0 -> 242 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/folder.sec.gifbin0 -> 243 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/forward.gifbin0 -> 219 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/generic.gifbin0 -> 221 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/generic.red.gifbin0 -> 220 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/generic.sec.gifbin0 -> 249 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/hand.right.gifbin0 -> 217 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/hand.up.gifbin0 -> 223 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/htdig.gifbin0 -> 1822 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/icon.sheet.gifbin0 -> 11977 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/image1.gifbin0 -> 274 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/image2.gifbin0 -> 309 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/image3.gifbin0 -> 286 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/index.gifbin0 -> 268 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/layout.gifbin0 -> 276 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/left.gifbin0 -> 172 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/link.gifbin0 -> 249 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/movie.gifbin0 -> 243 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/p.gifbin0 -> 237 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/patch.gifbin0 -> 251 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/pdf.gifbin0 -> 249 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/pie0.gifbin0 -> 188 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/pie1.gifbin0 -> 198 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/pie2.gifbin0 -> 198 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/pie3.gifbin0 -> 191 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/pie4.gifbin0 -> 193 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/pie5.gifbin0 -> 189 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/pie6.gifbin0 -> 186 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/pie7.gifbin0 -> 185 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/pie8.gifbin0 -> 173 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/portal.gifbin0 -> 254 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/poweredby.gifbin0 -> 2748 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/ps.gifbin0 -> 244 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/quill.gifbin0 -> 267 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/right.gifbin0 -> 172 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/screw1.gifbin0 -> 258 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/screw2.gifbin0 -> 263 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/script.gifbin0 -> 242 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/sound1.gifbin0 -> 248 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/sound2.gifbin0 -> 221 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/sphere1.gifbin0 -> 285 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/sphere2.gifbin0 -> 264 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/star.gifbin0 -> 89 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/star_blank.gifbin0 -> 53 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/tar.gifbin0 -> 243 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/tex.gifbin0 -> 251 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/text.gifbin0 -> 229 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/transfer.gifbin0 -> 242 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/unknown.gifbin0 -> 245 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/up.gifbin0 -> 164 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/uu.gifbin0 -> 236 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/uuencoded.gifbin0 -> 236 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/world1.gifbin0 -> 228 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/icons/world2.gifbin0 -> 261 bytes
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip1
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_client.pem22
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_server.pem22
-rw-r--r--lib/inets/test/httpd_basic_SUITE.erl281
-rw-r--r--lib/inets/test/httpd_block.erl299
-rw-r--r--lib/inets/test/httpd_load.erl99
-rw-r--r--lib/inets/test/httpd_mod.erl947
-rw-r--r--lib/inets/test/httpd_poll.erl496
-rw-r--r--lib/inets/test/httpd_test_data/server_root/auth/group3
-rw-r--r--lib/inets/test/httpd_test_data/server_root/auth/passwd4
-rw-r--r--lib/inets/test/httpd_test_data/server_root/cgi-bin/printenv.bat9
-rwxr-xr-xlib/inets/test/httpd_test_data/server_root/cgi-bin/printenv.sh6
-rw-r--r--lib/inets/test/httpd_test_data/server_root/conf/8080.conf79
-rw-r--r--lib/inets/test/httpd_test_data/server_root/conf/8888.conf63
-rw-r--r--lib/inets/test/httpd_test_data/server_root/conf/httpd.conf268
-rw-r--r--lib/inets/test/httpd_test_data/server_root/conf/mime.types465
-rw-r--r--lib/inets/test/httpd_test_data/server_root/conf/ssl.conf66
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/config.shtml70
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/dets_open/dummy.html10
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/dets_secret/dummy.html10
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/dets_secret/top_secret/index.html9
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/echo.shtml35
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/exec.shtml30
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/flastmod.shtml29
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/fsize.shtml29
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/include.shtml33
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/index.html25
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/last_modified.html22
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/misc/friedrich.html7
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/misc/oech.html4
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/misc/welcome.html1
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/mnesia_open/dummy.html10
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/mnesia_secret/dummy.html10
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/mnesia_secret/top_secret/index.html9
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/open/dummy.html10
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/secret/dummy.html10
-rw-r--r--lib/inets/test/httpd_test_data/server_root/htdocs/secret/top_secret/index.html9
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/README161
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/a.gifbin0 -> 246 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/alert.black.gifbin0 -> 242 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/alert.red.gifbin0 -> 247 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/apache_pb.gifbin0 -> 2326 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/back.gifbin0 -> 216 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/ball.gray.gifbin0 -> 233 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/ball.red.gifbin0 -> 205 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/binary.gifbin0 -> 246 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/binhex.gifbin0 -> 246 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/blank.gifbin0 -> 148 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/bomb.gifbin0 -> 308 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/box1.gifbin0 -> 251 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/box2.gifbin0 -> 268 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/broken.gifbin0 -> 247 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/burst.gifbin0 -> 235 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/button1.gifbin0 -> 755 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/button10.gifbin0 -> 781 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/button2.gifbin0 -> 785 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/button3.gifbin0 -> 745 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/button4.gifbin0 -> 786 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/button5.gifbin0 -> 780 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/button6.gifbin0 -> 791 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/button7.gifbin0 -> 796 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/button8.gifbin0 -> 784 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/button9.gifbin0 -> 784 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/buttonl.gifbin0 -> 587 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/buttonr.gifbin0 -> 576 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/c.gifbin0 -> 242 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/comp.blue.gifbin0 -> 251 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/comp.gray.gifbin0 -> 246 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/compressed.gifbin0 -> 1038 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/continued.gifbin0 -> 214 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/dir.gifbin0 -> 225 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/down.gifbin0 -> 163 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/dvi.gifbin0 -> 238 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/f.gifbin0 -> 236 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/folder.gifbin0 -> 225 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/folder.open.gifbin0 -> 242 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/folder.sec.gifbin0 -> 243 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/forward.gifbin0 -> 219 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/generic.gifbin0 -> 221 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/generic.red.gifbin0 -> 220 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/generic.sec.gifbin0 -> 249 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/hand.right.gifbin0 -> 217 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/hand.up.gifbin0 -> 223 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/htdig.gifbin0 -> 1822 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/icon.sheet.gifbin0 -> 11977 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/image1.gifbin0 -> 274 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/image2.gifbin0 -> 309 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/image3.gifbin0 -> 286 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/index.gifbin0 -> 268 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/layout.gifbin0 -> 276 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/left.gifbin0 -> 172 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/link.gifbin0 -> 249 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/movie.gifbin0 -> 243 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/p.gifbin0 -> 237 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/patch.gifbin0 -> 251 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/pdf.gifbin0 -> 249 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/pie0.gifbin0 -> 188 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/pie1.gifbin0 -> 198 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/pie2.gifbin0 -> 198 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/pie3.gifbin0 -> 191 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/pie4.gifbin0 -> 193 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/pie5.gifbin0 -> 189 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/pie6.gifbin0 -> 186 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/pie7.gifbin0 -> 185 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/pie8.gifbin0 -> 173 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/portal.gifbin0 -> 254 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/poweredby.gifbin0 -> 2748 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/ps.gifbin0 -> 244 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/quill.gifbin0 -> 267 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/right.gifbin0 -> 172 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/screw1.gifbin0 -> 258 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/screw2.gifbin0 -> 263 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/script.gifbin0 -> 242 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/sound1.gifbin0 -> 248 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/sound2.gifbin0 -> 221 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/sphere1.gifbin0 -> 285 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/sphere2.gifbin0 -> 264 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/star.gifbin0 -> 89 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/star_blank.gifbin0 -> 53 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/tar.gifbin0 -> 243 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/tex.gifbin0 -> 251 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/text.gifbin0 -> 229 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/transfer.gifbin0 -> 242 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/unknown.gifbin0 -> 245 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/up.gifbin0 -> 164 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/uu.gifbin0 -> 236 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/uuencoded.gifbin0 -> 236 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/world1.gifbin0 -> 228 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/icons/world2.gifbin0 -> 261 bytes
-rw-r--r--lib/inets/test/httpd_test_data/server_root/logs/Dummy_File_Needed_By_WinZip1
-rw-r--r--lib/inets/test/httpd_test_data/server_root/ssl/ssl_client.pem22
-rw-r--r--lib/inets/test/httpd_test_data/server_root/ssl/ssl_server.pem22
-rw-r--r--lib/inets/test/httpd_test_lib.erl379
-rw-r--r--lib/inets/test/httpd_time_test.erl500
-rw-r--r--lib/inets/test/inets.config1
-rw-r--r--lib/inets/test/inets.spec2
-rw-r--r--lib/inets/test/inets.spec.vxworks5
-rw-r--r--lib/inets/test/inets_SUITE.erl583
-rw-r--r--lib/inets/test/inets_SUITE_data/.gitignore0
-rw-r--r--lib/inets/test/inets_app_test.erl296
-rw-r--r--lib/inets/test/inets_appup_test.erl336
l---------lib/inets/test/inets_internal.hrl1
-rw-r--r--lib/inets/test/inets_sup_SUITE.erl414
-rw-r--r--lib/inets/test/inets_sup_SUITE_data/mime.types3
-rw-r--r--lib/inets/test/inets_sup_SUITE_data/simple.conf6
-rw-r--r--lib/inets/test/inets_test_lib.erl355
-rw-r--r--lib/inets/test/inets_test_lib.hrl104
-rw-r--r--lib/inets/test/rules.mk59
-rw-r--r--lib/inets/test/tftp_SUITE.erl903
-rw-r--r--lib/inets/test/tftp_test_lib.erl307
-rw-r--r--lib/inets/test/tftp_test_lib.hrl43
309 files changed, 21416 insertions, 0 deletions
diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile
new file mode 100644
index 0000000000..668752da9e
--- /dev/null
+++ b/lib/inets/test/Makefile
@@ -0,0 +1,343 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+#
+# 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)
+
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(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)
+
+SOURCE = $(ERL_FILES) $(HRL_FILES)
+
+TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+INETS_SPECS = inets.spec inets.spec.vxworks
+INETS_FILES = inets.config $(INETS_SPECS)
+
+# SUB_SUITES = \
+# inets_sup_suite \
+# inets_httpd_suite \
+# inets_httpc_suite \
+# inets_ftp_suite \
+# inets_tftp_suite
+
+INETS_DATADIRS = inets_SUITE_data inets_sup_SUITE_data
+HTTPD_DATADIRS = httpd_test_data httpd_SUITE_data
+HTTPC_DATADIRS = httpc_SUITE_data
+FTP_DATADIRS = ftp_SUITE_data
+
+DATADIRS = $(INETS_DATADIRS) $(HTTPD_DATADIRS) $(HTTPC_DATADIRS) $(FTP_DATADIRS)
+
+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
+# ----------------------------------------------------
+
+RELTESTSYSDIR = $(RELEASE_PATH)/inets_test
+RELTESTSYSALLDATADIR = $(RELTESTSYSDIR)/all_SUITE_data
+RELTESTSYSBINDIR = $(RELTESTSYSALLDATADIR)/bin
+
+
+# ----------------------------------------------------
+# 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
+# ----------------------------------------------------
+
+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
+ @for d in $(DATADIRS); do \
+ echo "installing data dir $$d"; \
+ echo $$d/TAR.exclude2 > $$d/TAR.exclude2; \
+ cat $$d/TAR.exclude >> $$d/TAR.exclude2; \
+ find $$d -name '*.contrib*' >> $$d/TAR.exclude2; \
+ find $$d -name '*.keep*' >> $$d/TAR.exclude2; \
+ find $$d -name '*.mkelem*' >> $$d/TAR.exclude2; \
+ find $$d -name '*~' >> $$d/TAR.exclude2; \
+ find $$d -name 'erl_crash.dump' >> $$d/TAR.exclude2; \
+ find $$d -name 'core' >> $$d/TAR.exclude2; \
+ find $$d -name '.cmake.state' >> $$d/TAR.exclude2; \
+ tar cfX - $$d/TAR.exclude2 $$d | (cd $(RELSYSDIR)/test; tar xf -); \
+ done
+
+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_DIR) $(RELTESTSYSBINDIR)
+ chmod -f -R +x $(RELTESTSYSBINDIR)
+ $(INSTALL_DIR) $(RELTESTSYSALLDATADIR)/win32/lib
+
+release_docs_spec:
+
+info:
+ @echo "MAKE_EMAKE = $(MAKE_EMAKE)"
+ @echo "EMAKEFILE = $(EMAKEFILE)"
+ @echo "BUILDTARGET = $(BUILDTARGET)"
+ @echo ""
+ @echo "MODULES = $(MODULES)"
+ @echo "ERL_FILES = $(ERL_FILES)"
+ @echo "SOURCE = $(SOURCE)"
+ @echo "TARGET_FILES = $(TARGET_FILES)"
+ @echo ""
+ @echo "INETS_SPECS = $(INETS_SPECS)"
+ @echo "INETS_FILES = $(INETS_FILES)"
+ @echo ""
+ @echo "RELEASE_PATH = $(RELEASE_PATH)"
+ @echo "RELSYSDIR = $(RELSYSDIR)"
+ @echo "RELTESTSYSDIR = $(RELTESTSYSDIR)"
+ @echo "RELTESTSYSALLDATADIR = $(RELTESTSYSALLDATADIR)"
+ @echo "RELTESTSYSBINDIR = $(RELTESTSYSBINDIR)"
+ @echo ""
+ @echo "DATADIRS = $(DATADIRS)"
+ @echo "REL_DATADIRS = $(REL_DATADIRS)"
+ @echo ""
+ @echo "INETS_DATA_DIR = $(INETS_DATA_DIR)"
+ @echo "INETS_PRIV_DIR = $(INETS_PRIV_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/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/ftpd_hosts.skel b/lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel
new file mode 100644
index 0000000000..75096ce687
--- /dev/null
+++ b/lib/inets/test/ftp_SUITE_data/ftpd_hosts.skel
@@ -0,0 +1,18 @@
+%% Add a host name in the appropriate list
+%% Each "platform" contains a list of hostnames (a string) that can
+%% be used for testing the ftp client.
+%% The definition below are an example!!
+[{solaris8_sparc, ["solaris8_sparc_dummy1", "solaris8_sparc_dummy2"]},
+ {solaris9_sparc, ["solaris9_sparc_dummy1"]},
+ {solaris10_sparc, ["solaris10_sparc_dummy1"]},
+ {solaris10_x86, ["solaris10_x86_dummy1", "solaris10_x86_dummy2"]},
+ {linux_x86, ["linux_x86_dummy1", "linux_x86_dummy2"]},
+ {linux_ppc, ["linux_ppc_dummy1"]},
+ {macosx_ppc, ["macosx_ppc_dummy1"]},
+ {macosx_x86, ["macosx_x86_dummy1", "macosx_x86_dummy2"]},
+ {openbsd_x86, []},
+ {freebsd_x86, ["freebsd_x86_dummy1"]},
+ {netbsd_x86, []},
+ {windows_xp, []},
+ {windows_2003_server, ["win2003_dummy1"]},
+ {ticket_test, ["solaris8_x86_dummy1", "linux_x86_dummy1"]}].
diff --git a/lib/inets/test/ftp_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..5566d4feaa
--- /dev/null
+++ b/lib/inets/test/ftp_macosx_x86_test.erl
@@ -0,0 +1,152 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(ftp_macosx_x86_test).
+
+-compile(export_all).
+
+-include("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([{wildcard_support, false} | X]).
+passive_rename(X) -> ?LIB_MOD:passive_rename(X).
+passive_delete(X) -> ?LIB_MOD:passive_delete(X).
+passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
+passive_send(X) -> ?LIB_MOD:passive_send(X).
+passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
+passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
+passive_append(X) -> ?LIB_MOD:passive_append(X).
+passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
+passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
+passive_recv(X) -> ?LIB_MOD:passive_recv(X).
+passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
+passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
+passive_type(X) -> ?LIB_MOD:passive_type(X).
+passive_quote(X) -> ?LIB_MOD:passive_quote(X).
+passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
+active_user(X) -> ?LIB_MOD:active_user(X).
+active_pwd(X) -> ?LIB_MOD:active_pwd(X).
+active_cd(X) -> ?LIB_MOD:active_cd(X).
+active_lcd(X) -> ?LIB_MOD:active_lcd(X).
+active_ls(X) -> ?LIB_MOD:active_ls(X).
+active_nlist(X) -> ?LIB_MOD:active_nlist([{wildcard_support, false} | X]).
+active_rename(X) -> ?LIB_MOD:active_rename(X).
+active_delete(X) -> ?LIB_MOD:active_delete(X).
+active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
+active_send(X) -> ?LIB_MOD:active_send(X).
+active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
+active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
+active_append(X) -> ?LIB_MOD:active_append(X).
+active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
+active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
+active_recv(X) -> ?LIB_MOD:active_recv(X).
+active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
+active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
+active_type(X) -> ?LIB_MOD:active_type(X).
+active_quote(X) -> ?LIB_MOD:active_quote(X).
+active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
+progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
+progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
+
+fin(Config) ->
+ ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_netbsd_x86_test.erl b/lib/inets/test/ftp_netbsd_x86_test.erl
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..b3c4ff2657
--- /dev/null
+++ b/lib/inets/test/ftp_suite_lib.erl
@@ -0,0 +1,1599 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(ftp_suite_lib).
+
+
+-include("test_server.hrl").
+-include("test_server_line.hrl").
+-include("inets_test_lib.hrl").
+
+%% Test server specific exports
+% -export([init_per_testcase/2, end_per_testcase/2]).
+
+-compile(export_all).
+
+
+-record(progress, {
+ current = 0,
+ total
+ }).
+
+
+
+-define(FTP_USER, "anonymous").
+-define(FTP_PASS, passwd()).
+-define(FTP_PORT, 21).
+
+-define(BAD_HOST, "badhostname").
+-define(BAD_USER, "baduser").
+-define(BAD_DIR, "baddirectory").
+
+-ifdef(ftp_debug_client).
+-define(ftp_open(Host, Flags),
+ do_ftp_open(Host, [debug, {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 ->
+ ftpd_hosts(data_dir(Config));
+ H ->
+ H
+ end,
+ p("ftpd_init -> "
+ "~n Hosts: ~p"
+ "~n Config: ~p"
+ "~n FtpdTag: ~p", [Hosts, Config, FtpdTag]),
+ %% Get the first host that actually have a running FTP server
+ case lists:keysearch(FtpdTag, 1, Hosts) of
+ {value, {_, TagHosts}} when is_list(TagHosts) ->
+ inets:start(),
+ case (catch get_ftpd_host(TagHosts)) of
+ {ok, Host} ->
+ inets:stop(),
+ [{ftp_remote_host, Host}|Config];
+ _ ->
+ inets:stop(),
+ Reason = lists:flatten(
+ io_lib:format("Could not find a valid "
+ "FTP server for ~p (~p)",
+ [FtpdTag, TagHosts])),
+ {skip, Reason}
+ end;
+ _ ->
+ Reason = lists:flatten(
+ io_lib:format("No host(s) running FTPD server "
+ "for ~p", [FtpdTag])),
+ {skip, Reason}
+ end.
+
+ftpd_fin(Config) ->
+ lists:keydelete(ftp_remote_host, 1, Config).
+
+get_ftpd_host([]) ->
+ {error, no_host};
+get_ftpd_host([Host|Hosts]) ->
+ p("get_ftpd_host -> entry with"
+ "~n Host: ~p"
+ "~n", [Host]),
+ case (catch ftp:open({option_list,
+ [{host, Host}, {port, ?FTP_PORT},
+ {timeout, 20000}]})) of
+ {ok, Pid} ->
+ (catch ftp:close(Pid)),
+ {ok, Host};
+ _ ->
+ get_ftpd_host(Hosts)
+ end.
+
+
+%%--------------------------------------------------------------------
+
+dirty_select_ftpd_host(Config) ->
+ Hosts =
+ case ?config(ftpd_hosts, Config) of
+ undefined ->
+ ftpd_hosts(data_dir(Config));
+ H ->
+ H
+ end,
+ dirty_select_ftpd_host2(Hosts).
+
+dirty_select_ftpd_host2([]) ->
+ throw({error, not_found});
+dirty_select_ftpd_host2([{PlatformTag, Hosts} | PlatformHosts]) ->
+ case dirty_select_ftpd_host3(Hosts) of
+ none ->
+ dirty_select_ftpd_host2(PlatformHosts);
+ {ok, Host} ->
+ {PlatformTag, Host}
+ end.
+
+dirty_select_ftpd_host3([]) ->
+ none;
+dirty_select_ftpd_host3([Host|Hosts]) when is_list(Host) ->
+ case dirty_select_ftpd_host4(Host) of
+ true ->
+ {ok, Host};
+ false ->
+ dirty_select_ftpd_host3(Hosts)
+ end;
+dirty_select_ftpd_host3([_|Hosts]) ->
+ dirty_select_ftpd_host3(Hosts).
+
+%% This is a very simple and dirty test that there is a
+%% (FTP) deamon on the other end.
+dirty_select_ftpd_host4(Host) ->
+ Port = 21,
+ IpFam = inet,
+ Opts = [IpFam, binary, {packet, 0}, {active, false}],
+ Timeout = ?SECS(5),
+ case gen_tcp:connect(Host, Port, Opts, Timeout) of
+ {ok, Sock} ->
+ gen_tcp:close(Sock),
+ true;
+ _Error ->
+ false
+ end.
+
+
+%%--------------------------------------------------------------------
+
+test_filenames() ->
+ {ok, Host} = inet:gethostname(),
+ File = Host ++ "_ftp_test.txt",
+ NewFile = "new_" ++ File,
+ {File, NewFile}.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(Case, Config) -> Config
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Initiation before each test case
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_testcase(Case, Config)
+ when (Case =:= open) orelse (Case =:= open_port) ->
+ 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),
+ WildcardSupport = ?config(wildcard_support, Config),
+ do_nlist(Pid, WildcardSupport).
+
+
+%%-------------------------------------------------------------------------
+
+passive_rename(doc) ->
+ ["Transfer a file to the server, and rename it; then remove it."];
+passive_rename(suite) ->
+ [];
+passive_rename(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_rename(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+passive_delete(doc) ->
+ ["Transfer a file to the server, and then delete it"];
+passive_delete(suite) ->
+ [];
+passive_delete(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_delete(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+passive_mkdir(doc) ->
+ ["Make a remote directory, cd to it, go to parent directory, and "
+ "remove the directory."];
+passive_mkdir(suite) ->
+ [];
+passive_mkdir(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_mkdir(Pid).
+
+
+%%-------------------------------------------------------------------------
+
+passive_send(doc) ->
+ ["Create a local file in priv_dir; open an ftp connection to a host; "
+ "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
+ "priv_dir; send the file; get a directory listing and check that "
+ "the file is on the list;, delete the remote file; get another listing "
+ "and check that the file is not on the list; close the session; "
+ "delete the local file."];
+passive_send(suite) ->
+ [];
+passive_send(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_send(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+passive_append(doc) ->
+ ["Create a local file in priv_dir; open an ftp connection to a host; "
+ "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
+ "priv_dir; append the file to a file at the remote side that not exits"
+ "this will create the file at the remote side. Then it append the file "
+ "again. When this is done it recive the remote file and control that"
+ "the content is doubled in it.After that it will remove the files"];
+passive_append(suite) ->
+ [];
+passive_append(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_append(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+passive_send_bin(doc) ->
+ ["Open a connection to a host; cd to the directory \"incoming\"; "
+ "send a binary; remove file; close the connection."];
+passive_send_bin(suite) ->
+ [];
+passive_send_bin(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_send_bin(Pid, Config).
+
+%%-------------------------------------------------------------------------
+
+passive_append_bin(doc) ->
+ ["Open a connection to a host; cd to the directory \"incoming\"; "
+ "append a binary twice; get the file and compare the content"
+ "remove file; close the connection."];
+passive_append_bin(suite) ->
+ [];
+passive_append_bin(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_append_bin(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+passive_send_chunk(doc) ->
+ ["Open a connection to a host; cd to the directory \"incoming\"; "
+ "send chunks; remove file; close the connection."];
+passive_send_chunk(suite) ->
+ [];
+passive_send_chunk(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_send_chunk(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+passive_append_chunk(doc) ->
+ ["Open a connection to a host; cd to the directory \"incoming\"; "
+ "append chunks;control content remove file; close the connection."];
+passive_append_chunk(suite) ->
+ [];
+passive_append_chunk(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_append_chunk(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+passive_recv(doc) ->
+ ["Create a local file and transfer it to the remote host into the "
+ "the \"incoming\" directory, remove "
+ "the local file. Then open a new connection; cd to \"incoming\", "
+ "lcd to the private directory; receive the file; delete the "
+ "remote file; close connection; check that received file is in "
+ "the correct directory; cleanup." ];
+passive_recv(suite) ->
+ [];
+passive_recv(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_recv(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+passive_recv_bin(doc) ->
+ ["Send a binary to the remote host; and retreive "
+ "the file; then remove the file."];
+passive_recv_bin(suite) ->
+ [];
+passive_recv_bin(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_recv_bin(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+passive_recv_chunk(doc) ->
+ ["Send a binary to the remote host; Connect again, and retreive "
+ "the file; then remove the file."];
+passive_recv_chunk(suite) ->
+ [];
+passive_recv_chunk(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_recv_chunk(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+passive_type(doc) ->
+ ["Test that we can change btween ASCCI and binary transfer mode"];
+passive_type(suite) ->
+ [];
+passive_type(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_type(Pid).
+
+
+%%-------------------------------------------------------------------------
+
+passive_quote(doc) ->
+ [""];
+passive_quote(suite) ->
+ [];
+passive_quote(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_quote(Pid).
+
+
+%%-------------------------------------------------------------------------
+
+passive_ip_v6_disabled(doc) ->
+ ["Test ipv4 command PASV"];
+passive_ip_v6_disabled(suite) ->
+ [];
+passive_ip_v6_disabled(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_send(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+active_user(doc) ->
+ ["Open an ftp connection to a host, and logon as anonymous ftp."];
+active_user(suite) ->
+ [];
+active_user(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_user(Pid).
+
+
+%%-------------------------------------------------------------------------
+
+active_pwd(doc) ->
+ ["Test ftp:pwd/1 & ftp:lpwd/1"];
+active_pwd(suite) ->
+ [];
+active_pwd(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_pwd(Pid).
+
+
+%%-------------------------------------------------------------------------
+
+active_cd(doc) ->
+ ["Open an ftp connection, log on as anonymous ftp, and cd to the"
+ "directory \"/pub\" and to a non-existent directory."];
+active_cd(suite) ->
+ [];
+active_cd(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_cd(Pid).
+
+
+%%-------------------------------------------------------------------------
+
+active_lcd(doc) ->
+ ["Test api function ftp:lcd/2"];
+active_lcd(suite) ->
+ [];
+active_lcd(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ PrivDir = ?config(priv_dir, Config),
+ do_lcd(Pid, PrivDir).
+
+
+%%-------------------------------------------------------------------------
+
+active_ls(doc) ->
+ ["Open an ftp connection; ls the current directory, and the "
+ "\"incoming\" directory. We assume that ls never fails, since "
+ "it's output is meant to be read by humans. "];
+active_ls(suite) ->
+ [];
+active_ls(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_ls(Pid).
+
+
+%%-------------------------------------------------------------------------
+
+active_nlist(doc) ->
+ ["Open an ftp connection; nlist the current directory, and the "
+ "\"incoming\" directory. Nlist does not behave consistenly over "
+ "operating systems. On some it is an error to have an empty "
+ "directory."];
+active_nlist(suite) ->
+ [];
+active_nlist(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ WildcardSupport = ?config(wildcard_support, Config),
+ do_nlist(Pid, WildcardSupport).
+
+
+%%-------------------------------------------------------------------------
+
+active_rename(doc) ->
+ ["Transfer a file to the server, and rename it; then remove it."];
+active_rename(suite) ->
+ [];
+active_rename(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_rename(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+active_delete(doc) ->
+ ["Transfer a file to the server, and then delete it"];
+active_delete(suite) ->
+ [];
+active_delete(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_delete(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+active_mkdir(doc) ->
+ ["Make a remote directory, cd to it, go to parent directory, and "
+ "remove the directory."];
+active_mkdir(suite) ->
+ [];
+active_mkdir(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_mkdir(Pid).
+
+
+%%-------------------------------------------------------------------------
+
+active_send(doc) ->
+ ["Create a local file in priv_dir; open an ftp connection to a host; "
+ "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
+ "priv_dir; send the file; get a directory listing and check that "
+ "the file is on the list;, delete the remote file; get another listing "
+ "and check that the file is not on the list; close the session; "
+ "delete the local file."];
+active_send(suite) ->
+ [];
+active_send(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_send(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+active_append(doc) ->
+ ["Create a local file in priv_dir; open an ftp connection to a host; "
+ "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
+ "priv_dir; append the file to a file at the remote side that not exits"
+ "this will create the file at the remote side. Then it append the file "
+ "again. When this is done it recive the remote file and control that"
+ "the content is doubled in it.After that it will remove the files"];
+active_append(suite) ->
+ [];
+active_append(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_append(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+active_send_bin(doc) ->
+ ["Open a connection to a host; cd to the directory \"incoming\"; "
+ "send a binary; remove file; close the connection."];
+active_send_bin(suite) ->
+ [];
+active_send_bin(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_send_bin(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+active_append_bin(doc) ->
+ ["Open a connection to a host; cd to the directory \"incoming\"; "
+ "append a binary twice; get the file and compare the content"
+ "remove file; close the connection."];
+active_append_bin(suite) ->
+ [];
+active_append_bin(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_append_bin(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+active_send_chunk(doc) ->
+ ["Open a connection to a host; cd to the directory \"incoming\"; "
+ "send chunks; remove file; close the connection."];
+active_send_chunk(suite) ->
+ [];
+active_send_chunk(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_send_chunk(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+active_append_chunk(doc) ->
+ ["Open a connection to a host; cd to the directory \"incoming\"; "
+ "append chunks;control content remove file; close the connection."];
+active_append_chunk(suite) ->
+ [];
+active_append_chunk(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_append_chunk(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+active_recv(doc) ->
+ ["Create a local file and transfer it to the remote host into the "
+ "the \"incoming\" directory, remove "
+ "the local file. Then open a new connection; cd to \"incoming\", "
+ "lcd to the private directory; receive the file; delete the "
+ "remote file; close connection; check that received file is in "
+ "the correct directory; cleanup." ];
+active_recv(suite) ->
+ [];
+active_recv(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_recv(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+active_recv_bin(doc) ->
+ ["Send a binary to the remote host; and retreive "
+ "the file; then remove the file."];
+active_recv_bin(suite) ->
+ [];
+active_recv_bin(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_recv_bin(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+active_recv_chunk(doc) ->
+ ["Send a binary to the remote host; Connect again, and retreive "
+ "the file; then remove the file."];
+active_recv_chunk(suite) ->
+ [];
+active_recv_chunk(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_recv_chunk(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+active_type(doc) ->
+ ["Test that we can change btween ASCCI and binary transfer mode"];
+active_type(suite) ->
+ [];
+active_type(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_type(Pid).
+
+
+%%-------------------------------------------------------------------------
+
+active_quote(doc) ->
+ [""];
+active_quote(suite) ->
+ [];
+active_quote(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_quote(Pid).
+
+
+%%-------------------------------------------------------------------------
+
+active_ip_v6_disabled(doc) ->
+ ["Test ipv4 command PORT"];
+active_ip_v6_disabled(suite) ->
+ [];
+active_ip_v6_disabled(Config) when is_list(Config) ->
+ Pid = ?config(ftp, Config),
+ do_send(Pid, Config).
+
+
+%%-------------------------------------------------------------------------
+
+api_missuse(doc)->
+ ["Test that behaviour of the ftp process if the api is abused"];
+api_missuse(suite) -> [];
+api_missuse(Config) when is_list(Config) ->
+ 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"]),
+ try
+ begin
+ Host = dirty_select_ftpd_host(Config),
+ Pid = spawn(?MODULE, open_wait_6035, [Host, self()]),
+ error_logger:logfile({open, LogFile}),
+ ok = kill_ftp_proc_6035(Pid,LogFile),
+ error_logger:logfile(close),
+ p("ticket_6035 -> done", []),
+ ok
+ end
+ catch
+ throw:{error, not_found} ->
+ {skip, "No available FTP servers"}
+ end.
+
+kill_ftp_proc_6035(Pid, LogFile) ->
+ p("kill_ftp_proc_6035 -> entry"),
+ receive
+ open ->
+ p("kill_ftp_proc_6035 -> received open: 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(FtpServer, From) ->
+ 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, WildcardSupport) ->
+ {ok, _} = ftp:nlist(Pid),
+ {ok, _} = ftp:nlist(Pid, "incoming"),
+ %% neither nlist nor ls operates on a directory
+ %% they operate on a pathname, which *can* be a
+ %% directory, but can also be a filename or a group
+ %% of files (including wildcards).
+ case WildcardSupport of
+ true ->
+ {ok, _} = ftp:nlist(Pid, "incom*"),
+ ok;
+ _ ->
+ ok
+ end.
+
+do_rename(Pid, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ LFile = ?config(file, Config),
+ NewLFile = ?config(new_file, Config),
+ AbsLFile = filename:absname(LFile, PrivDir),
+ Contents = "ftp_SUITE test ...",
+ ok = file:write_file(AbsLFile, list_to_binary(Contents)),
+ ok = ftp:cd(Pid, "incoming"),
+ ok = ftp:lcd(Pid, PrivDir),
+ ftp:delete(Pid, LFile), % reset
+ ftp:delete(Pid, NewLFile), % reset
+ ok = ftp:send(Pid, LFile),
+ {error, epath} = ftp:rename(Pid, NewLFile, LFile),
+ ok = ftp:rename(Pid, LFile, NewLFile),
+ ftp:delete(Pid, LFile), % cleanup
+ ftp:delete(Pid, NewLFile), % cleanup
+ ok.
+
+do_delete(Pid, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ LFile = ?config(file, Config),
+ AbsLFile = filename:absname(LFile, PrivDir),
+ Contents = "ftp_SUITE test ...",
+ ok = file:write_file(AbsLFile, list_to_binary(Contents)),
+ ok = ftp:cd(Pid, "incoming"),
+ ok = ftp:lcd(Pid, PrivDir),
+ ftp:delete(Pid,LFile), % reset
+ ok = ftp:send(Pid, LFile),
+ ok = ftp:delete(Pid,LFile),
+ ok.
+
+do_mkdir(Pid) ->
+ {A, B, C} = erlang:now(),
+ NewDir = "nisse_" ++ integer_to_list(A) ++ "_" ++
+ integer_to_list(B) ++ "_" ++ integer_to_list(C),
+ ok = ftp:cd(Pid, "incoming"),
+ {ok, CurrDir} = ftp:pwd(Pid),
+ ok = ftp:mkdir(Pid, NewDir),
+ ok = ftp:cd(Pid, NewDir),
+ ok = ftp:cd(Pid, CurrDir),
+ ok = ftp:rmdir(Pid, NewDir),
+ ok.
+
+do_send(Pid, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ LFile = ?config(file, Config),
+ RFile = LFile ++ ".remote",
+ AbsLFile = filename:absname(LFile, PrivDir),
+ Contents = "ftp_SUITE test ...",
+ ok = file:write_file(AbsLFile, list_to_binary(Contents)),
+ ok = ftp:cd(Pid, "incoming"),
+ ok = ftp:lcd(Pid, PrivDir),
+ ok = ftp:send(Pid, LFile, RFile),
+ {ok, RFilesString} = ftp:nlist(Pid),
+ RFiles = split(RFilesString),
+ true = lists:member(RFile, RFiles),
+ ok = ftp:delete(Pid, RFile),
+ case ftp:nlist(Pid) of
+ {error, epath} ->
+ ok; % No files
+ {ok, RFilesString1} ->
+ RFiles1 = split(RFilesString1),
+ false = lists:member(RFile, RFiles1)
+ end,
+ ok = file:delete(AbsLFile).
+
+do_append(Pid, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ LFile = ?config(file, Config),
+ RFile = ?config(new_file, Config),
+ AbsLFile = filename:absname(LFile, PrivDir),
+ Contents = "ftp_SUITE test:appending\r\n",
+
+ ok = file:write_file(AbsLFile, list_to_binary(Contents)),
+ ok = ftp:cd(Pid, "incoming"),
+ ok = ftp:lcd(Pid, PrivDir),
+
+ %% remove files from earlier failed test case
+ ftp:delete(Pid, RFile),
+ ftp:delete(Pid, LFile),
+
+ ok = ftp:append(Pid, LFile, RFile),
+ ok = ftp:append(Pid, LFile, RFile),
+ ok = ftp:append(Pid, LFile),
+
+ %% Control the contents of the file
+ {ok, Bin1} = ftp:recv_bin(Pid, RFile),
+ ok = ftp:delete(Pid, RFile),
+ ok = file:delete(AbsLFile),
+ ok = check_content(binary_to_list(Bin1), Contents, double),
+
+ {ok, Bin2} = ftp:recv_bin(Pid, LFile),
+ ok = ftp:delete(Pid, LFile),
+ ok = check_content(binary_to_list(Bin2), Contents, singel),
+ ok.
+
+do_send_bin(Pid, Config) ->
+ File = ?config(file, Config),
+ Contents = "ftp_SUITE test ...",
+ Bin = list_to_binary(Contents),
+ ok = ftp:cd(Pid, "incoming"),
+ {error, enotbinary} = ftp:send_bin(Pid, Contents, File),
+ ok = ftp:send_bin(Pid, Bin, File),
+ {ok, RFilesString} = ftp:nlist(Pid),
+ RFiles = split(RFilesString),
+ true = lists:member(File, RFiles),
+ ok = ftp:delete(Pid, File),
+ ok.
+
+do_append_bin(Pid, Config) ->
+ File = ?config(file, Config),
+ Contents = "ftp_SUITE test ...",
+ Bin = list_to_binary(Contents),
+ ok = ftp:cd(Pid, "incoming"),
+ {error, enotbinary} = ftp:append_bin(Pid, Contents, File),
+ ok = ftp:append_bin(Pid, Bin, File),
+ ok = ftp:append_bin(Pid, Bin, File),
+ %% Control the contents of the file
+ {ok, Bin2} = ftp:recv_bin(Pid, File),
+ ok = ftp:delete(Pid,File),
+ ok = check_content(binary_to_list(Bin2),binary_to_list(Bin), double).
+
+do_send_chunk(Pid, Config) ->
+ File = ?config(file, Config),
+ Contents = "ftp_SUITE test ...",
+ Bin = list_to_binary(Contents),
+ ok = ftp:cd(Pid, "incoming"),
+ ok = ftp:send_chunk_start(Pid, File),
+ {error, echunk} = ftp:cd(Pid, "incoming"),
+ {error, enotbinary} = ftp:send_chunk(Pid, Contents),
+ ok = ftp:send_chunk(Pid, Bin),
+ ok = ftp:send_chunk(Pid, Bin),
+ ok = ftp:send_chunk_end(Pid),
+ {ok, RFilesString} = ftp:nlist(Pid),
+ RFiles = split(RFilesString),
+ true = lists:member(File, RFiles),
+ ok = ftp:delete(Pid, File),
+ ok.
+
+do_append_chunk(Pid, Config) ->
+ File = ?config(file, Config),
+ Contents = ["ER","LE","RL"],
+ ok = ftp:cd(Pid, "incoming"),
+ ok = ftp:append_chunk_start(Pid, File),
+ {error, enotbinary} = ftp:append_chunk(Pid, lists:nth(1,Contents)),
+ ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(1,Contents))),
+ ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(2,Contents))),
+ ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(3,Contents))),
+ ok = ftp:append_chunk_end(Pid),
+ %%Control the contents of the file
+ {ok, Bin2} = ftp:recv_bin(Pid, File),
+ ok = check_content(binary_to_list(Bin2),"ERL", double),
+ ok = ftp:delete(Pid, File),
+ ok.
+
+do_recv(Pid, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ File = ?config(file, Config),
+ Newfile = ?config(new_file, Config),
+ AbsFile = filename:absname(File, PrivDir),
+ Contents = "ftp_SUITE:recv test ...",
+ ok = file:write_file(AbsFile, list_to_binary(Contents)),
+ ok = ftp:cd(Pid, "incoming"),
+ ftp:delete(Pid, File), % reset
+ ftp:lcd(Pid, PrivDir),
+ ok = ftp:send(Pid, File),
+ ok = file:delete(AbsFile), % cleanup
+ test_server:sleep(100),
+ ok = ftp:lcd(Pid, PrivDir),
+ ok = ftp:recv(Pid, File),
+ {ok, Files} = file:list_dir(PrivDir),
+ true = lists:member(File, Files),
+ ok = file:delete(AbsFile), % cleanup
+ ok = ftp:recv(Pid, File, Newfile),
+ ok = ftp:delete(Pid, File), % cleanup
+ ok.
+
+do_recv_bin(Pid, Config) ->
+ File = ?config(file, Config),
+ Contents1 = "ftp_SUITE test ...",
+ Bin1 = list_to_binary(Contents1),
+ ok = ftp:cd(Pid, "incoming"),
+ ok = ftp:send_bin(Pid, Bin1, File),
+ test_server:sleep(100),
+ {ok, Bin2} = ftp:recv_bin(Pid, File),
+ ok = ftp:delete(Pid, File), % cleanup
+ Contents2 = binary_to_list(Bin2),
+ Contents1 = Contents2,
+ ok.
+
+do_recv_chunk(Pid, Config) ->
+ File = ?config(file, Config),
+ Data = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
+ "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
+ "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG"
+ "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH"
+ "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII",
+
+ Contents1 = lists:flatten(lists:duplicate(10, Data)),
+ Bin1 = list_to_binary(Contents1),
+ ok = ftp:cd(Pid, "incoming"),
+ ok = ftp:type(Pid, binary),
+ ok = ftp:send_bin(Pid, Bin1, File),
+ test_server:sleep(100),
+ {error, "ftp:recv_chunk_start/2 not called"} = recv_chunk(Pid, <<>>),
+ ok = ftp:recv_chunk_start(Pid, File),
+ {ok, Contents2} = recv_chunk(Pid, <<>>),
+ ok = ftp:delete(Pid, File), % cleanup
+ ok = find_diff(Contents2, Contents1, 1),
+ ok.
+
+do_type(Pid) ->
+ ok = ftp:type(Pid, ascii),
+ ok = ftp:type(Pid, binary),
+ ok = ftp:type(Pid, ascii),
+ {error, etype} = ftp:type(Pid, foobar),
+ ok.
+
+do_quote(Pid) ->
+ ["257 \"/\""++_Rest] = ftp:quote(Pid, "pwd"), %% 257
+ [_| _] = ftp:quote(Pid, "help"),
+ %% This negativ test causes some ftp servers to hang. This test
+ %% is not important for the client, so we skip it for now.
+ %%["425 Can't build data connection: Connection refused."]
+ %% = ftp:quote(Pid, "list"),
+ ok.
+
+ watch_dog(Config) ->
+ Dog = test_server:timetrap(inets_test_lib:minutes(1)),
+ NewConfig = lists:keydelete(watchdog, 1, Config),
+ [{watchdog, Dog} | NewConfig].
+
+ close_connection(Config) ->
+ case ?config(ftp, Config) of
+ Pid when is_pid(Pid) ->
+ ok = ftp:close(Pid),
+ lists:delete({ftp, Pid}, Config);
+ _ ->
+ Config
+ end.
+
+ftp_host(Config) ->
+ case ?config(ftp_remote_host, Config) of
+ undefined ->
+ exit({skip, "No host specified"});
+ Host ->
+ Host
+ end.
+
+check_content(RContent, LContent, Amount) ->
+ LContent2 = case Amount of
+ double ->
+ LContent ++ LContent;
+ singel ->
+ LContent
+ end,
+ case string:equal(RContent, LContent2) of
+ true ->
+ ok;
+ false ->
+ %% Find where the diff is
+ Where = find_diff(RContent, LContent2, 1),
+ Where
+ end.
+
+find_diff(A, A, _) ->
+ ok;
+find_diff([H|T1], [H|T2], Pos) ->
+ find_diff(T1, T2, Pos+1);
+find_diff(RC, LC, Pos) ->
+ {error, {diff, Pos, RC, LC}}.
+
+recv_chunk(Pid, Acc) ->
+ case ftp:recv_chunk(Pid) of
+ ok ->
+ {ok, binary_to_list(Acc)};
+ {ok, Bin} ->
+ recv_chunk(Pid, <<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.
+
+ftpd_hosts(Config) ->
+ DataDir = ?config(data_dir, Config),
+ FileName = filename:join([DataDir, "../ftp_SUITE_data/", ftpd_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}),
+ NewConfig;
+ _ -> Config
+ end.
+
+
+
+p(F) ->
+ p(F, []).
+
+p(F, A) ->
+ case get(ftp_testcase) of
+ undefined ->
+ io:format("~w [~w] " ++ F ++ "~n", [?MODULE, self() | A]);
+ TC when is_atom(TC) ->
+ io:format("~w [~w] ~w:" ++ F ++ "~n", [?MODULE, self(), TC | A])
+ end.
diff --git a/lib/inets/test/ftp_ticket_test.erl b/lib/inets/test/ftp_ticket_test.erl
new file mode 100644
index 0000000000..6748df03bb
--- /dev/null
+++ b/lib/inets/test/ftp_ticket_test.erl
@@ -0,0 +1,51 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(ftp_ticket_test).
+
+-compile(export_all).
+
+-define(LIB_MOD,ftp_suite_lib).
+-define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
+-define(PLATFORM,"Solaris 8 sparc ").
+
+
+%% Test server callbacks
+init_per_testcase(Case, Config) ->
+ ftp_suite_lib:init_per_testcase(Case, Config).
+
+end_per_testcase(Case, Config) ->
+ ftp_suite_lib:end_per_testcase(Case, Config).
+
+
+all(suite) ->
+ {conf,init,tickets(),fin}.
+
+init(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..79945f0f4d
--- /dev/null
+++ b/lib/inets/test/http_format_SUITE.erl
@@ -0,0 +1,591 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(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"),
+ {{2006,12,12},{8,59,38}} =
+ http_util:convert_netscapecookie_date("Sun 12-Dec-2006 08:59:38 GMT"),
+ {{2006,12,12},{8,59,38}} =
+ http_util:convert_netscapecookie_date("Sun, 12-Dec-06 08:59:38 GMT"),
+ {{2006,12,12},{8,59,38}} =
+ http_util:convert_netscapecookie_date("Sun 12-Dec-06 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..3a1f4cc83d
--- /dev/null
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -0,0 +1,3163 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+%%
+%% ts:run(inets, httpc_SUITE, [batch]).
+%%
+
+-module(httpc_SUITE).
+
+-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(otp_8154_1 = Case, Config) ->
+ init_per_testcase(Case, 5, Config);
+init_per_testcase(Case, Config) ->
+ init_per_testcase(Case, 2, Config).
+
+init_per_testcase(Case, Timeout, Config) ->
+ io:format(user,
+ "~n~n*** INIT ~w:~w[~w] ***"
+ "~n~n", [?MODULE, Case, Timeout]),
+ PrivDir = ?config(priv_dir, Config),
+ application:stop(inets),
+ Dog = test_server:timetrap(inets_test_lib:minutes(Timeout)),
+ TmpConfig = lists:keydelete(watchdog, 1, Config),
+ IpConfFile = integer_to_list(?IP_PORT) ++ ".conf",
+ SslConfFile = integer_to_list(?SSL_PORT) ++ ".conf",
+
+ NewConfig =
+ case atom_to_list(Case) of
+ "ssl" ++ _ ->
+ application:stop(ssl),
+ TmpConfig2 =
+ lists:keydelete(local_ssl_server, 1, TmpConfig),
+ %% Will start inets
+ Server = start_http_server(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;
+ _ ->
+ %% We use erlang.org for the proxy tests
+ %% and after the switch to erlang-web, many
+ %% of the test cases no longer work (erlang.org
+ %% previously run on Apache).
+ %% Until we have had time to update inets
+ %% (and updated erlang.org to use that inets)
+ %% and the test cases, we simply skip the
+ %% problematic test cases.
+ %% This is not ideal, but I am busy....
+ case is_proxy_available(?PROXY, ?PROXY_PORT) of
+ true ->
+ BadCases =
+ [
+ "delete",
+ "get",
+ "head",
+ "not_modified_otp_6821",
+ "options",
+ "page_does_not_exist",
+ "post",
+ "put",
+ "stream"
+ ],
+ case lists:member(Rest, BadCases) of
+ true ->
+ [skip("TC and server not compatible") |
+ TmpConfig];
+ false ->
+ inets:start(),
+ [{watchdog, Dog} | TmpConfig]
+ end;
+ false ->
+ [skip("proxy not responding") | TmpConfig]
+ end
+ end;
+ _ ->
+ TmpConfig2 = lists:keydelete(local_server, 1, TmpConfig),
+ %% Will start inets
+ Server = start_http_server(PrivDir, IpConfFile),
+ [{watchdog, Dog}, {local_server, Server} | TmpConfig2]
+ end,
+
+ ProxyExceptions = ["localhost", ?IPV6_LOCAL_HOST],
+ http:set_options([{proxy, {{?PROXY, ?PROXY_PORT}, ProxyExceptions}}]),
+ inets:enable_trace(max, io, httpc),
+ %% inets:enable_trace(max, io, all),
+ %% snmp:set_trace([gen_tcp, inet_tcp, prim_inet]),
+ NewConfig.
+
+start_http_server(ConfDir, ConfFile) ->
+ inets_test_lib:start_http_server( filename:join(ConfDir, ConfFile) ).
+
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(Case, Config) -> _
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Cleanup after each test case
+%%--------------------------------------------------------------------
+end_per_testcase(http_save_to_file, 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,
+ (catch application:stop(inets)).
+
+
+%%-------------------------------------------------------------------------
+%% 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,
+ otp_8106,
+ otp_8056,
+ otp_8352,
+ otp_8371,
+ otp_8739
+ ].
+
+
+%%-------------------------------------------------------------------------
+
+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} ->
+ tsf({wrong_reply, WrongReply});
+ Error ->
+ tsf({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) ->
+ tsp("http_get -> entry with"
+ "~n Config: ~p", [Config]),
+ case ?config(local_server, Config) of
+ ok ->
+ tsp("local-server running"),
+ Method = get,
+ Port = ?config(local_port, Config),
+ URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
+ Request = {URL, []},
+ Timeout = timer:seconds(1),
+ ConnTimeout = Timeout + timer:seconds(1),
+ HttpOptions1 = [{timeout, Timeout}, {connect_timeout, ConnTimeout}],
+ Options1 = [],
+ Body =
+ case http:request(Method, Request, HttpOptions1, Options1) of
+ {ok, {{_,200,_}, [_ | _], ReplyBody = [_ | _]}} ->
+ ReplyBody;
+ {ok, UnexpectedReply1} ->
+ tsf({unexpected_reply, UnexpectedReply1});
+ {error, _} = Error1 ->
+ tsf({bad_reply, Error1})
+ end,
+
+ %% eqvivivalent to http:request(get, {URL, []}, [], []),
+ inets_test_lib:check_body(Body),
+
+ HttpOptions2 = [],
+ Options2 = [{body_format, binary}],
+ case http:request(Method, Request, HttpOptions2, Options2) of
+ {ok, {{_,200,_}, [_ | _], Bin}} when is_binary(Bin) ->
+ ok;
+ {ok, {{_,200,_}, [_ | _], BadBin}} ->
+ tsf({body_format_not_binary, BadBin});
+ {ok, UnexpectedReply2} ->
+ tsf({unexpected_reply, UnexpectedReply2});
+ {error, _} = Error2 ->
+ tsf({bad_reply, Error2})
+ end;
+ _ ->
+ {skip, "Failed to start local http-server"}
+ end.
+
+%%-------------------------------------------------------------------------
+http_post(doc) ->
+ ["Test http post request against local server. We do in this case"
+ " only care about the client side of the the post. The server"
+ " script will not actually use the post data."];
+http_post(suite) ->
+ [];
+http_post(Config) when is_list(Config) ->
+ case ?config(local_server, Config) of
+ ok ->
+ Port = ?config(local_port, Config),
+
+ URL = case test_server:os_type() of
+ {win32, _} ->
+ ?URL_START ++ integer_to_list(Port) ++
+ "/cgi-bin/cgi_echo.exe";
+ _ ->
+ ?URL_START ++ integer_to_list(Port) ++
+ "/cgi-bin/cgi_echo"
+
+ end,
+ %% Cgi-script expects the body length to be 100
+ Body = lists:duplicate(100, "1"),
+
+ {ok, {{_,200,_}, [_ | _], [_ | _]}} =
+ 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, Port} = dummy_server(self(), ipv4),
+
+ 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, Port} = dummy_server(self(), ipv4),
+
+ 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) ->
+ p("test_pipeline -> entry with"
+ "~n URL: ~p", [URL]),
+
+ http:set_options([{pipeline_timeout, 50000}]),
+
+ p("test_pipeline -> issue (async) request 1"),
+ {ok, RequestId1} =
+ http:request(get, {URL, []}, [], [{sync, false}]),
+ test_server:format("RequestId1: ~p~n", [RequestId1]),
+ p("test_pipeline -> RequestId1: ~p", [RequestId1]),
+
+ %% Make sure pipeline is initiated
+ p("test_pipeline -> sleep some", []),
+ test_server:sleep(4000),
+
+ p("test_pipeline -> issue (async) request 2"),
+ {ok, RequestId2} =
+ http:request(get, {URL, []}, [], [{sync, false}]),
+ tsp("RequestId2: ~p", [RequestId2]),
+ p("test_pipeline -> RequestId2: ~p", [RequestId2]),
+
+ p("test_pipeline -> issue (sync) request 3"),
+ {ok, {{_,200,_}, [_ | _], [_ | _]}} =
+ http:request(get, {URL, []}, [], []),
+
+ p("test_pipeline -> expect reply for (async) request 1 or 2"),
+ receive
+ {http, {RequestId1, {{_, 200, _}, _, _}}} ->
+ p("test_pipeline -> received reply for (async) request 1 - now wait for 2"),
+ receive
+ {http, {RequestId2, {{_, 200, _}, _, _}}} ->
+ p("test_pipeline -> received reply for (async) request 2"),
+ ok;
+ {http, Msg1} ->
+ test_server:fail(Msg1)
+ end;
+ {http, {RequestId2, {{_, 200, _}, _, _}}} ->
+ io:format("test_pipeline -> received reply for (async) request 2 - now wait for 1"),
+ receive
+ {http, {RequestId1, {{_, 200, _}, _, _}}} ->
+ io:format("test_pipeline -> received reply for (async) request 1"),
+ ok;
+ {http, Msg2} ->
+ test_server:fail(Msg2)
+ end;
+ {http, Msg3} ->
+ test_server:fail(Msg3)
+ after 60000 ->
+ receive Any1 ->
+ tsp("received crap after timeout: ~n ~p", [Any1]),
+ test_server:fail({error, {timeout, Any1}})
+ end
+ end,
+
+ p("test_pipeline -> sleep some"),
+ test_server:sleep(4000),
+
+ p("test_pipeline -> issue (async) request 4"),
+ {ok, RequestId3} =
+ http:request(get, {URL, []}, [], [{sync, false}]),
+ tsp("RequestId3: ~p", [RequestId3]),
+ p("test_pipeline -> RequestId3: ~p", [RequestId3]),
+
+ p("test_pipeline -> issue (async) request 5"),
+ {ok, RequestId4} =
+ http:request(get, {URL, []}, [], [{sync, false}]),
+ tsp("RequestId4: ~p~n", [RequestId4]),
+ p("test_pipeline -> RequestId4: ~p", [RequestId4]),
+
+ p("test_pipeline -> cancel (async) request 4"),
+ ok = http:cancel_request(RequestId3),
+
+ p("test_pipeline -> expect *no* reply for cancelled (async) request 4 (for 3 secs)"),
+ receive
+ {http, {RequestId3, _}} ->
+ test_server:fail(http_cancel_request_failed)
+ after 3000 ->
+ ok
+ end,
+
+ p("test_pipeline -> expect reply for (async) request 4"),
+ Body =
+ receive
+ {http, {RequestId4, {{_, 200, _}, _, BinBody4}}} = Res ->
+ p("test_pipeline -> received reply for (async) request 5"),
+ tsp("Receive : ~p", [Res]),
+ BinBody4;
+ {http, Msg4} ->
+ test_server:fail(Msg4)
+ after 60000 ->
+ receive Any2 ->
+ tsp("received crap after timeout: ~n ~p", [Any2]),
+ test_server:fail({error, {timeout, Any2}})
+ end
+ end,
+
+ p("test_pipeline -> check reply for (async) request 5"),
+ inets_test_lib:check_body(binary_to_list(Body)),
+
+ p("test_pipeline -> ensure no unexpected incomming"),
+ receive
+ {http, Any} ->
+ test_server:fail({unexpected_message, Any})
+ after 500 ->
+ ok
+ end,
+
+ p("test_pipeline -> done"),
+ ok.
+
+
+
+%%-------------------------------------------------------------------------
+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, Port} = dummy_server(self(), ipv4),
+
+ URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy_headers.html",
+
+ Foo = http_chunk:encode("foobar") ++
+ binary_to_list(http_chunk:encode_last()),
+ FooBar = Foo ++ "\r\n\r\nOther:inets_test\r\n\r\n",
+
+ UserPasswd = base64:encode_to_string("Alladin:Sesame"),
+ Auth = "Basic " ++ UserPasswd,
+
+ %% The dummy server will ignore the headers, we only want to test
+ %% that the client header-handling code. This would not
+ %% be a vaild http-request!
+ {ok, {{_,200,_}, [_ | _], [_|_]}} =
+ 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, Port} = dummy_server(self(), ipv4),
+
+ 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) ->
+ tsp("http_redirect -> entry with"
+ "~n Config: ~p", [Config]),
+ case ?config(local_server, Config) of
+ ok ->
+ tsp("http_redirect -> set ipfamily option to inet"),
+ ok = http:set_options([{ipfamily, inet}]),
+
+ tsp("http_redirect -> start dummy server inet"),
+ {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ tsp("http_redirect -> server port = ~p", [Port]),
+
+ URL300 = ?URL_START ++ integer_to_list(Port) ++ "/300.html",
+
+ tsp("http_redirect -> issue request 1: "
+ "~n ~p", [URL300]),
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
+ = http:request(get, {URL300, []}, [], []),
+
+ tsp("http_redirect -> issue request 2: "
+ "~n ~p", [URL300]),
+ {ok, {{_,300,_}, [_ | _], _}} =
+ http:request(get, {URL300, []}, [{autoredirect, false}], []),
+
+ URL301 = ?URL_START ++ integer_to_list(Port) ++ "/301.html",
+
+ tsp("http_redirect -> issue request 3: "
+ "~n ~p", [URL301]),
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
+ = http:request(get, {URL301, []}, [], []),
+
+ tsp("http_redirect -> issue request 4: "
+ "~n ~p", [URL301]),
+ {ok, {{_,200,_}, [_ | _], []}}
+ = http:request(head, {URL301, []}, [], []),
+
+ tsp("http_redirect -> issue request 5: "
+ "~n ~p", [URL301]),
+ {ok, {{_,301,_}, [_ | _], [_|_]}}
+ = http:request(post, {URL301, [],"text/plain", "foobar"},
+ [], []),
+
+ URL302 = ?URL_START ++ integer_to_list(Port) ++ "/302.html",
+
+ tsp("http_redirect -> issue request 6: "
+ "~n ~p", [URL302]),
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
+ = http:request(get, {URL302, []}, [], []),
+ case http:request(get, {URL302, []}, [], []) of
+ {ok, Reply7} ->
+ case Reply7 of
+ {{_,200,_}, [_ | _], [_|_]} ->
+ tsp("http_redirect -> "
+ "expected reply for request 7"),
+ ok;
+ {StatusLine, Headers, Body} ->
+ tsp("http_redirect -> "
+ "unexpected reply for request 7: "
+ "~n StatusLine: ~p"
+ "~n Headers: ~p"
+ "~n Body: ~p",
+ [StatusLine, Headers, Body]),
+ tsf({unexpected_reply, Reply7})
+ end;
+ Error7 ->
+ tsp("http_redirect -> "
+ "unexpected result for request 7: "
+ "~n Error7: ~p",
+ [Error7]),
+ tsf({unexpected_result, Error7})
+ end,
+
+ tsp("http_redirect -> issue request 7: "
+ "~n ~p", [URL302]),
+ {ok, {{_,200,_}, [_ | _], []}}
+ = http:request(head, {URL302, []}, [], []),
+
+ tsp("http_redirect -> issue request 8: "
+ "~n ~p", [URL302]),
+ {ok, {{_,302,_}, [_ | _], [_|_]}}
+ = http:request(post, {URL302, [],"text/plain", "foobar"},
+ [], []),
+
+ URL307 = ?URL_START ++ integer_to_list(Port) ++ "/307.html",
+
+ tsp("http_redirect -> issue request 9: "
+ "~n ~p", [URL307]),
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
+ = http:request(get, {URL307, []}, [], []),
+
+ tsp("http_redirect -> issue request 10: "
+ "~n ~p", [URL307]),
+ {ok, {{_,200,_}, [_ | _], []}}
+ = http:request(head, {URL307, []}, [], []),
+
+ tsp("http_redirect -> issue request 11: "
+ "~n ~p", [URL307]),
+ {ok, {{_,307,_}, [_ | _], [_|_]}}
+ = http:request(post, {URL307, [],"text/plain", "foobar"},
+ [], []),
+
+ tsp("http_redirect -> stop dummy server"),
+ DummyServerPid ! stop,
+ tsp("http_redirect -> reset ipfamily option (to inet6fb4)"),
+ ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ tsp("http_redirect -> done"),
+ ok;
+
+ _ ->
+ {skip, "Failed to start local http-server"}
+ end.
+
+
+
+%%-------------------------------------------------------------------------
+http_redirect_loop(doc) ->
+ ["Test redirect loop detection"];
+http_redirect_loop(suite) ->
+ [];
+http_redirect_loop(Config) when is_list(Config) ->
+ ok = http:set_options([{ipfamily, inet}]),
+ {DummyServerPid, Port} = dummy_server(self(), ipv4),
+
+ 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, Port} = dummy_server(self(), ipv4),
+
+ 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, Port} = dummy_server(self(), ipv4),
+
+ 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, Port} = dummy_server(self(), ipv4),
+
+ 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 ->
+ Command =
+ fun() -> http:request(options, {?PROXY_URL, []}, [], []) end,
+ Verify =
+ fun({ok, {{_,200,_}, Headers, _}}) ->
+ case lists:keysearch("allow", 1, Headers) of
+ {value, {"allow", _}} ->
+ ok;
+ _ ->
+ tsf(http_options_request_failed)
+ end;
+ ({ok, {{_, 501, "Not Implemented"}, _Hdrs, _}}) ->
+ skip(options_not_implemented_in_server_501_ni);
+ ({ok, Unexpected}) ->
+ tsf({unexpected_success, Unexpected});
+ (Unexpected) ->
+ tsf({unexpected_result, Unexpected})
+ end,
+ expect(Command, Verify);
+ 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) ->
+ tsp("proxy_head -> entry with"
+ "~n Config: ~p", [Config]),
+ case ?config(skip, Config) of
+ undefined ->
+ Command =
+ fun() -> http:request(head, {?PROXY_URL, []}, [], []) end,
+ Verify =
+ fun({ok, {{_,200, _}, [_ | _], []}}) ->
+ ok;
+ ({ok, {{_,503,"Service Unavailable"}, [_ | _], []}}) ->
+ skip(head_not_implemented_in_server_503_su);
+ ({ok, Result}) ->
+ tsf({unexpected_success, Result});
+ (Unexpected) ->
+ tsf({unexpected_result, Unexpected})
+ end,
+ expect(Command, Verify);
+ 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 ->
+ Result09 = pelv_get("HTTP/0.9"),
+ case Result09 of
+ {ok, [_| _] = Body0} ->
+ inets_test_lib:check_body(Body0),
+ ok;
+ _ ->
+ tsf({unexpected_result, "HTTP/0.9", Result09})
+ end,
+
+ %% We do not check the version here as many servers
+ %% do not behave according to the rfc and send
+ %% 1.1 in its response.
+ Result10 = pelv_get("HTTP/1.0"),
+ case Result10 of
+ {ok,{{_, 200, _}, [_ | _], Body1 = [_ | _]}} ->
+ inets_test_lib:check_body(Body1),
+ ok;
+ _ ->
+ tsf({unexpected_result, "HTTP/1.0", Result10})
+ end,
+
+ Result11 = pelv_get("HTTP/1.1"),
+ case Result11 of
+ {ok, {{"HTTP/1.1", 200, _}, [_ | _], Body2 = [_ | _]}} ->
+ inets_test_lib:check_body(Body2);
+ _ ->
+ tsf({unexpected_result, "HTTP/1.1", Result11})
+ end;
+
+ Reason ->
+ {skip, Reason}
+ end.
+
+pelv_get(Version) ->
+ http:request(get, {?PROXY_URL, []}, [{version, Version}], []).
+
+
+%%-------------------------------------------------------------------------
+
+proxy_trace(doc) ->
+ ["Perform a TRACE request that goes through a proxy."];
+proxy_trace(suite) ->
+ [];
+proxy_trace(Config) when is_list(Config) ->
+ %%{ok, {{_,200,_}, [_ | _], "TRACE " ++ _}} =
+ %% 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) ->
+ %% After the switch to the erlang.org app,
+ %% the post will succeed, so we skip this
+ %% until we can find a new way of testing
+ %% this.
+ %% case ?config(skip, Config) of
+ %% undefined ->
+ %% Command =
+ %% fun() ->
+ %% http:request(post, {?PROXY_URL, [],
+ %% "text/plain", "foobar"}, [],[])
+ %% end,
+ %% Verify =
+ %% fun({ok, {{_,405,_}, [_ | _], [_ | _]}}) ->
+ %% ok;
+ %% ({ok, Result}) ->
+ %% tsf({unexpected_success, Result});
+ %% (Unexpected) ->
+ %% tsf({unexpected_result, Unexpected})
+ %% end,
+ %% expect(Command, Verify);
+ %% Reason ->
+ %% skip(Reason)
+ %% end.
+ skip("temporary skip").
+
+
+%%-------------------------------------------------------------------------
+
+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) ->
+ %% After the switch to the erlang.org app,
+ %% the post will succeed, so we skip this
+ %% until we can find a new way of testing
+ %% this.
+ %% 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.
+ skip("temporary skip").
+
+
+%%-------------------------------------------------------------------------
+
+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) ->
+ %% After the switch to the erlang.org app,
+ %% the post will succeed, so we skip this
+ %% until we can find a new way of testing
+ %% this.
+ %% case ?config(skip, Config) of
+ %% undefined ->
+ %% URL = ?PROXY_URL ++ "/foobar.html",
+ %% Command = fun() -> http:request(delete, {URL, []}, [], []) end,
+ %% Verify =
+ %% fun({ok, {{_,404,_}, [_ | _], [_ | _]}}) ->
+ %% ok;
+ %% ({ok, Result}) ->
+ %% tsf({unexpected_success, Result});
+ %% (Unexpected) ->
+ %% tsf({unexpected_result, Unexpected})
+ %% end,
+ %% expect(Command, Verify);
+
+ %% Reason ->
+ %% skip(Reason)
+ %% end.
+ skip("temporary skip").
+
+
+%%-------------------------------------------------------------------------
+
+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) ->
+ Result = http:request(get, {"https://login.yahoo.com", []}, [], []),
+ case Result of
+ {error, Reason} ->
+ %% ok so far
+ case Reason of
+ {failed_connecting, Why} ->
+ %% ok, now check why
+ case Why of
+ https_through_proxy_is_not_currently_supported ->
+ ok;
+ _ ->
+ tsf({unexpected_why, Why})
+ end;
+ _ ->
+ tsf({unexpected_reason, Reason})
+ end;
+ _ ->
+ tsf({unexpected_result, Result})
+ end,
+ 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) ->
+ p("http_stream_once -> entry with"
+ "~n Config: ~p", [Config]),
+
+ p("http_stream_once -> set ipfamily to inet", []),
+ ok = http:set_options([{ipfamily, inet}]),
+ p("http_stream_once -> start dummy server", []),
+ {DummyServerPid, Port} = dummy_server(self(), ipv4),
+
+ PortStr = integer_to_list(Port),
+ p("http_stream_once -> once", []),
+ once(?URL_START ++ PortStr ++ "/once.html"),
+ p("http_stream_once -> once_chunked", []),
+ once(?URL_START ++ PortStr ++ "/once_chunked.html"),
+ p("http_stream_once -> dummy", []),
+ once(?URL_START ++ PortStr ++ "/dummy.html"),
+
+ p("http_stream_once -> stop dummy server", []),
+ DummyServerPid ! stop,
+ p("http_stream_once -> set ipfamily to inet6fb4", []),
+ ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ p("http_stream_once -> done", []),
+ ok.
+
+once(URL) ->
+ p("once -> issue sync request for ~p", [URL]),
+ {ok, {{_,200,_}, [_ | _], Body}} =
+ http:request(get, {URL, []}, [], []),
+
+ p("once -> issue async (self stream) request for ~p", [URL]),
+ {ok, RequestId} =
+ http:request(get, {URL, []}, [], [{sync, false},
+ {stream, {self, once}}]),
+
+ p("once -> await stream_start reply for (async) request ~p", [RequestId]),
+ NewPid =
+ receive
+ {http, {RequestId, stream_start, _Headers, Pid}} ->
+ p("once -> received stream_start reply for (async) request ~p: ~p",
+ [RequestId, Pid]),
+ Pid;
+ {http, Msg} ->
+ test_server:fail(Msg)
+ end,
+
+ tsp("once -> request handler: ~p", [NewPid]),
+
+ p("once -> await stream reply for (async) request ~p", [RequestId]),
+ BodyPart =
+ receive
+ {http, {RequestId, stream, BinBodyPart}} ->
+ p("once -> received stream reply for (async) request ~p: "
+ "~n~p", [RequestId, binary_to_list(BinBodyPart)]),
+ BinBodyPart
+ end,
+
+ tsp("once -> first body part '~p' received", [binary_to_list(BodyPart)]),
+
+ StreamedBody = receive_streamed_body(RequestId, BinBodyPart, NewPid),
+
+ Body = binary_to_list(StreamedBody),
+
+ p("once -> done when Bode: ~p", [Body]),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+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, Port} = dummy_server(self(), ipv6),
+
+ 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, Port} = dummy_server(self(), ipv4),
+
+ 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, Port} = dummy_server(self(), ipv4),
+
+ 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, Port} = dummy_server(self(), ipv4),
+
+ 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, Port} = dummy_server(self(), ipv4),
+
+ 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, Port} = dummy_server(self(), ipv4),
+
+ 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, Port} = dummy_server(self(), ipv4),
+
+ 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, Port} = dummy_server(self(), ipv4),
+
+ 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(3)),
+ Server ! shutdown,
+ RespSeqNumServer ! shutdown,
+ ReqSeqNumServer ! shutdown,
+ ok.
+
+start_inets() ->
+ inets:start(),
+ ok.
+
+
+%% -----------------------------------------------------
+%% A sequence number handler
+%% The purpose is to be able to pair requests with responses.
+
+start_sequence_number_server() ->
+ proc_lib:spawn(fun() -> loop_sequence_number(1) end).
+
+loop_sequence_number(N) ->
+ receive
+ shutdown ->
+ ok;
+ {From, get_next} ->
+ From ! {next_is, N},
+ loop_sequence_number(N + 1)
+ end.
+
+get_next_sequence_number(SeqNumServer) ->
+ SeqNumServer ! {self(), get_next},
+ receive {next_is, N} -> N end.
+
+%% -----------------------------------------------------
+%% Client part
+%% Sends requests randomly parallel
+
+run_clients(NumClients, ServerPort, SeqNumServer) ->
+ io:format("start clients when"
+ "~n NumClients: ~w"
+ "~n ServerPort: ~w"
+ "~n SeqNumServer: ~w"
+ "~n", [NumClients, ServerPort, SeqNumServer]),
+ set_random_seed(),
+ lists:map(
+ fun(Id) ->
+ io:format("starting client ~w~n", [Id]),
+ Req = f("req~3..0w", [get_next_sequence_number(SeqNumServer)]),
+ Url = f(?URL_START ++ "~w/~s", [ServerPort, Req]),
+ Pid = proc_lib:spawn(
+ fun() ->
+ io:format("[~w] client started - "
+ "issue request~n", [Id]),
+ case 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)).
+
+
+
+
+%%-------------------------------------------------------------------------
+
+otp_8106(suite) ->
+ [
+ otp_8106_pid,
+ otp_8106_fun,
+ otp_8106_mfa
+ ].
+
+
+otp_8106_pid(doc) ->
+ ["OTP-8106 - deliver reply info using \"other\" pid"];
+otp_8106_pid(suite) ->
+ [];
+otp_8106_pid(Config) when is_list(Config) ->
+ case ?config(local_server, Config) of
+ ok ->
+ ReceiverPid = create_receiver(pid),
+ Receiver = ReceiverPid,
+
+ otp8106(ReceiverPid, Receiver, Config),
+
+ stop_receiver(ReceiverPid),
+
+ ok;
+ _ ->
+ {skip, "Failed to start local http-server"}
+ end.
+
+
+otp_8106_fun(doc) ->
+ ["OTP-8106 - deliver reply info using fun"];
+otp_8106_fun(suite) ->
+ [];
+otp_8106_fun(Config) when is_list(Config) ->
+ case ?config(local_server, Config) of
+ ok ->
+ ReceiverPid = create_receiver(function),
+ Receiver = otp_8106_deliver_fun(ReceiverPid),
+
+ otp8106(ReceiverPid, Receiver, Config),
+
+ stop_receiver(ReceiverPid),
+
+ ok;
+ _ ->
+ {skip, "Failed to start local http-server"}
+ end.
+
+
+otp_8106_mfa(doc) ->
+ ["OTP-8106 - deliver reply info using mfa callback"];
+otp_8106_mfa(suite) ->
+ [];
+otp_8106_mfa(Config) when is_list(Config) ->
+ case ?config(local_server, Config) of
+ ok ->
+ ReceiverPid = create_receiver(mfa),
+ Receiver = {?MODULE, otp_8106_deliver, [mfa, ReceiverPid]},
+
+ otp8106(ReceiverPid, Receiver, Config),
+
+ stop_receiver(ReceiverPid),
+
+ ok;
+ _ ->
+ {skip, "Failed to start local http-server"}
+ end.
+
+
+ otp8106(ReceiverPid, Receiver, Config) ->
+ Port = ?config(local_port, Config),
+ URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
+ Request = {URL, []},
+ HTTPOptions = [],
+ Options = [{sync, false}, {receiver, Receiver}],
+
+ {ok, RequestId} =
+ httpc:request(get, Request, HTTPOptions, Options),
+
+ Body =
+ receive
+ {reply, ReceiverPid, {RequestId, {{_, 200, _}, _, B}}} ->
+ B;
+ {reply, ReceiverPid, Msg} ->
+ tsf(Msg);
+ {bad_reply, ReceiverPid, Msg} ->
+ tsf(Msg)
+ end,
+
+ inets_test_lib:check_body(binary_to_list(Body)),
+ ok.
+
+
+create_receiver(Type) ->
+ Parent = self(),
+ Receiver = fun() -> receiver(Type, Parent) end,
+ spawn_link(Receiver).
+
+stop_receiver(Pid) ->
+ Pid ! {stop, self()}.
+
+receiver(Type, Parent) ->
+ receive
+ {stop, Parent} ->
+ exit(normal);
+
+ {http, ReplyInfo} when (Type =:= pid) ->
+ Parent ! {reply, self(), ReplyInfo},
+ receiver(Type, Parent);
+
+ {Type, ReplyInfo} ->
+ Parent ! {reply, self(), ReplyInfo},
+ receiver(Type, Parent);
+
+ Crap ->
+ Parent ! {reply, self(), {bad_reply, Crap}},
+ receiver(Type, Parent)
+ end.
+
+
+otp_8106_deliver_fun(ReceiverPid) ->
+ fun(ReplyInfo) -> otp_8106_deliver(ReplyInfo, function, ReceiverPid) end.
+
+otp_8106_deliver(ReplyInfo, Type, ReceiverPid) ->
+ ReceiverPid ! {Type, ReplyInfo},
+ ok.
+
+
+
+%%-------------------------------------------------------------------------
+
+otp_8056(doc) ->
+ "OTP-8056";
+otp_8056(suite) ->
+ [];
+otp_8056(Config) when is_list(Config) ->
+ Method = get,
+ Port = ?config(local_port, Config),
+ URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
+ Request = {URL, []},
+ HTTPOptions = [],
+ Options1 = [{sync, true}, {stream, {self, once}}],
+ Options2 = [{sync, true}, {stream, self}],
+ {error, streaming_error} = httpc:request(Method, Request,
+ HTTPOptions, Options1),
+ tsp("request 1 failed as expected"),
+ {error, streaming_error} = httpc:request(Method, Request,
+ HTTPOptions, Options2),
+ tsp("request 2 failed as expected"),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+otp_8352(doc) ->
+ "OTP-8352";
+otp_8352(suite) ->
+ [];
+otp_8352(Config) when is_list(Config) ->
+ tsp("otp_8352 -> entry with"
+ "~n Config: ~p", [Config]),
+ case ?config(local_server, Config) of
+ ok ->
+ tsp("local-server running"),
+
+ tsp("initial profile info(1): ~p", [httpc:info()]),
+
+ MaxSessions = 5,
+ MaxKeepAlive = 10,
+ KeepAliveTimeout = timer:minutes(2),
+ ConnOptions = [{max_sessions, MaxSessions},
+ {max_keep_alive_length, MaxKeepAlive},
+ {keep_alive_timeout, KeepAliveTimeout}],
+ http:set_options(ConnOptions),
+
+ Method = get,
+ Port = ?config(local_port, Config),
+ URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
+ Request = {URL, []},
+ Timeout = timer:seconds(1),
+ ConnTimeout = Timeout + timer:seconds(1),
+ HttpOptions1 = [{timeout, Timeout}, {connect_timeout, ConnTimeout}],
+ Options1 = [{socket_opts, [{tos, 87},
+ {recbuf, 16#FFFF},
+ {sndbuf, 16#FFFF}]}],
+ case http:request(Method, Request, HttpOptions1, Options1) of
+ {ok, {{_,200,_}, [_ | _], ReplyBody1 = [_ | _]}} ->
+ %% equivaliant to http:request(get, {URL, []}, [], []),
+ inets_test_lib:check_body(ReplyBody1);
+ {ok, UnexpectedReply1} ->
+ tsf({unexpected_reply, UnexpectedReply1});
+ {error, _} = Error1 ->
+ tsf({bad_reply, Error1})
+ end,
+
+ tsp("profile info (2): ~p", [httpc:info()]),
+
+ HttpOptions2 = [],
+ Options2 = [{socket_opts, [{tos, 84},
+ {recbuf, 32#1FFFF},
+ {sndbuf, 32#1FFFF}]}],
+ case http:request(Method, Request, HttpOptions2, Options2) of
+ {ok, {{_,200,_}, [_ | _], ReplyBody2 = [_ | _]}} ->
+ %% equivaliant to http:request(get, {URL, []}, [], []),
+ inets_test_lib:check_body(ReplyBody2);
+ {ok, UnexpectedReply2} ->
+ tsf({unexpected_reply, UnexpectedReply2});
+ {error, _} = Error2 ->
+ tsf({bad_reply, Error2})
+ end,
+ tsp("profile info (3): ~p", [httpc:info()]),
+ ok;
+
+ _ ->
+ {skip, "Failed to start local http-server"}
+ end.
+
+
+%%-------------------------------------------------------------------------
+
+otp_8371(doc) ->
+ ["OTP-8371"];
+otp_8371(suite) ->
+ [];
+otp_8371(Config) when is_list(Config) ->
+ ok = http:set_options([{ipv6, disabled}]), % also test the old option
+ {DummyServerPid, Port} = dummy_server(self(), ipv4),
+
+ URL = ?URL_START ++ integer_to_list(Port) ++
+ "/ensure_host_header_with_port.html",
+
+ case http:request(get, {URL, []}, [], []) of
+ {ok, Result} ->
+ case Result of
+ {{_, 200, _}, _Headers, Body} ->
+ tsp("expected response with"
+ "~n Body: ~p", [Body]),
+ ok;
+ {StatusLine, Headers, Body} ->
+ tsp("expected response with"
+ "~n StatusLine: ~p"
+ "~n Headers: ~p"
+ "~n Body: ~p", [StatusLine, Headers, Body]),
+ tsf({unexpected_result,
+ [{status_line, StatusLine},
+ {headers, Headers},
+ {body, Body}]});
+ _ ->
+ tsf({unexpected_result, Result})
+ end;
+ Error ->
+ tsf({request_failed, Error})
+ end,
+
+ DummyServerPid ! stop,
+ ok = http:set_options([{ipv6, enabled}]),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+otp_8739(doc) ->
+ ["OTP-8739"];
+otp_8739(suite) ->
+ [];
+otp_8739(Config) when is_list(Config) ->
+ {_DummyServerPid, Port} = otp_8739_dummy_server(),
+ URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
+ Method = get,
+ Request = {URL, []},
+ HttpOptions = [{connect_timeout, 500}, {timeout, 1}],
+ Options = [{sync, true}],
+ case http:request(Method, Request, HttpOptions, Options) of
+ {error, timeout} ->
+ %% And now we check the size of the handler db
+ Info = httpc:info(),
+ tsp("Info: ~p", [Info]),
+ {value, {handlers, Handlers}} =
+ lists:keysearch(handlers, 1, Info),
+ case Handlers of
+ [] ->
+ ok;
+ _ ->
+ tsf({unexpected_handlers, Handlers})
+ end;
+ Unexpected ->
+ tsf({unexpected, Unexpected})
+ end.
+
+
+otp_8739_dummy_server() ->
+ Parent = self(),
+ Pid = spawn_link(fun() -> otp_8739_dummy_server_init(Parent) end),
+ receive
+ {port, Port} ->
+ {Pid, Port}
+ end.
+
+otp_8739_dummy_server_init(Parent) ->
+ {ok, ListenSocket} =
+ gen_tcp:listen(0, [binary, inet, {packet, 0},
+ {reuseaddr,true},
+ {active, false}]),
+ {ok, Port} = inet:port(ListenSocket),
+ Parent ! {port, Port},
+ otp_8739_dummy_server_main(Parent, ListenSocket).
+
+otp_8739_dummy_server_main(_Parent, ListenSocket) ->
+ case gen_tcp:accept(ListenSocket) of
+ {ok, Sock} ->
+ %% Ignore the request, and simply wait for the socket to close
+ receive
+ {tcp_closed, Sock} ->
+ (catch gen_tcp:close(ListenSocket)),
+ exit(normal);
+ {tcp_error, Sock, Reason} ->
+ tsp("socket error: ~p", [Reason]),
+ (catch gen_tcp:close(ListenSocket)),
+ exit(normal)
+ after 10000 ->
+ %% Just in case
+ (catch gen_tcp:close(Sock)),
+ (catch gen_tcp:close(ListenSocket)),
+ exit(timeout)
+ end;
+ Error ->
+ exit(Error)
+ end.
+
+
+
+%%--------------------------------------------------------------------
+%% 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("~p:receive_streamed_body -> requested next stream ~n", [?MODULE]),
+ 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) ->
+ Pid = spawn(httpc_SUITE, dummy_server_init, [Caller, IpV]),
+ receive
+ {port, Port} ->
+ {Pid, Port}
+ end.
+
+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),
+ tsp("dummy_server_init -> Port: ~p", [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) ->
+ tsp("dummy_request_handler_loop -> entry with"
+ "~n Module: ~p"
+ "~n Function: ~p"
+ "~n Args: ~p", [Module, Function, Args]),
+ receive
+ {tcp, _, Data} ->
+ tsp("dummy_request_handler_loop -> Data ~p", [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) ->
+ tsp("handle_request -> entry with"
+ "~n Module: ~p"
+ "~n Function: ~p"
+ "~n Args: ~p", [Module, Function, Args]),
+ case Module:Function(Args) of
+ {ok, Result} ->
+ tsp("handle_request -> ok"
+ "~n Result: ~p", [Result]),
+ case (catch handle_http_msg(Result, Socket)) of
+ stop ->
+ stop;
+ <<>> ->
+ tsp("handle_request -> empty data"),
+ {httpd_request, parse, [[<<>>, ?HTTP_MAX_HEADER_SIZE]]};
+ Data ->
+ handle_request(httpd_request, parse,
+ [Data |[?HTTP_MAX_HEADER_SIZE]], Socket)
+ end;
+ NewMFA ->
+ tsp("handle_request -> "
+ "~n NewMFA: ~p", [NewMFA]),
+ NewMFA
+ end.
+
+handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) ->
+ tsp("handle_http_msg -> entry with: "
+ "~n RelUri: ~p"
+ "~n Headers: ~p"
+ "~n Body: ~p", [RelUri, Headers, Body]),
+ NextRequest =
+ case RelUri of
+ "/dummy_headers.html" ->
+ <<>>;
+ "/no_headers.html" ->
+ stop;
+ "/just_close.html" ->
+ stop;
+ _ ->
+ ContentLength = content_length(Headers),
+ case size(Body) - ContentLength of
+ 0 ->
+ <<>>;
+ _ ->
+ <<_BodyThisReq:ContentLength/binary,
+ Next/binary>> = Body,
+ Next
+ end
+ end,
+
+ tsp("handle_http_msg -> NextRequest: ~p", [NextRequest]),
+ case (catch ets:lookup(cookie, cookies)) of
+ [{cookies, true}]->
+ tsp("handle_http_msg -> check cookies ~p", []),
+ check_cookie(Headers);
+ _ ->
+ ok
+ end,
+
+ DefaultResponse = "HTTP/1.1 200 ok\r\n" ++
+ "Content-Length:32\r\n\r\n"
+ "<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";
+ "/ensure_host_header_with_port.html" ->
+ %% tsp("handle_http_msg -> validate host with port"),
+ case ensure_host_header_with_port(Headers) of
+ true ->
+ B =
+ "<HTML><BODY>" ++
+ "host with port" ++
+ "</BODY></HTML>",
+ Len = integer_to_list(length(B)),
+ "HTTP/1.1 200 ok\r\n" ++
+ "Content-Length:" ++ Len ++ "\r\n\r\n" ++ B;
+ false ->
+ B =
+ "<HTML><BODY>" ++
+ "Internal Server Error - host without port" ++
+ "</BODY></HTML>",
+ Len = integer_to_list(length(B)),
+ "HTTP/1.1 500 Internal Server Error\r\n" ++
+ "Content-Length:" ++ Len ++ "\r\n\r\n" ++ B
+ end;
+ "/300.html" ->
+ NewUri = ?URL_START ++
+ integer_to_list(?IP_PORT) ++ "/dummy.html",
+ "HTTP/1.1 300 Multiple Choices\r\n" ++
+ "Location:" ++ NewUri ++ "\r\n" ++
+ "Content-Length:0\r\n\r\n";
+ "/301.html" ->
+ NewUri = ?URL_START ++
+ integer_to_list(?IP_PORT) ++ "/dummy.html",
+ "HTTP/1.1 301 Moved Permanently\r\n" ++
+ "Location:" ++ NewUri ++ "\r\n" ++
+ "Content-Length:80\r\n\r\n" ++
+ "<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,
+
+ tsp("handle_http_msg -> Msg: ~p", [Msg]),
+ case Msg of
+ ok ->
+ %% Previously, this resulted in an {error, einval}. Now what?
+ ok;
+ close ->
+ %% Nothing to send, just close
+ gen_tcp:close(Socket);
+ _ when is_list(Msg) orelse is_binary(Msg) ->
+ gen_tcp:send(Socket, Msg)
+ end,
+ tsp("handle_http_msg -> done"),
+ NextRequest.
+
+ensure_host_header_with_port([]) ->
+ false;
+ensure_host_header_with_port(["host: " ++ Host| _]) ->
+ case string:tokens(Host, [$:]) of
+ [ActualHost, Port] ->
+ tsp("ensure_host_header_with_port -> "
+ "~n ActualHost: ~p"
+ "~n Port: ~p", [ActualHost, Port]),
+ true;
+ _ ->
+ false
+ end;
+ensure_host_header_with_port([_|T]) ->
+ ensure_host_header_with_port(T).
+
+auth_header([]) ->
+ auth_header_not_found;
+auth_header(["authorization:" ++ Value | _]) ->
+ {ok, string:strip(Value)};
+auth_header([_ | Tail]) ->
+ auth_header(Tail).
+
+handle_auth("Basic " ++ UserInfo, Challange, DefaultResponse) ->
+ case string:tokens(base64:decode_to_string(UserInfo), ":") of
+ ["alladin", "sesame"] = Auth ->
+ test_server:format("Auth: ~p~n", [Auth]),
+ DefaultResponse;
+ Other ->
+ test_server:format("UnAuth: ~p~n", [Other]),
+ Challange
+ end.
+
+check_cookie([]) ->
+ 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) ->
+ tsp("provocate_not_modified_bug -> entry with"
+ "~n Url: ~p", [Url]),
+
+ Timeout = 15000, %% 15s should be plenty
+
+ case http:request(get, {Url, []}, [{timeout, Timeout}], []) of
+ {ok, {{_, 200, _}, ReplyHeaders, _Body}} ->
+ tsp("provocate_not_modified_bug -> received 200 reply"
+ "~n ReplyHeaders: ~p", [ReplyHeaders]),
+ 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
+ tsp("provocate_not_modified_bug -> unchanged", []),
+ page_unchanged;
+ {ok, {{_, 200, _}, _, _}} ->
+ %% If the page has changed since the
+ %% last request we retry to
+ %% trigger the bug
+ tsp("provocate_not_modified_bug -> changed", []),
+ 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.
+ tsp("provocate_not_modified_bug -> timeout", []),
+ incorrect_result
+ end;
+ {ok, UnexpectedReply, ReplyHeaders, _} ->
+ tsp("provocate_not_modified_bug -> received unexpected reply"
+ "~n UnexpectedReply: ~p"
+ "~n ReplyHeaders: ~p", [UnexpectedReply, ReplyHeaders]),
+ unexpected_reply;
+ {error, Reason} ->
+ tsp("provocate_not_modified_bug -> unexpected failure"
+ "~n Reason: ~p", [Reason]),
+ unexpected_error
+ 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.
+
+
+not_implemented_yet() ->
+ exit(not_implemented_yet).
+
+
+expect(Command, Verify) ->
+ Result = (catch Command()),
+ Verify(Result).
+
+p(F) ->
+ p(F, []).
+
+p(F, A) ->
+ io:format("~p ~w:" ++ F ++ "~n", [self(), ?MODULE | A]).
+
+tsp(F) ->
+ tsp(F, []).
+tsp(F, A) ->
+ Timestamp = formated_timestamp(),
+ test_server:format("** ~s ** ~p ~p:" ++ F ++ "~n",
+ [Timestamp, self(), ?MODULE | A]).
+
+formated_timestamp() ->
+ format_timestamp( os:timestamp() ).
+
+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).
+
+tsf(Reason) ->
+ test_server:fail(Reason).
+
+skip(Reason) ->
+ {skip, Reason}.
+
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..ad5df656c6
--- /dev/null
+++ b/lib/inets/test/httpc_cookie_SUITE.erl
@@ -0,0 +1,451 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(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 = Case, Config0) ->
+ tsp("init_per_testcase(~p) -> entry with"
+ "~n Config0: ~p", [Case, Config0]),
+ Config = init_workdir(Case, Config0),
+ application:start(inets),
+ http:set_options([{cookies, verify}]),
+ watch_dog(Config);
+
+init_per_testcase(Case, Config0) ->
+ tsp("init_per_testcase(~p) -> entry with"
+ "~n Config0: ~p", [Case, Config0]),
+ Config = init_workdir(Case, Config0),
+ CaseDir = ?config(case_top_dir, Config),
+ application:load(inets),
+ application:set_env(inets, services, [{httpc, {default, CaseDir}}]),
+ 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].
+
+init_workdir(Case, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ SuiteTopDir = filename:join(PrivDir, ?MODULE),
+ case file:make_dir(SuiteTopDir) of
+ ok ->
+ ok;
+ {error, eexist} ->
+ ok;
+ Error ->
+ tsf({failed_creating_subsuite_top_dir, Error})
+ end,
+
+ CaseTopDir = filename:join(SuiteTopDir, Case),
+ ?line ok = file:make_dir(CaseTopDir),
+ [{suite_top_dir, SuiteTopDir},
+ {case_top_dir, CaseTopDir} | 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) ->
+ tsp("end_per_testcase(~p) -> entry with"
+ "~n Config: ~p", [Case, Config]),
+ application:stop(inets),
+ 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) ->
+ tsp("session_cookies_only -> Cookies 1: ~p", [httpc:which_cookies()]),
+
+ 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),
+
+ tsp("session_cookies_only -> Cookies 2: ~p", [httpc:which_cookies()]),
+ ok.
+
+netscape_cookies(doc) ->
+ ["Test that the old (original) format of cookies are accepted."];
+netscape_cookies(suite) ->
+ [];
+netscape_cookies(Config) when is_list(Config) ->
+ tsp("netscape_cookies -> Cookies 1: ~p", [httpc:which_cookies()]),
+
+ 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),
+
+ tsp("netscape_cookies -> Cookies 2: ~p", [httpc:which_cookies()]),
+ 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) ->
+ tsp("cookie_cancel -> Cookies 1: ~p", [httpc:which_cookies()]),
+
+ 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),
+
+ tsp("cookie_cancel -> Cookies 2: ~p", [httpc:which_cookies()]),
+ 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) ->
+ tsp("cookie_expires -> Cookies 1: ~p", [httpc:which_cookies()]),
+
+ 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),
+
+ tsp("cookie_expires -> Cookies 2: ~p", [httpc:which_cookies()]),
+ ok.
+
+persistent_cookie(doc) ->
+ ["Test domian cookie attribute"];
+persistent_cookie(suite) ->
+ [];
+persistent_cookie(Config) when is_list(Config)->
+ tsp("persistent_cookie -> Cookies 1: ~p", [httpc:which_cookies()]),
+
+ 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),
+ CaseDir = ?config(case_top_dir, Config),
+ application:stop(inets),
+ application:load(inets),
+ application:set_env(inets, services, [{httpc, {default, CaseDir}}]),
+ application:start(inets),
+ http:set_options([{cookies, enabled}]),
+ {"cookie","$Version=0; test_cookie=true; $Path=/"} = http:cookie_header(?URL),
+
+ tsp("persistent_cookie -> Cookies 2: ~p", [httpc:which_cookies()]),
+ ok.
+
+
+domain_cookie(doc) ->
+ ["Test the domian cookie attribute"];
+domain_cookie(suite) ->
+ [];
+domain_cookie(Config) when is_list(Config) ->
+ tsp("domain_cookie -> Cookies 1: ~p", [httpc:which_cookies()]),
+
+ 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),
+
+ tsp("domain_cookie -> Cookies 2: ~p", [httpc:which_cookies()]),
+ ok.
+
+
+secure_cookie(doc) ->
+ ["Test the secure cookie attribute"];
+secure_cookie(suite) ->
+ [];
+secure_cookie(Config) when is_list(Config) ->
+ tsp("secure_cookie -> entry with"
+ "~n Config: ~p", [Config]),
+
+ inets:enable_trace(max, io, httpc),
+
+ %% httpc:reset_cookies(),
+
+ tsp("secure_cookie -> Cookies 1: ~p", [httpc:which_cookies()]),
+
+ SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/; secure"}],
+ tsp("secure_cookie -> verify cookies (1)"),
+ ok = http:verify_cookies(SetCookieHeaders, ?URL),
+
+ tsp("secure_cookie -> Cookies 2: ~p", [httpc:which_cookies()]),
+
+ tsp("secure_cookie -> check cookie (secure)"),
+ check_cookie("$Version=0; test_cookie=true; $Path=/", ?URL_SECURE),
+
+ tsp("secure_cookie -> check cookie (plain)"),
+ check_cookie("", ?URL),
+
+ tsp("secure_cookie -> verify cookies (2)"),
+ SetCookieHeaders1 = [{"set-cookie", "test1_cookie=true; path=/; secure"}],
+ ok = http:verify_cookies(SetCookieHeaders1, ?URL),
+
+ tsp("secure_cookie -> Cookies 3: ~p", [httpc:which_cookies()]),
+
+ tsp("secure_cookie -> cookie header (3)"),
+ check_cookie("$Version=0; test_cookie=true; $Path=/; "
+ "test1_cookie=true; $Path=/",
+ ?URL_SECURE),
+%% {"cookie","$Version=0; test_cookie=true; $Path=/; "
+%% "test1_cookie=true; $Path=/"} = http:cookie_header(?URL_SECURE),
+
+ tsp("secure_cookie -> Cookies 4: ~p", [httpc:which_cookies()]),
+
+ inets:disable_trace(),
+ tsp("secure_cookie -> done"),
+ 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
+%%--------------------------------------------------------------------
+
+check_cookie(Expect, URL) ->
+ case http:cookie_header(URL) of
+ {"cookie", Expect} ->
+ ok;
+ {"cookie", Unexpected} ->
+ case lists:prefix(Expect, Unexpected) of
+ true ->
+ Extra = Unexpected -- Expect,
+ tsf({extra_cookie_info, Extra});
+ false ->
+ tsf({unknown_cookie, Expect, Unexpected})
+ end;
+ Bad ->
+ tsf({bad_cookies, Bad})
+ end.
+
+
+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".
+
+
+tsp(F) ->
+ tsp(F, []).
+tsp(F, A) ->
+ test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]).
+
+tsf(Reason) ->
+ test_server:fail(Reason).
+
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..07d94ea97a
--- /dev/null
+++ b/lib/inets/test/httpd_1_1.erl
@@ -0,0 +1,502 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(httpd_1_1).
+
+-include("test_server.hrl").
+-include("test_server_line.hrl").
+-include_lib("kernel/include/file.hrl").
+
+-export([host/4, chunked/4, expect/4, range/4, if_test/5, http_trace/4,
+ 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}]),
+
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET / HTTP/1.1\r\nHost:" ++ Host ++
+ "\r\nIf-Modified-Since:" ++
+ "AAA[...]AAAA" ++ "\r\n\r\n",
+ [{statuscode, 400}]),
+
+
+ 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}]),
+ ok.
+
+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..731a50c70b
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -0,0 +1,2081 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(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
new file mode 100644
index 0000000000..bb23d971f4
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/a.gif
Binary files differ
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
new file mode 100644
index 0000000000..eaecd2172a
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/alert.black.gif
Binary files differ
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
new file mode 100644
index 0000000000..a423894043
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/alert.red.gif
Binary files differ
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
new file mode 100644
index 0000000000..3a1c139fc4
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/apache_pb.gif
Binary files differ
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
new file mode 100644
index 0000000000..a694ae1ec3
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/back.gif
Binary files differ
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
new file mode 100644
index 0000000000..eb84268c4c
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/ball.gray.gif
Binary files differ
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
new file mode 100644
index 0000000000..a8425cb574
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/ball.red.gif
Binary files differ
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
new file mode 100644
index 0000000000..9a15cbae04
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/binary.gif
Binary files differ
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
new file mode 100644
index 0000000000..62d0363108
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/binhex.gif
Binary files differ
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
new file mode 100644
index 0000000000..0ccf01e198
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/blank.gif
Binary files differ
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
new file mode 100644
index 0000000000..270fdb1c06
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/bomb.gif
Binary files differ
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
new file mode 100644
index 0000000000..65dcd002ea
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/box1.gif
Binary files differ
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
new file mode 100644
index 0000000000..c43bc4faec
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/box2.gif
Binary files differ
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
new file mode 100644
index 0000000000..9f8cbe9f76
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/broken.gif
Binary files differ
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
new file mode 100644
index 0000000000..fbdcf575f7
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/burst.gif
Binary files differ
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
new file mode 100644
index 0000000000..eb97cb7333
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button1.gif
Binary files differ
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
new file mode 100644
index 0000000000..fe0c97998c
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button10.gif
Binary files differ
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
new file mode 100644
index 0000000000..7698455bf9
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button2.gif
Binary files differ
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
new file mode 100644
index 0000000000..a8b8319232
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button3.gif
Binary files differ
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
new file mode 100644
index 0000000000..0fd15a0d7f
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button4.gif
Binary files differ
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
new file mode 100644
index 0000000000..64241e5c5d
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button5.gif
Binary files differ
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
new file mode 100644
index 0000000000..867cfd1212
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button6.gif
Binary files differ
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
new file mode 100644
index 0000000000..b3f5fb248f
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button7.gif
Binary files differ
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
new file mode 100644
index 0000000000..7a308be8f6
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button8.gif
Binary files differ
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
new file mode 100644
index 0000000000..9acba576c0
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/button9.gif
Binary files differ
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
new file mode 100644
index 0000000000..3883088e7a
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/buttonl.gif
Binary files differ
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
new file mode 100644
index 0000000000..c4dc3887db
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/buttonr.gif
Binary files differ
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
new file mode 100644
index 0000000000..7555b6c164
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/c.gif
Binary files differ
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
new file mode 100644
index 0000000000..f8d76a8c23
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/comp.blue.gif
Binary files differ
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
new file mode 100644
index 0000000000..7664cd0364
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/comp.gray.gif
Binary files differ
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
new file mode 100644
index 0000000000..39e732739f
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/compressed.gif
Binary files differ
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
new file mode 100644
index 0000000000..b0ffb7e0cc
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/continued.gif
Binary files differ
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
new file mode 100644
index 0000000000..48264601ae
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/dir.gif
Binary files differ
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
new file mode 100644
index 0000000000..a354c871cd
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/down.gif
Binary files differ
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
new file mode 100644
index 0000000000..791be33105
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/dvi.gif
Binary files differ
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
new file mode 100644
index 0000000000..fbe353c282
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/f.gif
Binary files differ
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
new file mode 100644
index 0000000000..48264601ae
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/folder.gif
Binary files differ
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
new file mode 100644
index 0000000000..30979cb528
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/folder.open.gif
Binary files differ
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
new file mode 100644
index 0000000000..75332d9e59
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/folder.sec.gif
Binary files differ
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
new file mode 100644
index 0000000000..b2959b4c85
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/forward.gif
Binary files differ
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
new file mode 100644
index 0000000000..de60b2940f
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/generic.gif
Binary files differ
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
new file mode 100644
index 0000000000..94743981d9
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/generic.red.gif
Binary files differ
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
new file mode 100644
index 0000000000..88d5240c3c
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/generic.sec.gif
Binary files differ
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
new file mode 100644
index 0000000000..5cdbc7206d
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/hand.right.gif
Binary files differ
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
new file mode 100644
index 0000000000..85a5d68317
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/hand.up.gif
Binary files differ
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
new file mode 100644
index 0000000000..35443fb63a
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/htdig.gif
Binary files differ
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
new file mode 100644
index 0000000000..ad1686e448
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/icon.sheet.gif
Binary files differ
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
new file mode 100644
index 0000000000..01e442bfa9
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/image1.gif
Binary files differ
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
new file mode 100644
index 0000000000..751faeea36
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/image2.gif
Binary files differ
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
new file mode 100644
index 0000000000..4f30484ff6
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/image3.gif
Binary files differ
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
new file mode 100644
index 0000000000..162478fb3a
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/index.gif
Binary files differ
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
new file mode 100644
index 0000000000..c96338a152
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/layout.gif
Binary files differ
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
new file mode 100644
index 0000000000..279e6710d4
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/left.gif
Binary files differ
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
new file mode 100644
index 0000000000..c5b6889a76
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/link.gif
Binary files differ
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
new file mode 100644
index 0000000000..0035183774
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/movie.gif
Binary files differ
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
new file mode 100644
index 0000000000..7b917b4e91
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/p.gif
Binary files differ
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
new file mode 100644
index 0000000000..39bc90e795
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/patch.gif
Binary files differ
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
new file mode 100644
index 0000000000..c88fd777c4
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pdf.gif
Binary files differ
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
new file mode 100644
index 0000000000..6f7a0ae7a7
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie0.gif
Binary files differ
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
new file mode 100644
index 0000000000..03aa6be71e
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie1.gif
Binary files differ
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
new file mode 100644
index 0000000000..b04c5e0908
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie2.gif
Binary files differ
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
new file mode 100644
index 0000000000..4db9d023ed
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie3.gif
Binary files differ
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
new file mode 100644
index 0000000000..93471fdd88
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie4.gif
Binary files differ
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
new file mode 100644
index 0000000000..57aee93f07
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie5.gif
Binary files differ
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
new file mode 100644
index 0000000000..0dc327b569
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie6.gif
Binary files differ
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
new file mode 100644
index 0000000000..8661337f06
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie7.gif
Binary files differ
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
new file mode 100644
index 0000000000..59ddb34ce0
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/pie8.gif
Binary files differ
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
new file mode 100644
index 0000000000..0e6e506e00
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/portal.gif
Binary files differ
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
new file mode 100644
index 0000000000..d324ab80ea
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/poweredby.gif
Binary files differ
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
new file mode 100644
index 0000000000..0f565bc1db
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/ps.gif
Binary files differ
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
new file mode 100644
index 0000000000..818a5cdc7e
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/quill.gif
Binary files differ
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
new file mode 100644
index 0000000000..b256e5f75f
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/right.gif
Binary files differ
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
new file mode 100644
index 0000000000..af6ba2b097
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/screw1.gif
Binary files differ
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
new file mode 100644
index 0000000000..06dccb3e44
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/screw2.gif
Binary files differ
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
new file mode 100644
index 0000000000..d8a853bc58
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/script.gif
Binary files differ
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
new file mode 100644
index 0000000000..8efb49f55d
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/sound1.gif
Binary files differ
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
new file mode 100644
index 0000000000..48e6a7fb2f
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/sound2.gif
Binary files differ
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
new file mode 100644
index 0000000000..7067070da2
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/sphere1.gif
Binary files differ
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
new file mode 100644
index 0000000000..a9e462a377
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/sphere2.gif
Binary files differ
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
new file mode 100644
index 0000000000..4cfe0a5e0f
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/star.gif
Binary files differ
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
new file mode 100644
index 0000000000..a0c83cb85b
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/star_blank.gif
Binary files differ
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
new file mode 100644
index 0000000000..617e779efa
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/tar.gif
Binary files differ
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
new file mode 100644
index 0000000000..45e43233b8
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/tex.gif
Binary files differ
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
new file mode 100644
index 0000000000..4c623909fb
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/text.gif
Binary files differ
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
new file mode 100644
index 0000000000..33697dbb66
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/transfer.gif
Binary files differ
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
new file mode 100644
index 0000000000..32b1ea23fb
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/unknown.gif
Binary files differ
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
new file mode 100644
index 0000000000..6d6d6d1ebf
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/up.gif
Binary files differ
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
new file mode 100644
index 0000000000..4387d529f6
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/uu.gif
Binary files differ
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
new file mode 100644
index 0000000000..4387d529f6
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/uuencoded.gif
Binary files differ
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
new file mode 100644
index 0000000000..05b4ec2058
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/world1.gif
Binary files differ
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
new file mode 100644
index 0000000000..e3203f7a88
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/icons/world2.gif
Binary files differ
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..581c9c5782
--- /dev/null
+++ b/lib/inets/test/httpd_basic_SUITE.erl
@@ -0,0 +1,281 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(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).
+
+-define(URL_START, "http://localhost:").
+
+all(doc) ->
+ ["Basic test of httpd."];
+
+all(suite) ->
+ [
+ uri_too_long_414,
+ header_too_long_413,
+ escaped_url_in_error_body
+ ].
+
+%%--------------------------------------------------------------------
+%% 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),
+
+ Dummy =
+"<HTML>
+<HEAD>
+<TITLE>/index.html</TITLE>
+</HEAD>
+<BODY>
+DUMMY
+</BODY>
+</HTML>",
+
+ DummyFile = filename:join([PrivDir,"dummy.html"]),
+ {ok, Fd} = file:open(DummyFile, [write]),
+ ok = file:write(Fd, Dummy),
+ ok = file:close(Fd),
+ 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).
+
+
+%%-------------------------------------------------------------------------
+%%-------------------------------------------------------------------------
+
+escaped_url_in_error_body(doc) ->
+ ["Test Url-encoding see OTP-8940"];
+escaped_url_in_error_body(suite) ->
+ [];
+escaped_url_in_error_body(Config) when is_list(Config) ->
+ tsp("escaped_url_in_error_body -> entry"),
+ HttpdConf = ?config(httpd_conf, Config),
+ {ok, Pid} = inets:start(httpd, [{port, 0} | HttpdConf]),
+ Info = httpd:info(Pid),
+ Port = proplists:get_value(port, Info),
+ _Address = proplists:get_value(bind_address, Info),
+
+ %% Request 1
+ tsp("escaped_url_in_error_body -> request 1"),
+ URL1 = ?URL_START ++ integer_to_list(Port),
+ %% Make sure the server is ok, by making a request for a valid page
+ case httpc:request(get, {URL1 ++ "/dummy.html", []},
+ [{url_encode, false},
+ {version, "HTTP/1.0"}],
+ [{full_result, false}]) of
+ {ok, {200, _}} ->
+ %% Don't care about the the body, just that we get a ok response
+ ok;
+ {ok, UnexpectedOK1} ->
+ tsf({unexpected_ok_1, UnexpectedOK1})
+ end,
+
+ %% Request 2
+ tsp("escaped_url_in_error_body -> request 2"),
+ %% Make sure the server is ok, by making a request for a valid page
+ case httpc:request(get, {URL1 ++ "/dummy.html", []},
+ [{url_encode, true},
+ {version, "HTTP/1.0"}],
+ [{full_result, false}]) of
+ {ok, {200, _}} ->
+ %% Don't care about the the body, just that we get a ok response
+ ok;
+ {ok, UnexpectedOK2} ->
+ tsf({unexpected_ok_2, UnexpectedOK2})
+ end,
+
+ %% Request 3
+ tsp("escaped_url_in_error_body -> request 3"),
+ %% Ask for a non-existing page(1)
+ Path = "/<b>this_is_bold<b>",
+ HTMLEncodedPath = http_util:html_encode(Path),
+ URL2 = URL1 ++ Path,
+ case httpc:request(get, {URL2, []},
+ [{url_encode, true},
+ {version, "HTTP/1.0"}],
+ [{full_result, false}]) of
+ {ok, {404, Body3}} ->
+ case find_URL_path(string:tokens(Body3, " ")) of
+ HTMLEncodedPath ->
+ ok;
+ BadPath3 ->
+ tsf({unexpected_path_3, HTMLEncodedPath, BadPath3})
+ end;
+ {ok, UnexpectedOK3} ->
+ tsf({unexpected_ok_1, UnexpectedOK3})
+ end,
+
+ %% Request 4
+ tsp("escaped_url_in_error_body -> request 4"),
+ %% Ask for a non-existing page(2)
+ case httpc:request(get, {URL2, []},
+ [{url_encode, false},
+ {version, "HTTP/1.0"}],
+ [{full_result, false}]) of
+ {ok, {404, Body4}} ->
+ case find_URL_path(string:tokens(Body4, " ")) of
+ HTMLEncodedPath ->
+ ok;
+ BadPath4 ->
+ tsf({unexpected_path_2, HTMLEncodedPath, BadPath4})
+ end;
+ {ok, UnexpectedOK4} ->
+ tsf({unexpected_ok_4, UnexpectedOK4})
+ end,
+ tsp("escaped_url_in_error_body -> stop inets"),
+ inets:stop(httpd, Pid),
+ tsp("escaped_url_in_error_body -> done"),
+ ok.
+
+find_URL_path([]) ->
+ "";
+find_URL_path(["URL", URL | _]) ->
+ URL;
+find_URL_path([_ | Rest]) ->
+ find_URL_path(Rest).
+
+tsf(Reason) ->
+ test_server:fail(Reason).
+
+
+tsp(F) ->
+ tsp(F, []).
+tsp(F, A) ->
+ Timestamp = formated_timestamp(),
+ test_server:format("** ~s ** ~p ~p:" ++ F ++ "~n",
+ [Timestamp, self(), ?MODULE | A]).
+
+formated_timestamp() ->
+ format_timestamp( os:timestamp() ).
+
+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).
+
+
+skip(Reason) ->
+ {skip, Reason}.
+
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..617851c77d
--- /dev/null
+++ b/lib/inets/test/httpd_mod.erl
@@ -0,0 +1,947 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(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
new file mode 100644
index 0000000000..bb23d971f4
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/a.gif
Binary files differ
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
new file mode 100644
index 0000000000..eaecd2172a
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/alert.black.gif
Binary files differ
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
new file mode 100644
index 0000000000..a423894043
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/alert.red.gif
Binary files differ
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
new file mode 100644
index 0000000000..3a1c139fc4
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/apache_pb.gif
Binary files differ
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
new file mode 100644
index 0000000000..a694ae1ec3
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/back.gif
Binary files differ
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
new file mode 100644
index 0000000000..eb84268c4c
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/ball.gray.gif
Binary files differ
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
new file mode 100644
index 0000000000..a8425cb574
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/ball.red.gif
Binary files differ
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
new file mode 100644
index 0000000000..9a15cbae04
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/binary.gif
Binary files differ
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
new file mode 100644
index 0000000000..62d0363108
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/binhex.gif
Binary files differ
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
new file mode 100644
index 0000000000..0ccf01e198
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/blank.gif
Binary files differ
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
new file mode 100644
index 0000000000..270fdb1c06
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/bomb.gif
Binary files differ
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
new file mode 100644
index 0000000000..65dcd002ea
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/box1.gif
Binary files differ
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
new file mode 100644
index 0000000000..c43bc4faec
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/box2.gif
Binary files differ
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
new file mode 100644
index 0000000000..9f8cbe9f76
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/broken.gif
Binary files differ
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
new file mode 100644
index 0000000000..fbdcf575f7
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/burst.gif
Binary files differ
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
new file mode 100644
index 0000000000..eb97cb7333
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/button1.gif
Binary files differ
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
new file mode 100644
index 0000000000..fe0c97998c
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/button10.gif
Binary files differ
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
new file mode 100644
index 0000000000..7698455bf9
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/button2.gif
Binary files differ
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
new file mode 100644
index 0000000000..a8b8319232
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/button3.gif
Binary files differ
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
new file mode 100644
index 0000000000..0fd15a0d7f
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/button4.gif
Binary files differ
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
new file mode 100644
index 0000000000..64241e5c5d
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/button5.gif
Binary files differ
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
new file mode 100644
index 0000000000..867cfd1212
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/button6.gif
Binary files differ
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
new file mode 100644
index 0000000000..b3f5fb248f
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/button7.gif
Binary files differ
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
new file mode 100644
index 0000000000..7a308be8f6
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/button8.gif
Binary files differ
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
new file mode 100644
index 0000000000..9acba576c0
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/button9.gif
Binary files differ
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
new file mode 100644
index 0000000000..3883088e7a
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/buttonl.gif
Binary files differ
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
new file mode 100644
index 0000000000..c4dc3887db
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/buttonr.gif
Binary files differ
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
new file mode 100644
index 0000000000..7555b6c164
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/c.gif
Binary files differ
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
new file mode 100644
index 0000000000..f8d76a8c23
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/comp.blue.gif
Binary files differ
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
new file mode 100644
index 0000000000..7664cd0364
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/comp.gray.gif
Binary files differ
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
new file mode 100644
index 0000000000..39e732739f
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/compressed.gif
Binary files differ
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
new file mode 100644
index 0000000000..b0ffb7e0cc
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/continued.gif
Binary files differ
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
new file mode 100644
index 0000000000..48264601ae
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/dir.gif
Binary files differ
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
new file mode 100644
index 0000000000..a354c871cd
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/down.gif
Binary files differ
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
new file mode 100644
index 0000000000..791be33105
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/dvi.gif
Binary files differ
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
new file mode 100644
index 0000000000..fbe353c282
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/f.gif
Binary files differ
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
new file mode 100644
index 0000000000..48264601ae
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/folder.gif
Binary files differ
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
new file mode 100644
index 0000000000..30979cb528
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/folder.open.gif
Binary files differ
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
new file mode 100644
index 0000000000..75332d9e59
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/folder.sec.gif
Binary files differ
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
new file mode 100644
index 0000000000..b2959b4c85
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/forward.gif
Binary files differ
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
new file mode 100644
index 0000000000..de60b2940f
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/generic.gif
Binary files differ
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
new file mode 100644
index 0000000000..94743981d9
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/generic.red.gif
Binary files differ
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
new file mode 100644
index 0000000000..88d5240c3c
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/generic.sec.gif
Binary files differ
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
new file mode 100644
index 0000000000..5cdbc7206d
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/hand.right.gif
Binary files differ
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
new file mode 100644
index 0000000000..85a5d68317
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/hand.up.gif
Binary files differ
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
new file mode 100644
index 0000000000..35443fb63a
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/htdig.gif
Binary files differ
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
new file mode 100644
index 0000000000..ad1686e448
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/icon.sheet.gif
Binary files differ
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
new file mode 100644
index 0000000000..01e442bfa9
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/image1.gif
Binary files differ
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
new file mode 100644
index 0000000000..751faeea36
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/image2.gif
Binary files differ
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
new file mode 100644
index 0000000000..4f30484ff6
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/image3.gif
Binary files differ
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
new file mode 100644
index 0000000000..162478fb3a
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/index.gif
Binary files differ
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
new file mode 100644
index 0000000000..c96338a152
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/layout.gif
Binary files differ
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
new file mode 100644
index 0000000000..279e6710d4
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/left.gif
Binary files differ
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
new file mode 100644
index 0000000000..c5b6889a76
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/link.gif
Binary files differ
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
new file mode 100644
index 0000000000..0035183774
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/movie.gif
Binary files differ
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
new file mode 100644
index 0000000000..7b917b4e91
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/p.gif
Binary files differ
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
new file mode 100644
index 0000000000..39bc90e795
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/patch.gif
Binary files differ
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
new file mode 100644
index 0000000000..c88fd777c4
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/pdf.gif
Binary files differ
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
new file mode 100644
index 0000000000..6f7a0ae7a7
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/pie0.gif
Binary files differ
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
new file mode 100644
index 0000000000..03aa6be71e
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/pie1.gif
Binary files differ
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
new file mode 100644
index 0000000000..b04c5e0908
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/pie2.gif
Binary files differ
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
new file mode 100644
index 0000000000..4db9d023ed
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/pie3.gif
Binary files differ
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
new file mode 100644
index 0000000000..93471fdd88
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/pie4.gif
Binary files differ
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
new file mode 100644
index 0000000000..57aee93f07
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/pie5.gif
Binary files differ
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
new file mode 100644
index 0000000000..0dc327b569
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/pie6.gif
Binary files differ
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
new file mode 100644
index 0000000000..8661337f06
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/pie7.gif
Binary files differ
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
new file mode 100644
index 0000000000..59ddb34ce0
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/pie8.gif
Binary files differ
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
new file mode 100644
index 0000000000..0e6e506e00
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/portal.gif
Binary files differ
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
new file mode 100644
index 0000000000..d324ab80ea
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/poweredby.gif
Binary files differ
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
new file mode 100644
index 0000000000..0f565bc1db
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/ps.gif
Binary files differ
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
new file mode 100644
index 0000000000..818a5cdc7e
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/quill.gif
Binary files differ
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
new file mode 100644
index 0000000000..b256e5f75f
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/right.gif
Binary files differ
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
new file mode 100644
index 0000000000..af6ba2b097
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/screw1.gif
Binary files differ
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
new file mode 100644
index 0000000000..06dccb3e44
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/screw2.gif
Binary files differ
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
new file mode 100644
index 0000000000..d8a853bc58
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/script.gif
Binary files differ
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
new file mode 100644
index 0000000000..8efb49f55d
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/sound1.gif
Binary files differ
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
new file mode 100644
index 0000000000..48e6a7fb2f
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/sound2.gif
Binary files differ
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
new file mode 100644
index 0000000000..7067070da2
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/sphere1.gif
Binary files differ
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
new file mode 100644
index 0000000000..a9e462a377
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/sphere2.gif
Binary files differ
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
new file mode 100644
index 0000000000..4cfe0a5e0f
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/star.gif
Binary files differ
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
new file mode 100644
index 0000000000..a0c83cb85b
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/star_blank.gif
Binary files differ
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
new file mode 100644
index 0000000000..617e779efa
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/tar.gif
Binary files differ
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
new file mode 100644
index 0000000000..45e43233b8
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/tex.gif
Binary files differ
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
new file mode 100644
index 0000000000..4c623909fb
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/text.gif
Binary files differ
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
new file mode 100644
index 0000000000..33697dbb66
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/transfer.gif
Binary files differ
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
new file mode 100644
index 0000000000..32b1ea23fb
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/unknown.gif
Binary files differ
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
new file mode 100644
index 0000000000..6d6d6d1ebf
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/up.gif
Binary files differ
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
new file mode 100644
index 0000000000..4387d529f6
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/uu.gif
Binary files differ
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
new file mode 100644
index 0000000000..4387d529f6
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/uuencoded.gif
Binary files differ
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
new file mode 100644
index 0000000000..05b4ec2058
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/world1.gif
Binary files differ
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
new file mode 100644
index 0000000000..e3203f7a88
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/icons/world2.gif
Binary files differ
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..8d748defd8
--- /dev/null
+++ b/lib/inets/test/httpd_test_lib.erl
@@ -0,0 +1,379 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(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) ->
+ tsp("verify_request -> connect to [~w] ~p:~w", [SocketType, Host, Port]),
+ {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) ->
+ io:format("~p ~w[~w]request -> entry with"
+ "~n Module: ~p"
+ "~n Function: ~p"
+ "~n Args: ~p"
+ "~n", [self(), ?MODULE, ?LINE, Module, Function, Args]),
+ HeadRequest = lists:sublist(RequestStr, 1, 4),
+ receive
+ {tcp, Socket, Data} ->
+ io:format("~p ~w[~w]request -> received (tcp) data"
+ "~n Data: ~p"
+ "~n", [self(), ?MODULE, ?LINE, 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 ->
+ io:format("~p ~w[~w]request -> "
+ "received (tcp) closed when whole_body"
+ "~n", [self(), ?MODULE, ?LINE]),
+ print(tcp, "closed", State),
+ State#state{body = hd(Args)};
+ {tcp_closed, Socket} ->
+ io:format("~p ~w[~w]request -> received (tcp) closed"
+ "~n", [self(), ?MODULE, ?LINE]),
+ test_server:fail(connection_closed);
+ {tcp_error, Socket, Reason} ->
+ io:format("~p ~w[~w]request -> received (tcp) error"
+ "~n Reason: ~p"
+ "~n", [self(), ?MODULE, ?LINE, Reason]),
+ test_server:fail({tcp_error, Reason});
+ {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 ->
+ io:format("~p ~w[~w]request -> timeout"
+ "~n", [self(), ?MODULE, ?LINE]),
+ test_server:fail(connection_timed_out)
+ end.
+
+handle_http_msg({Version, StatusCode, ReasonPharse, Headers, Body},
+ State = #state{request = RequestStr}) ->
+ io:format("~p ~w[~w]handle_http_msg -> entry with"
+ "~n Version: ~p"
+ "~n StatusCode: ~p"
+ "~n ReasonPharse: ~p"
+ "~n Headers: ~p"
+ "~n Body: ~p"
+ "~n", [self(), ?MODULE, ?LINE,
+ Version, StatusCode, ReasonPharse, Headers, Body]),
+ 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) ->
+
+ 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}} ->
+ tsf({wrong_version, [{got, Version},
+ {expected, Ver}]});
+ _ ->
+ case Version of
+ "HTTP/1.1" ->
+ ok;
+ _ ->
+ tsf({wrong_version, [{got, Version},
+ {expected, "HTTP/1.1"}]})
+ end
+ end.
+
+check_status_code(StatusCode, [], Options) ->
+ tsf({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 ->
+ tsf({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) ->
+ 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) ->
+ tsf(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;
+ _ ->
+ tsf(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.
+
+tsf(Reason) ->
+ test_server:fail(Reason).
+
+%% tsp(F) ->
+%% tsp(F, []).
+tsp(F, A) ->
+ Timestamp = formated_timestamp(),
+ test_server:format("** ~s ** ~p ~p:" ++ F ++ "~n",
+ [Timestamp, self(), ?MODULE | A]).
+
+formated_timestamp() ->
+ format_timestamp( os:timestamp() ).
+
+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/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..ba525f62c1
--- /dev/null
+++ b/lib/inets/test/inets.spec
@@ -0,0 +1,2 @@
+{topcase, {dir, "../inets_test"}}.
+{hosts, ["fobi"]}.
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..56983caace
--- /dev/null
+++ b/lib/inets/test/inets_SUITE.erl
@@ -0,0 +1,583 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(inets_SUITE).
+
+-include("test_server.hrl").
+-include("test_server_line.hrl").
+-include("inets_test_lib.hrl").
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-define(NUM_DEFAULT_SERVICES, 1).
+
+all(doc) ->
+ ["Test suites for the inets application."];
+
+all(suite) ->
+ [
+ app_test,
+ appup_test,
+ services_test,
+ httpd_reload
+ ].
+
+services_test(suite) ->
+ [
+ start_inets,
+ start_httpc,
+ start_httpd,
+ start_ftpc,
+ start_tftpd
+ ].
+
+
+%%--------------------------------------------------------------------
+%% 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),
+ tsp("start_httpc -> entry with"
+ "~n Config: ~p", [Config]),
+
+ PrivDir = ?config(priv_dir, Config),
+
+ tsp("start_httpc -> start (empty) inets"),
+ ok = inets:start(),
+
+ tsp("start_httpc -> start httpc (as inets service) with profile foo"),
+ {ok, Pid0} = inets:start(httpc, [{profile, foo}]),
+
+ tsp("start_httpc -> check running services"),
+ Pids0 = [ServicePid || {_, ServicePid} <- inets:services()],
+ true = lists:member(Pid0, Pids0),
+ [_|_] = inets:services_info(),
+
+ tsp("start_httpc -> stop httpc"),
+ inets:stop(httpc, Pid0),
+
+ tsp("start_httpc -> sleep some"),
+ test_server:sleep(100),
+
+ tsp("start_httpc -> check running services"),
+ Pids1 = [ServicePid || {_, ServicePid} <- inets:services()],
+ false = lists:member(Pid0, Pids1),
+
+ tsp("start_httpc -> start httpc (stand-alone) with profile bar"),
+ {ok, Pid1} = inets:start(httpc, [{profile, bar}], stand_alone),
+
+ tsp("start_httpc -> check running services"),
+ Pids2 = [ServicePid || {_, ServicePid} <- inets:services()],
+ false = lists:member(Pid1, Pids2),
+
+ tsp("start_httpc -> stop httpc"),
+ ok = inets:stop(stand_alone, Pid1),
+ receive
+ {'EXIT', Pid1, shutdown} ->
+ ok
+ after 100 ->
+ tsf(stand_alone_not_shutdown)
+ end,
+
+ tsp("start_httpc -> stop inets"),
+ ok = inets:stop(),
+
+ tsp("start_httpc -> unload inets"),
+ application:load(inets),
+
+ tsp("start_httpc -> set inets environment (httpc profile foo)"),
+ application:set_env(inets, services, [{httpc,[{profile, foo},
+ {data_dir, PrivDir}]}]),
+
+ tsp("start_httpc -> start inets"),
+ ok = inets:start(),
+
+ tsp("start_httpc -> check running services"),
+ (?NUM_DEFAULT_SERVICES + 1) = length(inets:services()),
+
+ tsp("start_httpc -> unset inets env"),
+ application:unset_env(inets, services),
+
+ tsp("start_httpc -> stop inets"),
+ ok = inets:stop(),
+
+ tsp("start_httpc -> start (empty) inets"),
+ ok = inets:start(),
+
+ tsp("start_httpc -> start inets httpc service with profile foo"),
+ {ok, Pid3} = inets:start(httpc, [{profile, foo}]),
+
+ tsp("start_httpc -> stop inets service httpc with profile foo"),
+ ok = inets:stop(httpc, foo),
+
+ tsp("start_httpc -> check running services"),
+ Pids3 = [ServicePid || {_, ServicePid} <- inets:services()],
+ false = lists:member(Pid3, Pids3),
+
+ tsp("start_httpc -> stop inets"),
+ ok = inets:stop(),
+
+ tsp("start_httpc -> done"),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+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),
+ inets:disable_trace(),
+ inets:enable_trace(max, io, ftpc),
+ ok = inets:start(),
+ try
+ begin
+ {_Tag, FtpdHost} = ftp_suite_lib:dirty_select_ftpd_host(Config),
+ case inets:start(ftpc, [{host, FtpdHost}]) 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, FtpdHost}], 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 ->
+ tsf(stand_alone_not_shutdown)
+ end,
+ ok = inets:stop(),
+ inets:disable_trace(),
+ ok;
+ _ ->
+ inets:disable_trace(),
+ {skip, "Unable to reach selected FTP server " ++ FtpdHost}
+ end
+ end
+ catch
+ throw:{error, not_found} ->
+ inets:disable_trace(),
+ {skip, "No available FTP servers"}
+ end.
+
+
+
+%%-------------------------------------------------------------------------
+
+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.
+
+
+tsf(Reason) ->
+ test_server:fail(Reason).
+
+tsp(F) ->
+ tsp(F, []).
+tsp(F, A) ->
+ Timestamp = formated_timestamp(),
+ test_server:format("** ~s ** ~p ~p:" ++ F ++ "~n", [Timestamp, self(), ?MODULE | A]).
+
+i(F) ->
+ i(F, []).
+
+i(F, A) ->
+ Timestamp = formated_timestamp(),
+ io:format("*** ~s ~w:" ++ F ++ "~n", [Timestamp, ?MODULE | A]).
+
+formated_timestamp() ->
+ format_timestamp( os:timestamp() ).
+
+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..ba41e0960c
--- /dev/null
+++ b/lib/inets/test/inets_sup_SUITE.erl
@@ -0,0 +1,414 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(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).
+
+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) ->
+ inets:disable_trace(),
+ inets:enable_trace(max, io, ftpc),
+ [] = supervisor:which_children(ftp_sup),
+ try
+ begin
+ {_Tag, FtpdHost} = ftp_suite_lib:dirty_select_ftpd_host(Config),
+ case inets:start(ftpc, [{host, FtpdHost}]) 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),
+ inets:disable_trace(),
+ ok;
+ Children ->
+ inets:disable_trace(),
+ exit({unexpected_children, Children})
+ end;
+ _ ->
+ inets:disable_trace(),
+ {skip, "Unable to reach test FTP server"}
+ end
+ end
+ catch
+ throw:{error, not_found} ->
+ inets:disable_trace(),
+ {skip, "No available FTP servers"}
+ end.
+
+
+%%-------------------------------------------------------------------------
+%% 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) ->
+ tsp("httpc_subtree -> entry with"
+ "~n Config: ~p", [Config]),
+
+ tsp("httpc_subtree -> start inets service httpc with profile foo"),
+ {ok, Foo} = inets:start(httpc, [{profile, foo}]),
+
+ tsp("httpc_subtree -> "
+ "start stand-alone inets service httpc with profile bar"),
+ {ok, Bar} = inets:start(httpc, [{profile, bar}], stand_alone),
+
+ tsp("httpc_subtree -> retreive list of httpc instances"),
+ HttpcChildren = supervisor:which_children(httpc_profile_sup),
+ tsp("httpc_subtree -> HttpcChildren: ~n~p", [HttpcChildren]),
+
+ tsp("httpc_subtree -> verify httpc stand-alone instances"),
+ {value, {httpc_manager, _, worker, [httpc_manager]}} =
+ lists:keysearch(httpc_manager, 1, HttpcChildren),
+
+ tsp("httpc_subtree -> verify httpc (named) instances"),
+ {value,{{httpc,foo}, Pid, worker, [httpc_manager]}} =
+ lists:keysearch({httpc, foo}, 1, HttpcChildren),
+ false = lists:keysearch({httpc, bar}, 1, HttpcChildren),
+
+ tsp("httpc_subtree -> stop inets"),
+ inets:stop(httpc, Pid),
+
+ tsp("httpc_subtree -> done"),
+ ok.
+
+inet_port() ->
+ {ok, Socket} = gen_tcp:listen(0, [{reuseaddr, true}]),
+ {ok, Port} = inet:port(Socket),
+ gen_tcp:close(Socket),
+ Port.
+
+
+tsp(F) ->
+ tsp(F, []).
+tsp(F, A) ->
+ test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]).
+
+tsf(Reason) ->
+ test_server:fail(Reason).
+
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..609bc89e15
--- /dev/null
+++ b/lib/inets/test/inets_test_lib.erl
@@ -0,0 +1,355 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(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) ->
+ ?DEBUG("start_http_server -> entry with"
+ "~n Conf: ~p", [Conf]),
+ inets_ensure_loaded(),
+ inets_set_env(services, [{httpd, Conf}]),
+ inets_ensure_started(),
+ ok.
+
+
+start_http_server_ssl(FileName) ->
+ application:start(ssl),
+ catch start_http_server(FileName).
+
+inets_ensure_loaded() ->
+ ensure_loaded(inets).
+
+ensure_loaded(App) ->
+ case application:load(App) of
+ ok ->
+ ok;
+ {error, {already_loaded, _}} ->
+ ok;
+ LoadRes ->
+ ?LOG("start_http_server -> failed loading ~p: ~p", [App, LoadRes]),
+ tsf({failed_loading, LoadRes})
+ end.
+
+
+inets_set_env(Service, Config) ->
+ app_set_env(inets, Service, Config).
+
+app_set_env(App, Param, Value) ->
+ case application:set_env(App, Param, Value) of
+ ok ->
+ ?DEBUG("start_http_server -> env set", []),
+ ok;
+ SetEnvRes ->
+ ?LOG("start_http_server -> failed set env for ~p: ~p",
+ [App, SetEnvRes]),
+ exit({failed_set_env, App, SetEnvRes})
+ end.
+
+inets_ensure_started() ->
+ ensure_app_started(inets).
+
+ensure_app_started(App) ->
+ case application:start(App) of
+ ok ->
+ ?DEBUG("start_http_server -> ~p started", [App]),
+ ok;
+ {error, {already_started, _}} ->
+ ok;
+ StartRes ->
+ ?LOG("start_http_server -> failed starting ~p: ~p",
+ [App, StartRes]),
+ exit({failed_starting, App, StartRes})
+ end.
+
+
+%% ----------------------------------------------------------------------
+%% 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}}).
+
+tsf(Reason) ->
+ test_server:fail(Reason).
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()).