aboutsummaryrefslogtreecommitdiffstats
path: root/lib/inets
diff options
context:
space:
mode:
Diffstat (limited to 'lib/inets')
-rw-r--r--lib/inets/Makefile21
-rw-r--r--lib/inets/doc/archive/rfc1945.html21
-rw-r--r--lib/inets/doc/src/Makefile23
-rw-r--r--lib/inets/doc/src/book.xml25
-rw-r--r--lib/inets/doc/src/fascicules.xml2
-rw-r--r--lib/inets/doc/src/ftp.xml506
-rw-r--r--lib/inets/doc/src/ftp_client.xml75
-rw-r--r--lib/inets/doc/src/http_client.xml121
-rw-r--r--lib/inets/doc/src/http_server.xml1078
-rw-r--r--lib/inets/doc/src/http_uri.xml176
-rw-r--r--lib/inets/doc/src/httpc.xml869
-rw-r--r--lib/inets/doc/src/httpd.xml1288
-rw-r--r--lib/inets/doc/src/httpd_conf.xml162
-rw-r--r--lib/inets/doc/src/httpd_custom_api.xml78
-rw-r--r--lib/inets/doc/src/httpd_socket.xml49
-rw-r--r--lib/inets/doc/src/httpd_util.xml289
-rw-r--r--lib/inets/doc/src/inets.xml134
-rw-r--r--lib/inets/doc/src/inets_services.xml84
-rw-r--r--lib/inets/doc/src/introduction.xml57
-rw-r--r--lib/inets/doc/src/mod_alias.xml89
-rw-r--r--lib/inets/doc/src/mod_auth.xml409
-rw-r--r--lib/inets/doc/src/mod_esi.xml241
-rw-r--r--lib/inets/doc/src/mod_security.xml163
-rw-r--r--lib/inets/doc/src/notes.xml1060
-rw-r--r--lib/inets/doc/src/notes_history.xml27
-rw-r--r--lib/inets/doc/src/part.xml39
-rw-r--r--lib/inets/doc/src/part_notes.xml25
-rw-r--r--lib/inets/doc/src/part_notes_history.xml25
-rw-r--r--lib/inets/doc/src/ref_man.xml33
-rw-r--r--lib/inets/doc/src/tftp.xml583
-rw-r--r--lib/inets/examples/Makefile25
-rw-r--r--lib/inets/examples/httpd_load_test/Makefile21
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt.erl21
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt.sh.skel21
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt_client.erl21
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt_ctrl.erl21
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt_logger.erl21
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt_logger.hrl25
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt_random_html.erl26
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt_server.erl21
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt_slave.erl23
-rw-r--r--lib/inets/examples/httpd_load_test/modules.mk21
-rw-r--r--lib/inets/examples/server_root/Makefile21
-rw-r--r--lib/inets/examples/server_root/conf/8080.conf2
-rw-r--r--lib/inets/examples/server_root/conf/8888.conf2
-rw-r--r--lib/inets/examples/server_root/conf/httpd.conf25
-rw-r--r--lib/inets/examples/server_root/conf/ssl.conf2
-rw-r--r--lib/inets/include/httpd.hrl25
-rw-r--r--lib/inets/include/mod_auth.hrl25
-rw-r--r--lib/inets/priv/Makefile23
-rw-r--r--lib/inets/src/Makefile23
-rw-r--r--lib/inets/src/ftp/Makefile21
-rw-r--r--lib/inets/src/ftp/ftp.erl717
-rw-r--r--lib/inets/src/ftp/ftp_internal.hrl25
-rw-r--r--lib/inets/src/ftp/ftp_progress.erl23
-rw-r--r--lib/inets/src/ftp/ftp_response.erl26
-rw-r--r--lib/inets/src/ftp/ftp_sup.erl23
-rw-r--r--lib/inets/src/http_client/Makefile21
-rw-r--r--lib/inets/src/http_client/httpc.erl57
-rw-r--r--lib/inets/src/http_client/httpc_cookie.erl44
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl232
-rw-r--r--lib/inets/src/http_client/httpc_handler_sup.erl21
-rw-r--r--lib/inets/src/http_client/httpc_internal.hrl21
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl105
-rw-r--r--lib/inets/src/http_client/httpc_profile_sup.erl21
-rw-r--r--lib/inets/src/http_client/httpc_request.erl43
-rw-r--r--lib/inets/src/http_client/httpc_response.erl23
-rw-r--r--lib/inets/src/http_client/httpc_sup.erl23
-rw-r--r--lib/inets/src/http_lib/Makefile21
-rw-r--r--lib/inets/src/http_lib/http_chunk.erl205
-rw-r--r--lib/inets/src/http_lib/http_internal.hrl24
-rw-r--r--lib/inets/src/http_lib/http_request.erl50
-rw-r--r--lib/inets/src/http_lib/http_response.erl55
-rw-r--r--lib/inets/src/http_lib/http_transport.erl246
-rw-r--r--lib/inets/src/http_lib/http_uri.erl95
-rw-r--r--lib/inets/src/http_lib/http_util.erl90
-rw-r--r--lib/inets/src/http_server/Makefile38
-rw-r--r--lib/inets/src/http_server/httpd.erl371
-rw-r--r--lib/inets/src/http_server/httpd.hrl21
-rw-r--r--lib/inets/src/http_server/httpd_acceptor.erl133
-rw-r--r--lib/inets/src/http_server/httpd_acceptor_sup.erl115
-rw-r--r--lib/inets/src/http_server/httpd_cgi.erl21
-rw-r--r--lib/inets/src/http_server/httpd_conf.erl497
-rw-r--r--lib/inets/src/http_server/httpd_connection_sup.erl69
-rw-r--r--lib/inets/src/http_server/httpd_custom.erl95
-rw-r--r--lib/inets/src/http_server/httpd_custom_api.erl32
-rw-r--r--lib/inets/src/http_server/httpd_esi.erl21
-rw-r--r--lib/inets/src/http_server/httpd_example.erl57
-rw-r--r--lib/inets/src/http_server/httpd_file.erl23
-rw-r--r--lib/inets/src/http_server/httpd_instance_sup.erl91
-rw-r--r--lib/inets/src/http_server/httpd_internal.hrl23
-rw-r--r--lib/inets/src/http_server/httpd_log.erl38
-rw-r--r--lib/inets/src/http_server/httpd_manager.erl792
-rw-r--r--lib/inets/src/http_server/httpd_misc_sup.erl61
-rw-r--r--lib/inets/src/http_server/httpd_request.erl368
-rw-r--r--lib/inets/src/http_server/httpd_request_handler.erl238
-rw-r--r--lib/inets/src/http_server/httpd_response.erl140
-rw-r--r--lib/inets/src/http_server/httpd_script_env.erl39
-rw-r--r--lib/inets/src/http_server/httpd_socket.erl23
-rw-r--r--lib/inets/src/http_server/httpd_sup.erl123
-rw-r--r--lib/inets/src/http_server/httpd_util.erl100
-rw-r--r--lib/inets/src/http_server/mod_actions.erl43
-rw-r--r--lib/inets/src/http_server/mod_alias.erl109
-rw-r--r--lib/inets/src/http_server/mod_auth.erl705
-rw-r--r--lib/inets/src/http_server/mod_auth.hrl21
-rw-r--r--lib/inets/src/http_server/mod_auth_dets.erl69
-rw-r--r--lib/inets/src/http_server/mod_auth_mnesia.erl23
-rw-r--r--lib/inets/src/http_server/mod_auth_plain.erl221
-rw-r--r--lib/inets/src/http_server/mod_auth_server.erl263
-rw-r--r--lib/inets/src/http_server/mod_browser.erl44
-rw-r--r--lib/inets/src/http_server/mod_cgi.erl43
-rw-r--r--lib/inets/src/http_server/mod_dir.erl34
-rw-r--r--lib/inets/src/http_server/mod_disk_log.erl90
-rw-r--r--lib/inets/src/http_server/mod_esi.erl126
-rw-r--r--lib/inets/src/http_server/mod_get.erl23
-rw-r--r--lib/inets/src/http_server/mod_head.erl27
-rw-r--r--lib/inets/src/http_server/mod_htaccess.erl64
-rw-r--r--lib/inets/src/http_server/mod_include.erl598
-rw-r--r--lib/inets/src/http_server/mod_log.erl31
-rw-r--r--lib/inets/src/http_server/mod_range.erl23
-rw-r--r--lib/inets/src/http_server/mod_responsecontrol.erl23
-rw-r--r--lib/inets/src/http_server/mod_security.erl257
-rw-r--r--lib/inets/src/http_server/mod_security_server.erl382
-rw-r--r--lib/inets/src/http_server/mod_trace.erl23
-rw-r--r--lib/inets/src/inets_app/Makefile25
-rw-r--r--lib/inets/src/inets_app/inets.app.src33
-rw-r--r--lib/inets/src/inets_app/inets.appup.src35
-rw-r--r--lib/inets/src/inets_app/inets.erl23
-rw-r--r--lib/inets/src/inets_app/inets.mk21
-rw-r--r--lib/inets/src/inets_app/inets_app.erl23
-rw-r--r--lib/inets/src/inets_app/inets_internal.hrl25
-rw-r--r--lib/inets/src/inets_app/inets_lib.erl50
-rw-r--r--lib/inets/src/inets_app/inets_regexp.erl413
-rw-r--r--lib/inets/src/inets_app/inets_service.erl21
-rw-r--r--lib/inets/src/inets_app/inets_sup.erl23
-rw-r--r--lib/inets/src/inets_app/inets_trace.erl53
-rw-r--r--lib/inets/src/tftp/Makefile21
-rw-r--r--lib/inets/src/tftp/tftp.erl23
-rw-r--r--lib/inets/src/tftp/tftp.hrl23
-rw-r--r--lib/inets/src/tftp/tftp_binary.erl23
-rw-r--r--lib/inets/src/tftp/tftp_engine.erl76
-rw-r--r--lib/inets/src/tftp/tftp_file.erl23
-rw-r--r--lib/inets/src/tftp/tftp_lib.erl27
-rw-r--r--lib/inets/src/tftp/tftp_logger.erl27
-rw-r--r--lib/inets/src/tftp/tftp_sup.erl25
-rw-r--r--lib/inets/test/Makefile51
-rw-r--r--lib/inets/test/erl_make_certs.erl31
-rw-r--r--lib/inets/test/ftp_SUITE.erl1125
-rw-r--r--lib/inets/test/ftp_SUITE_data/vsftpd.conf26
-rw-r--r--lib/inets/test/ftp_format_SUITE.erl135
-rw-r--r--lib/inets/test/ftp_freebsd_x86_test.erl160
-rw-r--r--lib/inets/test/ftp_linux_ppc_test.erl158
-rw-r--r--lib/inets/test/ftp_linux_x86_test.erl160
-rw-r--r--lib/inets/test/ftp_macosx_ppc_test.erl159
-rw-r--r--lib/inets/test/ftp_macosx_x86_test.erl159
-rw-r--r--lib/inets/test/ftp_netbsd_x86_test.erl159
-rw-r--r--lib/inets/test/ftp_openbsd_x86_test.erl158
-rw-r--r--lib/inets/test/ftp_property_test_SUITE.erl53
-rw-r--r--lib/inets/test/ftp_solaris10_sparc_test.erl161
-rw-r--r--lib/inets/test/ftp_solaris10_x86_test.erl162
-rw-r--r--lib/inets/test/ftp_solaris8_sparc_test.erl159
-rw-r--r--lib/inets/test/ftp_solaris9_sparc_test.erl158
-rw-r--r--lib/inets/test/ftp_suite_lib.erl1675
-rw-r--r--lib/inets/test/ftp_ticket_test.erl61
-rw-r--r--lib/inets/test/ftp_windows_2003_server_test.erl167
-rw-r--r--lib/inets/test/ftp_windows_xp_test.erl157
-rw-r--r--lib/inets/test/http_format_SUITE.erl288
-rw-r--r--lib/inets/test/httpc_SUITE.erl442
-rw-r--r--lib/inets/test/httpc_SUITE_data/ssl_client_cert.pem45
-rw-r--r--lib/inets/test/httpc_SUITE_data/ssl_server_cert.pem45
-rw-r--r--lib/inets/test/httpc_cookie_SUITE.erl392
-rw-r--r--lib/inets/test/httpc_proxy_SUITE.erl78
-rw-r--r--lib/inets/test/httpc_proxy_SUITE_data/apache2/apache2.conf21
-rwxr-xr-xlib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh23
-rw-r--r--lib/inets/test/httpd_1_0.erl39
-rw-r--r--lib/inets/test/httpd_1_1.erl203
-rw-r--r--lib/inets/test/httpd_SUITE.erl4117
-rw-r--r--lib/inets/test/httpd_SUITE_data/Makefile.src5
-rw-r--r--lib/inets/test/httpd_SUITE_data/mime_types.txt100
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/config/mime.types4
-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_all.erl240
-rw-r--r--lib/inets/test/httpd_basic_SUITE.erl395
-rw-r--r--lib/inets/test/httpd_basic_SUITE_data/Makefile.src14
-rw-r--r--lib/inets/test/httpd_basic_SUITE_data/cgi_sleep.c26
l---------lib/inets/test/httpd_basic_SUITE_data/printenv.bat1
l---------lib/inets/test/httpd_basic_SUITE_data/printenv.sh1
-rw-r--r--lib/inets/test/httpd_block.erl82
-rw-r--r--lib/inets/test/httpd_load.erl31
-rw-r--r--lib/inets/test/httpd_mod.erl220
-rw-r--r--lib/inets/test/httpd_mod_SUITE.erl77
-rw-r--r--lib/inets/test/httpd_poll.erl27
-rw-r--r--lib/inets/test/httpd_test_data/server_root/Makefile21
-rw-r--r--lib/inets/test/httpd_test_data/server_root/conf/8080.conf2
-rw-r--r--lib/inets/test/httpd_test_data/server_root/conf/8888.conf2
-rw-r--r--lib/inets/test/httpd_test_data/server_root/conf/httpd.conf25
-rw-r--r--lib/inets/test/httpd_test_data/server_root/conf/ssl.conf2
-rw-r--r--lib/inets/test/httpd_test_lib.erl193
-rw-r--r--lib/inets/test/httpd_time_test.erl90
-rw-r--r--lib/inets/test/inets_SUITE.erl318
-rw-r--r--lib/inets/test/inets_app_test.erl307
-rw-r--r--lib/inets/test/inets_appup_test.erl358
-rw-r--r--lib/inets/test/inets_socketwrap_SUITE.erl154
-rw-r--r--lib/inets/test/inets_socketwrap_SUITE_data/Makefile.src39
-rw-r--r--lib/inets/test/inets_socketwrap_SUITE_data/setuid_socket_wrap.c259
-rw-r--r--lib/inets/test/inets_sup_SUITE.erl432
-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.erl61
-rw-r--r--lib/inets/test/inets_test_lib.hrl109
-rw-r--r--lib/inets/test/old_httpd_SUITE.erl2347
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/Makefile.src14
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/cgi_echo.c97
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/Makefile (renamed from lib/inets/test/httpd_SUITE_data/server_root/Makefile)21
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/auth/group3
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/auth/passwd4
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.bat9
-rwxr-xr-xlib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.sh6
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/conf/8080.conf (renamed from lib/inets/test/httpd_SUITE_data/server_root/conf/8080.conf)2
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/conf/8888.conf (renamed from lib/inets/test/httpd_SUITE_data/server_root/conf/8888.conf)2
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/conf/httpd.conf (renamed from lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf)25
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/conf/mime.types (renamed from lib/inets/test/httpd_SUITE_data/server_root/conf/mime.types)0
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/conf/ssl.conf (renamed from lib/inets/test/httpd_SUITE_data/server_root/conf/ssl.conf)2
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/config.shtml70
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_open/dummy.html10
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/dummy.html10
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/top_secret/index.html9
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/echo.shtml35
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/exec.shtml30
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/flastmod.shtml29
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/fsize.shtml29
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/include.shtml33
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/index.html25
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/last_modified.html22
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/friedrich.html7
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/oech.html4
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/welcome.html1
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_open/dummy.html10
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/dummy.html10
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/top_secret/index.html9
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/open/dummy.html10
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/dummy.html10
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/top_secret/index.html9
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/README (renamed from lib/inets/test/httpd_SUITE_data/server_root/icons/README)0
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/a.gifbin0 -> 246 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.black.gifbin0 -> 242 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.red.gifbin0 -> 247 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/apache_pb.gifbin0 -> 2326 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/back.gifbin0 -> 216 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.gray.gifbin0 -> 233 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.red.gifbin0 -> 205 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/binary.gifbin0 -> 246 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/binhex.gifbin0 -> 246 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/blank.gifbin0 -> 148 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/bomb.gifbin0 -> 308 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/box1.gifbin0 -> 251 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/box2.gifbin0 -> 268 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/broken.gifbin0 -> 247 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/burst.gifbin0 -> 235 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button1.gifbin0 -> 755 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button10.gifbin0 -> 781 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button2.gifbin0 -> 785 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button3.gifbin0 -> 745 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button4.gifbin0 -> 786 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button5.gifbin0 -> 780 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button6.gifbin0 -> 791 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button7.gifbin0 -> 796 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button8.gifbin0 -> 784 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/button9.gifbin0 -> 784 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonl.gifbin0 -> 587 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonr.gifbin0 -> 576 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/c.gifbin0 -> 242 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.blue.gifbin0 -> 251 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.gray.gifbin0 -> 246 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/compressed.gifbin0 -> 1038 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/continued.gifbin0 -> 214 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/dir.gifbin0 -> 225 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/down.gifbin0 -> 163 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/dvi.gifbin0 -> 238 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/f.gifbin0 -> 236 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.gifbin0 -> 225 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.open.gifbin0 -> 242 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.sec.gifbin0 -> 243 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/forward.gifbin0 -> 219 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.gifbin0 -> 221 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.red.gifbin0 -> 220 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.sec.gifbin0 -> 249 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.right.gifbin0 -> 217 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.up.gifbin0 -> 223 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/htdig.gifbin0 -> 1822 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/icon.sheet.gifbin0 -> 11977 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/image1.gifbin0 -> 274 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/image2.gifbin0 -> 309 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/image3.gifbin0 -> 286 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/index.gifbin0 -> 268 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/layout.gifbin0 -> 276 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/left.gifbin0 -> 172 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/link.gifbin0 -> 249 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/movie.gifbin0 -> 243 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/p.gifbin0 -> 237 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/patch.gifbin0 -> 251 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pdf.gifbin0 -> 249 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie0.gifbin0 -> 188 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie1.gifbin0 -> 198 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie2.gifbin0 -> 198 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie3.gifbin0 -> 191 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie4.gifbin0 -> 193 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie5.gifbin0 -> 189 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie6.gifbin0 -> 186 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie7.gifbin0 -> 185 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie8.gifbin0 -> 173 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/portal.gifbin0 -> 254 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/poweredby.gifbin0 -> 2748 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/ps.gifbin0 -> 244 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/quill.gifbin0 -> 267 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/right.gifbin0 -> 172 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw1.gifbin0 -> 258 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw2.gifbin0 -> 263 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/script.gifbin0 -> 242 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound1.gifbin0 -> 248 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound2.gifbin0 -> 221 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere1.gifbin0 -> 285 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere2.gifbin0 -> 264 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/star.gifbin0 -> 89 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/star_blank.gifbin0 -> 53 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/tar.gifbin0 -> 243 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/tex.gifbin0 -> 251 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/text.gifbin0 -> 229 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/transfer.gifbin0 -> 242 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/unknown.gifbin0 -> 245 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/up.gifbin0 -> 164 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/uu.gifbin0 -> 236 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/uuencoded.gifbin0 -> 236 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/world1.gifbin0 -> 228 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/icons/world2.gifbin0 -> 261 bytes
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip (renamed from lib/inets/test/httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip)0
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_client.pem31
-rw-r--r--lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_server.pem31
-rw-r--r--lib/inets/test/property_test/README12
-rw-r--r--lib/inets/test/property_test/ftp_simple_client_server.erl307
-rw-r--r--lib/inets/test/property_test/ftp_simple_client_server_data/vsftpd.conf26
-rw-r--r--lib/inets/test/tftp_SUITE.erl60
-rw-r--r--lib/inets/test/tftp_test_lib.erl23
-rw-r--r--lib/inets/test/tftp_test_lib.hrl23
-rw-r--r--lib/inets/test/uri_SUITE.erl81
-rw-r--r--lib/inets/vsn.mk26
347 files changed, 17888 insertions, 18394 deletions
diff --git a/lib/inets/Makefile b/lib/inets/Makefile
index d837a3396a..872df9d055 100644
--- a/lib/inets/Makefile
+++ b/lib/inets/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+# Copyright Ericsson AB 1996-2016. 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/.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# 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.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
diff --git a/lib/inets/doc/archive/rfc1945.html b/lib/inets/doc/archive/rfc1945.html
index cf88f53319..3cb37c1f1e 100644
--- a/lib/inets/doc/archive/rfc1945.html
+++ b/lib/inets/doc/archive/rfc1945.html
@@ -1,14 +1,15 @@
<!--
- ``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 via the world wide web at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ ``Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
The Initial Developer of the Original Code is Ericsson Utvecklings AB.
Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
diff --git a/lib/inets/doc/src/Makefile b/lib/inets/doc/src/Makefile
index 1a8e1c7ca8..cb71fbeb9c 100644
--- a/lib/inets/doc/src/Makefile
+++ b/lib/inets/doc/src/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. All Rights Reserved.
+# Copyright Ericsson AB 1997-2015. All Rights Reserved.
#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# 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.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
@@ -51,7 +52,7 @@ XML_REF3_FILES = \
http_uri.xml\
httpc.xml\
httpd.xml \
- httpd_conf.xml \
+ httpd_custom_api.xml \
httpd_socket.xml \
httpd_util.xml \
mod_alias.xml \
diff --git a/lib/inets/doc/src/book.xml b/lib/inets/doc/src/book.xml
index 51cbb2d963..29f0c6a6d8 100644
--- a/lib/inets/doc/src/book.xml
+++ b/lib/inets/doc/src/book.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE book SYSTEM "book.dtd">
<book xmlns:xi="http://www.w3.org/2001/XInclude">
<header titlestyle="normal">
<copyright>
- <year>1997</year><year>2012</year>
+ <year>1997</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
diff --git a/lib/inets/doc/src/fascicules.xml b/lib/inets/doc/src/fascicules.xml
index ea3b988882..c075478967 100644
--- a/lib/inets/doc/src/fascicules.xml
+++ b/lib/inets/doc/src/fascicules.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
<fascicules>
diff --git a/lib/inets/doc/src/ftp.xml b/lib/inets/doc/src/ftp.xml
index f8f11ec705..42bece4d38 100644
--- a/lib/inets/doc/src/ftp.xml
+++ b/lib/inets/doc/src/ftp.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>1997</year><year>2011</year>
+ <year>1997</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
@@ -29,95 +30,94 @@
<file>ftp.xml</file>
</header>
<module>ftp</module>
- <modulesummary>A File Transfer Protocol client</modulesummary>
+ <modulesummary>A File Transfer Protocol client.</modulesummary>
<description>
- <p>The <c>ftp</c> module implements a client for file transfer
- according to a subset of the File Transfer Protocol (see <term
- id="RFC"></term>959). </p>
+ <p>This module implements a client for file transfer
+ according to a subset of the File Transfer Protocol (FTP), see
+ <url href="http://www.ietf.org/rfc/rfc959.txt">RFC 959</url>.</p>
- <p>Starting from inets version 4.4.1 the ftp
- client will always try to use passive ftp mode and only resort
- to active ftp mode if this fails. There is a start option
- <seealso marker="#mode">mode</seealso> where this default behavior
- may be changed. </p>
+ <p>As from <c>Inets</c> 4.4.1, the FTP
+ client always tries to use passive FTP mode and only resort
+ to active FTP mode if this fails. This default behavior can be
+ changed by start option <seealso marker="#mode">mode</seealso>.</p>
<marker id="two_start"></marker>
- <p>There are two ways to start an ftp client. One is using the
- <seealso marker="#service_start">Inets service framework</seealso>
- and the other is to start it directy as a standalone process
- using the <seealso marker="#open">open</seealso> function. </p>
+ <p>An FTP client can be started in two ways. One is using the
+ <seealso marker="#service_start">Inets service framework</seealso>,
+ the other is to start it directly as a standalone process
+ using function <seealso marker="#open">open</seealso>.</p>
- <p>For a simple example of an ftp session see
- <seealso marker="ftp_client">Inets User's Guide.</seealso></p>
+ <p>For a simple example of an FTP session, see
+ <seealso marker="ftp_client">Inets User's Guide</seealso>.</p>
<p>In addition to the ordinary functions for receiving and sending
- files (see <c>recv/2</c>, <c>recv/3</c>, <c>send/2</c> and
+ files (see <c>recv/2</c>, <c>recv/3</c>, <c>send/2</c>, and
<c>send/3</c>) there are functions for receiving remote files as
- binaries (see <c>recv_bin/2</c>) and for sending binaries to to be
+ binaries (see <c>recv_bin/2</c>) and for sending binaries to be
stored as remote files (see <c>send_bin/3</c>).</p>
- <p>There is also a set of functions for sending and receiving
- contiguous parts of a file to be stored in a remote file (for send
- see <c>send_chunk_start/2</c>, <c>send_chunk/2</c> and
- <c>send_chunk_end/1</c> and for receive see
+ <p>A set of functions is provvided for sending and receiving
+ contiguous parts of a file to be stored in a remote file. For send,
+ see <c>send_chunk_start/2</c>, <c>send_chunk/2</c>, and
+ <c>send_chunk_end/1</c>. For receive, see
<c>recv_chunk_start/2</c> and <c>recv_chunk/</c>).</p>
- <p>The particular return values of the functions below depend very
+ <p>The return values of the following functions depend
much on the implementation of the FTP server at the remote
- host. In particular the results from <c>ls</c> and <c>nlist</c>
+ host. In particular, the results from <c>ls</c> and <c>nlist</c>
varies. Often real errors are not reported as errors by <c>ls</c>,
- even if for instance a file or directory does not
+ even if, for example, a file or directory does not
exist. <c>nlist</c> is usually more strict, but some
implementations have the peculiar behaviour of responding with an
- error, if the request is a listing of the contents of directory
- which exists but is empty.</p>
+ error if the request is a listing of the contents of a directory
+ that exists but is empty.</p>
<marker id="service_start"></marker>
</description>
<section>
- <title>FTP CLIENT SERVICE START/STOP </title>
+ <title>FTP CLIENT SERVICE START/STOP</title>
<p>The FTP client can be started and stopped dynamically in runtime by
- calling the Inets application API
+ calling the <c>Inets</c> application API
<c>inets:start(ftpc, ServiceConfig)</c>,
or <c>inets:start(ftpc, ServiceConfig, How)</c>, and
<c>inets:stop(ftpc, Pid)</c>.
- See <seealso marker="inets">inets(3)</seealso> for more info. </p>
- <p>Below follows a description of
- the available configuration options.</p>
+ For details, see <seealso marker="inets">inets(3)</seealso>.</p>
+
+ <p>The available configuration options are as follows:</p>
<taglist>
<tag>{host, Host}</tag>
<item>
<marker id="host"></marker>
- <p>Host = <c>string() | ip_address()</c> </p>
+ <p>Host = <c>string() | ip_address()</c></p>
</item>
<tag>{port, Port}</tag>
<item>
<marker id="port"></marker>
- <p>Port = <c>integer() > 0</c> </p>
- <p>Default is 21.</p>
+ <p>Port = <c>integer() > 0</c></p>
+ <p>Default is <c>21</c>.</p>
</item>
<tag>{mode, Mode}</tag>
<item>
<marker id="mode"></marker>
- <p>Mode = <c>active | passive</c> </p>
- <p>Default is <c>passive</c>. </p>
+ <p>Mode = <c>active | passive</c></p>
+ <p>Default is <c>passive</c>.</p>
</item>
<tag>{verbose, Verbose}</tag>
<item>
<marker id="verbose"></marker>
<p>Verbose = <c>boolean()</c> </p>
- <p>This determines if the FTP communication should be
- verbose or not. </p>
- <p>Default is <c>false</c>. </p>
+ <p>Determines if the FTP communication is to be
+ verbose or not.</p>
+ <p>Default is <c>false</c>.</p>
</item>
<tag>{debug, Debug}</tag>
@@ -125,93 +125,90 @@
<marker id="debug"></marker>
<p>Debug = <c>trace | debug | disable</c> </p>
<p>Debugging using the dbg toolkit. </p>
- <p>Default is <c>disable</c>. </p>
+ <p>Default is <c>disable</c>.</p>
</item>
<tag>{ipfamily, IpFamily}</tag>
<item>
<marker id="ipfamily"></marker>
<p>IpFamily = <c>inet | inet6 | inet6fb4</c> </p>
- <p>With <c>inet6fb4</c> the client behaves as before
- (it tries to use IPv6 and only if that does not work, it
- uses IPv4). </p>
- <p>Default is <c>inet</c> (IPv4). </p>
+ <p>With <c>inet6fb4</c> the client behaves as before, that is,
+ tries to use IPv6, and only if that does not work it
+ uses IPv4).</p>
+ <p>Default is <c>inet</c> (IPv4).</p>
</item>
<tag>{timeout, Timeout}</tag>
<item>
<marker id="timeout"></marker>
- <p>Timeout = <c>non_neg_integer()</c> </p>
- <p>Connection timeout. </p>
- <p>Default is 60000 (milliseconds). </p>
+ <p>Timeout = <c>non_neg_integer()</c></p>
+ <p>Connection time-out.</p>
+ <p>Default is <c>60000</c> (milliseconds).</p>
</item>
<tag>{dtimeout, DTimeout}</tag>
<item>
<marker id="dtimeout"></marker>
<p>DTimeout = <c>non_neg_integer() | infinity</c> </p>
- <p>Data Connect timeout.
- The time the client will wait for the server to connect to the
- data socket. </p>
- <p>Default is infinity. </p>
+ <p>Data connect time-out.
+ The time the client waits for the server to connect to the
+ data socket.</p>
+ <p>Default is <c>infinity</c>. </p>
</item>
<tag>{progress, Progress}</tag>
<item>
<marker id="progress"></marker>
<p>Progress = <c>ignore | {CBModule, CBFunction, InitProgress}</c></p>
- <p>CBModule = <c>atom()</c>, CBFunction = <c>atom()</c> </p>
- <p>InitProgress = <c>term()</c> </p>
- <p>Default is <c>ignore</c>. </p>
+ <p><c>CBModule = atom()</c>, <c>CBFunction = atom()</c></p>
+ <p><c>InitProgress = term()</c></p>
+ <p>Default is <c>ignore</c>.</p>
</item>
</taglist>
- <p>The progress option is intended to be used by applications that
- want to create some type of progress report such as a progress bar in
- a GUI. The default value for the progress option is ignore
- e.i. the option is not used. When the progress option is
- specified the following will happen when ftp:send/[3,4] or
- ftp:recv/[3,4] are called.</p>
+ <p>Option <c>progress</c> is intended to be used by applications that
+ want to create some type of progress report, such as a progress bar in
+ a GUI. Default for the progress option is <c>ignore</c>,
+ that is, the option is not used. When the progress option is
+ specified, the following happens when <c>ftp:send/[3,4]</c> or
+ <c>ftp:recv/[3,4]</c> are called:</p>
<list type="bulleted">
<item>
- <p>Before a file is transfered the following call will
- be made to indicate the start of the file transfer and how big
+ <p>Before a file is transferred, the following call is
+ made to indicate the start of the file transfer and how large
the file is. The return value of the callback function
- should be a new value for the UserProgressTerm that will
- bu used as input next time the callback function is
+ is to be a new value for the <c>UserProgressTerm</c> that will
+ be used as input the next time the callback function is
called.</p>
- <br></br>
<p><c>
CBModule:CBFunction(InitProgress, File, {file_size, FileSize})
</c></p>
- <br></br>
</item>
<item>
- <p>Every time a chunk of bytes is transfered the
- following call will be made:</p>
- <br></br>
+ <p>Every time a chunk of bytes is transferred the
+ following call is made:</p>
<p><c>
- CBModule:CBFunction(UserProgressTerm, File, {transfer_size, TransferSize}) </c></p>
- <br></br>
+ CBModule:CBFunction(UserProgressTerm, File, {transfer_size, TransferSize})
+ </c></p>
</item>
<item>
- <p>At the end of the file the following call will be
- made to indicate the end of the transfer.</p>
- <br></br>
+ <p>At the end of the file the following call is
+ made to indicate the end of the transfer:</p>
<p><c>
- CBModule:CBFunction(UserProgressTerm, File, {transfer_size, 0}) </c></p>
- <br></br>
+ CBModule:CBFunction(UserProgressTerm, File, {transfer_size, 0})
+ </c></p>
</item>
</list>
- <p>The callback function should be defined as </p>
+ <p>The callback function is to be defined as follows:</p>
<p><c>
- CBModule:CBFunction(UserProgressTerm, File, Size) -> UserProgressTerm </c></p>
+ CBModule:CBFunction(UserProgressTerm, File, Size) -> UserProgressTerm
+ </c></p>
<p><c>
CBModule = CBFunction = atom()
@@ -226,50 +223,51 @@
</c></p>
<p><c>
- Size = {transfer_size, integer()} | {file_size, integer()} | {file_size, unknown} </c></p>
+ Size = {transfer_size, integer()} | {file_size, integer()} | {file_size, unknown}
+ </c></p>
- <p>Alas for remote files it is not possible for ftp to determine the
+ <p>For remote files, <c>ftp</c> cannot determine the
file size in a platform independent way. In this case the size
- will be <c>unknown</c> and it is left to the application to find
- out the size. </p>
+ becomes <c>unknown</c> and it is left to the application to
+ determine the size.</p>
<note>
<p>The callback is made by a middleman process, hence the
- file transfer will not be affected by the code in the progress
- callback function. If the callback should crash this will be
- detected by the ftp connection process that will print an
- info-report and then go one as if the progress option was set
- to ignore. </p>
+ file transfer is not affected by the code in the progress
+ callback function. If the callback crashes, this is
+ detected by the FTP connection process, which then prints an
+ info-report and goes on as if the progress option was set
+ to <c>ignore</c>.</p>
</note>
<p>The file transfer type is set to the default of the FTP server
- when the session is opened. This is usually ASCCI-mode.
+ when the session is opened. This is usually ASCCI mode.
</p>
- <p>The current local working directory (cf. <c>lpwd/1</c>) is set to
- the value reported by <c>file:get_cwd/1</c>. the wanted
+ <p>The current local working directory (compare <c>lpwd/1</c>) is set
+ to the value reported by <c>file:get_cwd/1</c>, the wanted
local directory.
</p>
<p>The return value <c>Pid</c> is used as a reference to the
- newly created ftp client in all other functions, and they should
- be called by the process that created the connection. The ftp
+ newly created FTP client in all other functions, and they are to
+ be called by the process that created the connection. The FTP
client process monitors the process that created it and
- will terminate if that process terminates.</p>
+ terminates if that process terminates.</p>
</section>
<section>
- <title>COMMON DATA TYPES </title>
- <p>Here follows type definitions that are used by more than one
- function in the FTP client API. </p>
- <p><c> pid() - identifier of an ftp connection.</c></p>
- <p><c> string() = list of ASCII characters.</c></p>
- <p><c> shortage_reason() = etnospc | epnospc</c></p>
- <p><c> restriction_reason() = epath | efnamena | elogin | enotbinary
- - note not all restrictions may always relevant to all functions
- </c></p>
- <p><c>common_reason() = econn | eclosed | term() - some kind of
- explanation of what went wrong.</c></p>
+ <title>DATA TYPES</title>
+ <p>The following type definitions are used by more than one
+ function in the FTP client API:</p>
+ <p><c>pid()</c> = identifier of an FTP connection</p>
+ <p><c>string()</c> = list of ASCII characters</p>
+ <p><c>shortage_reason()</c> = <c>etnospc | epnospc</c></p>
+ <p><c>restriction_reason()</c> = <c>epath | efnamena | elogin | enotbinary</c>
+ - all restrictions are not always relevant to all functions
+ </p>
+ <p><c>common_reason()</c> = <c>econn | eclosed | term()</c>
+ - some explanation of what went wrong</p>
<marker id="account"></marker>
</section>
@@ -277,15 +275,14 @@
<funcs>
<func>
<name>account(Pid, Account) -> ok | {error, Reason}</name>
- <fsummary>Specify which account to use.</fsummary>
+ <fsummary>Specifies which account to use.</fsummary>
<type>
<v>Pid = pid()</v>
<v>Account = string()</v>
<v>Reason = eacct | common_reason()</v>
</type>
<desc>
- <p>If an account is needed for an operation set the account
- with this operation.</p>
+ <p>Sets the account for an operation, if needed.</p>
<marker id="append"></marker>
<marker id="append2"></marker>
@@ -296,7 +293,8 @@
<func>
<name>append(Pid, LocalFile) -> </name>
<name>append(Pid, LocalFile, RemoteFile) -> ok | {error, Reason}</name>
- <fsummary>Transfer file to remote server, and append it to Remotefile.</fsummary>
+ <fsummary>Transfers a file to remote server, and appends it to
+ <c>Remotefile</c>.</fsummary>
<type>
<v>Pid = pid()</v>
<v>LocalFile = RemoteFile = string()</v>
@@ -305,9 +303,9 @@
<desc>
<p>Transfers the file <c>LocalFile</c> to the remote server. If
<c>RemoteFile</c> is specified, the name of the remote file that the
- file will be appended to is set to <c>RemoteFile</c>; otherwise
- the name is set to <c>LocalFile</c> If the file does not exists the
- file will be created.</p>
+ file is appended to is set to <c>RemoteFile</c>, otherwise
+ to <c>LocalFile</c>. If the file does not exists,
+ it is created.</p>
<marker id="append_bin"></marker>
</desc>
@@ -315,17 +313,17 @@
<func>
<name>append_bin(Pid, Bin, RemoteFile) -> ok | {error, Reason}</name>
- <fsummary>Transfer a binary into a remote file.</fsummary>
+ <fsummary>Transfers a binary into a remote file.</fsummary>
<type>
<v>Pid = pid()</v>
<v>Bin = binary()()</v>
<v>RemoteFile = string()</v>
- <v>Reason = restriction_reason()| shortage_reason() | common_reason()</v>
+ <v>Reason = restriction_reason()| shortage_reason() | common_reason()</v>
</type>
<desc>
- <p>Transfers the binary <c>Bin</c> to the remote server and append
- it to the file <c>RemoteFile</c>. If the file does not exists it
- will be created.</p>
+ <p>Transfers the binary <c>Bin</c> to the remote server and appends
+ it to the file <c>RemoteFile</c>. If the file does not exist, it
+ is created.</p>
<marker id="append_chunk"></marker>
</desc>
@@ -333,18 +331,18 @@
<func>
<name>append_chunk(Pid, Bin) -> ok | {error, Reason}</name>
- <fsummary>append a chunk to the remote file.</fsummary>
+ <fsummary>Appends a chunk to the remote file.</fsummary>
<type>
<v>Pid = pid()</v>
<v>Bin = binary()</v>
<v>Reason = echunk | restriction_reason() | common_reason()</v>
</type>
<desc>
- <p>Transfer the chunk <c>Bin</c> to the remote server, which
- append it into the file specified in the call to
- <c>append_chunk_start/2</c>. </p>
- <p>Note that for some errors, e.g. file system full, it is
- necessary to to call <c>append_chunk_end</c> to get the
+ <p>Transfers the chunk <c>Bin</c> to the remote server, which
+ appends it to the file specified in the call to
+ <c>append_chunk_start/2</c>.</p>
+ <p>For some errors, for example, file system full, it is
+ necessary to call <c>append_chunk_end</c> to get the
proper reason.</p>
<marker id="append_chunk_start"></marker>
@@ -353,16 +351,16 @@
<func>
<name>append_chunk_start(Pid, File) -> ok | {error, Reason}</name>
- <fsummary>Start transfer of file chunks for appending to File.</fsummary>
+ <fsummary>Starts transfer of file chunks for appending to <c>File</c>.</fsummary>
<type>
<v>Pid = pid()</v>
<v>File = string()</v>
<v>Reason = restriction_reason() | common_reason()</v>
</type>
<desc>
- <p>Start the transfer of chunks for appending to the file
- <c>File</c> at the remote server. If the file does not exists
- it will be created.</p>
+ <p>Starts the transfer of chunks for appending to the file
+ <c>File</c> at the remote server. If the file does not exist,
+ it is created.</p>
<marker id="append_chunk_end"></marker>
</desc>
@@ -370,7 +368,7 @@
<func>
<name>append_chunk_end(Pid) -> ok | {error, Reason}</name>
- <fsummary>Stop transfer of chunks for appending.</fsummary>
+ <fsummary>Stops transfer of chunks for appending.</fsummary>
<type>
<v>Pid = pid()</v>
<v>Reason = echunk | restriction_reason() | shortage_reason() </v>
@@ -378,7 +376,7 @@
<desc>
<p>Stops transfer of chunks for appending to the remote server.
The file at the remote server, specified in the call to
- <c>append_chunk_start/2</c> is closed by the server.</p>
+ <c>append_chunk_start/2</c>, is closed by the server.</p>
<marker id="cd"></marker>
</desc>
@@ -386,7 +384,7 @@
<func>
<name>cd(Pid, Dir) -> ok | {error, Reason}</name>
- <fsummary>Change remote working directory.</fsummary>
+ <fsummary>Changes remote working directory.</fsummary>
<type>
<v>Pid = pid()</v>
<v>Dir = string()</v>
@@ -402,13 +400,13 @@
<func>
<name>close(Pid) -> ok</name>
- <fsummary>End the ftp session.</fsummary>
+ <fsummary>Ends the FTP session.</fsummary>
<type>
<v>Pid = pid()</v>
</type>
<desc>
- <p>Ends an ftp session, created using the
- <seealso marker="#open">open</seealso> function. </p>
+ <p>Ends an FTP session, created using function
+ <seealso marker="#open">open</seealso>.</p>
<marker id="delete"></marker>
</desc>
@@ -416,7 +414,7 @@
<func>
<name>delete(Pid, File) -> ok | {error, Reason}</name>
- <fsummary>Delete a file at the remote server..</fsummary>
+ <fsummary>Deletes a file at the remote server.</fsummary>
<type>
<v>Pid = pid()</v>
<v>File = string()</v>
@@ -431,7 +429,7 @@
<func>
<name>formaterror(Tag) -> string()</name>
- <fsummary>Return error diagnostics.</fsummary>
+ <fsummary>Returns error diagnostics.</fsummary>
<type>
<v>Tag = {error, atom()} | atom()</v>
</type>
@@ -445,14 +443,14 @@
<func>
<name>lcd(Pid, Dir) -> ok | {error, Reason}</name>
- <fsummary>Change local working directory.</fsummary>
+ <fsummary>Changes local working directory.</fsummary>
<type>
<v>Pid = pid()</v>
<v>Dir = string()</v>
<v>Reason = restriction_reason()</v>
</type>
<desc>
- <p>Changes the working directory to <c>Dir</c> for the local client. </p>
+ <p>Changes the working directory to <c>Dir</c> for the local client.</p>
<marker id="lpwd"></marker>
</desc>
@@ -460,7 +458,7 @@
<func>
<name>lpwd(Pid) -> {ok, Dir}</name>
- <fsummary>Get local current working directory.</fsummary>
+ <fsummary>Gets local current working directory.</fsummary>
<type>
<v>Pid = pid()</v>
</type>
@@ -484,13 +482,13 @@
<v>Reason = restriction_reason() | common_reason()</v>
</type>
<desc>
- <p>Returns a list of files in long format. </p>
- <p><c>Pathname</c> can be a directory, a group of files or
- even a file. The <c>Pathname</c> string can contain wildcard(s). </p>
- <p><c>ls/1</c> implies the user's current remote directory. </p>
- <p>The format of <c>Listing</c> is operating system dependent
- (on UNIX it is typically produced from the output of the
- <c>ls -l</c> shell command).</p>
+ <p>Returns a list of files in long format.</p>
+ <p><c>Pathname</c> can be a directory, a group of files, or
+ a file. The <c>Pathname</c> string can contain wildcards.</p>
+ <p><c>ls/1</c> implies the current remote directory of the user.</p>
+ <p>The format of <c>Listing</c> depends on the operating system.
+ On UNIX, it is typically produced from the output of the
+ <c>ls -l</c> shell command.</p>
<marker id="mkdir"></marker>
</desc>
@@ -498,7 +496,7 @@
<func>
<name>mkdir(Pid, Dir) -> ok | {error, Reason}</name>
- <fsummary>Create remote directory.</fsummary>
+ <fsummary>Creates a remote directory.</fsummary>
<type>
<v>Pid = pid()</v>
<v>Dir = string()</v>
@@ -524,15 +522,15 @@
<v>Reason = restriction_reason() | common_reason()</v>
</type>
<desc>
- <p>Returns a list of files in short format. </p>
- <p><c>Pathname</c> can be a directory, a group of files or
- even a file. The <c>Pathname</c> string can contain wildcard(s). </p>
- <p><c>nlist/1</c> implies the user's current remote directory. </p>
+ <p>Returns a list of files in short format.</p>
+ <p><c>Pathname</c> can be a directory, a group of files, or
+ a file. The <c>Pathname</c> string can contain wildcards.</p>
+ <p><c>nlist/1</c> implies the current remote directory of the user.</p>
<p>The format of <c>Listing</c> is a stream of
- file names, where each name is separated by &lt;CRLF&gt; or
- &lt;NL&gt;. Contrary to the <c>ls</c> function, the purpose of
- <c>nlist</c> is to make it possible for a program to
- automatically process file name information.</p>
+ filenames where each filename is separated by &lt;CRLF&gt; or
+ &lt;NL&gt;. Contrary to function <c>ls</c>, the purpose of
+ <c>nlist</c> is to enable a program to
+ process filename information automatically.</p>
<marker id="open"></marker>
</desc>
@@ -541,24 +539,23 @@
<func>
<name>open(Host) -> {ok, Pid} | {error, Reason}</name>
<name>open(Host, Opts) -> {ok, Pid} | {error, Reason}</name>
- <fsummary>Start an standalone ftp client.</fsummary>
+ <fsummary>Starts a standalone FTP client.</fsummary>
<type>
<v>Host = string() | ip_address()</v>
<v>Opts = options()</v>
<v>options() = [option()]</v>
<v>option() = start_option() | open_option()</v>
- <!-- <v>start_options() = [start_option()]</v> -->
<v>start_option() = {verbose, verbose()} | {debug, debug()}</v>
- <v>verbose() = boolean() (defaults to false)</v>
- <v>debug() = disable | debug | trace (defaults to disable)</v>
- <!-- <v>open_options() = [open_option()]</v> -->
- <v>open_option() = {ipfamily, ipfamily()} | {port, port()} | {mode, mode()} | {timeout, timeout()} | {dtimeout, dtimeout()} | {progress, progress()}</v>
- <v>ipfamily() = inet | inet6 | inet6fb4 (defaults to inet)</v>
- <v>port() = integer() > 0 (defaults to 21)</v>
- <v>mode() = active | passive (defaults to passive)</v>
- <v>timeout() = integer() > 0 (defaults to 60000 milliseconds)</v>
- <v>dtimeout() = integer() > 0 | infinity (defaults to infinity)</v>
- <v>pogress() = ignore | {module(), function(), initial_data()} (defaults to ignore)</v>
+ <v>verbose() = boolean() (default is false)</v>
+ <v>debug() = disable | debug | trace (default is disable)</v>
+ <v>open_option() = {ipfamily, ipfamily()} | {port, port()} | {mode, mode()} | {tls, tls_options()} | {timeout, timeout()} | {dtimeout, dtimeout()} | {progress, progress()}</v>
+ <v>ipfamily() = inet | inet6 | inet6fb4 (default is inet)</v>
+ <v>port() = integer() > 0 (default is 21)</v>
+ <v>mode() = active | passive (default is passive)</v>
+ <v>tls_options() = [<seealso marker="ssl:ssl#type-ssloption">ssl:ssloption()</seealso>]</v>
+ <v>timeout() = integer() > 0 (default is 60000 milliseconds)</v>
+ <v>dtimeout() = integer() > 0 | infinity (default is infinity)</v>
+ <v>pogress() = ignore | {module(), function(), initial_data()} (default is ignore)</v>
<v>module() = atom()</v>
<v>function() = atom()</v>
<v>initial_data() = term()</v>
@@ -566,35 +563,31 @@
</type>
<desc>
- <p>This function is used to start a standalone ftp client process
- (without the inets service framework) and
- open a session with the FTP server at <c>Host</c>. </p>
+ <p>Starts a standalone FTP client process
+ (without the <c>Inets</c> service framework) and
+ opens a session with the FTP server at <c>Host</c>. </p>
- <p>A session opened in this way, is closed using the
- <seealso marker="#close">close</seealso> function. </p>
+ <p>If option <c>{tls, tls_options()}</c> is present, the FTP session
+ is transported over <c>tls</c> (<c>ftps</c>, see
+ <url href="http://www.ietf.org/rfc/rfc4217.txt">RFC 4217</url>).
+ The list <c>tls_options()</c> can be empty. The function
+ <seealso marker="ssl:ssl#connect/3"><c>ssl:connect/3</c></seealso>
+ is used for securing both the control connection and the data sessions.
+ </p>
- <marker id="pwd"></marker>
- </desc>
- </func>
+ <p>A session opened in this way is closed using function
+ <seealso marker="#close">close</seealso>.</p>
- <func>
- <name>pwd(Pid) -> {ok, Dir} | {error, Reason}</name>
- <fsummary>Get remote current working directory.</fsummary>
- <type>
- <v>Pid = pid()</v>
- <v>Reason = restriction_reason() | common_reason() </v>
- </type>
- <desc>
- <p>Returns the current working directory at the remote server. </p>
+ <marker id="pwd"></marker>
</desc>
</func>
<func>
<name>pwd(Pid) -> {ok, Dir} | {error, Reason}</name>
- <fsummary>Get remote current working directory.</fsummary>
+ <fsummary>Gets the remote current working directory.</fsummary>
<type>
<v>Pid = pid()</v>
- <v>Reason = restriction_reason() | common_reason() </v>
+ <v>Reason = restriction_reason() | common_reason()</v>
</type>
<desc>
<p>Returns the current working directory at the remote server.</p>
@@ -608,7 +601,7 @@
<func>
<name>recv(Pid, RemoteFile) -> </name>
<name>recv(Pid, RemoteFile, LocalFile) -> ok | {error, Reason}</name>
- <fsummary>Transfer file from remote server.</fsummary>
+ <fsummary>Transfers a file from remote server.</fsummary>
<type>
<v>Pid = pid()</v>
<v>RemoteFile = LocalFile = string()</v>
@@ -616,14 +609,14 @@
<v>file_write_error_reason() = see file:write/2</v>
</type>
<desc>
- <p>Transfer the file <c>RemoteFile</c> from the remote server
- to the the file system of the local client. If
+ <p>Transfers the file <c>RemoteFile</c> from the remote server
+ to the file system of the local client. If
<c>LocalFile</c> is specified, the local file will be
- <c>LocalFile</c>; otherwise it will be
+ <c>LocalFile</c>, otherwise
<c>RemoteFile</c>.</p>
- <p>If the file write fails
- (e.g. enospc), then the command is aborted and <c>{error, file_write_error_reason()}</c> is returned. The file is
- however <em>not</em> removed.</p>
+ <p>If the file write fails (for example, <c>enospc</c>), the command is
+ aborted and <c>{error, file_write_error_reason()}</c> is returned.
+ However, the file is <em>not</em> removed.</p>
<marker id="recv_bin"></marker>
</desc>
@@ -631,7 +624,7 @@
<func>
<name>recv_bin(Pid, RemoteFile) -> {ok, Bin} | {error, Reason}</name>
- <fsummary>Transfer file from remote server as a binary.</fsummary>
+ <fsummary>Transfers a file from remote server as a binary.</fsummary>
<type>
<v>Pid = pid()</v>
<v>Bin = binary()</v>
@@ -648,14 +641,14 @@
<func>
<name>recv_chunk_start(Pid, RemoteFile) -> ok | {error, Reason}</name>
- <fsummary>Start chunk-reading of the remote file.</fsummary>
+ <fsummary>Starts chunk-reading of the remote file.</fsummary>
<type>
<v>Pid = pid()</v>
<v>RemoteFile = string()</v>
<v>Reason = restriction_reason() | common_reason()</v>
</type>
<desc>
- <p>Start transfer of the file <c>RemoteFile</c> from the
+ <p>Starts transfer of the file <c>RemoteFile</c> from the
remote server.</p>
<marker id="recv_chunk"></marker>
@@ -664,20 +657,20 @@
<func>
<name>recv_chunk(Pid) -> ok | {ok, Bin} | {error, Reason}</name>
- <fsummary>Receive a chunk of the remote file.</fsummary>
+ <fsummary>Receives a chunk of the remote file.</fsummary>
<type>
<v>Pid = pid()</v>
<v>Bin = binary()</v>
<v>Reason = restriction_reason() | common_reason()</v>
</type>
<desc>
- <p>Receive a chunk of the remote file (<c>RemoteFile</c> of
- <c>recv_chunk_start</c>). The return values has the following
+ <p>Receives a chunk of the remote file (<c>RemoteFile</c> of
+ <c>recv_chunk_start</c>). The return values have the following
meaning:</p>
<list type="bulleted">
- <item><c>ok</c> the transfer is complete.</item>
- <item><c>{ok, Bin}</c> just another chunk of the file.</item>
- <item><c>{error, Reason}</c> transfer failed.</item>
+ <item><c>ok</c> = the transfer is complete.</item>
+ <item><c>{ok, Bin}</c> = just another chunk of the file.</item>
+ <item><c>{error, Reason}</c> = transfer failed.</item>
</list>
<marker id="rename"></marker>
@@ -686,7 +679,7 @@
<func>
<name>rename(Pid, Old, New) -> ok | {error, Reason}</name>
- <fsummary>Rename a file at the remote server..</fsummary>
+ <fsummary>Renames a file at the remote server.</fsummary>
<type>
<v>Pid = pid()</v>
<v>CurrFile = NewFile = string()</v>
@@ -701,7 +694,7 @@
<func>
<name>rmdir(Pid, Dir) -> ok | {error, Reason}</name>
- <fsummary>Remove a remote directory.</fsummary>
+ <fsummary>Removes a remote directory.</fsummary>
<type>
<v>Pid = pid()</v>
<v>Dir = string()</v>
@@ -719,7 +712,7 @@
<func>
<name>send(Pid, LocalFile) -></name>
<name>send(Pid, LocalFile, RemoteFile) -> ok | {error, Reason}</name>
- <fsummary>Transfer file to remote server.</fsummary>
+ <fsummary>Transfers a file to the remote server.</fsummary>
<type>
<v>Pid = pid()</v>
<v>LocalFile = RemoteFile = string()</v>
@@ -728,7 +721,7 @@
<desc>
<p>Transfers the file <c>LocalFile</c> to the remote server. If
<c>RemoteFile</c> is specified, the name of the remote file is set
- to <c>RemoteFile</c>; otherwise the name is set to <c>LocalFile</c>.</p>
+ to <c>RemoteFile</c>, otherwise to <c>LocalFile</c>.</p>
<marker id="send_bin"></marker>
</desc>
@@ -736,7 +729,7 @@
<func>
<name>send_bin(Pid, Bin, RemoteFile) -> ok | {error, Reason}</name>
- <fsummary>Transfer a binary into a remote file.</fsummary>
+ <fsummary>Transfers a binary into a remote file.</fsummary>
<type>
<v>Pid = pid()</v>
<v>Bin = binary()()</v>
@@ -753,17 +746,17 @@
<func>
<name>send_chunk(Pid, Bin) -> ok | {error, Reason}</name>
- <fsummary>Write a chunk to the remote file.</fsummary>
+ <fsummary>Writes a chunk to the remote file.</fsummary>
<type>
<v>Pid = pid()</v>
<v>Bin = binary()</v>
<v>Reason = echunk | restriction_reason() | common_reason()</v>
</type>
<desc>
- <p>Transfer the chunk <c>Bin</c> to the remote server, which
+ <p>Transfers the chunk <c>Bin</c> to the remote server, which
writes it into the file specified in the call to
- <c>send_chunk_start/2</c>. </p>
- <p>Note that for some errors, e.g. file system full, it is
+ <c>send_chunk_start/2</c>.</p>
+ <p>For some errors, for example, file system full, it is
necessary to to call <c>send_chunk_end</c> to get the
proper reason.</p>
@@ -773,14 +766,14 @@
<func>
<name>send_chunk_start(Pid, File) -> ok | {error, Reason}</name>
- <fsummary>Start transfer of file chunks.</fsummary>
+ <fsummary>Starts transfer of file chunks.</fsummary>
<type>
<v>Pid = pid()</v>
<v>File = string()</v>
<v>Reason = restriction_reason() | common_reason()</v>
</type>
<desc>
- <p>Start transfer of chunks into the file <c>File</c> at the
+ <p>Starts transfer of chunks into the file <c>File</c> at the
remote server.</p>
<marker id="send_chunk_end"></marker>
@@ -789,7 +782,7 @@
<func>
<name>send_chunk_end(Pid) -> ok | {error, Reason}</name>
- <fsummary>Stop transfer of chunks.</fsummary>
+ <fsummary>Stops transfer of chunks.</fsummary>
<type>
<v>Pid = pid()</v>
<v>Reason = restriction_reason() | common_reason() | shortage_reason()</v>
@@ -805,7 +798,7 @@
<func>
<name>type(Pid, Type) -> ok | {error, Reason}</name>
- <fsummary>Set transfer type to <c>ascii</c>or <c>binary</c>.</fsummary>
+ <fsummary>Sets transfer type to <c>ascii</c>or <c>binary</c>.</fsummary>
<type>
<v>Pid = pid()</v>
<v>Type = ascii | binary</v>
@@ -813,10 +806,9 @@
</type>
<desc>
<p>Sets the file transfer type to <c>ascii</c> or <c>binary</c>. When
- an ftp session is opened, the default transfer type of the
- server is used, most often <c>ascii</c>, which is the default
- according to RFC 959.</p>
-
+ an FTP session is opened, the default transfer type of the
+ server is used, most often <c>ascii</c>, which is default
+ according to <url href="http://www.ietf.org/rfc/rfc959.txt">RFC 959</url>.</p>
<marker id="user3"></marker>
</desc>
</func>
@@ -854,21 +846,22 @@
<func>
<name>quote(Pid, Command) -> [FTPLine]</name>
- <fsummary>Sends an arbitrary FTP command. </fsummary>
+ <fsummary>Sends an arbitrary FTP command.</fsummary>
<type>
<v>Pid = pid()</v>
<v>Command = string()</v>
- <v>FTPLine = string() - Note the telnet end of line characters, from the ftp protocol definition, CRLF e.g. "\\r\\n" has been removed.</v>
- </type>
- <desc>
- <p>Sends an arbitrary FTP command and returns verbatimly a list
- of the lines sent back by the FTP server. This functions is
- intended to give an application accesses to FTP commands
- that are server specific or that may not be provided by
- this FTP client. </p>
+ <v>FTPLine = string(</v>
+ </type>
+ <desc><note><p>The telnet end of line characters, from the FTP
+ protocol definition, CRLF, for example, "\\r\\n" has been removed.</p></note>
+ <p>Sends an arbitrary FTP command and returns verbatim a list
+ of the lines sent back by the FTP server. This function is
+ intended to give application accesses to FTP commands
+ that are server-specific or that cannot be provided by
+ this FTP client.</p>
<note>
- <p>FTP commands that require a data connection can not be
- successfully issued with this function. </p>
+ <p>FTP commands requiring a data connection cannot be
+ successfully issued with this function.</p>
</note>
</desc>
</func>
@@ -882,30 +875,31 @@
<taglist>
<tag><c>echunk</c></tag>
<item>
- <p>Synchronisation error during chunk sending.
- </p>
- <p>A call has been made to <c>send_chunk/2</c> or
- <c>send_chunk_end/1</c>, before a call to
- <c>send_chunk_start/2</c>; or a call has been made to another
- transfer function during chunk sending, i.e. before a call
- to <c>send_chunk_end/1</c>.</p>
+ <p>Synchronization error during chunk sending according to one
+ of the following:
+ </p><list type="bulleted">
+ <item>A call is made to <c>send_chunk/2</c> or <c>send_chunk_end/1</c>
+ before a call to <c>send_chunk_start/2</c>.</item>
+ <item>A call has been made to another transfer function during chunk
+ sending, that is, before a call to <c>send_chunk_end/1</c>.</item>
+ </list>
</item>
<tag><c>eclosed</c></tag>
<item>
- <p>The session has been closed.</p>
+ <p>The session is closed.</p>
</item>
<tag><c>econn</c></tag>
<item>
- <p>Connection to remote server prematurely closed.</p>
+ <p>Connection to the remote server is prematurely closed.</p>
</item>
<tag><c>ehost</c></tag>
<item>
- <p>Host not found, FTP server not found, or connection rejected
+ <p>Host is not found, FTP server is not found, or connection is rejected
by FTP server.</p>
</item>
<tag><c>elogin</c></tag>
<item>
- <p>User not logged in.</p>
+ <p>User is not logged in.</p>
</item>
<tag><c>enotbinary</c></tag>
<item>
@@ -922,7 +916,7 @@
</item>
<tag><c>euser</c></tag>
<item>
- <p>User name or password not valid.</p>
+ <p>Invalid username or password.</p>
</item>
<tag><c>etnospc</c></tag>
<item>
@@ -935,15 +929,17 @@
</item>
<tag><c>efnamena</c></tag>
<item>
- <p>File name not allowed [553].</p>
+ <p>Filename not allowed [553].</p>
</item>
</taglist>
</section>
<section>
<title>SEE ALSO</title>
- <p>file, filename, J. Postel and J. Reynolds: File Transfer Protocol
- (RFC 959).
+ <p><seealso marker="kernel:file">file(3)</seealso>
+ <seealso marker="stdlib:filename">filename(3)</seealso>
+ and J. Postel and J. Reynolds: File Transfer Protocol
+ (<url href="http://www.ietf.org/rfc/rfc959.txt">RFC 959</url>).
</p>
</section>
diff --git a/lib/inets/doc/src/ftp_client.xml b/lib/inets/doc/src/ftp_client.xml
index b44674d997..990dd68604 100644
--- a/lib/inets/doc/src/ftp_client.xml
+++ b/lib/inets/doc/src/ftp_client.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>2004</year><year>2012</year>
+ <year>2004</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
@@ -33,38 +34,24 @@
</header>
<section>
- <title>Introduction</title>
-
- <p>Ftp clients are consider to be rather temporary and are
- for that reason only started and stopped during
- runtime and can not be started at application startup.
- Due to the design of FTP client API, letting some
- functions return intermediate results, only the process
- that started the ftp client will be able to access it in
- order to preserve sane semantics. (This could be solved
- by changing the API and using the concept of a controlling
- process more in line with other OTP applications, but
- that is perhaps something for the future.)
- If the process that started the ftp session
- dies the ftp client process will terminate.</p>
+ <title>Getting Started</title>
- <p>The client supports ipv6 as long as the underlying mechanisms
- also do so. </p>
+ <p>FTP clients are considered to be rather temporary. Thus,
+ they are only started and stopped during runtime and cannot
+ be started at application startup.
+ The FTP client API is designed to allow some functions to
+ return intermediate results. This implies that only the process
+ that started the FTP client can access it with
+ preserved sane semantics.
+ If the process that started the FTP session
+ dies, the FTP client process terminates.</p>
- </section>
+ <p>The client supports IPv6 as long as the underlying mechanisms
+ also do so.</p>
- <section>
- <title>Using the FTP Client API</title>
- <p>The following is a simple example of an ftp session, where
+ <p>The following is a simple example of an FTP session, where
the user <c>guest</c> with password <c>password</c> logs on to
- the remote host <c>erlang.org</c>, and where the file
- <c>appl.erl</c> is transferred from the remote to the local
- host. When the session is opened, the current directory at
- the remote host is <c>/home/guest</c>, and <c>/home/fred</c>
- at the local host. Before transferring the file, the current
- local directory is changed to <c>/home/eproj/examples</c>, and
- the remote directory is set to
- <c>/home/guest/appl/examples</c>.</p>
+ the remote host <c>erlang.org</c>:</p>
<code type="erl"><![CDATA[
1> inets:start().
ok
@@ -85,6 +72,14 @@
9> inets:stop(ftpc, Pid).
ok
]]></code>
+ <p> The file
+ <c>appl.erl</c> is transferred from the remote to the local
+ host. When the session is opened, the current directory at
+ the remote host is <c>/home/guest</c>, and <c>/home/fred</c>
+ at the local host. Before transferring the file, the current
+ local directory is changed to <c>/home/eproj/examples</c>, and
+ the remote directory is set to
+ <c>/home/guest/appl/examples</c>.</p>
</section>
</chapter>
diff --git a/lib/inets/doc/src/http_client.xml b/lib/inets/doc/src/http_client.xml
index 4542211d71..212958f17f 100644
--- a/lib/inets/doc/src/http_client.xml
+++ b/lib/inets/doc/src/http_client.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>2004</year><year>2010</year>
+ <year>2004</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
@@ -33,103 +34,91 @@
</header>
<section>
- <title>Introduction</title>
+ <title>Configuration</title>
- <p>The HTTP client default profile will be started when the inets
+ <p>The HTTP client default profile is started when the <c>Inets</c>
application is started and is then available to all processes on
- that erlang node. Other profiles may also be started at
+ that Erlang node. Other profiles can also be started at
application startup, or profiles can be started and stopped
- dynamically in runtime. Each client profile will spawn a new
- process to handle each request unless there is a possibility to use
- a persistent connection with or without pipelining.
- The client will add a <c>host</c> header and an empty
+ dynamically in runtime. Each client profile spawns a new
+ process to handle each request, unless a persistent connection
+ can be used with or without pipelining.
+ The client adds a <c>host</c> header and an empty
<c>te</c> header if there are no such headers present in the request.</p>
- <p>The client supports ipv6 as long as the underlying mechanisms also do
+ <p>The client supports IPv6 as long as the underlying mechanisms also do
so.</p>
- </section>
- <section>
- <title>Configuration</title>
- <p> What to put in the erlang node application configuration file
- in order to start a profile at application startup.</p>
+ <p>The following is to be put in the Erlang node application configuration file
+ to start a profile at application startup:</p>
<pre>
- [{inets, [{services, [{httpc, PropertyList}]}]}]
- </pre>
- <p>For valid properties see
+ [{inets, [{services, [{httpc, PropertyList}]}]}]</pre>
+ <p>For valid properties, see
<seealso marker="httpc">httpc(3)</seealso>. </p>
</section>
<section>
- <title>Using the HTTP Client API</title>
+ <title>Getting Started</title>
+ <p>Start <c>Inets</c>:</p>
<code type="erl">
1 > inets:start().
- ok
- </code>
- <p> The following calls uses the default client profile.
- Use the proxy "www-proxy.mycompany.com:8000",
- but not for requests to localhost. This will apply to all subsequent
- requests</p>
+ ok</code>
+ <p>The following calls use the default client profile.
+ Use the proxy <c>"www-proxy.mycompany.com:8000"</c>,
+ except from requests to localhost. This applies to all the
+ following requests.</p>
+ <p>Example:</p>
<code type="erl">
2 > httpc:set_options([{proxy, {{"www-proxy.mycompany.com", 8000},
["localhost"]}}]).
- ok
- </code>
- <p>An ordinary synchronous request. </p>
+ ok</code>
+ <p>The following is an ordinary synchronous request:</p>
<code type="erl">
3 > {ok, {{Version, 200, ReasonPhrase}, Headers, Body}} =
- httpc:request(get, {"http://www.erlang.org", []}, [], []).
- </code>
- <p>With all default values, as above, a get request can also be written
- like this.</p>
+ httpc:request(get, {"http://www.erlang.org", []}, [], []).</code>
+ <p>With all the default values presented, a get request can also be written
+ as follows:</p>
<code type="erl">
4 > {ok, {{Version, 200, ReasonPhrase}, Headers, Body}} =
- httpc:request("http://www.erlang.org").
- </code>
- <p>An ordinary asynchronous request. The result will be sent
- to the calling process in the form <c>{http, {ReqestId, Result}}</c></p>
+ httpc:request("http://www.erlang.org").</code>
+ <p>The following is an ordinary asynchronous request:</p>
<code type="erl">
5 > {ok, RequestId} =
- httpc:request(get, {"http://www.erlang.org", []}, [], [{sync, false}]).
- </code>
- <p>In this case the calling process is the shell, so we receive the
- result.</p>
+ httpc:request(get, {"http://www.erlang.org", []}, [], [{sync, false}]).</code>
+ <p>The result is sent to the calling process as
+ <c>{http, {ReqestId, Result}}</c>.</p>
+ <p>In this case, the calling process is the shell, so the following
+ result is received:</p>
<code type="erl">
6 > receive {http, {RequestId, Result}} -> ok after 500 -> error end.
- ok
- </code>
- <p>Send a request with a specified connection header. </p>
+ ok</code>
+ <p>This sends a request with a specified connection header:</p>
<code type="erl">
7 > {ok, {{NewVersion, 200, NewReasonPhrase}, NewHeaders, NewBody}} =
httpc:request(get, {"http://www.erlang.org", [{"connection", "close"}]},
- [], []).
- </code>
+ [], []).</code>
- <p>Start a HTTP client profile. </p>
+ <p>Start an HTTP client profile:</p>
<code><![CDATA[
8 > {ok, Pid} = inets:start(httpc, [{profile, foo}]).
{ok, <0.45.0>}
]]></code>
- <p>The new profile has no proxy settings so the connection will
- be refused</p>
+ <p>The new profile has no proxy settings, so the connection is refused:</p>
<code type="erl">
9 > httpc:request("http://www.erlang.org", foo).
- {error, econnrefused}
- </code>
+ {error, econnrefused}</code>
- <p>Stop a HTTP client profile. </p>
+ <p>Stop the HTTP client profile:</p>
<code type="erl">
10 > inets:stop(httpc, foo).
- ok
- </code>
+ ok</code>
- <p>Alternatively:</p>
+ <p>Alternative way to stop the HTTP client profile:</p>
<code type="erl">
10 > inets:stop(httpc, Pid).
- ok
- </code>
+ ok</code>
</section>
</chapter>
diff --git a/lib/inets/doc/src/http_server.xml b/lib/inets/doc/src/http_server.xml
index f29b505bc7..aeda961714 100644
--- a/lib/inets/doc/src/http_server.xml
+++ b/lib/inets/doc/src/http_server.xml
@@ -1,97 +1,87 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>2004</year><year>2011</year>
+ <year>2004</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
-
- <title>HTTP server </title>
- <prepared>Ingela Anderton Andin</prepared>
- <responsible></responsible>
- <docno></docno>
- <approved></approved>
- <checked></checked>
- <date></date>
- <rev></rev>
+ <title>HTTP server</title>
<file>http_server.xml</file>
-
- <marker id="intro"></marker>
</header>
<section>
- <title>Introduction</title>
-
+ <title>Configuration</title>
+ <marker id="config"></marker>
<p>The HTTP server, also referred to as httpd, handles HTTP requests
- as described in RFC 2616 with a few exceptions such as gateway
- and proxy functionality. The server supports ipv6 as long as the
- underlying mechanisms also do so. </p>
-
- <p>The server implements numerous features such as SSL (Secure Sockets
- Layer), ESI (Erlang Scripting Interface), CGI (Common Gateway
- Interface), User Authentication(using Mnesia, dets or plain text
- database), Common Logfile Format (with or without disk_log(3)
- support), URL Aliasing, Action Mappings, Directory Listings and SSI
- (Server-Side Includes).</p>
-
- <p>The configuration of the server is provided as an erlang
- property list, and for backwards compatibility also a configuration
+ as described in
+ <url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>
+ with a few exceptions, such as gateway
+ and proxy functionality. The server supports IPv6 as long as the
+ underlying mechanisms also do so.</p>
+
+ <p>The server implements numerous features, such as:</p>
+ <list type="bulleted">
+ <item>Secure Sockets Layer (SSL)</item>
+ <item>Erlang Scripting Interface (ESI)</item>
+ <item>Common Gateway Interface (CGI)</item>
+ <item>User Authentication (using <c>Mnesia</c>,
+ <c>Dets</c> or plain text database)</item>
+ <item>Common Logfile Format (with or without disk_log(3) support)</item>
+ <item>URL Aliasing</item>
+ <item>Action Mappings</item>
+ <item>Directory Listings</item>
+ </list>
+
+ <p>The configuration of the server is provided as an Erlang
+ property list. For backwards compatibility, a configuration
file using apache-style configuration directives is
supported.</p>
- <p>As of inets version 5.0 the HTTP server is an easy to
- start/stop and customize web server that provides the most basic
- web server functionality. Depending on your needs there
- are also other erlang based web servers that may be of interest
- such as Yaws, http://yaws.hyber.org, that for instance has its own
- markup support to generate html, and supports certain buzzword
- technologies such as SOAP.</p>
+ <p>As of <c>Inets</c> 5.0 the HTTP server is an easy to
+ start/stop and customize web server providing the most basic
+ web server functionality. Inets is designed for embedded systems
+ and if you want a full-fledged web server there are exists other
+ erlang open source alternatives.</p>
- <p>Allmost all server functionality has been implemented using an
- especially crafted server API which is described in the Erlang Web
- Server API. This API can be used to advantage by all who wish
+ <p>Almost all server functionality has been implemented using an
+ especially crafted server API, which is described in the Erlang Web
+ Server API. This API can be used
to enhance the core server functionality, for example with custom
logging and authentication.</p>
- <marker id="config"></marker>
- </section>
-
- <section>
- <title>Configuration</title>
-
- <p> What to put in the erlang node application configuration file
- in order to start a http server at application startup.</p>
+ <p>The following is to be put in the Erlang node application configuration
+ file to start an HTTP server at application startup:</p>
<code type="erl">
[{inets, [{services, [{httpd, [{proplist_file,
"/var/tmp/server_root/conf/8888_props.conf"}]},
{httpd, [{proplist_file,
- "/var/tmp/server_root/conf/8080_props.conf"}]}]}]}].
- </code>
-
- <p>The server is configured using an erlang property list.
- For the available properties see
- <seealso marker="httpd">httpd(3)</seealso>
- For backwards compatibility also apache-like config files
- are supported.
+ "/var/tmp/server_root/conf/8080_props.conf"}]}]}]}].</code>
+
+ <p>The server is configured using an Erlang property list.
+ For the available properties, see
+ <seealso marker="httpd">httpd(3)</seealso>.
+ For backwards compatibility, apache-like configuration files
+ are also supported.
</p>
- <p>All possible config properties are as follows </p>
+ <p>The available configuration properties are as follows:</p>
<code type="none">
httpd_service() -> {httpd, httpd()}
httpd() -> [httpd_config()]
@@ -103,40 +93,43 @@
debug_options() -> {all_functions, modules()} |
{exported_functions, modules()} |
{disable, modules()}
- modules() -> [atom()]
- </code>
- <p>{proplist_file, file()} File containing an erlang property
+ modules() -> [atom()]</code>
+ <p>Here:</p>
+ <taglist>
+ <tag><c>{file, file()}</c></tag>
+ <item><p>If you use an old apace-like configuration file.</p></item>
+ <tag><c>{proplist_file, file()}</c></tag>
+ <item><p>File containing an Erlang property
list, followed by a full stop, describing the HTTP server
- configuration.</p>
- <p>{file, file()} If you use an old apace-like configuration file.</p>
- <p>{debug, debug()} - Can enable trace on all
- functions or only exported functions on chosen modules.</p>
- <p>{accept_timeout, integer()} sets the wanted timeout value for
- the server to set up a request connection.</p>
-
- <marker id="using_http_server_api"></marker>
+ configuration.</p></item>
+ <tag><c>{debug, debug()}</c></tag>
+ <item><p>Can enable trace on all functions or only exported functions
+ on chosen modules.</p></item>
+ <tag><c>{accept_timeout, integer()}</c></tag>
+ <item><p>Sets the wanted time-out value for
+ the server to set up a request connection.</p></item>
+ </taglist>
</section>
<section>
- <title>Using the HTTP Server API</title>
+ <title>Getting Started</title>
+ <marker id="using_http_server_api"></marker>
+ <p>Start <c>Inets</c>:</p>
<code type="none">
1 > inets:start().
- ok
- </code>
- <p> Start a HTTP server with minimal
- required configuration. Note that if you
- specify port 0 an arbitrary available port will be
- used and you can use the info function to find out
- which port number that was picked.
- </p>
+ ok</code>
+ <p>Start an HTTP server with minimal required configuration.
+ If you specify port <c>0</c>, an arbitrary available port is
+ used, and you can use function <c>info</c> to find which port
+ number that was picked:</p>
<code type="none">
2 > {ok, Pid} = inets:start(httpd, [{port, 0},
{server_name,"httpd_test"}, {server_root,"/tmp"},
{document_root,"/tmp/htdocs"}, {bind_address, "localhost"}]).
- {ok, 0.79.0}
- </code>
+ {ok, 0.79.0} </code>
+ <p>Call <c>info</c>:</p>
<code type="none">
3 > httpd:info(Pid).
[{mime_types,[{"html","text/html"},{"htm","text/html"}]},
@@ -144,571 +137,404 @@
{bind_address, {127,0,0,1}},
{server_root,"/tmp"},
{port,59408},
- {document_root,"/tmp/htdocs"}]
- </code>
+ {document_root,"/tmp/htdocs"}]</code>
- <p> Reload the configuration without restarting the server.
- Note port and bind_address can not be changed. Clients
- trying to access the server during the reload will
- get a service temporary unavailable answer.
+ <p>Reload the configuration without restarting the server:
+
</p>
<code type="none">
4 > httpd:reload_config([{port, 59408},
{server_name,"httpd_test"}, {server_root,"/tmp/www_test"},
{document_root,"/tmp/www_test/htdocs"},
{bind_address, "localhost"}], non_disturbing).
- ok.
- </code>
+ ok.</code>
+
+ <note><p><c>port</c> and <c>bind_address</c> cannot be changed.
+ Clients trying to access the server during the reload
+ get a service temporary unavailable answer.</p></note>
<code type="none">
5 > httpd:info(Pid, [server_root, document_root]).
- [{server_root,"/tmp/www_test"},{document_root,"/tmp/www_test/htdocs"}]
- </code>
+ [{server_root,"/tmp/www_test"},{document_root,"/tmp/www_test/htdocs"}] </code>
<code type="none">
- 6 > ok = inets:stop(httpd, Pid).
- </code>
+ 6 > ok = inets:stop(httpd, Pid).</code>
- <p> Alternative:</p>
+ <p>Alternative:</p>
<code type="none">
- 6 > ok = inets:stop(httpd, {{127,0,0,1}, 59408}).
- </code>
+ 6 > ok = inets:stop(httpd, {{127,0,0,1}, 59408}).</code>
- <p>Note that bind_address has to be
- the ip address reported by the info function and can
- not be the hostname that is allowed when inputting bind_address.</p>
-
- <marker id="htaccess"></marker>
+ <p>Notice that <c>bind_address</c> must be the IP address reported
+ by function <c>info</c> and cannot be the hostname that is allowed
+ when putting in <c>bind_address</c>.</p>
</section>
<section>
- <title>Htaccess - User Configurable Authentication.</title>
- <p>If users of the web server needs to manage authentication of
- web pages that are local to their user and do not have
- server administrative privileges. They can use the
- per-directory runtime configurable user-authentication scheme
- that Inets calls htaccess. It works the following way: </p>
+ <title>Htaccess - User Configurable Authentication</title>
+ <marker id="htaccess"></marker>
+ <p>Web server users without server administrative privileges
+ that need to manage authentication of web pages that are local
+ to their user can use the per-directory runtime configurable
+ user-authentication scheme <c>htaccess</c>.
+ It works as follows:</p>
<list type="bulleted">
<item>Each directory in the path to the requested asset is
- searched for an access-file (default .htaccess), that restricts
- the web servers rights to respond to a request. If an access-file
- is found the rules in that file is applied to the
- request. </item>
- <item>The rules in an access-file applies both to files in the same
- directories and in subdirectories. If there exists more than one
- access-file in the path to an asset, the rules in the
- access-file nearest the requested asset will be applied.</item>
- <item>To change the rules that restricts the use of
- an asset. The user only needs to have write access
- to the directory where the asset exists.</item>
- <item>All the access-files in the path to a requested asset is read
- once per request, this means that the load on the server will
- increase when this scheme is used.</item>
- <item>If a directory is
- limited both by auth directives in the HTTP server configuration
- file and by the htaccess files. The user must be allowed to get
- access the file by both methods for the request to succeed.</item>
+ searched for an access file (default is <c>.htaccess</c>), which
+ restricts the web servers rights to respond to a request.
+ If an access file is found, the rules in that file is applied to the
+ request.</item>
+ <item>The rules in an access file apply to files in the same
+ directory and in subdirectories. If there exists more than one
+ access file in the path to an asset, the rules in the
+ access file nearest the requested asset is applied.</item>
+ <item>To change the rules that restrict the use of
+ an asset, the user only needs write access
+ to the directory where the asset is.</item>
+ <item>All access files in the path to a requested asset are read
+ once per request. This means that the load on the server
+ increases when <c>htaccess</c> is used.</item>
+ <item>If a directory is limited both by authentication directives
+ in the HTTP server configuration file and by the <c>htaccess</c>
+ files, the user must be allowed to get access to the file by both
+ methods for the request to succeed.</item>
</list>
<section>
<title>Access Files Directives</title>
- <p>In every directory under the <c>DocumentRoot</c> or under an
- <c>Alias</c> a user can place an access-file. An access-file
- is a plain text file that specify the restrictions that
- shall be considered before the web server answer to a
- request. If there are more than one access-file in the path
- to the requested asset, the directives in the access-file in
- the directory nearest the asset will be used.</p>
- <list type="bulleted">
+ <p>In every directory under <c>DocumentRoot</c> or under an
+ <c>Alias</c> a user can place an access file. An access file
+ is a plain text file that specifies the restrictions to
+ consider before the web server answers to a
+ request. If there are more than one access file in the path
+ to the requested asset, the directives in the access file in
+ the directory nearest the asset is used.</p>
+ <taglist>
+ <tag><em>"allow"</em></tag>
<item>
- <p><em>DIRECTIVE: "allow"</em></p>
- <p><em>Syntax:</em><c>Allow</c> from subnet subnet|from all <br></br>
-<em>Default:</em><c>from all </c> <br></br>
-</p>
- <p>Same as the directive allow for the server config file. </p>
+ <p><em>Syntax:</em> <c>Allow</c> from subnet <c>subnet | from all</c></p>
+ <p><em>Default:</em> <c>from all</c></p>
+ <p>Same as directive <c>allow</c> for the server configuration file.</p>
</item>
- <item>
- <p><em>DIRECTIVE: "AllowOverRide"</em></p>
- <p><em>Syntax:</em><c>AllowOverRide</c> all | none |
- Directives <br></br>
-<em>Default:</em><c>- None -</c> <br></br>
-<c>AllowOverRide</c> Specify which parameters that not
- access-files in subdirectories are allowed to alter the value
- for. If the parameter is set to none no more
- access-files will be parsed.
+ <tag><em>"AllowOverRide"</em></tag>
+ <item>
+ <p><em>Syntax:</em> <c>AllowOverRide</c> <c>all | none | Directives</c></p>
+ <p><em>Default:</em> <c>none</c></p>
+ <p><c>AllowOverRide</c> specifies the parameters that
+ access files in subdirectories are not allowed to alter the value
+ for. If the parameter is set to <c>none</c>, no further
+ access files is parsed.
</p>
- <p>If only one access-file exists setting this parameter to
- none can lessen the burden on the server since the server
- will stop looking for access-files.</p>
+ <p>If only one access file exists, setting this parameter to
+ <c>none</c> can ease the burden on the server as the server
+ then stops looking for access files.</p>
</item>
+ <tag><em>"AuthGroupfile"</em></tag>
<item>
- <p><em>DIRECTIVE: "AuthGroupfile"</em></p>
- <p><em>Syntax:</em><c>AuthGroupFile</c> Filename <br></br>
-<em>Default:</em><c>- None -</c> <br></br>
-</p>
- <p>AuthGroupFile indicates which file that contains the list
- of groups. Filename must contain the absolute path to the
- file. The format of the file is one group per row and
+ <p><em>Syntax:</em> <c>AuthGroupFile</c> Filename</p>
+ <p><em>Default:</em> <c>none</c></p>
+ <p><c>AuthGroupFile</c> indicates which file that contains the list
+ of groups. The filename must contain the absolute path to the
+ file. The format of the file is one group per row and
every row contains the name of the group and the members
- of the group separated by a space, for example:</p>
+ of the group, separated by a space, for example:</p>
<pre>
-GroupName: Member1 Member2 .... MemberN
- </pre>
+GroupName: Member1 Member2 .... MemberN</pre>
</item>
+ <tag><em>"AuthName"</em></tag>
<item>
- <p><em>DIRECTIVE: "AuthName"</em></p>
- <p><em>Syntax:</em><c>AuthName</c> auth-domain <br></br>
-<em>Default:</em><c>- None -</c> <br></br>
-</p>
- <p>Same as the directive AuthName for the server config file. </p>
+ <p><em>Syntax:</em> <c>AuthName</c> auth-domain</p>
+ <p><em>Default:</em> <c>none</c></p>
+ <p>Same as directive <c>AuthName</c> for the server
+ configuration file.</p>
</item>
+ <tag><em>"AuthType"</em></tag>
<item>
- <p><em>DIRECTIVE: "AuthType"</em></p>
- <p><em>Syntax:</em><c>AuthType</c> Basic <br></br>
-<em>Default:</em><c>Basic</c> <br></br>
-</p>
- <p><c>AuthType</c> Specify which authentication scheme that shall
- be used. Today only Basic Authenticating using UUEncoding of
- the password and user ID is implemented. </p>
+ <p><em>Syntax:</em> <c>AuthType</c> <c>Basic</c></p>
+ <p><em>Default:</em> <c>Basic</c></p>
+ <p><c>AuthType</c> specifies which authentication scheme to
+ be used. Only Basic Authenticating using UUEncoding of
+ the password and user ID is implemented.</p>
</item>
+ <tag><em>"AuthUserFile"</em></tag>
<item>
- <p><em>DIRECTIVE: "AuthUserFile"</em></p>
- <p><em>Syntax:</em><c>AuthUserFile</c> Filename <br></br>
-<em>Default:</em><c>- None -</c> <br></br>
-</p>
- <p><c>AuthUserFile</c> indicate which file that contains the list
- of users. Filename must contain the absolute path to the
- file. The users name and password are not encrypted so do not
+ <p><em>Syntax:</em> <c>AuthUserFile</c> Filename</p>
+ <p><em>Default:</em><c>none</c></p>
+ <p><c>AuthUserFile</c> indicates which file that contains the list
+ of users. The filename must contain the absolute path to the
+ file. The username and password are not encrypted so do not
place the file with users in a directory that is accessible
- via the web server. The format of the file is one user per row
- and every row contains User Name and Password separated by a
- colon, for example:</p>
+ through the web server. The format of the file is one user per row.
+ Every row contains <c>UserName</c> and <c>Password</c> separated
+ by a colon, for example:</p>
<pre>
UserName:Password
-UserName:Password
- </pre>
+UserName:Password</pre>
</item>
+ <tag><em>"deny"</em></tag>
<item>
- <p><em>DIRECTIVE: "deny"</em></p>
- <p><em>Syntax:</em><c>deny</c> from subnet subnet|from all <br></br>
-<em>Context:</em> Limit</p>
- <p>Same as the directive deny for the server config file. </p>
+ <p><em>Syntax:</em> <c>deny</c> from subnet <c>subnet | from all</c></p>
+ <p><em>Context:</em> Limit</p>
+ <p>Same as directive <c>deny</c> for the server configuration file.</p>
</item>
- <item>
- <p><em>DIRECTIVE: "Limit"</em> <br></br>
-</p>
- <p><em>Syntax:</em><c><![CDATA[<Limit]]></c> RequestMethods<c>></c> <br></br>
-<em>Default:</em> - None - <br></br>
-</p>
- <p><c><![CDATA[<Limit>]]></c> and &lt;/Limit&gt; are used to enclose
- a group of directives which applies only to requests using
- the specified methods. If no request method is specified
+ <tag><em>"Limit"</em></tag>
+ <item>
+ <p><em>Syntax:</em> <c><![CDATA[<Limit]]></c> RequestMethods<c>></c></p>
+ <p><em>Default:</em> <c>none</c></p>
+ <p><c><![CDATA[<Limit>]]></c> and <c>&lt;/Limit&gt;</c> are used to enclose
+ a group of directives applying only to requests using
+ the specified methods. If no request method is specified,
all request methods are verified against the restrictions.</p>
+ <p>Example:</p>
<pre>
&lt;Limit POST GET HEAD&gt;
order allow deny
require group group1
allow from 123.145.244.5
-&lt;/Limit&gt;
- </pre>
+&lt;/Limit&gt;</pre>
</item>
- <item>
- <p><em>DIRECTIVE: "order"</em> <br></br>
-<em>Syntax:</em><c>order</c> allow deny | deny allow <br></br>
-<em>Default:</em> allow deny <br></br>
-</p>
- <p><c>order</c>, defines if the deny or allow control shall
- be preformed first.</p>
- <p>If the order is set to allow deny, then first the users
- network address is controlled to be in the allow subset. If
- the users network address is not in the allowed subset he will
- be denied to get the asset. If the network-address is in the
- allowed subset then a second control will be preformed, that
- the users network address is not in the subset of network
- addresses that shall be denied as specified by the deny
- parameter.</p>
- <p>If the order is set to deny allow then only users from networks
- specified to be in the allowed subset will succeed to request
+ <tag><em>"order"</em></tag>
+ <item>
+ <p><em>Syntax:</em> <c>order</c> <c>allow deny | deny allow</c></p>
+ <p><em>Default:</em> <c>allow deny</c></p>
+ <p><c>order</c> defines if the deny or allow control is to
+ be performed first.</p>
+ <p>If the order is set to <c>allow deny</c>, the users
+ network address is first controlled to be in the allow subset.
+ If the user network address is not in the allowed subset, the user
+ is denied to get the asset. If the network address is in the
+ allowed subset, a second control is performed. That is,
+ the user network address is not in the subset of network
+ addresses to be denied as specified by parameter <c>deny</c>.</p>
+ <p>If the order is set to <c>deny allow</c>, only users from networks
+ specified to be in the allowed subset succeeds to request
assets in the limited area.</p>
</item>
- <item>
- <p><em>DIRECTIVE: "require"</em></p>
- <p><em>Syntax:</em><c>require</c>
- group group1 group2...|user user1 user2... <br></br>
-<em>Default:</em><c>- None -</c> <br></br>
-<em>Context:</em> Limit <br></br>
-</p>
- <p>See the require directive in the documentation of mod_auth(3)
- for more information.</p>
+ <tag><em>"require"</em></tag>
+ <item>
+ <p><em>Syntax:</em> <c>require</c>
+ <c>group group1 group2... | user user1 user2...</c></p>
+ <p><em>Default:</em> <c>none</c></p>
+ <p><em>Context:</em> Limit</p>
+ <p>For more information, see directive <c>require</c> in
+ <seealso marker="mod_auth">mod_auth(3)</seealso>.</p>
</item>
- </list>
+ </taglist>
</section>
-
- <marker id="dynamic_we_pages"></marker>
</section>
<section>
<title>Dynamic Web Pages</title>
- <p>The Inets HTTP server provides two ways of creating dynamic web
- pages, each with its own advantages and disadvantages. </p>
- <p>First there are CGI-scripts that can be written in any programming
- language. CGI-scripts are standardized and supported by most
- web servers. The drawback with CGI-scripts is that they are resource
- intensive because of their design. CGI requires the server to fork a
- new OS process for each executable it needs to start. </p>
- <p>Second there are ESI-functions that provide a tight and efficient
- interface to the execution of Erlang functions, this interface
- on the other hand is Inets specific. </p>
-
+ <marker id="dynamic_we_pages"></marker>
+ <p><c>Inets</c> HTTP server provides two ways of creating dynamic web
+ pages, each with its own advantages and disadvantages:</p>
+ <taglist>
+ <tag><em>CGI scripts</em></tag>
+ <item><p>Common Gateway Interface (CGI) scripts can be written
+ in any programming language. CGI scripts are standardized and
+ supported by most web servers. The drawback with CGI scripts is that
+ they are resource-intensive because of their design. CGI requires the
+ server to fork a new OS process for each executable it needs to start.
+ </p></item>
+ <tag><em>ESI-functions</em></tag>
+ <item><p>Erlang Server Interface (ESI) functions provide a tight and efficient
+ interface to the execution of Erlang functions. This interface,
+ on the other hand, is <c>Inets</c> specific.</p></item>
+ </taglist>
+
<section>
- <title>The Common Gateway Interface (CGI) Version 1.1, RFC 3875.</title>
- <p>The mod_cgi module makes it possible to execute CGI scripts
- in the server. A file that matches the definition of a
- ScriptAlias config directive is treated as a CGI script. A CGI
+ <title>CGI Version 1.1, RFC 3875</title>
+ <p>The module <c>mod_cgi</c> enables execution of
+ <url href="http://www.ietf.org/rfc/rfc3875.txt">CGI scripts</url>
+ on the server. A file matching the definition of a
+ ScriptAlias config directive is treated as a CGI script. A CGI
script is executed by the server and its output is returned to
- the client. </p>
- <p>The CGI Script response comprises a message-header and a
- message-body, separated by a blank line. The message-header
- contains one or more header fields. The body may be
- empty. Example: </p>
+ the client.</p>
+ <p>The CGI script response comprises a message header and a
+ message body, separated by a blank line. The message header
+ contains one or more header fields. The body can be
+ empty.</p>
+ <p>Example:</p>
<code>"Content-Type:text/plain\nAccept-Ranges:none\n\nsome very
- plain text" </code>
+ plain text"</code>
- <p>The server will interpret the cgi-headers and most of them
- will be transformed into HTTP headers and sent back to the
- client together with the body.</p>
- <p>Support for CGI-1.1 is implemented in accordance with the RFC
- 3875. </p>
+ <p>The server interprets the message headers and most of them
+ are transformed into HTTP headers and sent back to the
+ client together with the message-body.</p>
+ <p>Support for CGI-1.1 is implemented in accordance with
+ <url href="http://www.ietf.org/rfc/rfc3875.txt">RFC 3875</url>.</p>
</section>
<section>
- <title>Erlang Server Interface (ESI)</title>
- <p>The erlang server interface is implemented by the
- module mod_esi.</p>
+ <title>ESI</title>
+ <p>The Erlang server interface is implemented by
+ module <c>mod_esi</c>.</p>
<section>
- <title>ERL Scheme </title>
+ <title>ERL Scheme</title>
<p>The erl scheme is designed to mimic plain CGI, but without
- the extra overhead. An URL which calls an Erlang erl function
+ the extra overhead. An URL that calls an Erlang <c>erl</c> function
has the following syntax (regular expression): </p>
<code type="none">
-http://your.server.org/***/Module[:/]Function(?QueryString|/PathInfo)
- </code>
- <p>*** above depends on how the ErlScriptAlias config
- directive has been used</p>
- <p>The module (Module) referred to must be found in the code
- path, and it must define a function (Function) with an arity
- of two or three. It is preferable to implement a funtion
- with arity three as it permits you to send chunks of the
- webpage beeing generated to the client during the generation
+http://your.server.org/***/Module[:/]Function(?QueryString|/PathInfo)</code>
+ <p>*** depends on how the ErlScriptAlias config
+ directive has been used.</p>
+ <p>The module <c>Module</c> referred to must be found in the code
+ path, and it must define a function <c>Function</c> with an arity
+ of two or three. It is preferable to implement a function
+ with arity three, as it permits to send chunks of the
+ web page to the client during the generation
phase instead of first generating the whole web page and
then sending it to the client. The option to implement a
function with arity two is only kept for
backwards compatibility reasons.
- See <seealso marker="mod_esi">mod_esi(3)</seealso> for
- implementation details of the esi callback function.</p>
+ For implementation details of the ESI callback function,
+ see <seealso marker="mod_esi">mod_esi(3)</seealso>.</p>
</section>
<section>
- <title>EVAL Scheme </title>
+ <title>EVAL Scheme</title>
<p>The eval scheme is straight-forward and does not mimic the
- behavior of plain CGI. An URL which calls an Erlang eval
+ behavior of plain CGI. An URL that calls an Erlang <c>eval</c>
function has the following syntax:</p>
<code type="none">
-http://your.server.org/***/Mod:Func(Arg1,...,ArgN)
- </code>
- <p>*** above depends on how the ErlScriptAlias config
- directive has been used</p>
- <p>The module (Mod) referred to must be found in the code
- path, and data returned by the function (Func) is passed
+http://your.server.org/***/Mod:Func(Arg1,...,ArgN)</code>
+ <p>*** depends on how the ErlScriptAlias config
+ directive has been used.</p>
+ <p>The module <c>Mod</c> referred to must be found in the code
+ path and data returned by the function <c>Func</c> is passed
back to the client. Data returned from the
- function must furthermore take the form as specified in
- the CGI specification. See <seealso marker="mod_esi">mod_esi(3)</seealso> for implementation details of the esi
- callback function.</p>
+ function must take the form as specified in
+ the CGI specification. For implementation details of the ESI
+ callback function,
+ see <seealso marker="mod_esi">mod_esi(3)</seealso>.</p>
<note>
<p>The eval scheme can seriously threaten the
- integrity of the Erlang node housing a Web server, for
- example: </p>
+ integrity of the Erlang node housing a web server, for
+ example:</p>
<code type="none">
-http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[])))
- </code>
- <p>which effectively will close down the Erlang node,
- therefor, use the erl scheme instead, until this
- security breach has been fixed.</p>
- <p>Today there are no good way of solving this problem
- and therefore Eval Scheme may be removed in future
- release of Inets. </p>
+http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[])))</code>
+ <p>This effectively closes down the Erlang node.
+ Therefore, use the erl scheme instead, until this
+ security breach is fixed.</p>
+ <p>Today there are no good ways of solving this problem
+ and therefore the eval scheme can be removed in future
+ release of <c>Inets</c>.</p>
</note>
</section>
</section>
-
- <marker id="logging"></marker>
</section>
<section>
- <title>Logging </title>
- <p>There are three types of logs supported. Transfer logs,
- security logs and error logs. The de-facto standard Common
+ <title>Logging</title>
+ <marker id="logging"></marker>
+ <p>Three types of logs are supported: transfer logs,
+ security logs, and error logs. The de-facto standard Common
Logfile Format is used for the transfer and security logging.
There are numerous statistics programs available to analyze Common
Logfile Format. The Common Logfile Format looks as follows:
</p>
<p><em>remotehost rfc931 authuser [date] "request" status bytes</em></p>
+ <p>Here:</p>
<taglist>
<tag><em>remotehost</em></tag>
- <item>Remote hostname</item>
+ <item>Remote hostname.</item>
<tag><em>rfc931</em></tag>
- <item>The client's remote username (RFC 931).</item>
+ <item>The client remote username (<url href="http://www.ietf.org/rfc/rfc931.txt">RFC 931</url>).</item>
<tag><em>authuser</em></tag>
- <item>The username with which the user authenticated himself.</item>
+ <item>The username used for authentication.</item>
<tag><em>[date]</em></tag>
- <item>Date and time of the request (RFC 1123).</item>
+ <item>Date and time of the request (<url href="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</url>).</item>
<tag><em>"request"</em></tag>
- <item>The request line exactly as it came from the client (RFC 1945).</item>
+ <item>The request line exactly as it came from the client (<url href="http://www.ietf.org/rfc/rfc1945.txt">RFC 1945</url>).</item>
<tag><em>status</em></tag>
- <item>The HTTP status code returned to the client (RFC 1945).</item>
+ <item>The HTTP status code returned to the client (<url href="http://www.ietf.org/rfc/rfc1945.txt">RFC 1945</url>).</item>
<tag><em>bytes</em></tag>
<item>The content-length of the document transferred. </item>
</taglist>
<p>Internal server errors are recorded in the error log file. The
- format of this file is a more ad hoc format than the logs using
+ format of this file is a more unplanned format than the logs using
Common Logfile Format, but conforms to the following syntax:
</p>
<p><em>[date]</em> access to <em>path</em> failed for
<em>remotehost</em>, reason: <em>reason</em></p>
-
- <marker id="ssi"></marker>
</section>
-
+
<section>
- <title>Server Side Includes</title>
- <p>Server Side Includes enables the server to run code embedded
- in HTML pages to generate the response to the client.</p>
- <note>
- <p>Having the server parse HTML pages is a double edged sword!
- It can be costly for a heavily loaded server to perform
- parsing of HTML pages while sending them. Furthermore, it can
- be considered a security risk to have average users executing
- commands in the name of the Erlang node user. Carefully
- consider these items before activating server-side includes.</p>
- </note>
-
- <section>
- <marker id="ssi_setup"></marker>
- <title>SERVER-SIDE INCLUDES (SSI) SETUP</title>
- <p>The server must be told which filename extensions to be used
- for the parsed files. These files, while very similar to HTML,
- are not HTML and are thus not treated the same. Internally, the
- server uses the magic MIME type <c>text/x-server-parsed-html</c>
- to identify parsed documents. It will then perform a format
- conversion to change these files into HTML for the
- client. Update the <c>mime.types</c> file, as described in the
- Mime Type Settings, to tell the server which extension to use
- for parsed files, for example:
- </p>
- <pre>
- text/x-server-parsed-html shtml shtm
- </pre>
- <p>This makes files ending with <c>.shtml</c> and <c>.shtm</c>
- into parsed files. Alternatively, if the performance hit is not a
- problem, <em>all</em> HTML pages can be marked as parsed:
- </p>
- <pre>
- text/x-server-parsed-html html htm
- </pre>
- </section>
-
- <section>
- <marker id="ssi_format"></marker>
- <title>Server-Side Includes (SSI) Format</title>
- <p>All server-side include directives to the server are formatted
- as SGML comments within the HTML page. This is in case the
- document should ever find itself in the client's hands
- unparsed. Each directive has the following format:
- </p>
- <pre>
- &lt;!--#command tag1="value1" tag2="value2" --&gt;
- </pre>
- <p>Each command takes different arguments, most only accept one
- tag at a time. Here is a breakdown of the commands and their
- associated tags:
- </p>
- <p>The config directive controls various aspects of the
- file parsing. There are two valid tags:
- </p>
- <taglist>
- <tag><c>errmsg</c></tag>
- <item>
- <p>controls the message sent back to the client if an
- error occurred while parsing the document. All errors are
- logged in the server's error log.</p>
- </item>
- <tag><c>sizefmt</c></tag>
- <item>
- <p>determines the format used to display the size of
- a file. Valid choices are <c>bytes</c> or
- <c>abbrev</c>. <c>bytes</c> for a formatted byte count
- or <c>abbrev</c> for an abbreviated version displaying
- the number of kilobytes.</p>
- </item>
- </taglist>
- <p>The include directory
- will insert the text of a document into the parsed
- document. This command accepts two tags:</p>
- <taglist>
- <tag><c>virtual</c></tag>
- <item>
- <p>gives a virtual path to a document on the
- server. Only normal files and other parsed documents can
- be accessed in this way.</p>
- </item>
- <tag><c>file</c></tag>
- <item>
- <p>gives a pathname relative to the current
- directory. <c>../</c> cannot be used in this pathname, nor
- can absolute paths. As above, you can send other parsed
- documents, but you cannot send CGI scripts.</p>
- </item>
- </taglist>
- <p>The echo directive prints the value of one of the include
- variables (defined below). The only valid tag to this
- command is <c>var</c>, whose value is the name of the
- variable you wish to echo.</p>
- <p>The fsize directive prints the size of the specified
- file. Valid tags are the same as with the <c>include</c>
- command. The resulting format of this command is subject
- to the <c>sizefmt</c> parameter to the <c>config</c>
- command.</p>
- <p>The lastmod directive prints the last modification date of
- the specified file. Valid tags are the same as with the
- <c>include</c> command.</p>
- <p>The exec directive executes a given shell command or CGI
- script. Valid tags are:</p>
- <taglist>
- <tag><c>cmd</c></tag>
- <item>
- <p>executes the given string using <c>/bin/sh</c>. All
- of the variables defined below are defined, and can be
- used in the command.</p>
- </item>
- <tag><c>cgi</c></tag>
- <item>
- <p>executes the given virtual path to a CGI script and
- includes its output. The server does not perform error
- checking on the script output.</p>
- </item>
- </taglist>
- </section>
-
- <section>
- <marker id="ssi_environment_variables"></marker>
- <title>Server-Side Includes (SSI) Environment Variables</title>
- <p>A number of variables are made available to parsed
- documents. In addition to the CGI variable set, the following
- variables are made available:
- </p>
- <taglist>
- <tag><c>DOCUMENT_NAME</c></tag>
- <item>
- <p>The current filename.</p>
- </item>
- <tag><c>DOCUMENT_URI</c></tag>
- <item>
- <p>The virtual path to this document (such as
- <c>/docs/tutorials/foo.shtml</c>).</p>
- </item>
- <tag><c>QUERY_STRING_UNESCAPED</c></tag>
- <item>
- <p>The unescaped version of any search query the client
- sent, with all shell-special characters escaped with
- <c>\</c>.</p>
- </item>
- <tag><c>DATE_LOCAL</c></tag>
- <item>
- <p>The current date, local time zone.</p>
- </item>
- <tag><c>DATE_GMT</c></tag>
- <item>
- <p>Same as DATE_LOCAL but in Greenwich mean time.</p>
- </item>
- <tag><c>LAST_MODIFIED</c></tag>
- <item>
- <p>The last modification date of the current document.</p>
- </item>
- </taglist>
- </section>
- </section>
-
- <section>
- <title>The Erlang Web Server API</title>
- <p>The process of handling a HTTP request involves several steps
+ <title>Erlang Web Server API</title>
+ <p>The process of handling an HTTP request involves several steps,
such as:</p>
<list type="bulleted">
- <item>Seting up connections, sending and receiving data.</item>
- <item>URI to filename translation</item>
- <item>Authenication/access checks.</item>
- <item>Retriving/generating the response.</item>
- <item>Logging</item>
+ <item>Setting up connections, sending and receiving data.</item>
+ <item>URI to filename translation.</item>
+ <item>Authentication/access checks.</item>
+ <item>Retrieving/generating the response.</item>
+ <item>Logging.</item>
</list>
- <p>To provide customization and extensibility of the HTTP servers
- request handling most of these steps are handled by one or more
- modules that may be replaced or removed at runtime, and of course
- new ones can be added. For each request all modules will be
- traversed in the order specified by the modules directive in the
- server configuration file. Some parts mainly the communication
- related steps are considered server core functionality and are
- not implemented using the Erlang Web Server API. A description of
- functionality implemented by the Erlang Webserver API is described
- in the section Inets Webserver Modules.</p>
+ <p>To provide customization and extensibility of the request
+ handling of the HTTP servers, most of these steps are handled by
+ one or more modules. These modules can be replaced or removed at
+ runtime and new ones can be added. For each request, all modules are
+ traversed in the order specified by the module directive in the
+ server configuration file. Some parts, mainly the communication-
+ related steps, are considered server core functionality and are
+ not implemented using the Erlang web server API. A description of
+ functionality implemented by the Erlang webserver API is described
+ in <seealso marker="#Inets_Web_Server_Modules">Section
+ Inets Web Server Modules</seealso>.</p>
+
<p>A module can use data generated by previous modules in the
- Erlang Webserver API module sequence or generate data to be used
- by consecutive Erlang Web Server API modules. This is made
- possible due to an internal list of key-value tuples, also referred to
- as interaction data. </p>
+ Erlang webserver API module sequence or generate data to be used
+ by consecutive Erlang Web Server API modules. This is
+ possible owing to an internal list of key-value tuples, referred to
+ as interaction data.</p>
<note>
<p>Interaction data enforces module dependencies and
- should be avoided if possible. This means the order
- of modules in the Modules property is significant.</p>
+ is to be avoided if possible. This means that the order
+ of modules in the modules property is significant.</p>
</note>
<section>
<title>API Description</title>
- <p>Each module implements server functionality
- using the Erlang Web Server API should implement the following
+ <p>Each module that implements server functionality
+ using the Erlang web server API is to implement the following
call back functions:</p>
<list type="bulleted">
- <item>do/1 (mandatory) - the function called when
- a request should be handled.</item>
- <item>load/2</item>
- <item>store/2</item>
- <item>remove/1</item>
+ <item><c>do/1</c> (mandatory) - the function called when
+ a request is to be handled</item>
+ <item><c>load/2</c></item>
+ <item><c>store/2</c></item>
+ <item><c>remove/1</c></item>
</list>
<p>The latter functions are needed only when new config
- directives are to be introduced. For details see
- <seealso marker="httpd">httpd(3)</seealso></p>
+ directives are to be introduced. For details, see
+ <seealso marker="httpd">httpd(3)</seealso>.</p>
</section>
</section>
<section>
- <title>Inets Web Server Modules</title> <p>The convention is that
- all modules implementing some webserver functionality has the
- name mod_*. When configuring the web server an appropriate
- selection of these modules should be present in the Module
- directive. Please note that there are some interaction dependencies
- to take into account so the order of the modules can not be
- totally random.</p>
+ <title>Inets Web Server Modules</title>
+ <marker id="Inets_Web_Server_Modules"></marker>
+ <p>The convention is that
+ all modules implementing some web server functionality has the
+ name <c>mod_*</c>. When configuring the web server, an appropriate
+ selection of these modules is to be present in the module
+ directive. Notice that there are some interaction dependencies
+ to take into account, so the order of the modules cannot be
+ random.</p>
<section>
- <title>mod_action - Filetype/Method-Based Script Execution.</title>
- <p>Runs CGI scripts whenever a file of a
- certain type or HTTP method (See RFC 1945) is requested.
+ <title>mod_action - Filetype/Method-Based Script Execution</title>
+ <p>This module runs CGI scripts whenever a file of a
+ certain type or HTTP method (see
+ <url href="http://tools.ietf.org/html/rfc1945">RFC 1945</url>)
+ is requested.
</p>
<p>Uses the following Erlang Web Server API interaction data:
</p>
<list type="bulleted">
- <item>real_name - from mod_alias</item>
+ <item><c>real_name</c> - from <seealso marker="mod_alias">mod_alias</seealso>.</item>
</list>
<p>Exports the following Erlang Web Server API interaction data, if possible:
</p>
@@ -720,48 +546,51 @@ http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[
<section>
<title>mod_alias - URL Aliasing</title>
- <p>This module makes it possible to map different parts of the
- host file system into the document tree e.i. creates aliases and
+ <p>The <seealso marker="mod_alias">mod_alias</seealso>
+ module makes it possible to map different parts of the
+ host file system into the document tree, that is, creates aliases and
redirections.</p>
<p>Exports the following Erlang Web Server API interaction data, if possible:
</p>
<taglist>
<tag><c>{real_name, PathData}</c></tag>
- <item>PathData is the argument used for API function mod_alias:path/3.</item>
+ <item><c>PathData</c> is the argument used for API function
+ <seealso marker="mod_alias#path/3">mod_alias:path/3</seealso>.</item>
</taglist>
</section>
<section>
- <title>mod_auth - User Authentication </title>
- <p>This module provides for basic user authentication using
- textual files, dets databases as well as mnesia databases.</p>
+ <title>mod_auth - User Authentication</title>
+ <p>The <seealso marker="mod_auth">mod_auth(3)</seealso>
+ module provides for basic user authentication using
+ textual files, <c>Dets</c> databases as well as <c>Mnesia</c> databases.</p>
<p>Uses the following Erlang Web Server API interaction data:
</p>
<list type="bulleted">
- <item>real_name - from mod_alias</item>
+ <item><c>real_name</c> - from <seealso marker="mod_alias">mod_alias</seealso></item>
</list>
<p>Exports the following Erlang Web Server API interaction data:
</p>
<taglist>
<tag><c>{remote_user, User}</c></tag>
- <item>The user name with which the user has authenticated himself.</item>
+ <item>The username used for authentication.</item>
</taglist>
<section>
- <title>Mnesia as Authentication Database</title>
+ <title>Mnesia As Authentication Database</title>
- <p> If Mnesia is used as storage method, Mnesia must be
- started prio to the HTTP server. The first time Mnesia is
- started the schema and the tables must be created before
- Mnesia is started. A naive example of a module with two
- functions that creates and start mnesia is provided
- here. The function shall be used the first
- time. first_start/0 creates the schema and the tables. The
- second function start/0 shall be used in consecutive
- startups. start/0 Starts Mnesia and wait for the tables to
+ <p>If <c>Mnesia</c> is used as storage method, <c>Mnesia</c> must be
+ started before the HTTP server. The first time <c>Mnesia</c> is
+ started, the schema and the tables must be created before
+ <c>Mnesia</c> is started. A simple example of a module with two
+ functions that creates and start <c>Mnesia</c> is provided
+ here. Function <c>first_start/0</c> is to be used the first
+ time. It creates the schema and the tables.
+ <c>start/0</c> is to be used in consecutive startups.
+ <c>start/0</c> starts <c>Mnesia</c> and waits for the tables to
be initiated. This function must only be used when the
- schema and the tables already is created. </p>
+ schema and the tables are already created.</p>
<code>
-module(mnesia_test).
@@ -785,28 +614,28 @@ first_start() ->
start() ->
mnesia:start(),
- mnesia:wait_for_tables([httpd_user, httpd_group], 60000).
- </code>
+ mnesia:wait_for_tables([httpd_user, httpd_group], 60000). </code>
- <p>To create the Mnesia tables we use two records defined in
- mod_auth.hrl so the file must be included. The first
- function first_start/0 creates a schema that specify on
- which nodes the database shall reside. Then it starts Mnesia
- and creates the tables. The first argument is the name of
- the tables, the second argument is a list of options how the
- table will be created, see Mnesia documentation for more
- information. Since the current implementation of the
- mod_auth_mnesia saves one row for each user the type must be
- bag. When the schema and the tables is created the second
- function start/0 shall be used to start Mensia. It starts
- Mnesia and wait for the tables to be loaded. Mnesia use the
- directory specified as mnesia_dir at startup if specified,
- otherwise Mnesia use the current directory. For security
- reasons, make sure that the Mnesia tables are stored outside
- the document tree of the HTTP server. If it is placed in the
- directory which it protects, clients will be able to
- download the tables. Only the dets and mnesia storage
- methods allow writing of dynamic user data to disk. plain is
+ <p>To create the <c>Mnesia</c> tables, we use two records defined in
+ <c>mod_auth.hrl</c>, so that file must be included. <c>first_start/0</c>
+ creates a schema that specifies on which nodes the database is to reside.
+ Then it starts <c>Mnesia</c> and creates the tables. The first argument
+ is the name of the tables, the second argument is a list of options of
+ how to create the table, see
+ <seealso marker="mnesia:mnesia"><c>mnesia</c></seealso>, documentation for
+ more information. As the implementation of the <c>mod_auth_mnesia</c>
+ saves one row for each user, the type must be <c>bag</c>.
+ When the schema and the tables are created, function
+ <seealso marker="mnesia:mnesia#start-0">mnesia:start/0</seealso>
+ is used to start <c>Mnesia</c> and
+ waits for the tables to be loaded. <c>Mnesia</c> uses the
+ directory specified as <c>mnesia_dir</c> at startup if specified,
+ otherwise <c>Mnesia</c> uses the current directory. For security
+ reasons, ensure that the <c>Mnesia</c> tables are stored outside
+ the document tree of the HTTP server. If they are placed in the
+ directory which it protects, clients can download the tables.
+ Only the <c>Dets</c> and <c>Mnesia</c> storage
+ methods allow writing of dynamic user data to disk. <c>plain</c> is
a read only method.</p>
</section>
@@ -814,19 +643,19 @@ start() ->
<section>
<title>mod_cgi - CGI Scripts</title>
- <p>This module handles invoking of CGI scripts</p>
+ <p>This module handles invoking of CGI scripts.</p>
</section>
<section>
<title>mod_dir - Directories</title>
<p>This module generates an HTML directory listing
(Apache-style) if a client sends a request for a directory
- instead of a file. This module needs to be removed from the
+ instead of a file. This module must be removed from the
Modules config directive if directory listings is unwanted.</p>
<p>Uses the following Erlang Web Server API interaction data:
</p>
<list type="bulleted">
- <item>real_name - from mod_alias</item>
+ <item><c>real_name</c> - from <seealso marker="mod_alias">mod_alias</seealso></item>
</list>
<p>Exports the following Erlang Web Server API interaction data:
</p>
@@ -838,27 +667,27 @@ start() ->
</section>
<section>
- <title>mod_disk_log - Logging Using disk_log.</title>
+ <title>mod_disk_log - Logging Using Disk_Log.</title>
<p>Standard logging using the "Common Logfile Format" and
- disk_log(3).</p>
+ <seealso marker="kernel:disk_log">kernel:disk_log(3)</seealso>.</p>
<p>Uses the following Erlang Web Server API interaction data:
</p>
<list type="bulleted">
- <item>remote_user - from mod_auth</item>
+ <item><c>remote_user</c> - from <c>mod_auth</c></item>
</list>
</section>
<section>
<title>mod_esi - Erlang Server Interface</title>
- <p>This module implements
- the Erlang Server Interface (ESI) that provides a tight and
- efficient interface to the execution of Erlang functions. </p>
- <p>Uses the following Erlang Web Server API interaction data:
+ <p>The <seealso marker="mod_esi">mod_esi(3)</seealso>
+ module implements the Erlang Server Interface (ESI) providing a
+ tight and efficient interface to the execution of Erlang functions.</p>
+ <p>Uses the following Erlang web server API interaction data:
</p>
<list type="bulleted">
- <item>remote_user - from mod_auth</item>
+ <item><c>remote_user</c> - from <c>mod_auth</c></item>
</list>
- <p>Exports the following Erlang Web Server API interaction data:
+ <p>Exports the following Erlang web server API interaction data:
</p>
<taglist>
<tag><c>{mime_type, MimeType}</c></tag>
@@ -870,11 +699,11 @@ start() ->
<section>
<title>mod_get - Regular GET Requests</title>
<p>This module is responsible for handling GET requests to regular
- files. GET requests for parts of files is handled by mod_range.</p>
- <p>Uses the following Erlang Web Server API interaction data:
+ files. GET requests for parts of files is handled by <c>mod_range</c>.</p>
+ <p>Uses the following Erlang web server API interaction data:
</p>
<list type="bulleted">
- <item>real_name - from mod_alias</item>
+ <item><c>real_name</c> - from <seealso marker="mod_alias">mod_alias</seealso></item>
</list>
</section>
@@ -886,7 +715,7 @@ start() ->
<p>Uses the following Erlang Web Server API interaction data:
</p>
<list type="bulleted">
- <item>real_name - from mod_alias</item>
+ <item><c>real_name</c> - from <seealso marker="mod_alias">mod_alias</seealso></item>
</list>
</section>
@@ -897,120 +726,97 @@ start() ->
<p>Uses the following Erlang Web Server API interaction data:
</p>
<list type="bulleted">
- <item>real_name - from mod_alias</item>
+ <item><c>real_name</c> - from <seealso marker="mod_alias">mod_alias</seealso></item>
</list>
<p>Exports the following Erlang Web Server API interaction data:
</p>
<taglist>
<tag><c>{remote_user_name, User}</c></tag>
- <item>The user name with which the user has authenticated himself.</item>
+ <item>The username used for authentication.</item>
</taglist>
</section>
- <section>
- <title>mod_include - SSI</title>
- <p>This module makes it possible to expand "macros" embedded in
- HTML pages before they are delivered to the client, that is
- Server-Side Includes (SSI).
- </p>
- <p>Uses the following Erlang Webserver API interaction data:
- </p>
- <list type="bulleted">
- <item>real_name - from mod_alias</item>
- <item>remote_user - from mod_auth</item>
- </list>
- <p>Exports the following Erlang Webserver API interaction data:
- </p>
- <taglist>
- <tag><c>{mime_type, MimeType}</c></tag>
- <item>The file suffix of the incoming URL mapped into a
- <c>MimeType</c> as defined in the Mime Type Settings
- section.</item>
- </taglist>
- </section>
-
<section>
<title>mod_log - Logging Using Text Files.</title>
<p>Standard logging using the "Common Logfile Format" and text
files.</p>
- <p>Uses the following Erlang Webserver API interaction data:
+ <p>Uses the following Erlang Web Server API interaction data:
</p>
<list type="bulleted">
- <item>remote_user - from mod_auth</item>
+ <item><c>remote_user</c> - from <c>mod_auth</c></item>
</list>
</section>
<section>
<title>mod_range - Requests with Range Headers</title>
- <p>This module response to requests for one or many ranges of a
- file. This is especially useful when downloading large files,
- since a broken download may be resumed.</p>
- <p>Note that request for multiple parts of a document will report a
+ <p>This module responses to requests for one or many ranges of a
+ file. This is especially useful when downloading large files,
+ as a broken download can be resumed.</p>
+ <p>Notice that request for multiple parts of a document report a
size of zero to the log file.</p>
- <p>Uses the following Erlang Webserver API interaction data:
+ <p>Uses the following Erlang Web Server API interaction data:
</p>
<list type="bulleted">
- <item>real_name - from mod_alias</item>
+ <item><c>real_name</c> - from <seealso marker="mod_alias">mod_alias</seealso></item>
</list>
</section>
<section>
<title>mod_response_control - Requests with If* Headers</title>
- <p>This module controls that the conditions in the requests is
- fulfilled. For example a request may specify that the answer
- only is of interest if the content is unchanged since last
- retrieval. Or if the content is changed the range-request shall
- be converted to a request for the whole file instead.</p> <p>If
- a client sends more then one of the header fields that restricts
- the servers right to respond, the standard does not specify how
- this shall be handled. httpd will control each field in the
- following order and if one of the fields not match the current
- state the request will be rejected with a proper response.
- <br></br>
+ <p>This module controls that the conditions in the requests are
+ fulfilled. For example, a request can specify that the answer
+ only is of interest if the content is unchanged since the last
+ retrieval. If the content is changed, the range request is to
+ be converted to a request for the whole file instead.</p>
+ <p>If a client sends more than one of the header fields that
+ restricts the servers right to respond, the standard does not
+ specify how this is to be handled.
+ <seealso marker="httpd">httpd(3)</seealso> controls each
+ field in the following order and if one of the fields does not
+ match the current state, the request is rejected with a proper
+ response:</p>
+ <p><c>If-modified</c></p>
+ <p><c>If-Unmodified</c></p>
+ <p><c>If-Match</c></p>
+ <p><c>If-Nomatch</c></p>
- 1.If-modified <br></br>
-
- 2.If-Unmodified <br></br>
-
- 3.If-Match <br></br>
-
- 4.If-Nomatch <br></br>
-</p>
- <p>Uses the following Erlang Webserver API interaction data:
+ <p>Uses the following Erlang Web Server API interaction data:
</p>
<list type="bulleted">
- <item>real_name - from mod_alias</item>
+ <item><c>real_name</c> - from <seealso marker="mod_alias">mod_alias</seealso></item>
</list>
- <p>Exports the following Erlang Webserver API interaction data:
+ <p>Exports the following Erlang Web Server API interaction data:
</p>
<taglist>
<tag><c>{if_range, send_file}</c></tag>
- <item>The conditions for the range request was not fulfilled.
+ <item>The conditions for the range request are not fulfilled.
The response must not be treated as a range request, instead it
- must be treated as a ordinary get request. </item>
+ must be treated as an ordinary get request.</item>
</taglist>
</section>
<section>
<title>mod_security - Security Filter</title>
- <p>This module serves as a filter for authenticated requests
- handled in mod_auth. It provides possibility to restrict users
- from access for a specified amount of time if they fail to
+ <p>The <seealso marker="mod_security">mod_security</seealso>
+ module serves as a filter for authenticated requests
+ handled in <seealso marker="mod_auth">mod_auth(3)</seealso>.
+ It provides a possibility to restrict users from
+ access for a specified amount of time if they fail to
authenticate several times. It logs failed authentication as
- well as blocking of users, and it also calls a configurable
- call-back module when the events occur. </p>
+ well as blocking of users, and it calls a configurable
+ callback module when the events occur.</p>
<p>There is also an
- API to manually block, unblock and list blocked users or users,
- who have been authenticated within a configurable amount of
- time.</p>
+ API to block or unblock users manually. This API can also list
+ blocked users or users who have been authenticated within a
+ configurable amount of time.</p>
</section>
<section>
<title>mod_trace - TRACE Request</title>
- <p>mod_trace is responsible for handling of TRACE requests.
+ <p><c>mod_trace</c> is responsible for handling of TRACE requests.
Trace is a new request method in HTTP/1.1. The intended use of
trace requests is for testing. The body of the trace response is
- the request message that the responding Web server or proxy
+ the request message that the responding web server or proxy
received.</p>
</section>
</section>
diff --git a/lib/inets/doc/src/http_uri.xml b/lib/inets/doc/src/http_uri.xml
index d9e8587bbf..8e0301c520 100644
--- a/lib/inets/doc/src/http_uri.xml
+++ b/lib/inets/doc/src/http_uri.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>2012</year><year>2013</year>
+ <year>2012</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
@@ -34,124 +35,151 @@
<description>
<p>This module provides utility functions for working with URIs,
- according to RFC 3986. </p>
-
+ according to
+ <url href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</url>.</p>
</description>
<section>
- <title>COMMON DATA TYPES </title>
+ <title>DATA TYPES</title>
<p>Type definitions that are used more than once in
this module:</p>
- <code type="none"><![CDATA[
-boolean() = true | false
-string() = list of ASCII characters
- ]]></code>
+ <p><c>boolean() = true | false</c></p>
+ <p><c>string()</c> = list of ASCII characters</p>
</section>
<section>
- <title>URI DATA TYPES </title>
+ <title>URI DATA TYPES</title>
<p>Type definitions that are related to URI:</p>
- <p>For more information about URI, see RFC 3986. </p>
-
- <code type="none"><![CDATA[
-uri() = string() - Syntax according to the URI definition in rfc 3986,
- e.g.: "http://www.erlang.org/"
-user_info() = string()
-scheme() = atom() - Example: http, https
-host() = string()
-port() = pos_integer()
-path() = string() - Representing a file path or directory path
-query() = string()
- ]]></code>
-
+
+<taglist>
+ <tag><c>uri() = string()</c></tag>
+ <item><p>Syntax according to the URI definition in RFC 3986,
+ for example, "http://www.erlang.org/"</p></item>
+ <tag><c>user_info() = string()</c></tag>
+ <item><p></p></item>
+ <tag><c>scheme() = atom()</c></tag>
+ <item><p>Example: http, https</p></item>
+ <tag><c>host() = string()</c></tag>
+ <item><p></p></item>
+ <tag><c>port() = pos_integer()</c></tag>
+ <item><p></p></item>
+ <tag><c>path() = string()</c></tag>
+ <item><p>Represents a file path or directory path</p></item>
+ <tag><c>query() = string()</c></tag>
+ <item><p></p></item>
+ <tag><c>fragment() = string()</c></tag>
+ <item><p></p></item>
+ </taglist>
+
+ <p>For more information about URI, see
+ <url href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</url>.</p>
<marker id="scheme_defaults"></marker>
</section>
<funcs>
<func>
- <name>scheme_defaults() -> SchemeDefaults</name>
- <fsummary>A list of scheme and their default ports</fsummary>
+ <name>decode(HexEncodedURI) -> URI</name>
+
+ <fsummary>Decodes a hexadecimal encoded URI.</fsummary>
<type>
- <v>SchemeDefaults = [{scheme(), default_scheme_port_number()}] </v>
- <v>default_scheme_port_number() = pos_integer()</v>
+ <v>HexEncodedURI = string() - A possibly hexadecimal encoded URI</v>
+ <v>URI = uri()</v>
</type>
+
<desc>
- <p>This function provides a list of the scheme and their default
- port numbers currently supported (by default) by this utility. </p>
+ <p>Decodes a possibly hexadecimal encoded URI.</p>
- <marker id="parse"></marker>
+ </desc>
+ </func>
+ <func>
+ <name>encode(URI) -> HexEncodedURI</name>
+
+ <fsummary>Encodes a hexadecimal encoded URI.</fsummary>
+ <type>
+ <v>URI = uri()</v>
+ <v>HexEncodedURI = string() - Hexadecimal encoded URI</v>
+ </type>
+
+ <desc>
+ <p>Encodes a hexadecimal encoded URI.</p>
+
+ <marker id="decode"></marker>
</desc>
</func>
<func>
<name>parse(URI) -> {ok, Result} | {error, Reason}</name>
<name>parse(URI, Options) -> {ok, Result} | {error, Reason}</name>
- <fsummary>Parse an URI</fsummary>
+ <fsummary>Parses a URI.</fsummary>
<type>
- <v>URI = uri() </v>
- <v>Options = [Option] </v>
+ <v>URI = uri()</v>
+ <v>Options = [Option]</v>
<v>Option = {ipv6_host_with_brackets, boolean()} |
- {scheme_defaults, scheme_defaults()}]</v>
- <v>Result = {Scheme, UserInfo, Host, Port, Path, Query}</v>
+ {scheme_defaults, scheme_defaults()} |
+ {fragment, boolean()} |
+ {schema_validation_fun, fun()}]</v>
+ <v>Result = {Scheme, UserInfo, Host, Port, Path, Query} |
+ {Scheme, UserInfo, Host, Port, Path, Query, Fragment}</v>
<v>UserInfo = user_info()</v>
<v>Host = host()</v>
<v>Port = pos_integer()</v>
<v>Path = path()</v>
<v>Query = query()</v>
- <v>Reason = term() </v>
+ <v>Fragment = fragment()</v>
+ <v>Reason = term()</v>
</type>
<desc>
- <p>This function is used to parse an URI. If no scheme defaults
- are provided, the value of
+ <p>Parses a URI. If no scheme defaults
+ are provided, the value of the
<seealso marker="#scheme_defaults">scheme_defaults</seealso>
- function will be used. </p>
+ function is used.</p>
- <p>Note that when parsing an URI with an unknown scheme (that is,
- a scheme not found in the scheme defaults) a port number must be
- provided or else the parsing will fail. </p>
+ <p>When parsing a URI with an unknown scheme (that is,
+ a scheme not found in the scheme defaults), a port number must be
+ provided, otherwise the parsing fails.</p>
- <marker id="encode"></marker>
- </desc>
- </func>
+ <p>If the fragment option is <c>true</c>, the URI fragment is returned as
+ part of the parsing result, otherwise it is ignored.</p>
- <func>
- <name>encode(URI) -> HexEncodedURI</name>
-
- <fsummary>Hex encode an URI</fsummary>
- <type>
- <v>URI = uri()</v>
- <v>HexEncodedURI = string() - Hex encoded uri</v>
- </type>
+ <p>Scheme validation fun is to be defined as follows:</p>
- <desc>
- <p>Hex encode an URI. </p>
+ <code>
+fun(SchemeStr :: string()) ->
+ valid | {error, Reason :: term()}.
+ </code>
- <marker id="decode"></marker>
+ <p>It is called before scheme string gets converted into scheme atom and
+ thus possible atom leak could be prevented</p>
+
+ <marker id="encode"></marker>
</desc>
</func>
<func>
- <name>decode(HexEncodedURI) -> URI</name>
-
- <fsummary>Decode a hex encoded URI</fsummary>
+ <name>scheme_defaults() -> SchemeDefaults</name>
+ <fsummary>A list of the scheme and their default ports.</fsummary>
<type>
- <v>HexEncodedURI = string() - A possibly hex encoded uri</v>
- <v>URI = uri()</v>
+ <v>SchemeDefaults = [{scheme(), default_scheme_port_number()}] </v>
+ <v>default_scheme_port_number() = pos_integer()</v>
</type>
-
<desc>
- <p>Decode a possibly hex encoded URI. </p>
+ <p>Provides a list of the scheme and their default
+ port numbers supported (by default) by this utility.</p>
+ <marker id="parse"></marker>
</desc>
</func>
+
+
</funcs>
<!--
<section>
<title>SEE ALSO</title>
- <p>RFC 2616, <seealso marker="inets">inets(3)</seealso>,
+ <p><url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>,
+ <seealso marker="inets">inets(3)</seealso>,
<seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso>,
<seealso marker="ssl:ssl">ssl(3)</seealso>
</p>
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index 741f2abaef..ca9b268a03 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>2004</year><year>2012</year>
+ <year>2004</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
@@ -30,143 +31,252 @@
</header>
<module>httpc</module>
- <modulesummary>An HTTP/1.1 client </modulesummary>
+ <modulesummary>An HTTP/1.1 client</modulesummary>
<description>
- <p>This module provides the API to a HTTP/1.1 compatible client according
- to RFC 2616, caching is currently not supported.</p>
+ <p>This module provides the API to an HTTP/1.1 compatible client according
+ to <url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>.
+ Caching is not supported.</p>
<note>
- <p>When starting the Inets application a manager process for the
- default profile will be started. The functions in this API
- that do not explicitly use a profile will access the
+ <p>When starting the <c>Inets</c> application, a manager process for the
+ default profile is started. The functions in this API
+ that do not explicitly use a profile accesses the
default profile. A profile keeps track of proxy options,
- cookies and other options that can be applied to more than one
- request. </p>
+ cookies, and other options that can be applied to more than one
+ request.</p>
- <p>If the scheme https is used the ssl application needs to be
- started. When https links needs to go through a proxy the
+ <p>If the scheme <c>https</c> is used, the <c>SSL</c> application must
+ be started. When <c>https</c> links need to go through a proxy, the
CONNECT method extension to HTTP-1.1 is used to establish a
- tunnel and then the connection is upgraded to TLS,
- however "TLS upgrade" according to RFC 2817 is not
+ tunnel and then the connection is upgraded to TLS.
+ However, "TLS upgrade" according to <url href="http://www.ietf.org/rfc/rfc2817.txt">RFC 2817</url>is not
supported.</p>
- <p>Also note that pipelining will only be used if the pipeline
- timeout is set, otherwise persistent connections without
- pipelining will be used e.i. the client always waits for
+ <p>Pipelining is only used if the pipeline
+ time-out is set, otherwise persistent connections without
+ pipelining are used. That is, the client always waits for
the previous response before sending the next request.</p>
</note>
- <p>There are some usage examples in the <seealso
- marker="http_client">Inets User's Guide.</seealso></p>
+ <p>Some examples are provided in the <seealso
+ marker="http_client">Inets User's Guide</seealso>.</p>
</description>
<section>
- <title>COMMON DATA TYPES </title>
+ <title>DATA TYPES</title>
+ <marker id="DATA_TYPES"></marker>
<p>Type definitions that are used more than once in
this module:</p>
- <code type="none"><![CDATA[
-boolean() = true | false
-string() = list of ASCII characters
-request_id() = ref()
-profile() = atom()
-path() = string() representing a file path or directory path
-ip_address() = See inet(3)
-socket_opt() = See the Options used by gen_tcp(3) and
- ssl(3) connect(s)
- ]]></code>
-
+ <p><c>boolean() = true | false</c></p>
+ <p><c>string()</c> = list of ASCII characters</p>
+ <p><c>request_id() = ref()</c></p>
+ <p><c>profile() = atom()</c></p>
+ <p><c>path() = string()</c> representing a file path or directory path</p>
+ <p><c>ip_address()</c> = See the
+ <seealso marker="kernel:inet">inet(3)</seealso> manual page in <c>Kernel</c>.</p>
+ <p><c>socket_opt()</c> = See the options used by
+ <seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso> <c>gen_tcp(3)</c> and
+ <seealso marker="ssl:ssl">ssl(3)</seealso> connect(s)</p>
+
</section>
<section>
- <title>HTTP DATA TYPES </title>
- <p>Type definitions that are related to HTTP:</p>
- <p>For more information about HTTP see rfc 2616</p>
-
- <code type="none"><![CDATA[
-method() = head | get | put | post | trace | options | delete
-request() = {url(), headers()} |
- {url(), headers(), content_type(), body()}
-url() = string() - Syntax according to the URI definition in rfc 2396, ex: "http://www.erlang.org"
-status_line() = {http_version(), status_code(), reason_phrase()}
-http_version() = string() ex: "HTTP/1.1"
-status_code() = integer()
-reason_phrase() = string()
-content_type() = string()
-headers() = [header()]
-header() = {field(), value()}
-field() = string()
-value() = string()
-body() = string() |
- binary() |
- {fun(accumulator()) -> body_processing_result(),
- accumulator()} |
- {chunkify,
- fun(accumulator()) -> body_processing_result(),
- accumulator()}
-body_processing_result() = eof | {ok, iolist(), accumulator()}
-accumulator() = term()
-filename() = string()
- ]]></code>
+ <title>HTTP DATA TYPES</title>
+ <p>Type definitions related to HTTP:</p>
- </section>
+ <p><c>method() = head | get | put | post | trace | options | delete</c></p>
+ <taglist>
+ <tag><c>request()</c></tag>
+ <item><p>= <c>{url(), headers()}</c></p>
+ <p>| <c>{url(), headers(), content_type(), body()}</c></p>
+ </item>
+ </taglist>
+ <p><c>url() = string()</c> syntax according to the URI definition in
+ <url href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</url>,
+ for example <c>"http://www.erlang.org"</c></p>
+ <p><c>status_line() = {http_version(), status_code(), reason_phrase()}</c></p>
+ <p><c>http_version() = string()</c>, for example, <c>"HTTP/1.1"</c></p>
+ <p><c>status_code() = integer()</c></p>
+ <p><c>reason_phrase() = string()</c></p>
+ <p><c>content_type() = string()</c></p>
+ <p><c>headers() = [header()]</c></p>
+ <p><c>header() = {field(), value()}</c></p>
+ <p><c>field() = string()</c></p>
+ <p><c>value() = string()</c></p>
+ <taglist>
+ <tag><c>body()</c></tag>
+ <item><p>= <c>string() | binary()</c></p>
+ <p>| <c>{fun(accumulator())</c></p>
+ <p><c> -> body_processing_result(), accumulator()}</c></p>
+ <p>| <c>{chunkify, fun(accumulator())</c></p>
+ <p><c> -> body_processing_result(), accumulator()}</c></p>
+ </item>
+ </taglist>
+ <p><c>body_processing_result() = eof | {ok, iolist(), accumulator()}</c></p>
+ <p><c>accumulator() = term()</c></p>
+ <p><c>filename() = string()</c></p>
+ <p>For more information about HTTP, see
+ <url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>.</p>
+</section>
<section>
- <title>SSL DATA TYPES </title>
+ <title>SSL DATA TYPES</title>
<p>See <seealso marker="ssl:ssl">ssl(3)</seealso> for information
- about ssl options (<c>ssloptions()</c>). </p>
+ about <c>SSL</c> options (<c>ssloptions()</c>). </p>
</section>
<section>
- <title>HTTP CLIENT SERVICE START/STOP </title>
+ <title>HTTP CLIENT SERVICE START/STOP</title>
- <p>A HTTP client can be configured to start when starting the inets
+ <p>An HTTP client can be configured to start when starting the <c>Inets</c>
application or started dynamically in runtime by calling the
- inets application API <c>inets:start(httpc, ServiceConfig)</c>, or
- <c>inets:start(httpc, ServiceConfig, How)</c>
- see <seealso marker="inets">inets(3)</seealso>. Below follows a
- description of the available configuration options.</p>
+ <c>Inets</c> application API <c>inets:start(httpc, ServiceConfig)</c>
+ or <c>inets:start(httpc, ServiceConfig, How)</c>,
+ see <seealso marker="inets">inets(3)</seealso>.
+ The configuration options are as follows:</p>
<taglist>
<tag>{profile, profile()}</tag>
- <item>Name of the profile, see
- common data types below, this option is mandatory.</item>
+ <item><p>Name of the profile, see
+ <seealso marker="#DATA_TYPES">DATA TYPES</seealso>.
+ This option is mandatory.</p></item>
<tag>{data_dir, path()}</tag>
- <item>Directory where the profile
- may save persistent data, if omitted all cookies will be treated
- as session cookies.</item>
+ <item><p>Directory where the profile
+ can save persistent data. If omitted, all cookies are treated
+ as session cookies.</p></item>
</taglist>
<p>The client can be stopped using <c>inets:stop(httpc, Pid)</c> or
<c>inets:stop(httpc, Profile)</c>.</p>
-
- <marker id="request1"></marker>
</section>
<funcs>
+
+ <func>
+ <name>cancel_request(RequestId) -></name>
+ <name>cancel_request(RequestId, Profile) -> ok</name>
+ <fsummary>Cancels an asynchronous HTTP request.</fsummary>
+ <type>
+ <v>RequestId = request_id() - A unique identifier as returned
+ by request/4</v>
+ <v>Profile = profile() | pid()</v>
+ <d>When started <c>stand_alone</c> only the pid can be used.</d>
+ </type>
+ <desc>
+ <p>Cancels an asynchronous HTTP request. Notice that this does not guarantee
+ that the request response is not delivered. Because it is asynchronous,
+ the request can already have been completed when the cancellation arrives.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>cookie_header(Url) -> </name>
+ <name>cookie_header(Url, Profile | Opts) -> header() | {error, Reason}</name>
+ <name>cookie_header(Url, Opts, Profile) -> header() | {error, Reason}</name>
+ <fsummary>Returns the cookie header that would have been sent when
+ making a request to URL using the profile <c>Profile</c>.</fsummary>
+ <type>
+ <v>Url = url()</v>
+ <v>Opts = [cookie_header_opt()]</v>
+ <v>Profile = profile() | pid()</v>
+ <d>When started <c>stand_alone</c>.</d>
+ <v>cookie_header_opt() = {ipv6_host_with_brackets, boolean()}</v>
+ </type>
+ <desc>
+ <p>Returns the cookie header that would have been sent
+ when making a request to <c>Url</c> using profile <c>Profile</c>.
+ If no profile is specified, the default profile is used.</p>
+ <p>Option <c>ipv6_host_with_bracket</c> deals with how to
+ parse IPv6 addresses. For details,
+ see argument <c>Options</c> of
+ <seealso marker="#request-4">request/[4,5]</seealso>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>get_options(OptionItems) -> {ok, Values} | {error, Reason}</name>
+ <name>get_options(OptionItems, Profile) -> {ok, Values} | {error, Reason}</name>
+ <fsummary>Gets the currently used options.</fsummary>
+ <type>
+ <v>OptionItems = all | [option_item()]</v>
+ <v>option_item() = proxy |
+ https_proxy |
+ max_sessions |
+ keep_alive_timeout |
+ max_keep_alive_length |
+ pipeline_timeout |
+ max_pipeline_length |
+ cookies |
+ ipfamily |
+ ip |
+ port |
+ socket_opts |
+ verbose</v>
+ <v>Profile = profile() | pid()</v>
+ <d>When started <c>stand_alone</c> only the pid can used.</d>
+ <v>Values = [{option_item(), term()}]</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>Retrieves the options currently used by the client.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>info() -> list()</name>
+ <name>info(Profile) -> list()</name>
+ <fsummary>Produces a list of miscellaneous information.</fsummary>
+ <type>
+ <v>Profile = profile() | pid()</v>
+ <d>When started <c>stand_alone</c> only the pid can be used.</d>
+ </type>
+ <desc>
+ <p>Produces a list of miscellaneous information.
+ Intended for debugging.
+ If no profile is specified, the default profile is used.</p>
+ </desc>
+ </func>
+
+
+ <func>
+ <name>reset_cookies() -> void()</name>
+ <name>reset_cookies(Profile) -> void()</name>
+ <fsummary>Resets the cookie database.</fsummary>
+ <type>
+ <v>Profile = profile() | pid()</v>
+ <d>When started <c>stand_alone</c> only the pid can be used.</d>
+ </type>
+ <desc>
+ <p>Resets (clears) the cookie database for the specified
+ <c>Profile</c>. If no profile is specified the default profile
+ is used.</p>
+ </desc>
+ </func>
+
<func>
<name>request(Url) -> </name>
<name>request(Url, Profile) -> {ok, Result} | {error, Reason}</name>
- <fsummary>Sends a get HTTP-request</fsummary>
+ <fsummary>Sends a get HTTP request.</fsummary>
<type>
- <v>Url = url() </v>
+ <v>Url = url()</v>
<v>Result = {status_line(), headers(), Body} |
- {status_code(), Body} | request_id() </v>
+ {status_code(), Body} | request_id()</v>
<v>Body = string() | binary()</v>
- <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
- <v>Reason = term() </v>
+ <v>Profile = profile() | pid()</v>
+ <d>When started <c>stand_alone</c> only the pid can be used.</d>
+ <v>Reason = term()</v>
</type>
<desc>
<p>Equivalent to <c>httpc:request(get, {Url, []}, [], [])</c>.</p>
-
- <marker id="request2"></marker>
</desc>
</func>
<func>
- <name>request(Method, Request, HTTPOptions, Options) -> </name>
+ <name>request(Method, Request, HTTPOptions, Options) -></name>
<name>request(Method, Request, HTTPOptions, Options, Profile) -> {ok, Result} | {ok, saved_to_file} | {error, Reason}</name>
- <fsummary>Sends a HTTP-request</fsummary>
+ <fsummary>Sends an HTTP request.</fsummary>
<type>
- <v>Method = method() </v>
+ <v>Method = method()</v>
<v>Request = request()</v>
<v>HTTPOptions = http_options()</v>
<v>http_options() = [http_option()]</v>
@@ -190,215 +300,200 @@ filename() = string()
{socket_opts, socket_opts()} |
{receiver, receiver()},
{ipv6_host_with_brackets, boolean()}}</v>
- <v>stream_to() = none | self | {self, once} | filename() </v>
+ <v>stream_to() = none | self | {self, once} | filename()</v>
<v>socket_opts() = [socket_opt()]</v>
- <v>receiver() = pid() | function()/1 | {Module, Function, Args} </v>
- <v>Module = atom() </v>
- <v>Function = atom() </v>
- <v>Args = list() </v>
- <v>body_format() = string | binary </v>
+ <v>receiver() = pid() | function()/1 | {Module, Function, Args}</v>
+ <v>Module = atom()</v>
+ <v>Function = atom()</v>
+ <v>Args = list()</v>
+ <v>body_format() = string | binary</v>
<v>Result = {status_line(), headers(), Body} |
- {status_code(), Body} | request_id() </v>
+ {status_code(), Body} | request_id()</v>
<v>Body = string() | binary()</v>
- <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
+ <v>Profile = profile() | pid()</v>
+ <d>When started <c>stand_alone</c> only the pid can be used.</d>
<v>Reason = {connect_failed, term()} |
- {send_failed, term()} | term() </v>
+ {send_failed, term()} | term()</v>
</type>
<desc>
- <p>Sends a HTTP-request. The function can be both synchronous
- and asynchronous. In the later case the function will return
- <c>{ok, RequestId}</c> and later on the information will be delivered
- to the <c>receiver</c> depending on that value. </p>
+ <p>Sends an HTTP request. The function can be both synchronous
+ and asynchronous. In the latter case, the function returns
+ <c>{ok, RequestId}</c> and then the information is delivered
+ to the <c>receiver</c> depending on that value.</p>
- <p>Http option (<c>http_option()</c>) details: </p>
+ <p>HTTP option (<c>http_option()</c>) details:</p>
<marker id="request2_http_options"></marker>
<taglist>
<tag><c><![CDATA[timeout]]></c></tag>
<item>
- <p>Timeout time for the request. </p>
- <p>The clock starts ticking as soon as the request has been
- sent. </p>
- <p>Time is in milliseconds. </p>
- <p>Defaults to <c>infinity</c>. </p>
+ <p>Time-out time for the request.</p>
+ <p>The clock starts ticking when the request is sent.</p>
+ <p>Time is in milliseconds.</p>
+ <p>Default is <c>infinity</c>.</p>
</item>
<tag><c><![CDATA[connect_timeout]]></c></tag>
<item>
- <p>Connection timeout time, used during the initial request,
- when the client is <em>connecting</em> to the server. </p>
- <p>Time is in milliseconds. </p>
- <p>Defaults to the value of the <c>timeout</c> option. </p>
+ <p>Connection time-out time, used during the initial request,
+ when the client is <em>connecting</em> to the server.</p>
+ <p>Time is in milliseconds.</p>
+ <p>Default is the value of option <c>timeout</c>.</p>
</item>
<tag><c><![CDATA[ssl]]></c></tag>
<item>
- <p>This is the default ssl config option, currently defaults to
- <c>essl</c>, see below. </p>
- <p>Defaults to <c>[]</c>. </p>
- </item>
-
- <tag><c><![CDATA[essl]]></c></tag>
- <item>
- <p>If using the Erlang based implementation of SSL,
- these SSL-specific options are used. </p>
- <p>Defaults to <c>[]</c>. </p>
+ <p>This is the <c>SSL/TLS</c> connectin configuration option.</p>
+ <p>Defaults to <c>[]</c>. See <seealso marker="ssl:ssl">ssl:connect/[2, 3,4]</seealso> for availble options.</p>
</item>
<tag><c><![CDATA[autoredirect]]></c></tag>
<item>
- <p>Should the client automatically retrieve the information
- from the new URI and return that as the result instead
- of a 30X-result code. </p>
- <p>Note that for some 30X-result codes automatic redirect
- is not allowed. In these cases the 30X-result will always
- be returned. </p>
- <p>Defaults to <c>true</c>. </p>
+ <p>The client automatically retrieves the information
+ from the new URI and returns that as the result, instead
+ of a 30X-result code.</p>
+ <p>For some 30X-result codes, automatic redirect
+ is not allowed. In these cases the 30X-result is always
+ returned.</p>
+ <p>Default is <c>true</c>.</p>
</item>
<tag><c><![CDATA[proxy_auth]]></c></tag>
<item>
- <p>A proxy-authorization header using the provided user name and
- password will be added to the request. </p>
+ <p>A proxy-authorization header using the provided username and
+ password is added to the request.</p>
</item>
<tag><c><![CDATA[version]]></c></tag>
<item>
<p>Can be used to make the client act as an <c>HTTP/1.0</c> or
<c>HTTP/0.9</c> client. By default this is an <c>HTTP/1.1</c>
- client. When using <c>HTTP/1.0</c> persistent connections will
- not be used. </p>
- <p>Defaults to the string <c>"HTTP/1.1"</c>. </p>
+ client. When using <c>HTTP/1.0</c> persistent connections are
+ not used.</p>
+ <p>Default is the string <c>"HTTP/1.1"</c>.</p>
</item>
<tag><c><![CDATA[relaxed]]></c></tag>
<item>
- <p>If set to <c>true</c> workarounds for known server deviations
- from the HTTP-standard are enabled. </p>
- <p>Defaults to <c>false</c>. </p>
+ <p>If set to <c>true</c>, workarounds for known server deviations
+ from the HTTP-standard are enabled.</p>
+ <p>Default is <c>false</c>.</p>
</item>
<tag><c><![CDATA[url_encode]]></c></tag>
<item>
- <p>Will apply Percent-encoding, also known as URL encoding on the
+ <p>Applies Percent-encoding, also known as URL encoding on the
URL.</p>
- <p>Defaults to <c>false</c>. </p>
+ <p>Default is <c>false</c>.</p>
</item>
</taglist>
- <p>Option (<c>option()</c>) details: </p>
+ <p>Option (<c>option()</c>) details:</p>
<taglist>
<tag><c><![CDATA[sync]]></c></tag>
<item>
- <p>Shall the request be synchronous or asynchronous. </p>
- <p>Defaults to <c>true</c>. </p>
+ <p>Option for the request to be synchronous or asynchronous.</p>
+ <p>Default is <c>true</c>.</p>
</item>
<tag><c><![CDATA[stream]]></c></tag>
<item>
<p>Streams the body of a 200 or 206 response to the calling
process or to a file. When streaming to the calling process
- using the option <c>self</c> the following stream messages
- will be sent to that process: <c>{http, {RequestId,
- stream_start, Headers}, {http, {RequestId, stream,
- BinBodyPart}, {http, {RequestId, stream_end, Headers}</c>. When
- streaming to to the calling processes using the option
- <c>{self, once}</c> the first message will have an additional
- element e.i. <c>{http, {RequestId, stream_start, Headers, Pid}</c>,
- this is the process id that should be used as an argument to
+ using option <c>self</c>, the following stream messages
+ are sent to that process: <c>{http, {RequestId,
+ stream_start, Headers}}, {http, {RequestId, stream,
+ BinBodyPart}}, and {http, {RequestId, stream_end, Headers}}</c>.</p>
+ <p>When streaming to the calling processes using option
+ <c>{self, once}</c>, the first message has an extra
+ element, that is, <c>{http, {RequestId, stream_start, Headers, Pid}}</c>.
+ This is the process id to be used as an argument to
<c>http:stream_next/1</c> to trigger the next message to be sent to
- the calling process. </p>
- <p>Note that it is possible that chunked encoding will add
+ the calling process.</p>
+ <p>Notice that chunked encoding can add
headers so that there are more headers in the <c>stream_end</c>
- message than in the <c>stream_start</c>.
- When streaming to a file and the request is asynchronous the
- message <c>{http, {RequestId, saved_to_file}}</c> will be sent. </p>
- <p>Defaults to <c>none</c>. </p>
+ message than in <c>stream_start</c>.
+ When streaming to a file and the request is asynchronous, the
+ message <c>{http, {RequestId, saved_to_file}}</c> is sent.</p>
+ <p>Default is <c>none</c>.</p>
</item>
<tag><c><![CDATA[body_format]]></c></tag>
<item>
- <p>Defines if the body shall be delivered as a string or as a
+ <p>Defines if the body is to be delivered as a string or
binary. This option is only valid for the synchronous
- request. </p>
- <p>Defaults to <c>string</c>. </p>
+ request.</p>
+ <p>Default is <c>string</c>.</p>
</item>
<tag><c><![CDATA[full_result]]></c></tag>
<item>
- <p>Should a "full result" be returned to the caller (that is,
- the body, the headers and the entire status-line) or not
- (the body and the status code). </p>
- <p>Defaults to <c>true</c>. </p>
+ <p>Defines if a "full result" is to be returned to the caller (that is,
+ the body, the headers, and the entire status line) or not
+ (the body and the status code).</p>
+ <p>Default is <c>true</c>.</p>
</item>
- <tag><c><![CDATA[header_as_is]]></c></tag>
+ <tag><c><![CDATA[headers_as_is]]></c></tag>
<item>
- <p>Shall the headers provided by the user be made
- lower case or be regarded as case sensitive. </p>
- <p>Note that the http standard requires them to be
- case insenstive. This feature should only be used if there is
+ <p>Defines if the headers provided by the user are to be made
+ lower case or to be regarded as case sensitive.</p>
+ <p>The HTTP standard requires them to be
+ case insensitive. Use this feature only if there is
no other way to communicate with the server or for testing
- purpose. Also note that when this option is used no headers
- will be automatically added, all necessary headers have to be
- provided by the user. </p>
- <p>Defaults to <c>false</c>. </p>
+ purpose. When this option is used, no headers
+ are automatically added. All necessary headers must be
+ provided by the user.</p>
+ <p>Default is <c>false</c>.</p>
</item>
<tag><c><![CDATA[socket_opts]]></c></tag>
<item>
<p>Socket options to be used for this and subsequent
- request(s). </p>
- <p>Overrides any value set by the
- <seealso marker="#set_options">set_options</seealso>
- function. </p>
- <p>Note that the validity of the options are <em>not</em>
- checked in any way. </p>
- <p>Note that this may change the socket behaviour
- (see <seealso marker="kernel:inet#setopts/2">inet:setopts/2</seealso>)
- for an already existing one, and therefore an already connected
- request handler. </p>
- <p>By default the socket options set by the
- <seealso marker="#set_options">set_options/1,2</seealso>
- function are used when establishing a connection. </p>
+ requests.</p>
+ <p>Overrides any value set by function
+ <seealso marker="#set_options-1">set_options</seealso>.</p>
+ <p>The validity of the options is <em>not</em> checked by
+ the HTTP client they are assumed to be correct and passed
+ on to ssl application and inet driver, which may reject
+ them if they are not correct. Note that the current
+ implementation assumes the requests to the same host, port
+ combination will use the same socket options.
+ </p>
+
+ <p>By default the socket options set by function
+ <seealso marker="#set_options-1">set_options/[1,2]</seealso>
+ are used when establishing a connection.</p>
</item>
<tag><c><![CDATA[receiver]]></c></tag>
<item>
- <p>Defines how the client will deliver the result of an
- asynchroneous request (<c>sync</c> has the value
- <c>false</c>). </p>
+ <p>Defines how the client delivers the result of an
+ asynchronous request (<c>sync</c> has the value
+ <c>false</c>).</p>
<taglist>
<tag><c><![CDATA[pid()]]></c></tag>
<item>
- <p>Message(s) will be sent to this process in the format: </p>
-<pre>
-{http, ReplyInfo}
-</pre>
+ <p>Messages are sent to this process in the format
+ <c>{http, ReplyInfo}</c>.</p>
</item>
<tag><c><![CDATA[function/1]]></c></tag>
<item>
- <p>Information will be delivered to the receiver via calls
- to the provided fun: </p>
-<pre>
-Receiver(ReplyInfo)
-</pre>
+ <p>Information is delivered to the receiver through calls
+ to the provided fun <c>Receiver(ReplyInfo)</c>.</p>
</item>
- <tag><c><![CDATA[{Module, Funcion, Args}]]></c></tag>
+ <tag><c><![CDATA[{Module, Function, Args}]]></c></tag>
<item>
- <p>Information will be delivered to the receiver via calls
- to the callback function: </p>
-<pre>
-apply(Module, Function, [ReplyInfo | Args])
-</pre>
+ <p>Information is delivered to the receiver through calls
+ to the callback function
+ <c>apply(Module, Function, [ReplyInfo | Args])</c>.</p>
</item>
-
</taglist>
- <p>In all of the above cases, <c>ReplyInfo</c> has the following
- structure: </p>
+ <p>In all of these cases, <c>ReplyInfo</c> has the following
+ structure:</p>
<pre>
{RequestId, saved_to_file}
@@ -406,11 +501,10 @@ apply(Module, Function, [ReplyInfo | Args])
{RequestId, Result}
{RequestId, stream_start, Headers}
{RequestId, stream_start, Headers, HandlerPid}
-{RequestId, stream, BinBodyPart}
-{RequestId, stream_end, Headers}
-</pre>
+{RequestId, stream, BinBodyPart}
+{RequestId, stream_end, Headers}</pre>
- <p>Defaults to the <c>pid()</c> of the process calling the request
+ <p>Default is the <c>pid</c> of the process calling the request
function (<c>self()</c>). </p>
<marker id="ipv6_host_with_brackets"></marker>
@@ -418,273 +512,178 @@ apply(Module, Function, [ReplyInfo | Args])
<tag><c><![CDATA[ipv6_host_with_brackets]]></c></tag>
<item>
- <p>When parsing the Host-Port part of an URI with a IPv6 address
- with brackets, shall we retain those brackets (<c>true</c>) or
- strip them (<c>false</c>). </p>
- <p>Defaults to <c>false</c>. </p>
+ <p>Defines when parsing the Host-Port part of an URI with an IPv6 address
+ with brackets, if those brackets are to be retained (<c>true</c>)
+ or stripped (<c>false</c>).</p>
+ <p>Default is <c>false</c>.</p>
</item>
</taglist>
-
- <marker id="cancel_request"></marker>
- </desc>
- </func>
-
- <func>
- <name>cancel_request(RequestId) -> </name>
- <name>cancel_request(RequestId, Profile) -> ok</name>
- <fsummary>Cancels an asynchronous HTTP-request.</fsummary>
- <type>
- <v>RequestId = request_id() - A unique identifier as returned
- by request/4</v>
- <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
- </type>
- <desc>
- <p>Cancels an asynchronous HTTP-request. </p>
-
- <marker id="set_options"></marker>
</desc>
</func>
+
<func>
<name>set_options(Options) -> </name>
<name>set_options(Options, Profile) -> ok | {error, Reason}</name>
<fsummary>Sets options to be used for subsequent requests.</fsummary>
<type>
<v>Options = [Option]</v>
- <v>Option = {proxy, {Proxy, NoProxy}} |
- {https_proxy, {Proxy, NoProxy}} |
- {max_sessions, MaxSessions} |
- {max_keep_alive_length, MaxKeepAlive} |
- {keep_alive_timeout, KeepAliveTimeout} |
- {max_pipeline_length, MaxPipeline} |
- {pipeline_timeout, PipelineTimeout} |
- {cookies, CookieMode} |
- {ipfamily, IpFamily} |
- {ip, IpAddress} |
- {port, Port} |
- {socket_opts, socket_opts()} |
- {verbose, VerboseMode} </v>
-
+ <v>Option = {proxy, {Proxy, NoProxy}}</v>
+ <v>| {https_proxy, {Proxy, NoProxy}}</v>
+ <v>| {max_sessions, MaxSessions}</v>
+ <v>| {max_keep_alive_length, MaxKeepAlive}</v>
+ <v>| {keep_alive_timeout, KeepAliveTimeout}</v>
+ <v>| {max_pipeline_length, MaxPipeline}</v>
+ <v>| {pipeline_timeout, PipelineTimeout}</v>
+ <v>| {cookies, CookieMode}</v>
+ <v>| {ipfamily, IpFamily}</v>
+ <v>| {ip, IpAddress}</v>
+ <v>| {port, Port}</v>
+ <v>| {socket_opts, socket_opts()}</v>
+ <v>| {verbose, VerboseMode}</v>
<v>Proxy = {Hostname, Port}</v>
- <v>Hostname = string() </v>
- <d>ex: "localhost" or "foo.bar.se"</d>
+ <v>Hostname = string()</v>
+ <d>Example: "localhost" or "foo.bar.se"</d>
<v>Port = integer()</v>
- <d>ex: 8080 </d>
+ <d>Example: 8080</d>
<v>NoProxy = [NoProxyDesc]</v>
<v>NoProxyDesc = DomainDesc | HostName | IPDesc</v>
<v>DomainDesc = "*.Domain"</v>
- <d>ex: "*.ericsson.se"</d>
+ <d>Example: "*.ericsson.se"</d>
<v>IpDesc = string()</v>
- <d>ex: "134.138" or "[FEDC:BA98" (all IP-addresses starting with 134.138 or FEDC:BA98), "66.35.250.150" or "[2010:836B:4179::836B:4179]" (a complete IP-address).</d>
-
- <d>proxy defaults to {undefined, []} e.i. no proxy is configured and https_proxy defaults to
- the value of proxy.</d>
-
- <v>MaxSessions = integer() </v>
- <d>Default is <c>2</c>.
- Maximum number of persistent connections to a host.</d>
- <v>MaxKeepAlive = integer() </v>
- <d>Default is <c>5</c>.
- Maximum number of outstanding requests on the same connection to
- a host.</d>
- <v>KeepAliveTimeout = integer() </v>
- <d>Default is <c>120000</c> (= 2 min).
- If a persistent connection is idle longer than the
+ <d>Example: "134.138" or "[FEDC:BA98"
+ (all IP addresses starting with 134.138 or FEDC:BA98),
+ "66.35.250.150" or "[2010:836B:4179::836B:4179]" (a complete IP address).
+ <c>proxy</c> defaults to <c>{undefined, []}</c>,
+ that is, no proxy is configured and
+ <c>https_proxy</c> defaults to the value of <c>proxy</c>.</d>
+ <v>MaxSessions = integer()</v>
+ <d>Maximum number of persistent connections to a host.
+ Default is <c>2</c>.</d>
+ <v>MaxKeepAlive = integer()</v>
+ <d>Maximum number of outstanding requests on the same connection to
+ a host. Default is <c>5</c>.</d>
+ <v>KeepAliveTimeout = integer()</v>
+ <d>If a persistent connection is idle longer than the
<c>keep_alive_timeout</c> in milliseconds,
- the client will close the connection.
- The server may also have such a time out but you should
- not count on it!</d>
- <v>MaxPipeline = integer() </v>
- <d>Default is <c>2</c>.
- Maximum number of outstanding requests on a pipelined connection
- to a host.</d>
- <v>PipelineTimeout = integer() </v>
- <d>Default is <c>0</c>,
- which will result in pipelining not being used.
- If a persistent connection is idle longer than the
+ the client closes the connection.
+ The server can also have such a time-out but do not take that for granted.
+ Default is <c>120000</c> (= 2 min).</d>
+ <v>MaxPipeline = integer()</v>
+ <d>Maximum number of outstanding requests on a pipelined connection
+ to a host. Default is <c>2</c>.</d>
+ <v>PipelineTimeout = integer()</v>
+ <d>If a persistent connection is idle longer than the
<c>pipeline_timeout</c> in milliseconds,
- the client will close the connection. </d>
- <v>CookieMode = enabled | disabled | verify </v>
- <d>Default is <c>disabled</c>.
- If Cookies are enabled all valid cookies will automatically be
- saved in the client manager's cookie database.
- If the option <c>verify</c> is used the function <c>store_cookies/2</c>
- has to be called for the cookies to be saved.</d>
- <v>IpFamily = inet | inet6 | inet6fb4 </v>
- <d>By default <c>inet</c>.
- When it is set to <c>inet6fb4</c> you can use both ipv4 and ipv6.
- It first tries <c>inet6</c> and if that does not works falls back to <c>inet</c>.
- The option is here to provide a workaround for buggy ipv6 stacks to ensure that
- ipv4 will always work.</d>
- <v>IpAddress = ip_address() </v>
- <d>If the host has several network interfaces, this option specifies which one to use.
- See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d>
- <v>Port = integer() </v>
- <d>Specify which local port number to use.
- See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d>
+ the client closes the connection. Default is <c>0</c>,
+ which results in pipelining not being used.</d>
+ <v>CookieMode = enabled | disabled | verify</v>
+ <d>If cookies are enabled, all valid cookies are automatically
+ saved in the cookie database of the client manager.
+ If option <c>verify</c> is used, function <c>store_cookies/2</c>
+ has to be called for the cookies to be saved.
+ Default is <c>disabled</c>.</d>
+ <v>IpFamily = inet | inet6 </v>
+ <d>Default is <c>inet</c>.</d>
+ <v>IpAddress = ip_address()</v>
+ <d>If the host has several network interfaces, this option specifies
+ which one to use.
+ See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso>
+ for details.</d>
+ <v>Port = integer()</v>
+ <d>Local port number to use.
+ See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso>
+ for details.</d>
<v>socket_opts() = [socket_opt()]</v>
<d>The options are appended to the socket options used by the
- client. </d>
- <d>These are the default values when a new request handler
+ client.
+ These are the default values when a new request handler
is started (for the initial connect). They are passed directly
- to the underlying transport (gen_tcp or ssl) <em>without</em>
- verification! </d>
- <v>VerboseMode = false | verbose | debug | trace </v>
+ to the underlying transport (<c>gen_tcp</c> or <c>SSL</c>)
+ <em>without</em> verification.</d>
+ <v>VerboseMode = false | verbose | debug | trace</v>
<d>Default is <c>false</c>.
This option is used to switch on (or off)
- different levels of erlang trace on the client.
+ different levels of Erlang trace on the client.
It is a debug feature.</d>
- <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
+ <v>Profile = profile() | pid()</v>
+ <d>When started <c>stand_alone</c> only the pid can be used.</d>
</type>
<desc>
<p>Sets options to be used for subsequent requests.</p>
<note>
- <p>If possible the client will keep its connections
- alive and use persistent connections
- with or without pipeline depending on configuration
+ <p>If possible, the client keeps its connections
+ alive and uses persistent connections
+ with or without pipeline depending on configuration
and current circumstances. The HTTP/1.1 specification does not
- provide a guideline for how many requests would be
- ideal to be sent on a persistent connection,
- this very much depends on the
- application. Note that a very long queue of requests may cause a
- user perceived delay as earlier requests may take a long time
- to complete. The HTTP/1.1 specification does suggest a
- limit of 2 persistent connections per server, which is the
- default value of the <c>max_sessions</c> option. </p>
+ provide a guideline for how many requests that are
+ ideal to be sent on a persistent connection.
+ This depends much on the application.</p>
+ <p>A long queue of requests can cause a
+ user-perceived delay, as earlier requests can take a long time
+ to complete. The HTTP/1.1 specification suggests a
+ limit of two persistent connections per server, which is the
+ default value of option <c>max_sessions</c>.</p>
</note>
<marker id="get_options"></marker>
</desc>
</func>
-
- <func>
- <name>get_options(OptionItems) -> {ok, Values} | {error, Reason}</name>
- <name>get_options(OptionItems, Profile) -> {ok, Values} | {error, Reason}</name>
- <fsummary>Gets the currently used options.</fsummary>
- <type>
- <v>OptionItems = all | [option_item()]</v>
- <v>option_item() = proxy |
- https_proxy
- max_sessions |
- keep_alive_timeout |
- max_keep_alive_length |
- pipeline_timeout |
- max_pipeline_length |
- cookies |
- ipfamily |
- ip |
- port |
- socket_opts |
- verbose</v>
- <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
- <v>Values = [{option_item(), term()}]</v>
- <v>Reason = term() </v>
- </type>
- <desc>
- <p>Retrieves the options currently used by the client.</p>
-
- <marker id="stream_next"></marker>
- </desc>
- </func>
-
- <func>
- <name>stream_next(Pid) -> ok</name>
- <fsummary> Triggers the next message to be streamed, e.i.
- same behavior as active once for sockets.
- </fsummary>
- <type>
- <v>Pid = pid() - as received in the stream_start message</v>
- </type>
- <desc>
- <p>Triggers the next message to be streamed, e.i.
- same behavior as active once for sockets. </p>
-
- <marker id="verify_cookies"></marker>
- <marker id="store_cookies"></marker>
- </desc>
- </func>
-
+
<func>
<name>store_cookies(SetCookieHeaders, Url) -> </name>
<name>store_cookies(SetCookieHeaders, Url, Profile) -> ok | {error, Reason}</name>
- <fsummary>Saves the cookies defined in SetCookieHeaders in the client profile's cookie database.</fsummary>
+ <fsummary>Saves the cookies defined in <c>SetCookieHeaders</c> in the
+ client profile cookie database.</fsummary>
<type>
<v>SetCookieHeaders = headers() - where field = "set-cookie"</v>
<v>Url = url()</v>
- <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
- </type>
- <desc>
- <p>Saves the cookies defined in SetCookieHeaders
- in the client profile's cookie database. You need to
- call this function if you have set the option <c>cookies</c>
- to <c>verify</c>.
- If no profile is specified the default profile will be used. </p>
-
- <marker id="cookie_header"></marker>
- </desc>
- </func>
-
- <func>
- <name>cookie_header(Url) -> </name>
- <name>cookie_header(Url, Profile | Opts) -> header() | {error, Reason}</name>
- <name>cookie_header(Url, Opts, Profile) -> header() | {error, Reason}</name>
- <fsummary>Returns the cookie header that would be sent when
- making a request to Url using the profile <c>Profile</c>.</fsummary>
- <type>
- <v>Url = url()</v>
- <v>Opts = [cookie_header_opt()]</v>
- <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
- <v>cookie_header_opt() = {ipv6_host_with_brackets, boolean()}</v>
+ <v>Profile = profile() | pid()</v>
+ <d>When started <c>stand_alone</c> only the pid can be used.</d>
</type>
<desc>
- <p>Returns the cookie header that would be sent
- when making a request to <c>Url</c> using the profile <c>Profile</c>.
- If no profile is specified the default profile will be used. </p>
- <p>The option <c>ipv6_host_with_bracket</c> deals with how to
- parse IPv6 addresses.
- See the <c>Options</c> argument of the
- <seealso marker="#request2">request/4,5</seealso> for more info. </p>
-
- <marker id="reset_cookies"></marker>
+ <p>Saves the cookies defined in <c>SetCookieHeaders</c>
+ in the client profile cookie database.
+ Call this function if option <c>cookies</c> is set to <c>verify</c>.
+ If no profile is specified, the default profile is used.</p>
</desc>
</func>
-
<func>
- <name>reset_cookies() -> void()</name>
- <name>reset_cookies(Profile) -> void()</name>
- <fsummary>Reset the cookie database.</fsummary>
+ <name>stream_next(Pid) -> ok</name>
+ <fsummary>Triggers the next message to be streamed, that is,
+ the same behavior as active one for sockets.
+ </fsummary>
<type>
- <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
+ <v>Pid = pid()</v>
+ <d>As received in the <c>stream_start message</c></d>
</type>
<desc>
- <p>Resets (clears) the cookie database for the specified
- <c>Profile</c>. If no profile is specified the default profile
- will be used. </p>
+ <p>Triggers the next message to be streamed, that is,
+ the same behavior as active ones for sockets.</p>
- <marker id="which_cookies"></marker>
+ <marker id="verify_cookies"></marker>
+ <marker id="store_cookies"></marker>
</desc>
</func>
-
-
+
<func>
<name>which_cookies() -> cookies()</name>
<name>which_cookies(Profile) -> cookies()</name>
- <fsummary>Dumps out the entire cookie database.</fsummary>
+ <fsummary>Dumps the entire cookie database.</fsummary>
<type>
- <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
+ <v>Profile = profile() | pid()</v>
+ <d>When started <c>stand_alone</c> only the pid can be used.</d>
<v>cookies() = [cookie_stores()]</v>
<v>cookie_stores() = {cookies, cookies()} | {session_cookies, cookies()}</v>
<v>cookies() = [cookie()]</v>
<v>cookie() = term()</v>
</type>
<desc>
- <p>This function produces a list of the entire cookie database.
- It is intended for debugging/testing purposes.
- If no profile is specified the default profile will be used. </p>
-
- <marker id="which_sessions"></marker>
+ <p>Produces a list of the entire cookie database.
+ Intended for debugging/testing purposes.
+ If no profile is specified, the default profile is used.</p>
</desc>
</func>
@@ -693,41 +692,29 @@ apply(Module, Function, [ReplyInfo | Args])
<name>which_sessions(Profile) -> session_info()</name>
<fsummary>Produces a slightly processed dump of the sessions database.</fsummary>
<type>
- <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
+ <v>Profile = profile() | pid()</v>
+ <d>When started <c>stand_alone</c> only the pid can be used.</d>
<v>session_info() = {GoodSessions, BadSessions, NonSessions}</v>
<v>GoodSessions = session()</v>
<v>BadSessions = tuple()</v>
<v>NonSessions = term()</v>
</type>
<desc>
- <p>This function produces a slightly processed dump of the session
+ <p>Produces a slightly processed dump of the session
database. It is intended for debugging.
- If no profile is specified the default profile will be used. </p>
-
- <marker id="info"></marker>
+ If no profile is specified, the default profile is used.</p>
</desc>
</func>
- <func>
- <name>info() -> list()</name>
- <name>info(Profile) -> list()</name>
- <fsummary>Produces a list of miscelleneous info</fsummary>
- <type>
- <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
- </type>
- <desc>
- <p>This function produces a list of miscelleneous info.
- It is intended for debugging.
- If no profile is specified the default profile will be used. </p>
- </desc>
- </func>
+
</funcs>
<section>
<title>SEE ALSO</title>
- <p>RFC 2616, <seealso marker="inets">inets(3)</seealso>,
- <seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso>,
- <seealso marker="ssl:ssl">ssl(3)</seealso>
+ <p><url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>,
+ <seealso marker="inets">inets(3)</seealso>,
+ <seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso>,
+ <seealso marker="ssl:ssl">ssl(3)</seealso>
</p>
</section>
diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml
index 8438961511..62b92b8356 100644
--- a/lib/inets/doc/src/httpd.xml
+++ b/lib/inets/doc/src/httpd.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>1997</year><year>2013</year>
+ <year>1997</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- 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/.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
- 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.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
@@ -29,919 +30,892 @@
<file>httpd.sgml</file>
</header>
<module>httpd</module>
- <modulesummary>An implementation of an HTTP
- 1.1 compliant Web server, as defined in RFC 2616.
+ <modulesummary>
+ HTTP server API
</modulesummary>
<description>
- <p>Documents the HTTP server start options, some administrative
- functions and also specifies the Erlang Web server callback
- API</p>
+ <p>An implementation of an HTTP 1.1 compliant web server, as defined in
+ <url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>.
+ Provides web server start options, administrative functions, and
+ an Erlang callback API.</p>
</description>
<section>
- <title>COMMON DATA TYPES </title>
+ <title>DATA TYPES</title>
<p>Type definitions that are used more than once in
this module:</p>
- <p><c>boolean() = true | false </c></p>
- <p><c>string() = list of ASCII characters</c></p>
- <p><c>path() = string() - representing a file or directory path.</c></p>
- <p><c> ip_address() = {N1,N2,N3,N4} % IPv4
+ <p><c>boolean() = true | false</c></p>
+ <p><c>string()</c> = list of ASCII characters</p>
+ <p><c>path() = string()</c> representing a file or a directory path</p>
+ <p><c> ip_address() = {N1,N2,N3,N4} % IPv4
| {K1,K2,K3,K4,K5,K6,K7,K8} % IPv6</c></p>
- <p><c>hostname() = string() - representing a host ex "foo.bar.com"</c></p>
+ <p><c>hostname() = string()</c> representing a host, for example,
+ "foo.bar.com"</p>
<p><c>property() = atom()</c></p>
</section>
<section>
- <title>ERLANG HTTP SERVER SERVICE START/STOP </title>
- <p>A web server can be configured to start when starting the inets
- application or started dynamically in runtime by calling the
- Inets application API <c>inets:start(httpd, ServiceConfig)</c>, or
+ <title>ERLANG HTTP SERVER SERVICE START/STOP</title>
+ <p>A web server can be configured to start when starting the <c>Inets</c>
+ application, or dynamically in runtime by calling the
+ <c>Inets</c> application API <c>inets:start(httpd, ServiceConfig)</c> or
<c>inets:start(httpd, ServiceConfig, How)</c>,
- see <seealso marker="inets">inets(3)</seealso> Below follows a
- description of the available configuration options, also called
- properties.</p>
+ see <seealso marker="inets">inets(3)</seealso>.
+ The configuration options, also called
+ properties, are as follows:</p>
<marker id="props_file"></marker>
- <p><em>File properties</em></p>
+ <p><em>File Properties</em></p>
<p>When the web server is started
- at application start time the properties should be fetched from a
- configuration file that could consist of a regular erlang property
- list, e.i. <c>[{Option, Value}] </c> where <c> Option = property()
+ at application start time, the properties are to be fetched from a
+ configuration file that can consist of a regular Erlang property
+ list, that is, <c>[{Option, Value}]</c>, where <c> Option = property()
</c> and <c>Value = term()</c>, followed by a full stop, or for
- backwards compatibility an Apache like configuration file. If the
- web server is started dynamically at runtime you may still specify
- a file but you could also just specify the complete property
+ backwards compatibility, an Apache-like configuration file. If the
+ web server is started dynamically at runtime,
+ a file can still be specified but also the complete property
list.</p>
<taglist>
- <marker id="prop_proplist_file"></marker>
- <tag>{proplist_file, path()}</tag>
+ <tag><marker id="prop_proplist_file"></marker>{proplist_file, path()}</tag>
<item>
- <p>If this property is defined inets will expect to find
- all other properties defined in this file. Note that the
+ <p>If this property is defined, <c>Inets</c> expects to find
+ all other properties defined in this file. The
file must include all properties listed under mandatory
- properties. </p>
+ properties.</p>
</item>
- <marker id="prop_file"></marker>
- <tag>{file, path()}</tag>
+ <tag><marker id="prop_file"></marker>{file, path()}</tag>
<item>
- <p>If this property is defined inets will expect to find all
- other properties defined in this file, that uses Apache like
- syntax. Note that the file must include all properties listed
- under mandatory properties. The Apache like syntax is the property,
+ <p>If this property is defined, <c>Inets</c> expects to find all
+ other properties defined in this file, which uses Apache-like
+ syntax. The file must include all properties listed
+ under mandatory properties. The Apache-like syntax is the property,
written as one word where each new word begins with a capital,
- followed by a white-space followed by the value followed by a
- new line. Ex: </p>
-
+ followed by a white-space, followed by the value, followed by a
+ new line.</p>
+ <p>Example:</p>
<code>
-{server_root, "/urs/local/www"} -> ServerRoot /usr/local/www
- </code>
+{server_root, "/urs/local/www"} -> ServerRoot /usr/local/www</code>
- <p>With a few exceptions, that are documented
+ <p>A few exceptions are documented
for each property that behaves differently,
- and the special case {directory, {path(), PropertyList}} and
- {security_directory, {Dir, PropertyList}} that are represented
+ and the special cases <c>{directory, {path(), PropertyList}}</c>
+ and <c>{security_directory, {Dir, PropertyList}}</c>, are represented
as:</p>
<pre>
<![CDATA[
<Directory Dir>
<Properties handled as described above>
</Directory>
- ]]>
- </pre>
+ ]]></pre>
</item>
</taglist>
<note>
- <p>The properties proplist_file and file are mutually exclusive.</p>
+ <p>The properties <c>proplist_file</c> and <c>file</c> are mutually exclusive. Also newer properties may not be supported as Apache-like options, this is a legacy feature.</p>
</note>
<marker id="props_mand"></marker>
- <p><em>Mandatory properties</em></p>
+ <p><em>Mandatory Properties</em></p>
<taglist>
- <marker id="prop_port"></marker>
- <tag>{port, integer()} </tag>
+ <tag><marker id="prop_port"></marker>{port, integer()} </tag>
<item>
- <p>The port that the HTTP server shall listen on.
+ <p>The port that the HTTP server listen to.
If zero is specified as port, an arbitrary available port
- will be picked and you can use the httpd:info/2 function to find
- out which port was picked. </p>
+ is picked and function <c>httpd:info/2</c> can be used to
+ determine which port was picked.</p>
</item>
- <marker id="prop_server_name"></marker>
- <tag>{server_name, string()} </tag>
+ <tag><marker id="prop_server_name"></marker>{server_name, string()}</tag>
<item>
- <p>The name of your server, normally a fully qualified domain name. </p>
+ <p>The name of your server, normally a fully qualified domain name.</p>
</item>
- <marker id="prop_server_root"></marker>
- <tag>{server_root, path()} </tag>
+ <tag><marker id="prop_server_root"></marker>{server_root, path()}</tag>
<item>
- <p>Defines the servers home directory where log files etc can
- be stored. Relative paths specified in other properties refer
- to this directory. </p>
+ <p>Defines the home directory of the server, where log files, and so on,
+ can be stored. Relative paths specified in other properties refer
+ to this directory.</p>
</item>
- <marker id="prop_doc_root"></marker>
- <tag>{document_root, path()}</tag>
+ <tag> <marker id="prop_doc_root"></marker>{document_root, path()}</tag>
<item>
- Defines the top directory for the documents that
- are available on the HTTP server.
+ <p>Defines the top directory for the documents that
+ are available on the HTTP server.</p>
</item>
</taglist>
<marker id="props_comm"></marker>
- <p><em>Communication properties</em> </p>
+ <p><em>Communication Properties</em></p>
<taglist>
- <marker id="prop_bind_address"></marker>
- <tag>{bind_address, ip_address() | hostname() | any} </tag>
+ <tag><marker id="prop_bind_address"></marker>{bind_address, ip_address() | hostname() | any}</tag>
<item>
- <p>Defaults to <c>any</c>. Note that <c>any</c> is denoted <em>*</em>
- in the apache like configuration file. </p>
+ <p>Default is <c>any</c>. <c>any</c> is denoted <em>*</em>
+ in the Apache-like configuration file.</p>
</item>
- <marker id="prop_socket_type"></marker>
- <tag>{socket_type, ip_comm | {essl, Config::proplist()}}</tag>
+ <tag><marker id="profile"></marker>{profile, atom()}</tag>
<item>
- <p> For ssl configuration options see <seealso marker="ssl:ssl#listen-2">ssl:listen/2</seealso> </p>
- <p>Defaults to <c>ip_comm</c>. </p>
+ <p>Used together with <seealso marker="#prop_bind_address"><c>bind_address</c></seealso>
+ and <seealso marker="#prop_port"><c>port</c></seealso> to uniquely identify
+ a HTTP server. This can be useful in a virtualized environment,
+ where there can
+ be more that one server that has the same bind_address and port.
+ If this property is not explicitly set, it is assumed that the
+ <seealso marker="#prop_bind_address"><c>bind_address</c></seealso> and
+ <seealso marker="#prop_port"><c>port</c></seealso>uniquely identifies the HTTP server.
+ </p>
</item>
- <marker id="prop_ipfamily"></marker>
- <tag>{ipfamily, inet | inet6 | inet6fb4}</tag>
+ <tag><marker id="prop_socket_type"></marker>{socket_type, ip_comm | {ip_comm, Config::proplist()} | {essl, Config::proplist()}}</tag>
<item>
- <p>Defaults to <c>inet6fb4. </c> </p>
- <p>Note that this option is only used when the option
- <c>socket_type</c> has the value <c>ip_comm</c>. </p>
+ <p>For <c>ip_comm</c> configuration options, see
+ <seealso marker="kernel:gen_tcp#listen-2">gen_tcp:listen/2</seealso>, some options
+ that are used internally by httpd can not be set.</p>
+ <p>For <c>SSL</c> configuration options, see
+ <seealso marker="ssl:ssl#listen-2">ssl:listen/2</seealso>.</p>
+ <p>Default is <c>ip_comm</c>.</p>
</item>
- <marker id="prop_minimum_bytes_per_second"></marker>
- <tag>{minimum_bytes_per_second, integer()}</tag>
+
+ <tag><marker id="prop_ipfamily"></marker>{ipfamily, inet | inet6}</tag>
+ <item>
+ <p>Default is <c>inet</c>, legacy option <c>inet6fb4</c> no longer makes sense and will be translated
+ to inet.</p>
+ </item>
+
+ <tag><marker id="prop_minimum_bytes_per_second"></marker>{minimum_bytes_per_second, integer()}</tag>
<item>
- <p>If given, sets a minimum bytes per second value for connections.</p>
- <p>If the value is not reached, the socket will close for that connection.</p>
- <p>The option is good for reducing the risk of "slow dos" attacks.</p>
+ <p>If given, sets a minimum of bytes per second value for connections.</p>
+ <p>If the value is unreached, the socket closes for that connection.</p>
+ <p>The option is good for reducing the risk of "slow DoS" attacks.</p>
</item>
</taglist>
<marker id="props_api_modules"></marker>
- <p><em>Erlang Web server API modules</em> </p>
+ <p><em>Erlang Web Server API Modules</em> </p>
<taglist>
- <marker id="prop_modules"></marker>
- <tag>{modules, [atom()]} </tag>
+ <tag><marker id="prop_modules"></marker>{modules, [atom()]} </tag>
<item>
- <p>Defines which modules the HTTP server will use to handle
- requests. Defaults to: <c>[mod_alias, mod_auth, mod_esi,
+ <p>Defines which modules the HTTP server uses when handling
+ requests. Default is <c>[mod_alias, mod_auth, mod_esi,
mod_actions, mod_cgi, mod_dir, mod_get, mod_head, mod_log,
- mod_disk_log] </c>
- Note that some mod-modules are dependent on
- others, so the order can not be entirely arbitrary. See the
- <seealso marker="http_server"> Inets Web server Modules in the
- Users guide</seealso> for more information. </p>
+ mod_disk_log]</c>.
+ Notice that some <c>mod</c>-modules are dependent on
+ others, so the order cannot be entirely arbitrary. See the
+ <seealso marker="http_server">Inets Web Server Modules</seealso> in the
+ User's Guide for details.</p>
</item>
</taglist>
<marker id="props_limit"></marker>
<p><em>Limit properties</em> </p>
- <taglist>
- <marker id="prop_disable_chunked_encoding"></marker>
- <tag>{disable_chunked_transfer_encoding_send, boolean()}</tag>
+ <taglist>
+
+ <tag><marker id="prop_customize"></marker>{customize, atom()}</tag>
<item>
- <p>This property allows you to disable chunked
- transfer-encoding when sending a response to a HTTP/1.1
- client, by default this is false. </p>
+ <p>A callback module to customize the inets HTTP servers behaviour
+ see <seealso marker="httpd_custom_api"> httpd_custom_api</seealso> </p>
</item>
- <marker id="prop_keep_alive"></marker>
- <tag>{keep_alive, boolean()}</tag>
+ <tag><marker id="prop_disable_chunked_encoding"></marker>{disable_chunked_transfer_encoding_send, boolean()}</tag>
+ <item>
+ <p>Allows you to disable chunked
+ transfer-encoding when sending a response to an HTTP/1.1
+ client. Default is <c>false</c>.</p>
+ </item>
+
+ <tag><marker id="prop_keep_alive"></marker>{keep_alive, boolean()}</tag>
<item>
- <p>Instructs the server whether or not to use persistent
+ <p>Instructs the server whether to use persistent
connections when the client claims to be HTTP/1.1
- compliant, default is true. </p>
+ compliant. Default is <c>true</c>.</p>
</item>
- <marker id="prop_keep_alive_timeout"></marker>
- <tag>{keep_alive_timeout, integer()}</tag>
+ <tag><marker id="prop_keep_alive_timeout"></marker>{keep_alive_timeout, integer()}</tag>
<item>
- <p>The number of seconds the server will wait for a
+ <p>The number of seconds the server waits for a
subsequent request from the client before closing the
- connection. Default is 150. </p>
+ connection. Default is <c>150</c>.</p>
</item>
- <marker id="prop_max_body_size"></marker>
- <tag>{max_body_size, integer()}</tag>
+ <tag><marker id="prop_max_body_size"></marker>{max_body_size, integer()}</tag>
<item>
- <p>Limits the size of the message body of HTTP request.
- By the default there is no limit. </p>
+ <p>Limits the size of the message body of an HTTP request.
+ Default is no limit.</p>
</item>
- <marker id="prop_max_clients"></marker>
- <tag>{max_clients, integer()}</tag>
+ <tag><marker id="prop_max_clients"></marker>{max_clients, integer()}</tag>
<item>
<p>Limits the number of simultaneous requests that can be
- supported. Defaults to 150. </p>
+ supported. Default is <c>150</c>.</p>
</item>
- <marker id="prop_max_header_size"></marker>
- <tag>{max_header_size, integer()}</tag>
+ <tag><marker id="prop_max_header_size"></marker>{max_header_size, integer()}</tag>
<item>
- <p>Limits the size of the message header of HTTP request.
- Defaults to 10240. </p>
+ <p>Limits the size of the message header of an HTTP request.
+ Default is <c>10240</c>.</p>
</item>
-
- <marker id="prop_max_uri"></marker>
- <tag>{max_uri, integer()}</tag>
+
+ <tag><marker id="prop_max_content_length"></marker>{max_content_length, integer()}</tag>
<item>
- <p>Limits the size of the HTTP request URI. By
- default there is no limit. </p>
+ <p>Maximum content-length in an incoming request, in bytes. Requests
+ with content larger than this are answered with status 413.
+ Default is <c>100000000</c> (100 MB).
+ </p>
+ </item>
+
+ <tag><marker id="prop_max_uri"></marker>{max_uri_size, integer()}</tag>
+ <item>
+ <p>Limits the size of the HTTP request URI.
+ Default is no limit.</p>
</item>
- <marker id="prop_max_keep_alive_req"></marker>
- <tag>{max_keep_alive_requests, integer()}</tag>
+ <tag><marker id="prop_max_keep_alive_req"></marker>{max_keep_alive_request, integer()}</tag>
<item>
- <p>The number of request that a client can do on one
+ <p>The number of requests that a client can do on one
connection. When the server has responded to the number of
- requests defined by max_keep_alive_requests the server close the
- connection. The server will close it even if there are queued
- request. Defaults to no limit. </p>
+ requests defined by <c>max_keep_alive_requests</c>, the server
+ closes the connection. The server closes it even if there are
+ queued request. Default is no limit.</p>
</item>
</taglist>
<marker id="props_admin"></marker>
- <p><em>Administrative properties</em></p>
+ <p><em>Administrative Properties</em></p>
<taglist>
- <marker id="prop_mime_types"></marker>
- <tag>{mime_types, [{MimeType, Extension}] | path()}</tag>
+ <tag><marker id="prop_mime_types"></marker>{mime_types, [{MimeType, Extension}] | path()}</tag>
<item>
- <p>Where MimeType = string() and Extension = string().
+ <p><c>MimeType = string()</c> and <c>Extension = string()</c>.
Files delivered to the client are MIME typed according to RFC
1590. File suffixes are mapped to MIME types before file delivery.
The mapping between file suffixes and MIME types can be specified
- as an Apache like file as well as directly in the property list. Such
- a file may look like:</p>
+ as an Apache-like file or directly in the property list. Such
+ a file can look like the follwoing:</p>
<pre>
# MIME type Extension
text/html html htm
-text/plain asc txt
- </pre>
+text/plain asc txt</pre>
- <p>Defaults to [{"html","text/html"},{"htm","text/html"}]</p>
+ <p>Default is [{"html","text/html"},{"htm","text/html"}].</p>
</item>
- <marker id="prop_mime_type"></marker>
- <tag>{mime_type, string()}</tag>
+ <tag><marker id="prop_mime_type"></marker>{mime_type, string()}</tag>
<item>
- <p>When the server is asked to provide a document type which
- cannot be determined by the MIME Type Settings, the server will
- use this default type. </p>
+ <p>When the server is asked to provide a document type that
+ cannot be determined by the MIME Type Settings, the server
+ uses this default type.</p>
</item>
- <marker id="prop_server_admin"></marker>
- <tag>{server_admin, string()}</tag>
+ <tag><marker id="prop_server_admin"></marker>{server_admin, string()}</tag>
<item>
- <p>ServerAdmin defines the email-address of the server
- administrator, to be included in any error messages returned by
- the server. </p>
+ <p>Defines the email-address of the server
+ administrator to be included in any error messages returned by
+ the server.</p>
</item>
- <marker id="prop_server_tokens"></marker>
- <tag>{server_tokens, prod|major|minor|minimal|os|full|{private, string()}}</tag>
+ <tag><marker id="prop_server_tokens"></marker>{server_tokens, none|prod|major|minor|minimal|os|full|{private, string()}}</tag>
<item>
- <p>ServerTokens defines how the value of the server header
- should look. </p>
- <p>Example: Assuming the version of inets is 5.8.1,
- here is what the server header string could look like for
- the different values of server-tokens: </p>
- <pre>
-prod "inets"
-major "inets/5"
-minor "inets/5.8"
-minimal "inets/5.8.1"
-os "inets/5.8.1 (unix)"
-full "inets/5.8.1 (unix/linux) OTP/R15B"
-{private, "foo/bar"} "foo/bar"
- </pre>
- <p>By default, the value is as before, which is <c>minimal</c>. </p>
- </item>
-
- <marker id="prop_log_format"></marker>
- <tag>{log_format, common | combined}</tag>
+ <p>Defines the look of the value of the server header.</p>
+ <p>Example: Assuming the version of <c>Inets</c> is 5.8.1,
+ the server header string can look as follows for
+ the different values of server-tokens:</p>
+ <taglist>
+ <tag><c>none</c></tag>
+ <item><p>"" % A Server: header will not be generated</p></item>
+ <tag><c>prod</c></tag>
+ <item><p>"inets"</p></item>
+ <tag><c>major</c></tag>
+ <item><p>"inets/5"</p></item>
+ <tag><c>minor</c></tag>
+ <item><p>"inets/5.8"</p></item>
+ <tag><c>minimal</c></tag>
+ <item><p>"inets/5.8.1"</p></item>
+ <tag><c>os</c></tag>
+ <item><p>"inets/5.8.1 (unix)"</p></item>
+ <tag><c>full</c></tag>
+ <item><p>"inets/5.8.1 (unix/linux) OTP/R15B"</p></item>
+ <tag><c>{private, "foo/bar"}</c></tag>
+ <item><p>"foo/bar"</p></item>
+ </taglist>
+ <p>By default, the value is as before, that is, <c>minimal</c>.</p>
+ </item>
+
+ <tag><marker id="prop_log_format"></marker>{log_format, common | combined}</tag>
<item>
- <p>Defines if access logs should be written according to the common
- log format or to the extended common log format.
- The <c>common</c> format is one line that looks like this:
- <c>remotehost rfc931 authuser [date] "request" status bytes</c></p>
-
- <pre>
-remotehost
- Remote
-rfc931
- The client's remote username (RFC 931).
-authuser
- The username with which the user authenticated
- himself.
-[date]
- Date and time of the request (RFC 1123).
-"request"
- The request line exactly as it came from the client
- (RFC 1945).
-status
- The HTTP status code returned to the client
- (RFC 1945).
-bytes
- The content-length of the document transferred.
- </pre>
-
- <p>The <c>combined</c> format is on line that look like this:
+ <p>Defines if access logs are to be written according to the <c>common</c>
+ log format or the extended common log format.
+ The <c>common</c> format is one line looking like this:
+ <c>remotehost rfc931 authuser [date] "request" status bytes</c>.</p>
+ <p>Here:</p>
+ <taglist>
+ <tag><c>remotehost</c></tag>
+ <item>Remote.</item>
+ <tag><c>rfc931</c></tag>
+ <item>The remote username of the client (<url href="http://www.ietf.org/rfc/rfc931.txt">RFC 931</url>).</item>
+ <tag><c>authuser</c></tag>
+ <item>The username used for authentication.</item>
+ <tag><c>[date]</c></tag>
+ <item>Date and time of the request (<url href="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</url>).</item>
+ <tag><c>"request"</c></tag>
+ <item>The request line as it came from the client (<url href="http://www.ietf.org/rfc/rfc1945.txt">RFC 1945</url>).</item>
+ <tag><c>status</c></tag>
+ <item>The HTTP status code returned to the client (<url href="http://www.ietf.org/rfc/rfc1945.txt">RFC 1945</url>).</item>
+ <tag><c>bytes</c></tag>
+ <item>The content-length of the document transferred.</item>
+ </taglist>
+
+ <p>The <c>combined</c> format is one line looking like this:
<c>remotehost rfc931 authuser [date] "request" status bytes "referer" "user_agent" </c></p>
+ <p>In addition to the earlier:</p>
+ <taglist>
+ <tag><c>"referer"</c></tag>
+ <item>The URL the client was on before
+ requesting the URL (if it could not be determined,
+ a minus sign is placed in this field).</item>
+ <tag><c>"user_agent"</c></tag>
+ <item>The software the client claims to be using (if it
+ could not be determined, a minus sign is placed in
+ this field).</item>
+ </taglist>
- <pre>
-"referer"
- The url the client was on before
- requesting your url. (If it could not be determined
- a minus sign will be placed in this field)
-"user_agent"
- The software the client claims to be using. (If it
- could not be determined a minus sign will be placed in
- this field)
- </pre>
-
- <p>This affects the access logs written by mod_log and mod_disk_log.
- </p>
-
+ <p>This affects the access logs written by <c>mod_log</c> and
+ <c>mod_disk_log</c>.
+ </p>
</item>
- <marker id="prop_elog_format"></marker>
- <tag>{error_log_format, pretty | compact}</tag>
+ <tag><marker id="prop_elog_format"></marker>{error_log_format, pretty | compact}</tag>
<item>
- <p>Defaults to pretty. If the error log is meant to be read
- directly by a human <c>pretty</c> will be the best
- option. <c>pretty</c> has the format corresponding to:
- </p>
+ <p>Default is <c>pretty</c>. If the error log is meant to be read
+ directly by a human, <c>pretty</c> is the best option.</p>
+ <p><c>pretty</c> has a format corresponding to:</p>
- <code>io:format("[~s] ~s, reason: ~n ~p ~n~n", [Date, Msg, Reason]).
- </code>
+ <code>io:format("[~s] ~s, reason: ~n ~p ~n~n", [Date, Msg, Reason]).</code>
- <p><c>compact</c> has the format corresponding to:</p>
+ <p><c>compact</c> has a format corresponding to:</p>
- <code>io:format("[~s] ~s, reason: ~w ~n", [Date, Msg, Reason]).
- </code>
+ <code>io:format("[~s] ~s, reason: ~w ~n", [Date, Msg, Reason]).</code>
- <p>This affects the error logs written by mod_log and mod_disk_log.
+ <p>This affects the error logs written by <c>mod_log</c> and
+ <c>mod_disk_log</c>.
</p>
</item>
</taglist>
<marker id="props_alias"></marker>
- <p><em>URL aliasing properties - requires mod_alias</em></p>
+ <p><em>URL Aliasing Properties - Requires mod_alias</em></p>
<taglist>
- <marker id="prop_alias"></marker>
- <tag>{alias, {Alias, RealName}}</tag>
+ <tag><marker id="prop_alias"></marker>{alias, {Alias, RealName}}</tag>
<item>
- <p>Where Alias = string() and RealName = string().
- The Alias property allows documents to be stored in the local file
- system instead of the document_root location. URLs with a path that
- begins with url-path is mapped to local files that begins with
- directory-filename, for example:
+ <p><c>Alias = string()</c> and <c>RealName = string()</c>.
+ <c>alias</c> allows documents to be stored in the local file
+ system instead of the <c>document_root</c> location. URLs with a path
+ beginning with url-path is mapped to local files beginning with
+ directory-filename, for example:</p>
- <code>{alias, {"/image", "/ftp/pub/image"}</code>
+ <code>{alias, {"/image", "/ftp/pub/image"}}</code>
- and an access to http://your.server.org/image/foo.gif would refer to
- the file /ftp/pub/image/foo.gif. </p>
+ <p>Access to http://your.server.org/image/foo.gif would refer to
+ the file /ftp/pub/image/foo.gif.</p>
</item>
- <marker id="prop_re_write"></marker>
- <tag>{re_write, {Re, Replacement}}</tag>
+ <tag><marker id="prop_re_write"></marker>{re_write, {Re, Replacement}}</tag>
<item>
- <p>Where Re = string() and Replacement = string().
- The ReWrite property allows documents to be stored in the local file
- system instead of the document_root location. URLs are rewritten
- by re:replace/3 to produce a path in the local filesystem.
- For example:
+ <p><c>Re = string()</c> and <c>Replacement = string()</c>.
+ <c>re_write</c> allows documents to be stored in the local file
+ system instead of the <c>document_root</c> location. URLs are rewritten
+ by <c>re:replace/3</c> to produce a path in the local file-system,
+ for example:</p>
- <code>{re_write, {"^/[~]([^/]+)(.*)$", "/home/\\1/public\\2"}</code>
+ <code>{re_write, {"^/[~]([^/]+)(.*)$", "/home/\\1/public\\2"}}</code>
- and an access to http://your.server.org/~bob/foo.gif would refer to
+ <p>Access to http://your.server.org/~bob/foo.gif would refer to
the file /home/bob/public/foo.gif.
- In an Apache like configuration file the Re is separated
- from Replacement with one single space, and as expected
- backslashes do not need to be backslash escaped so the
- same example would become:
+ In an Apache-like configuration file, <c>Re</c> is separated
+ from <c>Replacement</c> with one single space, and as expected
+ backslashes do not need to be backslash escaped, the
+ same example would become:</p>
<code>ReWrite ^/[~]([^/]+)(.*)$ /home/\1/public\2</code>
- Beware of trailing space in Replacement that will be used.
- If you must have a space in Re use e.g the character encoding
- <code>\040</code> see <seealso marker="stdlib:re">re(3)</seealso>. </p>
+ <p>Beware of trailing space in <c>Replacement</c> to be used.
+ If you must have a space in <c>Re</c>, use, for example, the character
+ encoding <c>\040</c>, see
+ <seealso marker="stdlib:re">re(3)</seealso>.</p>
</item>
- <marker id="prop_dir_idx"></marker>
- <tag>{directory_index, [string()]}</tag>
+ <tag><marker id="prop_dir_idx"></marker>{directory_index, [string()]}</tag>
<item>
- <p>DirectoryIndex specifies a list of resources to look for
- if a client requests a directory using a / at the end of the
- directory name. file depicts the name of a file in the
- directory. Several files may be given, in which case the server
- will return the first it finds, for example:
+ <p><c>directory_index</c> specifies a list of resources to look for
+ if a client requests a directory using a <c>/</c> at the end of the
+ directory name. <c>file</c> depicts the name of a file in the
+ directory. Several files can be given, in which case the server
+ returns the first it finds, for example:</p>
<code>{directory_index, ["index.hml", "welcome.html"]}</code>
- and access to http://your.server.org/docs/ would return
+ <p>Access to http://your.server.org/docs/ would return
http://your.server.org/docs/index.html or
- http://your.server.org/docs/welcome.html if index.html do not
- exist. </p>
+ http://your.server.org/docs/welcome.html if index.html does not
+ exist.</p>
</item>
</taglist>
<marker id="props_cgi"></marker>
- <p><em>CGI properties - requires mod_cgi</em></p>
+ <p><em>CGI Properties - Requires mod_cgi</em></p>
<taglist>
- <marker id="prop_script_alias"></marker>
- <tag>{script_alias, {Alias, RealName}}</tag>
+ <tag><marker id="prop_script_alias"></marker>{script_alias, {Alias, RealName}}</tag>
<item>
- <p>Where Alias = string() and RealName = string().
- Has the same behavior as the Alias property, except that
- it also marks the target directory as containing CGI
+ <p><c>Alias = string()</c> and <c>RealName = string()</c>.
+ Have the same behavior as property <c>alias</c>, except that
+ they also mark the target directory as containing CGI
scripts. URLs with a path beginning with url-path are mapped to
- scripts beginning with directory-filename, for example:
+ scripts beginning with directory-filename, for example:</p>
- <code>{script_alias, {"/cgi-bin/", "/web/cgi-bin/"}</code>
+ <code>{script_alias, {"/cgi-bin/", "/web/cgi-bin/"}}</code>
- and an access to http://your.server.org/cgi-bin/foo would cause
- the server to run the script /web/cgi-bin/foo. </p>
+ <p>Access to http://your.server.org/cgi-bin/foo would cause
+ the server to run the script /web/cgi-bin/foo.</p>
</item>
- <marker id="prop_script_re_write"></marker>
- <tag>{script_re_write, {Re, Replacement}}</tag>
+ <tag><marker id="prop_script_re_write"></marker>{script_re_write, {Re, Replacement}}</tag>
<item>
- <p>Where Re = string() and Replacement = string().
- Has the same behavior as the ReWrite property, except that
- it also marks the target directory as containing CGI
+ <p><c>Re = string()</c> and <c>Replacement = string()</c>.
+ Have the same behavior as property <c>re_write</c>, except that
+ they also mark the target directory as containing CGI
scripts. URLs with a path beginning with url-path are mapped to
- scripts beginning with directory-filename, for example:
+ scripts beginning with directory-filename, for example:</p>
- <code>{script_re_write, {"^/cgi-bin/(\\d+)/", "/web/\\1/cgi-bin/"}</code>
+ <code>{script_re_write, {"^/cgi-bin/(\\d+)/", "/web/\\1/cgi-bin/"}}</code>
- and an access to http://your.server.org/cgi-bin/17/foo would cause
- the server to run the script /web/17/cgi-bin/foo. </p>
+ <p>Access to http://your.server.org/cgi-bin/17/foo would cause
+ the server to run the script /web/17/cgi-bin/foo.</p>
</item>
- <marker id="prop_script_nocache"></marker>
- <tag>{script_nocache, boolean()}</tag>
+ <tag><marker id="prop_script_nocache"></marker>{script_nocache, boolean()}</tag>
<item>
- <p>If ScriptNoCache is set to true the HTTP server will by
- default add the header fields necessary to prevent proxies from
- caching the page. Generally this is something you want. Defaults
- to false. </p>
+ <p>If <c>script_nocache</c> is set to <c>true</c>, the HTTP server by
+ default adds the header fields necessary to prevent proxies from
+ caching the page. Generally this is preferred.
+ Default to <c>false</c>.</p>
</item>
- <marker id="prop_script_timeout"></marker>
- <tag>{script_timeout, integer()}</tag>
+ <tag><marker id="prop_script_timeout"></marker>{script_timeout, integer()}</tag>
<item>
- <p>The time in seconds the web server will wait between each
- chunk of data from the script. If the CGI-script not delivers
- any data before the timeout the connection to the client will be
- closed. Defaults to 15. </p>
+ <p>The time in seconds the web server waits between each
+ chunk of data from the script. If the CGI script does not deliver
+ any data before the timeout, the connection to the client is
+ closed. Default is <c>15</c>.</p>
</item>
- <marker id="prop_action"></marker>
- <tag>{action, {MimeType, CgiScript}} - requires mod_action</tag>
+ <tag><marker id="prop_action"></marker>{action, {MimeType, CgiScript}} - requires mod_action</tag>
<item>
- <p>Where MimeType = string() and CgiScript = string().
- Action adds an action, which will activate a cgi-script
- whenever a file of a certain mime-type is requested. It
+ <p><c>MimeType = string()</c> and <c>CgiScript = string()</c>.
+ <c>action</c> adds an action activating a CGI script
+ whenever a file of a certain MIME type is requested. It
propagates the URL and file path of the requested document using
the standard CGI PATH_INFO and PATH_TRANSLATED environment
- variables.
+ variables.</p>
+ <p>Example:</p>
- <code>{action, {"text/plain", "/cgi-bin/log_and_deliver_text"}</code>
- </p>
+ <code>{action, {"text/plain", "/cgi-bin/log_and_deliver_text"}}</code>
</item>
- <marker id="prop_script"></marker>
- <tag>{script, {Method, CgiScript}} - requires mod_action</tag>
+ <tag><marker id="prop_script"></marker>{script, {Method, CgiScript}} - requires mod_action</tag>
<item>
- <p>Where Method = string() and CgiScript = string().
- Script adds an action, which will activate a cgi-script
+ <p><c>Method = string()</c> and <c>CgiScript = string()</c>.
+ <c>script</c> adds an action activating a CGI script
whenever a file is requested using a certain HTTP method. The
- method is either GET or POST as defined in RFC 1945. It
+ method is either GET or POST, as defined in <url href="http://www.ietf.org/rfc/rfc1945.txt">RFC 1945</url>. It
propagates the URL and file path of the requested document using
the standard CGI PATH_INFO and PATH_TRANSLATED environment
- variables.
+ variables.</p>
+ <p>Example:</p>
- <code>{script, {"PUT", "/cgi-bin/put"}</code>
- </p>
+ <code>{script, {"PUT", "/cgi-bin/put"}}</code>
</item>
</taglist>
<marker id="props_esi"></marker>
- <p><em>ESI properties - requires mod_esi</em></p>
+ <p><em>ESI Properties - Requires mod_esi</em></p>
<taglist>
- <marker id="prop_esi_alias"></marker>
- <tag>{erl_script_alias, {URLPath, [AllowedModule]}}</tag>
+ <tag><marker id="prop_esi_alias"></marker>{erl_script_alias, {URLPath, [AllowedModule]}}</tag>
<item>
- <p>Where URLPath = string() and AllowedModule = atom().
- erl_script_alias marks all URLs matching url-path as erl
+ <p><c>URLPath = string()</c> and <c>AllowedModule = atom()</c>.
+ <c>erl_script_alias</c> marks all URLs matching url-path as erl
scheme scripts. A matching URL is mapped into a specific module
- and function. For example:
+ and function, for example:</p>
- <code>{erl_script_alias, {"/cgi-bin/example", [httpd_example]}
- </code>
+ <code>{erl_script_alias, {"/cgi-bin/example", [httpd_example]}}</code>
- and a request to
+ <p>A request to
http://your.server.org/cgi-bin/example/httpd_example:yahoo
- would refer to httpd_example:yahoo/3 or, if that did not exist,
+ would refer to httpd_example:yahoo/3 or, if that does not exist,
httpd_example:yahoo/2 and
http://your.server.org/cgi-bin/example/other:yahoo would
- not be allowed to execute. </p>
+ not be allowed to execute.</p>
</item>
- <marker id="prop_esi_nocache"></marker>
- <tag>{erl_script_nocache, boolean()}</tag>
+ <tag><marker id="prop_esi_nocache"></marker>{erl_script_nocache, boolean()}</tag>
<item>
- <p>If erl_script_nocache is set to true the server will add
- http header fields that prevents proxies from caching the
- page. This is generally a good idea for dynamic content, since
- the content often vary between each request.
- Defaults to false. </p>
+ <p>If <c>erl_script_nocache</c> is set to <c>true</c>, the server adds
+ HTTP header fields preventing proxies from caching the
+ page. This is generally a good idea for dynamic content, as
+ the content often varies between each request.
+ Default is <c>false</c>.</p>
</item>
- <marker id="prop_esi_timeout"></marker>
- <tag>{erl_script_timeout, integer()}</tag>
+ <tag><marker id="prop_esi_timeout"></marker>{erl_script_timeout, integer()}</tag>
<item>
- <p>If erl_script_timeout sets the time in seconds the server will
- wait between each chunk of data to be delivered through
- mod_esi:deliver/2. Defaults to 15. This is only relevant
- for scripts that uses the erl scheme. </p>
+ <p>If <c>erl_script_timeout</c> sets the time in seconds the server
+ waits between each chunk of data to be delivered through
+ <c>mod_esi:deliver/2</c>. Default is <c>15</c>. This is only relevant
+ for scripts that use the erl scheme.</p>
</item>
- <marker id="prop_esi_timeout"></marker>
- <tag>{eval_script_alias, {URLPath, [AllowedModule]}}</tag>
+ <tag><marker id="prop_esi_timeout"></marker>{eval_script_alias, {URLPath, [AllowedModule]}}</tag>
<item>
- <p>Where URLPath = string() and AllowedModule = atom().
- Same as erl_script_alias but for scripts
- using the eval scheme. Note that this is only supported
- for backwards compatibility. The eval scheme is deprecated. </p>
+ <p><c>URLPath = string()</c> and <c>AllowedModule = atom()</c>.
+ Same as <c>erl_script_alias</c> but for scripts
+ using the eval scheme. This is only supported
+ for backwards compatibility. The eval scheme is deprecated.</p>
</item>
</taglist>
<marker id="props_log"></marker>
- <p><em>Log properties - requires mod_log</em></p>
+ <p><em>Log Properties - Requires mod_log</em></p>
<taglist>
- <marker id="prop_elog"></marker>
- <tag>{error_log, path()}</tag>
+ <tag><marker id="prop_elog"></marker>{error_log, path()}</tag>
<item>
<p>Defines the filename of the error log file to be used to log
- server errors. If the filename does not begin with a slash (/)
- it is assumed to be relative to the server_root. </p>
+ server errors. If the filename does not begin with a slash (/),
+ it is assumed to be relative to the <c>server_root</c>.</p>
</item>
- <marker id="prop_slog"></marker>
- <tag>{security_log, path()}</tag>
+ <tag><marker id="prop_slog"></marker>{security_log, path()}</tag>
<item>
<p>Defines the filename of the access log file to be used to
log security events. If the filename does not begin with a slash
- (/) it is assumed to be relative to the server_root. </p>
+ (/), it is assumed to be relative to the <c>server_root</c>.</p>
</item>
- <marker id="prop_tlog"></marker>
- <tag>{transfer_log, path()}</tag>
+ <tag><marker id="prop_tlog"></marker>{transfer_log, path()}</tag>
<item>
<p>Defines the filename of the access log file to be used to
log incoming requests. If the filename does not begin with a
- slash (/) it is assumed to be relative to the server_root. </p>
+ slash (/), it is assumed to be relative to the <c>server_root</c>.</p>
</item>
</taglist>
<marker id="props_dlog"></marker>
- <p><em>Disk Log properties - requires mod_disk_log</em></p>
+ <p><em>Disk Log Properties - Requires mod_disk_log</em></p>
<taglist>
- <marker id="prop_dlog_format"></marker>
- <tag>{disk_log_format, internal | external}</tag>
+ <tag><marker id="prop_dlog_format"></marker>{disk_log_format, internal | external}</tag>
<item>
- <p>Defines the file-format of the log files see disk_log for
- more information. If the internal file-format is used, the
- logfile will be repaired after a crash. When a log file is
- repaired data might get lost. When the external file-format is
- used httpd will not start if the log file is broken. Defaults to
- external. </p>
+ <p>Defines the file format of the log files. See <c>disk_log</c> for
+ details. If the internal file format is used, the
+ log file is repaired after a crash. When a log file is
+ repaired, data can disappear. When the external file format is
+ used, <c>httpd</c> does not start if the log file is broken. Default is
+ <c>external</c>.</p>
</item>
- <marker id="prop_edlog"></marker>
- <tag>{error_disk_log, internal | external}</tag>
+ <tag><marker id="prop_edlog"></marker>{error_disk_log, path()}</tag>
<item>
- <p>Defines the filename of the (disk_log(3)) error log file
+ <p>Defines the filename of the (<c>disk_log(3)</c>) error log file
to be used to log server errors. If the filename does not begin
- with a slash (/) it is assumed to be relative to the server_root. </p>
+ with a slash (/), it is assumed to be relative to the <c>server_root</c>.</p>
</item>
- <marker id="prop_edlog_size"></marker>
- <tag>{error_disk_log_size, {MaxBytes, MaxFiles}}</tag>
+ <tag><marker id="prop_edlog_size"></marker>{error_disk_log_size, {MaxBytes, MaxFiles}}</tag>
<item>
- <p>Where MaxBytes = integer() and MaxFiles = integer().
- Defines the properties of the (disk_log(3)) error log
- file. The disk_log(3) error log file is of type wrap log and
- max-bytes will be written to each file and max-files will be
- used before the first file is truncated and reused. </p>
+ <p><c>MaxBytes = integer()</c> and <c>MaxFiles = integer()</c>.
+ Defines the properties of the (<c>disk_log(3)</c>) error log
+ file. This file is of type wrap log and
+ max bytes is written to each file and max files is
+ used before the first file is truncated and reused.</p>
</item>
- <marker id="prop_sdlog"></marker>
- <tag>{security_disk_log, path()}</tag>
+ <tag><marker id="prop_sdlog"></marker>{security_disk_log, path()}</tag>
<item>
- <p>Defines the filename of the (disk_log(3)) access log file
- which logs incoming security events i.e authenticated
- requests. If the filename does not begin with a slash (/) it
- is assumed to be relative to the server_root. </p>
+ <p>Defines the filename of the (<c>disk_log(3)</c>) access log file
+ logging incoming security events, that is, authenticated
+ requests. If the filename does not begin with a slash (/), it
+ is assumed to be relative to the <c>server_root</c>.</p>
</item>
- <marker id="prop_sdlog_size"></marker>
- <tag>{security_disk_log_size, {MaxBytes, MaxFiles}}</tag>
+ <tag><marker id="prop_sdlog_size"></marker>{security_disk_log_size, {MaxBytes, MaxFiles}}</tag>
<item>
- <p>Where MaxBytes = integer() and MaxFiles = integer().
- Defines the properties of the disk_log(3) access log
- file. The disk_log(3) access log file is of type wrap log and
- max-bytes will be written to each file and max-files will be
- used before the first file is truncated and reused. </p>
+ <p><c>MaxBytes = integer()</c> and <c>MaxFiles = integer()</c>.
+ Defines the properties of the <c>disk_log(3)</c> access log
+ file. This file is of type wrap log and
+ max bytes is written to each file and max files is
+ used before the first file is truncated and reused.</p>
</item>
- <marker id="prop_tdlog"></marker>
- <tag>{transfer_disk_log, path()}</tag>
+ <tag><marker id="prop_tdlog"></marker>{transfer_disk_log, path()}</tag>
<item>
- <p>Defines the filename of the (disk_log(3)) access log file
- which logs incoming requests. If the filename does not begin
- with a slash (/) it is assumed to be relative to the
- server_root. </p>
+ <p>Defines the filename of the (<c>disk_log(3)</c>) access log file
+ logging incoming requests. If the filename does not begin
+ with a slash (/), it is assumed to be relative to the
+ <c>server_root</c>.</p>
</item>
- <marker id="prop_tdlog_size"></marker>
- <tag>{transfer_disk_log_size, {MaxBytes, MaxFiles}}</tag>
+ <tag><marker id="prop_tdlog_size"></marker>{transfer_disk_log_size, {MaxBytes, MaxFiles}}</tag>
<item>
- <p>Where MaxBytes = integer() and MaxFiles = integer().
- Defines the properties of the disk_log(3) access log
- file. The disk_log(3) access log file is of type wrap log and
- max-bytes will be written to each file and max-files will be
- used before the first file is truncated and reused. </p>
+ <p><c>MaxBytes = integer()</c> and <c>MaxFiles = integer()</c>.
+ Defines the properties of the <c>disk_log(3)</c> access log
+ file. This file is of type wrap log and
+ max bytes is written to each file and max files is
+ used before the first file is truncated and reused.</p>
</item>
</taglist>
<marker id="props_auth"></marker>
- <p><em>Authentication properties - requires mod_auth</em></p>
+ <p><em>Authentication Properties - Requires mod_auth</em></p>
<marker id="prop_dri"></marker>
<p><em>{directory, {path(), [{property(), term()}]}}</em></p>
<marker id="props_dir"></marker>
- <p>Here follows the valid properties for directories </p>
+ <p>The properties for directories are as follows:</p>
<taglist>
- <marker id="prop_allow_from"></marker>
- <tag>{allow_from, all | [RegxpHostString]}</tag>
+ <tag><marker id="prop_allow_from"></marker>{allow_from, all | [RegxpHostString]}</tag>
<item>
- <p>Defines a set of hosts which should be granted access to a
- given directory.
+ <p>Defines a set of hosts to be granted access to a
+ given directory, for example:</p>
- For example:
-
- <code>{allow_from, ["123.34.56.11", "150.100.23"] </code>
+ <code>{allow_from, ["123.34.56.11", "150.100.23"]}</code>
- The host 123.34.56.11 and all machines on the 150.100.23
- subnet are allowed access. </p>
+ <p>The host <c>123.34.56.11</c> and all machines on the <c>150.100.23</c>
+ subnet are allowed access.</p>
</item>
- <marker id="prop_deny_from"></marker>
- <tag>{deny_from, all | [RegxpHostString]}</tag>
+ <tag><marker id="prop_deny_from"></marker>{deny_from, all | [RegxpHostString]}</tag>
<item>
<p>Defines a set of hosts
- which should be denied access to a given directory.
- For example:
+ to be denied access to a given directory, for example:</p>
- <code>{deny_from, ["123.34.56.11", "150.100.23"] </code>
+ <code>{deny_from, ["123.34.56.11", "150.100.23"]}</code>
- The host 123.34.56.11 and all machines on the 150.100.23
- subnet are not allowed access. </p>
+ <p>The host <c>123.34.56.11</c> and all machines on the <c>150.100.23</c>
+ subnet are not allowed access.</p>
</item>
- <marker id="prop_auth_type"></marker>
- <tag>{auth_type, plain | dets | mnesia}</tag>
+ <tag><marker id="prop_auth_type"></marker>{auth_type, plain | dets | mnesia}</tag>
<item>
<p>Sets the type of authentication database that is used for the
- directory.The key difference between the different methods is
- that dynamic data can be saved when Mnesia and Dets is used.
- This property is called AuthDbType in the Apache like
- configuration files. </p>
+ directory. The key difference between the different methods is
+ that dynamic data can be saved when <c>Mnesia</c> and <c>Dets</c>
+ are used.
+ This property is called <c>AuthDbType</c> in the Apache-like
+ configuration files.</p>
</item>
- <marker id="prop_auth_user_file"></marker>
- <tag>{auth_user_file, path()}</tag>
+ <tag><marker id="prop_auth_user_file"></marker>{auth_user_file, path()}</tag>
<item>
- <p>Sets the name of a file which contains the list of users and
- passwords for user authentication. filename can be either
+ <p>Sets the name of a file containing the list of users and
+ passwords for user authentication. The filename can be either
absolute or relative to the <c>server_root</c>. If using the
- plain storage method, this file is a plain text file, where
- each line contains a user name followed by a colon, followed
- by the non-encrypted password. If user names are duplicated,
- the behavior is undefined. For example:
+ plain storage method, this file is a plain text file where
+ each line contains a username followed by a colon, followed
+ by the non-encrypted password. If usernames are duplicated,
+ the behavior is undefined.</p>
+ <p>Example:</p>
<code> ragnar:s7Xxv7
edward:wwjau8 </code>
- If using the dets storage method, the user database is
- maintained by dets and should not be edited by hand. Use the
- API functions in mod_auth module to create / edit the user
- database. This directive is ignored if using the mnesia
- storage method. For security reasons, make sure that the
- <c>auth_user_file</c> is stored outside the document tree of the Web
- server. If it is placed in the directory which it protects,
- clients will be able to download it. </p>
+ <p>If the <c>Dets</c> storage method is used, the user database is
+ maintained by <c>Dets</c> and must not be edited by hand. Use the
+ API functions in module <c>mod_auth</c> to create/edit the user
+ database. This directive is ignored if the <c>Mnesia</c>
+ storage method is used. For security reasons, ensure that
+ <c>auth_user_file</c> is stored outside the document tree of the web
+ server. If it is placed in the directory that it protects,
+ clients can download it.</p>
</item>
- <marker id="prop_auth_group_file"></marker>
- <tag>{auth_group_file, path()}</tag>
+ <tag><marker id="prop_auth_group_file"></marker>{auth_group_file, path()}</tag>
<item>
- <p>Sets the name of a file which contains the list of user
- groups for user authentication. Filename can be either
- absolute or relative to the <c>server_root</c>. If you use the plain
- storage method, the group file is a plain text file, where
+ <p>Sets the name of a file containing the list of user
+ groups for user authentication. The filename can be either
+ absolute or relative to the <c>server_root</c>. If the plain
+ storage method is used, the group file is a plain text file, where
each line contains a group name followed by a colon, followed
- by the member user names separated by spaces. For example:
+ by the members usernames separated by spaces.</p>
+ <p>Example:</p>
<code>group1: bob joe ante</code>
- If using the dets storage method, the group database is
- maintained by dets and should not be edited by hand. Use the
- API for mod_auth module to create / edit the group database.
- This directive is ignored if using the mnesia storage method.
- For security reasons, make sure that the <c>auth_group_file</c> is
- stored outside the document tree of the Web server. If it is
- placed in the directory which it protects, clients will be
- able to download it. </p>
- </item>
-
- <marker id="prop_auth_name"></marker>
- <tag>{auth_name, string()}</tag>
+ <p>If the <c>Dets</c> storage method is used, the group database is
+ maintained by <c>Dets</c> and must not be edited by hand. Use the
+ API for module <c>mod_auth</c> to create/edit the group database.
+ This directive is ignored if the <c>Mnesia</c> storage method is used.
+ For security reasons, ensure that the <c>auth_group_file</c> is
+ stored outside the document tree of the web server. If it is
+ placed in the directory that it protects, clients
+ can download it.</p>
+ </item>
+
+ <tag><marker id="prop_auth_name"></marker>{auth_name, string()}</tag>
<item>
<p>Sets the name of the authorization realm (auth-domain) for
- a directory. This string informs the client about which user
- name and password to use. </p>
+ a directory. This string informs the client about which
+ username and password to use.</p>
</item>
- <marker id="prop_auth_access_passwd"></marker>
- <tag>{auth_access_password, string()}</tag>
+ <tag><marker id="prop_auth_access_passwd"></marker>{auth_access_password, string()}</tag>
<item>
- <p>If set to other than "NoPassword" the password is required
- for all API calls. If the password is set to "DummyPassword" the
+ <p>If set to other than "NoPassword", the password is required
+ for all API calls. If the password is set to "DummyPassword", the
password must be changed before any other API calls. To secure
- the authenticating data the password must be changed after the
- web server is started since it otherwise is written in clear
- text in the configuration file. </p>
+ the authenticating data, the password must be changed after the
+ web server is started. Otherwise it is written in clear
+ text in the configuration file.</p>
</item>
- <marker id="prop_req_user"></marker>
- <tag>{require_user, [string()]}</tag>
+ <tag><marker id="prop_req_user"></marker>{require_user, [string()]}</tag>
<item>
- <p>Defines users which should be granted access to a given
- directory using a secret password. </p>
+ <p>Defines users to grant access to a given
+ directory using a secret password.</p>
</item>
- <marker id="prop_req_grp"></marker>
- <tag>{require_group, [string()]}</tag>
+ <tag><marker id="prop_req_grp"></marker>{require_group, [string()]}</tag>
<item>
- <p>Defines users which should be granted access to a given
- directory using a secret password. </p>
+ <p>Defines users to grant access to a given
+ directory using a secret password.</p>
</item>
</taglist>
<marker id="props_htaccess"></marker>
- <p><em>Htaccess authentication properties - requires mod_htaccess</em></p>
+ <p><em>Htaccess Authentication Properties - Requires mod_htaccess</em></p>
<taglist>
- <marker id="prop_access_files"></marker>
- <tag>{access_files, [path()]}</tag>
+ <tag><marker id="prop_access_files"></marker>{access_files, [path()]}</tag>
<item>
- <p>Specify which filenames that are used for
- access-files. When a request comes every directory in the path
- to the requested asset will be searched after files with the
- names specified by this parameter. If such a file is found the
- file will be parsed and the restrictions specified in it will
- be applied to the request. </p>
+ <p>Specifies the filenames that are used for
+ access files. When a request comes, every directory in the path
+ to the requested asset are searched after files with the
+ names specified by this parameter. If such a file is found, the
+ file is parsed and the restrictions specified in it are
+ applied to the request.</p>
</item>
</taglist>
<marker id="props_sec"></marker>
- <p><em>Security properties - requires mod_security </em></p>
+ <p><em>Security Properties - Requires mod_security</em></p>
<marker id="prop_sec_dir"></marker>
- <p><em>{security_directory, {path(), [{property(), term()}]}</em></p>
+ <p><em>{security_directory, {path(), [{property(), term()}]}}</em></p>
<marker id="props_sdir"></marker>
- <p>Here follows the valid properties for security directories</p>
+ <p>The properties for the security directories are as follows:</p>
<taglist>
- <marker id="prop_data_file"></marker>
- <tag>{data_file, path()}</tag>
+ <tag><marker id="prop_data_file"></marker>{data_file, path()}</tag>
<item>
- <p>Name of the security data file. The filename can either
- absolute or relative to the server_root. This file is used to
- store persistent data for the mod_security module. </p>
+ <p>Name of the security data file. The filename can either be
+ absolute or relative to the <c>server_root</c>. This file is used to
+ store persistent data for module <c>mod_security</c>.</p>
</item>
- <marker id="prop_max_retries"></marker>
- <tag>{max_retries, integer()}</tag>
+ <tag><marker id="prop_max_retries"></marker>{max_retries, integer()}</tag>
<item>
- <p>Specifies the maximum number of tries to authenticate a
- user has before the user is blocked out. If a user
- successfully authenticates when the user has been blocked, the
- user will receive a 403 (Forbidden) response from the
- server. If the user makes a failed attempt while blocked the
- server will return 401 (Unauthorized), for security
+ <p>Specifies the maximum number of attempts to authenticate a
+ user before the user is blocked out. If a user
+ successfully authenticates while blocked, the
+ user receives a 403 (Forbidden) response from the
+ server. If the user makes a failed attempt while blocked, the
+ server returns 401 (Unauthorized), for security
reasons.
- Defaults to 3 may also be set to infinity. </p>
+ Default is <c>3</c>. Can be set to infinity.</p>
</item>
- <marker id="prop_block_time"></marker>
- <tag>{block_time, integer()}</tag>
+ <tag><marker id="prop_block_time"></marker>{block_time, integer()}</tag>
<item>
<p>Specifies the number of minutes a user is blocked. After
- this amount of time, he automatically regains access.
- Defaults to 60. </p>
+ this timehas passed, the user automatically regains access.
+ Default is <c>60</c>.</p>
</item>
- <marker id="prop_fail_exp_time"></marker>
- <tag>{fail_expire_time, integer()}</tag>
+ <tag><marker id="prop_fail_exp_time"></marker>{fail_expire_time, integer()}</tag>
<item>
<p>Specifies the number of minutes a failed user authentication
- is remembered. If a user authenticates after this amount of
- time, his previous failed authentications are
+ is remembered. If a user authenticates after this
+ time has passed, the previous failed authentications are
forgotten.
- Defaults to 30. </p>
+ Default is <c>30</c>.</p>
</item>
- <marker id="prop_auth_timeout"></marker>
- <tag>{auth_timeout, integer()}</tag>
+ <tag><marker id="prop_auth_timeout"></marker>{auth_timeout, integer()}</tag>
<item>
Specifies the number of seconds a successful user
authentication is remembered. After this time has passed, the
- authentication will no longer be reported. Defaults to 30.
+ authentication is no longer reported. Default is <c>30</c>.
</item>
</taglist>
</section>
<funcs>
<func>
- <marker id="info1"></marker>
<name>info(Pid) -></name>
<name>info(Pid, Properties) -> [{Option, Value}]</name>
- <fsummary>Fetches information about the HTTP server</fsummary>
+ <fsummary>Fetches information about the HTTP server.</fsummary>
<type>
<v>Properties = [property()]</v>
- <v>Option = property()</v>
+ <v>Option = property()</v>
<v>Value = term()</v>
</type>
<desc>
<p>Fetches information about the HTTP server. When called
- with only the pid all properties are fetched, when called
- with a list of specific properties they are fetched.
- Available properties are the same as the servers start options.
+ with only the pid, all properties are fetched. When called
+ with a list of specific properties, they are fetched.
+ The available properties are the same as the start options
+ of the server.
</p>
- <note><p>Pid is the pid returned from inets:start/[2,3].
- Can also be retrieved form inets:services/0, inets:services_info/0
- see <seealso marker="inets">inets(3)</seealso>
+ <note><p>Pid is the pid returned from <c>inets:start/[2,3]</c>.
+ Can also be retrieved form <c>inets:services/0</c> and
+ <c>inets:services_info/0</c>,
+ see <seealso marker="inets">inets(3)</seealso>.
</p></note>
</desc>
</func>
<func>
- <marker id="info2"></marker>
<name>info(Address, Port) -> </name>
+ <name>info(Address, Port, Profile) -> </name>
+ <name>info(Address, Port, Profile, Properties) -> [{Option, Value}] </name>
<name>info(Address, Port, Properties) -> [{Option, Value}] </name>
- <fsummary>Fetches information about the HTTP server</fsummary>
+ <fsummary>Fetches information about the HTTP server.</fsummary>
<type>
<v>Address = ip_address()</v>
<v>Port = integer()</v>
+ <v>Profile = atom()</v>
<v>Properties = [property()]</v>
<v>Option = property()</v>
<v>Value = term()</v>
</type>
<desc>
<p>Fetches information about the HTTP server. When called with
- only the Address and Port all properties are fetched, when
- called with a list of specific properties they are fetched.
- Available properties are the same as the servers start
- options.
+ only <c>Address</c> and <c>Port</c>, all properties are
+ fetched. When called with a list of specific properties, they
+ are fetched. The available properties are the same as the
+ start options of the server.
</p>
- <note><p> Address has to be the ip-address and can not be
+ <note><p>The address must be the IP address and cannot be
the hostname.
</p></note>
</desc>
</func>
<func>
- <marker id="reload_config"></marker>
<name>reload_config(Config, Mode) -> ok | {error, Reason}</name>
<fsummary>Reloads the HTTP server configuration without
restarting the server.</fsummary>
@@ -953,24 +927,26 @@ bytes
</type>
<desc>
<p>Reloads the HTTP server configuration without restarting the
- server. Incoming requests will be answered with a temporary
- down message during the time the it takes to reload.</p>
+ server. Incoming requests are answered with a temporary
+ down message during the reload time.</p>
- <note><p>Available properties are the same as the servers
- start options, although the properties bind_address and
- port can not be changed.</p></note>
+ <note><p>Available properties are the same as the
+ start options of the server, but the properties
+ <c>bind_address</c> and <c>port</c>
+ cannot be changed.</p></note>
- <p>If mode is disturbing, the server is blocked forcefully and
- all ongoing requests are terminated and the reload will
- start immediately. If mode is non-disturbing, no new
- connections are accepted, but the ongoing requests are
+ <p>If mode is disturbing, the server is blocked forcefully,
+ all ongoing requests terminates, and the reload
+ starts immediately. If mode is non-disturbing, no new
+ connections are accepted, but ongoing requests are
allowed to complete before the reload is done.</p>
</desc>
</func>
</funcs>
<section>
- <title>ERLANG WEB SERVER API DATA TYPES </title>
+ <title>ERLANG WEB SERVER API DATA TYPES</title>
+ <p>The Erlang web server API data types are as follows:</p>
<code type="none">
ModData = #mod{}
@@ -987,73 +963,75 @@ bytes
parsed_header = [],
entity_body,
connection
- }).
- </code>
+ }).</code>
- <p>To acess the record in your callback-module use </p>
- <code> -include_lib("inets/include/httpd.hrl"). </code>
+ <p>To acess the record in your callback-module use:</p>
+ <code> -include_lib("inets/include/httpd.hrl").</code>
- <p>The fields of the <c>mod</c> record has the following meaning:
+ <p>The fields of record <c>mod</c> have the following meaning:
</p>
<taglist>
<tag><c>data</c></tag>
- <item>Type <c>[{InteractionKey,InteractionValue}]</c> is used to
+ <item><p>Type <c>[{InteractionKey,InteractionValue}]</c> is used to
propagate data between modules. Depicted
- <c>interaction_data()</c> in function type declarations.
+ <c>interaction_data()</c> in function type declarations.</p>
</item>
<tag><c>socket_type</c></tag>
- <item><c>socket_type()</c>,
- Indicates whether it is an ip socket or a ssl socket.
+ <item><p><c>socket_type()</c>
+ indicates whether it is an IP socket or an <c>ssl</c> socket.</p>
</item>
<tag><c>socket</c></tag>
- <item>The actual socket in <c>ip_comm</c> or <c>ssl</c> format
- depending on the <c>socket_type</c>.
+ <item><p>The socket, in format <c>ip_comm</c> or <c>ssl</c>,
+ depending on <c>socket_type</c>.</p>
</item>
<tag><c>config_db</c></tag>
- <item>The config file directives stored as key-value tuples in
- an ETS-table. Depicted <c>config_db()</c> in function type
- declarations.
+ <item><p>The config file directives stored as key-value tuples in
+ an ETS table. Depicted <c>config_db()</c> in function type
+ declarations.</p>
</item>
<tag><c>method</c></tag>
- <item>Type <c>"GET" | "POST" | "HEAD" | "TRACE"</c>, that is the
- HTTP method.
+ <item><p>Type <c>"GET" | "POST" | "HEAD" | "TRACE"</c>, that is, the
+ HTTP method.</p>
</item>
<tag><c>absolute_uri</c></tag>
- <item>If the request is a HTTP/1.1
- request the URI might be in the absolute URI format. In that
- case httpd will save the absolute URI in this field. An Example
- of an absolute URI could
- be<c>"http://ServerName:Part/cgi-bin/find.pl?person=jocke"</c></item>
+ <item><p>If the request is an HTTP/1.1
+ request, the URI can be in the absolute URI format. In that
+ case, <c>httpd</c> saves the absolute URI in this field. An Example
+ of an absolute URI is
+ <c>"http://ServerName:Part/cgi-bin/find.pl?person=jocke"</c></p></item>
<tag><c>request_uri</c></tag>
- <item>The <c>Request-URI</c> as defined
- in RFC 1945, for example <c>"/cgi-bin/find.pl?person=jocke"</c></item>
+ <item><p>The <c>Request-URI</c> as defined
+ in <url href="http://www.ietf.org/rfc/rfc1945.txt">RFC 1945</url>, for example, <c>"/cgi-bin/find.pl?person=jocke"</c>.</p>
+ </item>
<tag><c>http_version</c></tag>
- <item>The <c>HTTP</c> version of the
- request, that is "HTTP/0.9", "HTTP/1.0", or "HTTP/1.1".
+ <item><p>The <c>HTTP</c> version of the
+ request, that is, "HTTP/0.9", "HTTP/1.0", or "HTTP/1.1".</p>
</item>
<tag><c>request_line</c></tag>
- <item>The <c>Request-Line</c> as
- defined in RFC 1945, for example <c>"GET /cgi-bin/find.pl?person=jocke HTTP/1.0"</c>.
+ <item><p>The <c>Request-Line</c> as
+ defined in<url href="http://www.ietf.org/rfc/rfc1945.txt">RFC 1945</url>, for example,
+ <c>"GET /cgi-bin/find.pl?person=jocke HTTP/1.0"</c>.</p>
</item>
<tag><c>parsed_header</c></tag>
- <item>Type <c>[{HeaderKey,HeaderValue}]</c>,
+ <item>Type <c>[{HeaderKey,HeaderValue}]</c>.
<c>parsed_header</c> contains all HTTP header fields from the
- HTTP-request stored in a list as key-value tuples. See RFC 2616
- for a listing of all header fields. For example the date field
- would be stored as: <c>{"date","Wed, 15 Oct 1997 14:35:17 GMT"} </c>.
- RFC 2616 defines that HTTP is a case insensitive protocol and
- the header fields may be in lower case or upper case. Httpd will
- ensure that all header field names are in lower case.
+ HTTP request stored in a list as key-value tuples. See
+ <url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>
+ for a listing of all header fields. For example, the date field
+ is stored as <c>{"date","Wed, 15 Oct 1997 14:35:17 GMT"}</c>.
+ RFC 2616 defines that HTTP is a case-insensitive protocol and
+ the header fields can be in lower case or upper case. <c>httpd</c>
+ ensures that all header field names are in lower case.
</item>
<tag><c>entity_body</c></tag>
- <item>The <c>Entity-Body</c> as defined
- in RFC 2616, for example data sent from a CGI-script using the
- POST method.
+ <item><p>The <c>entity-Body</c> as defined
+ in <url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>, for example, data sent from a CGI script using the
+ POST method.</p>
</item>
<tag><c>connection</c></tag>
- <item><c>true | false</c> If set to true the connection to the
- client is a persistent connection and will not be closed when
- the request is served.</item>
+ <item><p><c>true | false</c>. If set to <c>true</c>, the connection to the
+ client is a persistent connection and is not closed when
+ the request is served.</p></item>
</taglist>
</section>
@@ -1062,56 +1040,63 @@ bytes
</section>
<funcs>
<func>
- <marker id="module_do"></marker>
<name>Module:do(ModData)-> {proceed, OldData} | {proceed, NewData} | {break, NewData} | done</name>
- <fsummary>Called for each request to the Web server.</fsummary>
+ <fsummary>Called for each request to the web server.</fsummary>
<type>
<v>OldData = list()</v>
- <v>NewData = [{response,{StatusCode,Body}}] | [{response,{response,Head,Body}}] | [{response,{already_sent,Statuscode,Size}] </v>
- <v>StausCode = integer()</v>
+ <v>NewData = [{response,{StatusCode,Body}}]</v>
+ <v>| [{response,{response,Head,Body}}]</v>
+ <v>| [{response,{already_sent,Statuscode,Size}}]</v>
+ <v>StatusCode = integer()</v>
<v>Body = io_list() | nobody | {Fun, Arg}</v>
<v>Head = [HeaderOption]</v>
<v>HeaderOption = {Option, Value} | {code, StatusCode}</v>
- <v>Option = accept_ranges | allow | cache_control | content_MD5 | content_encoding | content_language | content_length | content_location | content_range | content_type | date | etag | expires | last_modified | location | pragma | retry_after | server | trailer | transfer_encoding</v>
+ <v>Option = accept_ranges | allow</v>
+ <v>| cache_control | content_MD5</v>
+ <v>| content_encoding | content_language</v>
+ <v>| content_length | content_location</v>
+ <v>| content_range | content_type | date</v>
+ <v>| etag | expires | last_modified</v>
+ <v>| location | pragma | retry_after</v>
+ <v>| server | trailer | transfer_encoding</v>
<v>Value = string()</v>
<v>Fun = fun( Arg ) -> sent| close | Body </v>
<v>Arg = [term()]</v>
</type>
<desc>
- <p>When a valid request reaches httpd it calls <c>do/1</c> in
- each module defined by the Modules configuration
- option. The function may generate data for other modules
- or a response that can be sent back to the client.</p>
- <p>The field <c>data</c> in ModData is a list. This list will be
+ <p>When a valid request reaches <c>httpd</c>, it calls <c>do/1</c> in
+ each module, defined by the configuration
+ option of <c>Module</c>. The function can generate data for other
+ modules or a response that can be sent back to the client.</p>
+ <p>The field <c>data</c> in <c>ModData</c> is a list. This list is
the list returned from the last call to
<c>do/1</c>.</p>
- <p><c>Body</c> is the body of the http-response that will be
- sent back to the client an appropriate header will be
- appended to the message. <c>StatusCode</c> will be the
- status code of the response see RFC2616 for the appropriate
- values.</p>
+ <p><c>Body</c> is the body of the HTTP response that is
+ sent back to the client. An appropriate header is
+ appended to the message. <c>StatusCode</c> is the
+ status code of the response, see
+ <url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>
+ for the appropriate values.</p>
<p><c>Head</c> is a key value list of HTTP header fields. The
- server will construct a HTTP header from this data. See RFC
- 2616 for the appropriate value for each header field. If the
- client is a HTTP/1.0 client then the server will filter the
- list so that only HTTP/1.0 header fields will be sent back
- to the client.</p>
+ server constructs an HTTP header from this data. See <url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url> for the appropriate value for each header field. If the
+ client is an HTTP/1.0 client, the server filters the
+ list so that only HTTP/1.0 header fields are sent back
+ to the client.</p>
<p>If <c>Body</c> is returned and equal to <c>{Fun,Arg}</c>,
- the Web server will try <c>apply/2</c> on <c>Fun</c> with
- <c>Arg</c> as argument and expect that the fun either
- returns a list <c>(Body)</c> that is a HTTP-repsonse or the
- atom sent if the HTTP-response is sent back to the
- client. If close is returned from the fun something has gone
- wrong and the server will signal this to the client by
+ the web server tries <c>apply/2</c> on <c>Fun</c> with
+ <c>Arg</c> as argument. The web server expects that the fun either
+ returns a list <c>(Body)</c> that is an HTTP repsonse, or the
+ atom <c>sent</c> if the HTTP response is sent back to the
+ client. If <c>close</c> is returned from the fun, something has gone
+ wrong and the server signals this to the client by
closing the connection.</p>
</desc>
</func>
<func>
- <marker id="module_load"></marker>
- <name>Module:load(Line, AccIn)-> eof | ok | {ok, AccOut} | {ok, AccOut, {Option, Value}} | {ok, AccOut, [{Option, Value}]} | {error, Reason} </name>
- <fsummary>Load is used to convert a line in a Apache like config
- file to a <c>{Option, Value}</c> tuple.</fsummary>
+ <name>Module:load(Line, AccIn)-> eof | ok | {ok, AccOut} | {ok, AccOut, {Option, Value}} | {ok, AccOut, [{Option, Value}]} | {error, Reason}</name>
+ <fsummary>Converts a line in an Apache-like config
+ file to an <c>{Option, Value}</c> tuple.</fsummary>
<type>
<v>Line = string()</v>
<v>AccIn = [{Option, Value}]</v>
@@ -1121,55 +1106,53 @@ bytes
<v>Reason = term()</v>
</type>
<desc>
- <p>Load is used to convert a line in a Apache like
- configuration file to a <c>{Option, Value}</c> tuple. Some
- more complex configuration options such as <c>directory</c>
- and <c>security_directory</c> will create an
- accumulator.This function does only need clauses for the
+ <p>Converts a line in an Apache-like
+ configuration file to an <c>{Option, Value}</c> tuple. Some
+ more complex configuration options, such as <c>directory</c>
+ and <c>security_directory</c>, create an
+ accumulator. This function only needs clauses for the
options implemented by this particular callback module.
</p>
</desc>
</func>
-
+
+ <func>
+ <name>Module:remove(ConfigDB) -> ok | {error, Reason} </name>
+ <fsummary>Callback function that is called when the web server is closed.</fsummary>
+ <type>
+ <v>ConfigDB = ets_table()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>When <c>httpd</c> is shut down, it tries to execute
+ <c>remove/1</c> in each Erlang web server callback module. The
+ programmer can use this function to clean up resources
+ created in the store function.</p>
+ </desc>
+ </func>
+
<func>
- <marker id="module_store"></marker>
- <name>Module:store({Option, Value}, Config)-> {ok, {Option, NewValue}} | {error, Reason} </name>
- <fsummary></fsummary>
+ <name>Module:store({Option, Value}, Config)-> {ok, {Option, NewValue}} | {error, Reason}</name>
+ <fsummary>Checks the validity of the configuration options.</fsummary>
<type>
<v>Line = string()</v>
<v>Option = property()</v>
<v>Config = [{Option, Value}]</v>
- <v>Value = term() </v>
+ <v>Value = term()</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>This function is used to check the validity of the
+ <p>Checks the validity of the
configuration options before saving them in the internal
- database. This function may also have a side effect
- e.i. setup necessary extra resources implied by the
+ database. This function can also have a side effect,
+ that is, setup of necessary extra resources implied by the
configuration option. It can also
resolve possible dependencies among
configuration options by changing the value of the option.
- This function does only need clauses for the options
+ This function only needs clauses for the options
implemented by this particular callback module.</p>
</desc>
</func>
-
- <func>
- <marker id="module_remove"></marker>
- <name>Module:remove(ConfigDB) -> ok | {error, Reason} </name>
- <fsummary>Callback function that is called when the Web server is closed.</fsummary>
- <type>
- <v>ConfigDB = ets_table()</v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>When httpd is shutdown it will try to execute
- <c>remove/1</c> in each Erlang web server callback module. The
- programmer may use this function to clean up resources
- that may have been created in the store function.</p>
- </desc>
- </func>
</funcs>
<section>
@@ -1177,9 +1160,8 @@ bytes
</section>
<funcs>
<func>
- <marker id="parse_query"></marker>
- <name>parse_query(QueryString) -> [{Key,Value}]</name>
- <fsummary>Parse incoming data to <c>erl </c>and <c>eval </c>scripts.</fsummary>
+ <name>parse_query(QueryString) -> [{Key,Value}]</name>
+ <fsummary>Parses incoming data to <c>erl</c> and <c>eval</c> scripts.</fsummary>
<type>
<v>QueryString = string()</v>
<v>Key = string()</v>
@@ -1187,8 +1169,9 @@ bytes
</type>
<desc>
<p><c>parse_query/1</c> parses incoming data to <c>erl</c> and
- <c>eval</c> scripts (See <seealso marker="mod_esi">mod_esi(3)</seealso>) as defined in the standard
- URL format, that is '+' becomes 'space' and decoding of
+ <c>eval</c> scripts (see <seealso marker="mod_esi">mod_esi(3)</seealso>)
+ as defined in the standard
+ URL format, that is, '+' becomes 'space' and decoding of
hexadecimal characters (<c>%xx</c>).</p>
</desc>
</func>
@@ -1196,8 +1179,9 @@ bytes
<section>
<title>SEE ALSO</title>
- <p>RFC 2616, <seealso marker="inets">inets(3)</seealso>,
- <seealso marker="ssl:ssl">ssl(3)</seealso>
+ <p><url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>,
+ <seealso marker="inets">inets(3)</seealso>,
+ <seealso marker="ssl:ssl">ssl(3)</seealso>
</p>
</section>
diff --git a/lib/inets/doc/src/httpd_conf.xml b/lib/inets/doc/src/httpd_conf.xml
deleted file mode 100644
index fc34f14ec3..0000000000
--- a/lib/inets/doc/src/httpd_conf.xml
+++ /dev/null
@@ -1,162 +0,0 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
-
-<erlref>
- <header>
- <copyright>
- <year>1997</year><year>2012</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- 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.
-
- </legalnotice>
-
- <title>httpd_conf</title>
- <prepared>Joakim Greben&ouml;</prepared>
- <docno></docno>
- <date>1997-10-14</date>
- <rev>2.2</rev>
- <file>httpd_conf.sgml</file>
- </header>
- <module>httpd_conf</module>
- <modulesummary>Configuration utility functions to be used by the Erlang
- Web server API programmer.</modulesummary>
- <description>
- <p>This module provides the Erlang Webserver API programmer with
- utility functions for adding run-time configuration directives.</p>
-
- <marker id="check_enum"></marker>
- </description>
-
- <funcs>
- <func>
- <name>check_enum(EnumString, ValidEnumStrings) -> Result</name>
- <fsummary>Check if string is a valid enumeration.</fsummary>
- <type>
- <v>EnumString = string()</v>
- <v>ValidEnumStrings = [string()]</v>
- <v>Result = {ok,atom()} | {error,not_valid}</v>
- </type>
- <desc>
- <marker id="check_enum"></marker>
- <p><c>check_enum/2</c> checks if <c>EnumString</c> is a valid
- enumeration of <c>ValidEnumStrings</c> in which case it is
- returned as an atom.</p>
-
- <marker id="clean"></marker>
- </desc>
- </func>
-
- <func>
- <name>clean(String) -> Stripped</name>
- <fsummary>Remove leading and/or trailing white spaces.</fsummary>
- <type>
- <v>String = Stripped = string()</v>
- </type>
- <desc>
- <marker id="clean"></marker>
- <p><c>clean/1</c> removes leading and/or trailing white spaces
- from <c>String</c>.</p>
-
- <marker id="custom_clean"></marker>
- </desc>
- </func>
-
- <func>
- <name>custom_clean(String,Before,After) -> Stripped</name>
- <fsummary>Remove leading and/or trailing white spaces and custom characters.</fsummary>
- <type>
- <v>Before = After = regexp()</v>
- <v>String = Stripped = string()</v>
- </type>
- <desc>
- <marker id="custom_clean"></marker>
- <p><c>custom_clean/3</c> removes leading and/or trailing white
- spaces and custom characters from <c>String</c>. <c>Before</c>
- and <c>After</c> are regular expressions, as defined in
- <c>regexp(3)</c>, describing the custom characters.</p>
-
- <marker id="is_directory"></marker>
- </desc>
- </func>
-
- <func>
- <name>is_directory(FilePath) -> Result</name>
- <fsummary>Check if a file path is a directory.</fsummary>
- <type>
- <v>FilePath = string()</v>
- <v>Result = {ok,Directory} | {error,Reason}</v>
- <v>Directory = string()</v>
- <v>Reason = string() | enoent | eaccess | enotdir | FileInfo</v>
- <v>FileInfo = File info record</v>
- </type>
- <desc>
- <marker id="is_directory"></marker>
- <p><c>is_directory/1</c> checks if <c>FilePath</c> is a
- directory in which case it is returned. Please read
- <c>file(3)</c> for a description of <c>enoent</c>,
- <c>eaccess</c> and <c>enotdir</c>. The definition of
- the file info record can be found by including <c>file.hrl</c>
- from the kernel application, see file(3).</p>
-
- <marker id="is_file"></marker>
- </desc>
- </func>
-
- <func>
- <name>is_file(FilePath) -> Result</name>
- <fsummary>Check if a file path is a regular file.</fsummary>
- <type>
- <v>FilePath = string()</v>
- <v>Result = {ok,File} | {error,Reason}</v>
- <v>File = string()</v>
- <v>Reason = string() | enoent | eaccess | enotdir | FileInfo</v>
- <v>FileInfo = File info record</v>
- </type>
- <desc>
- <marker id="is_file"></marker>
- <p><c>is_file/1</c> checks if <c>FilePath</c> is a regular
- file in which case it is returned. Read <c>file(3)</c> for a
- description of <c>enoent</c>, <c>eaccess</c> and
- <c>enotdir</c>. The definition of the file info record can be
- found by including <c>file.hrl</c> from the kernel application,
- see file(3).</p>
-
- <marker id="make_integer"></marker>
- </desc>
- </func>
-
- <func>
- <name>make_integer(String) -> Result</name>
- <fsummary>Return an integer representation of a string.</fsummary>
- <type>
- <v>String = string()</v>
- <v>Result = {ok,integer()} | {error,nomatch}</v>
- </type>
- <desc>
- <marker id="make_integer"></marker>
- <p><c>make_integer/1</c> returns an integer representation of
- <c>String</c>.</p>
- </desc>
- </func>
- </funcs>
-
- <section>
- <marker id="see_also"></marker>
- <title>SEE ALSO</title>
- <p><seealso marker="httpd">httpd(3)</seealso></p>
- </section>
-
-</erlref>
-
-
diff --git a/lib/inets/doc/src/httpd_custom_api.xml b/lib/inets/doc/src/httpd_custom_api.xml
new file mode 100644
index 0000000000..d2e5441895
--- /dev/null
+++ b/lib/inets/doc/src/httpd_custom_api.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2015</year><year>2015</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>httpd_custom_api</title>
+ <file>httpd_custom_api.xml</file>
+ </header>
+ <module>httpd_custom_api</module>
+ <modulesummary>Behaviour with optional callbacks to customize the inets HTTP server.</modulesummary>
+ <description>
+ <p> The module implementing this behaviour shall be supplied to to the servers
+ configuration with the option <seealso marker="httpd#prop_customize"> customize</seealso></p>
+
+ </description>
+ <funcs>
+ <func>
+ <name>response_default_headers() -> [Header] </name>
+ <fsummary>Provide default headers for the HTTP servers responses.</fsummary>
+ <type>
+ <v>Header = {HeaderName :: string(), HeaderValue::string()}</v>
+ <d>string:to_lower/1 will be performed on the HeaderName</d>
+ </type>
+ <desc>
+ <p>Provide default headers for the HTTP servers responses. Note that this
+ option may override built-in defaults.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>response_header({HeaderName, HeaderValue}) -> {true, Header} | false </name>
+ <fsummary>Filter and possible alter HTTP response headers.</fsummary>
+ <type>
+ <v>Header = {HeaderName :: string(), HeaderValue::string()}</v>
+ <d>The header name will be in lower case and should not be altered.</d>
+ </type>
+ <desc>
+ <p> Filter and possible alter HTTP response headers before they are sent to the client.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>request_header({HeaderName, HeaderValue}) -> {true, Header} | false </name>
+ <fsummary>Filter and possible alter HTTP request headers.</fsummary>
+ <type>
+ <v>Header = {HeaderName :: string(), HeaderValue::string()}</v>
+ <d>The header name will be in lower case and should not be altered.</d>
+ </type>
+ <desc>
+ <p> Filter and possible alter HTTP request headers before they are processed by the server.
+ </p>
+ </desc>
+ </func>
+ </funcs>
+</erlref>
+
+
diff --git a/lib/inets/doc/src/httpd_socket.xml b/lib/inets/doc/src/httpd_socket.xml
index 58cd2ec575..d3aa82a540 100644
--- a/lib/inets/doc/src/httpd_socket.xml
+++ b/lib/inets/doc/src/httpd_socket.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>1997</year><year>2012</year>
+ <year>1997</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
@@ -30,12 +31,12 @@
</header>
<module>httpd_socket</module>
<modulesummary>Communication utility functions to be used by the Erlang
- Web server API programmer.</modulesummary>
+ web server API programmer.</modulesummary>
<description>
- <p>This module provides the Erlang Web server API module programmer
+ <p>This module provides the Erlang web server API module programmer
with utility functions for generic sockets communication. The
appropriate communication mechanism is transparently used, that
- is <c>ip_comm</c> or <c>ssl</c>.</p>
+ is, <c>ip_comm</c> or <c>ssl</c>.</p>
<marker id="deliver"></marker>
</description>
@@ -43,7 +44,7 @@
<funcs>
<func>
<name>deliver(SocketType, Socket, Data) -> Result</name>
- <fsummary>Send binary data over socket.</fsummary>
+ <fsummary>Sends binary data over socket.</fsummary>
<type>
<v>SocketType = socket_type()</v>
<v>Socket = socket()</v>
@@ -52,10 +53,10 @@
</type>
<desc>
<marker id="deliver"></marker>
- <p><c>deliver/3</c> sends the <c>Binary</c> over the
- <c>Socket</c> using the specified <c>SocketType</c>. Socket
- and SocketType should be the socket and the socket_type form
- the mod record as defined in httpd.hrl</p>
+ <p><c>deliver/3</c> sends <c>Data</c> over
+ <c>Socket</c> using the specified <c>SocketType</c>. <c>Socket</c>
+ and <c>SocketType</c> is to be the socket and the <c>socket_type</c>
+ form the <c>mod</c> record as defined in <c>httpd.hrl</c></p>
<marker id="peername"></marker>
</desc>
@@ -63,7 +64,7 @@
<func>
<name>peername(SocketType,Socket) -> {Port,IPAddress}</name>
- <fsummary>Return the port and IP-address of the remote socket.</fsummary>
+ <fsummary>Returns the port and IP address of the remote socket.</fsummary>
<type>
<v>SocketType = socket_type()</v>
<v>Socket = socket()</v>
@@ -72,8 +73,8 @@
</type>
<desc>
<marker id="peername"></marker>
- <p><c>peername/3</c> returns the <c>Port</c> and
- <c>IPAddress</c> of the remote <c>Socket</c>. </p>
+ <p><c>peername/2</c> returns the <c>Port</c> and
+ <c>IPAddress</c> of the remote <c>Socket</c>.</p>
<marker id="resolve"></marker>
</desc>
@@ -81,7 +82,7 @@
<func>
<name>resolve() -> HostName</name>
- <fsummary>Return the official name of the current host.</fsummary>
+ <fsummary>Returns the official name of the current host.</fsummary>
<type>
<v>HostName = string()</v>
</type>
diff --git a/lib/inets/doc/src/httpd_util.xml b/lib/inets/doc/src/httpd_util.xml
index 9f290084d2..220a2ede35 100644
--- a/lib/inets/doc/src/httpd_util.xml
+++ b/lib/inets/doc/src/httpd_util.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>1997</year><year>2012</year>
+ <year>1997</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
@@ -29,9 +30,10 @@
<file>httpd_util.sgml</file>
</header>
<module>httpd_util</module>
- <modulesummary>Miscellaneous utility functions to be used when implementing Erlang Web server API modules.</modulesummary>
+ <modulesummary>Miscellaneous utility functions to be used when implementing
+ Erlang web server API modules.</modulesummary>
<description>
- <p>This module provides the Erlang Web Server API module
+ <p>This module provides the Erlang web server API module
programmer with miscellaneous utility functions.</p>
<marker id="convert_request_date"></marker>
@@ -40,7 +42,7 @@
<funcs>
<func>
<name>convert_request_date(DateString) -> ErlDate|bad_date</name>
- <fsummary>Convert The the date to the Erlang date format.</fsummary>
+ <fsummary>Converts the date to the Erlang date format.</fsummary>
<type>
<v>DateString = string()</v>
<v>ErlDate = {{Year,Month,Date},{Hour,Min,Sec}}</v>
@@ -48,10 +50,9 @@
</type>
<desc>
<p><c>convert_request_date/1</c> converts <c>DateString</c> to
- the Erlang date format. DateString must be in one of the three
- date formats that is defined in the RFC 2616.</p>
-
- <marker id="create_etag"></marker>
+ the Erlang date format. <c>DateString</c> must be in one of the
+ three date formats defined in
+ <url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>.</p>
</desc>
</func>
@@ -63,128 +64,84 @@
<v>Etag = string()</v>
</type>
<desc>
- <p><c>create_etag/1</c> calculates the Etag for a file, from its
- size and time for last modification. fileinfo is a record defined
- in <c>kernel/include/file.hrl</c></p>
-
- <marker id="decode_hex"></marker>
- </desc>
- </func>
-
- <func>
- <name>decode_hex(HexValue) -> DecValue</name>
- <fsummary>Convert a hex value into its decimal equivalent.</fsummary>
- <type>
- <v>HexValue = DecValue = string()</v>
- </type>
- <desc>
- <p>Converts the hexadecimal value <c>HexValue</c> into its
- decimal equivalent (<c>DecValue</c>).</p>
-
- <marker id="day"></marker>
+ <p><c>create_etag/1</c> calculates the Etag for a file from its
+ size and time for last modification. <c>FileInfo</c> is a record defined
+ in <c>kernel/include/file.hrl</c>.</p>
</desc>
</func>
-
+
<func>
<name>day(NthDayOfWeek) -> DayOfWeek</name>
- <fsummary>Convert the day of the week (integer [1-7]) to an abbreviated string.</fsummary>
+ <fsummary>Converts the day of the week
+ (integer [1-7]) to an abbreviated string.</fsummary>
<type>
<v>NthDayOfWeek = 1-7</v>
<v>DayOfWeek = string()</v>
</type>
<desc>
- <marker id="day"></marker>
<p><c>day/1</c> converts the day of the week
- (<c>NthDayOfWeek</c>) as an integer (1-7) to an abbreviated
- string, that is: </p>
+ (<c>NthDayOfWeek</c>) from an integer (1-7) to an abbreviated
+ string, that is:</p>
<p>1 = "Mon", 2 = "Tue", ..., 7 = "Sat".</p>
-
- <marker id="flatlength"></marker>
</desc>
</func>
<func>
- <name>flatlength(NestedList) -> Size</name>
- <fsummary>Compute the size of a possibly nested list.</fsummary>
+ <name>decode_hex(HexValue) -> DecValue</name>
+ <fsummary>Converts a hexadecimal value into its decimal equivalent.</fsummary>
<type>
- <v>NestedList = list()</v>
- <v>Size = integer()</v>
+ <v>HexValue = DecValue = string()</v>
</type>
<desc>
- <marker id="flatlength"></marker>
- <p><c>flatlength/1</c> computes the size of the possibly nested
- list <c>NestedList</c>. Which may contain binaries.</p>
-
- <marker id="hexlist_to_integer"></marker>
+ <p>Converts the hexadecimal value <c>HexValue</c> into its
+ decimal equivalent (<c>DecValue</c>).</p>
</desc>
</func>
-
-<!--
+
<func>
- <name>header(StatusCode,PersistentConn)</name>
- <name>header(StatusCode,Date)</name>
- <name>header(StatusCode,MimeType,Date)</name>
- <name>header(StatusCode,MimeType,PersistentConn,Date) -> HTTPHeader</name>
- <fsummary>Generate a HTTP 1.1 header.</fsummary>
+ <name>flatlength(NestedList) -> Size</name>
+ <fsummary>Computes the size of a possibly nested list.</fsummary>
<type>
- <v>StatusCode = integer()</v>
- <v>Date = rfc1123_date()</v>
- <v>MimeType = string()</v>
- <v>PersistentConn = true | false</v>
+ <v>NestedList = list()</v>
+ <v>Size = integer()</v>
</type>
<desc>
- <marker id="header"></marker>
- <p><c>header</c> returns a HTTP 1.1 header string. The
- <c>StatusCode</c> is one of the status codes defined in RFC
- 2616 and the <c>Date</c> string is RFC 1123
- compliant. (See <seealso marker="#rfc1123_date">rfc1123_date/0</seealso>).
- </p>
- <p>Note that the two version of <c>header/n</c> that does not
- has a <c>PersistentConn</c> argument is there only for
- backward compatibility, and must not be used in new Erlang
- Webserver API modules. that will support persistent
- connections.</p>
-
- <marker id="hexlist_to_integer"></marker>
+ <p><c>flatlength/1</c> computes the size of the possibly nested
+ list <c>NestedList</c>, which can contain binaries.</p>
</desc>
</func>
--->
<func>
<name>hexlist_to_integer(HexString) -> Number</name>
- <fsummary>Convert a hexadecimal string to an integer.</fsummary>
+ <fsummary>Converts a hexadecimal string to an integer.</fsummary>
<type>
<v>Number = integer()</v>
<v>HexString = string()</v>
</type>
<desc>
- <p><c>hexlist_to_integer</c> Convert the Hexadecimal value of
- HexString to an integer.</p>
-
- <marker id="integer_to_hexlist"></marker>
+ <p><c>hexlist_to_integer</c> converts the hexadecimal value of
+ <c>HexString</c> to an integer.</p>
</desc>
</func>
<func>
<name>integer_to_hexlist(Number) -> HexString</name>
- <fsummary>Convert an integer to a hexadecimal string.</fsummary>
+ <fsummary>Converts an integer to a hexadecimal string.</fsummary>
<type>
<v>Number = integer()</v>
<v>HexString = string()</v>
</type>
<desc>
- <marker id="integer_to_hexlist"></marker>
- <p><c>integer_to_hexlist/1</c> Returns a string that represents
- the Number in a Hexadecimal form.</p>
-
- <marker id="lookup"></marker>
+ <p><c>integer_to_hexlist/1</c> returns a string representing
+ <c>Number</c> in a hexadecimal form.</p>
</desc>
</func>
<func>
<name>lookup(ETSTable,Key) -> Result</name>
<name>lookup(ETSTable,Key,Undefined) -> Result</name>
- <fsummary>Extract the first value associated with a key in an ETS table.</fsummary>
+ <fsummary>Extracts the first value associated with a <c>Key</c>
+ in an ETS table.</fsummary>
<type>
<v>ETSTable = ets_table()</v>
<v>Key = term()</v>
@@ -194,20 +151,18 @@
<desc>
<p><c>lookup</c> extracts <c>{Key,Value}</c> tuples from
<c>ETSTable</c> and returns the <c>Value</c> associated
- with <c>Key</c>. If <c>ETSTable</c> is of type <c>bag</c>
+ with <c>Key</c>. If <c>ETSTable</c> is of type <c>bag</c>,
only the first <c>Value</c> associated with <c>Key</c> is
returned. <c>lookup/2</c> returns <c>undefined</c> and
<c>lookup/3</c> returns <c>Undefined</c> if no <c>Value</c>
is found.</p>
-
- <marker id="lookup_mime"></marker>
</desc>
</func>
<func>
<name>lookup_mime(ConfigDB,Suffix)</name>
<name>lookup_mime(ConfigDB,Suffix,Undefined) -> MimeType</name>
- <fsummary>Return the mime type associated with a specific file suffix. </fsummary>
+ <fsummary>Returns the MIME type associated with a specific file suffix.</fsummary>
<type>
<v>ConfigDB = ets_table()</v>
<v>Suffix = string()</v>
@@ -215,20 +170,19 @@
<v>Undefined = term()</v>
</type>
<desc>
- <marker id="lookup_mime"></marker>
- <p><c>lookup_mime</c> returns the mime type associated with a
- specific file suffix as specified in the <c>mime.types</c>
- file (located in the
- <path unix="$SERVER_ROOT/conf/mime.types" windows="%SERVER_ROOT%\conf\mime.types">config directory</path>).</p>
-
- <marker id="lookup_mime_default"></marker>
+ <p><c>lookup_mime</c> returns the MIME type associated with a
+ specific file suffix as specified in the file <c>mime.types</c>
+ (located in the
+ <path unix="$SERVER_ROOT/conf/mime.types" windows="%SERVER_ROOT%\conf\mime.types">
+ config directory</path>).</p>
</desc>
</func>
<func>
<name>lookup_mime_default(ConfigDB,Suffix)</name>
<name>lookup_mime_default(ConfigDB,Suffix,Undefined) -> MimeType</name>
- <fsummary>Return the mime type associated with a specific file suffix or the value of the DefaultType.</fsummary>
+ <fsummary>Returns the MIME type associated with a specific file suffix
+ or the value of the DefaultType.</fsummary>
<type>
<v>ConfigDB = ets_table()</v>
<v>Suffix = string()</v>
@@ -236,22 +190,19 @@
<v>Undefined = term()</v>
</type>
<desc>
- <marker id="lookup_mime_default"></marker>
- <p><c>lookup_mime_default</c> returns the mime type associated
+ <p><c>lookup_mime_default</c> returns the MIME type associated
with a specific file suffix as specified in the
<c>mime.types</c> file (located in the
- <path unix="$SERVER_ROOT/conf/mime.types" windows="%SERVER_ROOT%\conf\mime.types">config directory</path>).
- If no appropriate association can be found
- the value of DefaultType is
+ <path unix="$SERVER_ROOT/conf/mime.types" windows="%SERVER_ROOT%\conf\mime.types">
+ config directory</path>).
+ If no appropriate association is found, the value of <c>DefaultType</c> is
returned.</p>
-
- <marker id="message"></marker>
</desc>
</func>
<func>
<name>message(StatusCode,PhraseArgs,ConfigDB) -> Message</name>
- <fsummary>Return an informative HTTP 1.1 status string in HTML.</fsummary>
+ <fsummary>Returns an informative HTTP 1.1 status string in HTML.</fsummary>
<type>
<v>StatusCode = 301 | 400 | 403 | 404 | 500 | 501 | 504</v>
<v>PhraseArgs = term()</v>
@@ -259,53 +210,48 @@
<v>Message = string()</v>
</type>
<desc>
- <marker id="message"></marker>
<p><c>message/3</c> returns an informative HTTP 1.1 status
string in HTML. Each <c>StatusCode</c> requires a specific
<c>PhraseArgs</c>:
</p>
<taglist>
<tag><c>301</c></tag>
- <item><c>string()</c>: A URL pointing at the new document
- position.</item>
+ <item><p><c>string()</c>: A URL pointing at the new document
+ position.</p></item>
<tag><c>400 | 401 | 500</c></tag>
- <item><c>none</c> (No <c>PhraseArgs</c>)</item>
+ <item><p><c>none</c> (no <c>PhraseArgs</c>).</p></item>
<tag><c>403 | 404</c></tag>
- <item><c>string()</c>: A <c>Request-URI</c> as described in
- RFC 2616.</item>
+ <item><p><c>string()</c>: A <c>Request-URI</c> as described in
+ <url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>.</p>
+ </item>
<tag><c>501</c></tag>
- <item><c>{Method,RequestURI,HTTPVersion}</c>: The HTTP
- <c>Method</c>, <c>Request-URI</c> and <c>HTTP-Version</c>
- as defined in RFC 2616.</item>
+ <item><p><c>{Method,RequestURI,HTTPVersion}</c>: The HTTP
+ <c>Method</c>, <c>Request-URI</c>, and <c>HTTP-Version</c>
+ as defined in RFC 2616.</p></item>
<tag><c>504</c></tag>
- <item><c>string()</c>: A string describing why the service
- was unavailable.</item>
+ <item><p><c>string()</c>: A string describing why the service
+ was unavailable.</p></item>
</taglist>
-
- <marker id="month"></marker>
</desc>
</func>
<func>
<name>month(NthMonth) -> Month</name>
- <fsummary>Convert the month as an integer (1-12) to an abbreviated string.</fsummary>
+ <fsummary>Converts the month as an integer (1-12) to an abbreviated string.</fsummary>
<type>
<v>NthMonth = 1-12</v>
<v>Month = string()</v>
</type>
<desc>
- <marker id="month"></marker>
<p><c>month/1</c> converts the month <c>NthMonth</c> as an
integer (1-12) to an abbreviated string, that is: </p>
<p>1 = "Jan", 2 = "Feb", ..., 12 = "Dec".</p>
-
- <marker id="multi_lookup"></marker>
</desc>
</func>
<func>
<name>multi_lookup(ETSTable,Key) -> Result</name>
- <fsummary>Extract the values associated with a key in a ETS table.</fsummary>
+ <fsummary>Extracts the values associated with a key in an ETS table.</fsummary>
<type>
<v>ETSTable = ets_table()</v>
<v>Key = term()</v>
@@ -313,49 +259,44 @@
</type>
<desc>
<p><c>multi_lookup</c> extracts all <c>{Key,Value}</c> tuples
- from an <c>ETSTable</c> and returns <em>all</em><c>Values</c> associated with the <c>Key</c> in a list.</p>
-
- <marker id="reason phrase"></marker>
+ from an <c>ETSTable</c> and returns <em>all</em> <c>Values</c>
+ associated with <c>Key</c> in a list.</p>
</desc>
</func>
<func>
<name>reason_phrase(StatusCode) -> Description</name>
- <fsummary>Return the description of an HTTP 1.1 status code.</fsummary>
+ <fsummary>Returns the description of an HTTP 1.1 status code.</fsummary>
<type>
- <v>StatusCode = 100| 200 | 201 | 202 | 204 | 205 | 206 | 300 | 301 | 302 | 303 | 304 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 410 411 | 412 | 413 | 414 415 | 416 | 417 | 500 | 501 | 502 | 503 | 504 | 505</v>
+ <v>StatusCode = 100| 200 | 201 | 202 | 204 | 205 | 206 | 300 | 301 | 302 | 303 | 304 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 410 411 | 412 | 413 | 414 415 | 416 | 417 | 500 | 501 | 502 | 503 | 504 | 505</v>
<v>Description = string()</v>
</type>
<desc>
- <p><c>reason_phrase</c> returns the <c>Description</c> of an
- HTTP 1.1 <c>StatusCode</c>, for example 200 is "OK" and 201
- is "Created". Read RFC 2616 for further information.</p>
-
- <marker id="rfc1123_date"></marker>
+ <p><c>reason_phrase</c> returns <c>Description</c> of an
+ HTTP 1.1 <c>StatusCode</c>, for example, 200 is "OK" and 201
+ is "Created". For more information, see
+ <url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>.</p>
</desc>
</func>
<func>
<name>rfc1123_date() -> RFC1123Date</name>
- <name>rfc1123_date({{YYYY,MM,DD},{Hour,Min,Sec}}}) -> RFC1123Date</name>
- <fsummary>Return the current date in RFC 1123 format.</fsummary>
+ <name>rfc1123_date({{YYYY,MM,DD},{Hour,Min,Sec}}) -> RFC1123Date</name>
+ <fsummary>Returns the current date in RFC 1123 format.</fsummary>
<type>
- <v>YYYY = MM = DD = Hour = Min =Sec = integer()</v>
+ <v>YYYY = MM = DD = Hour = Min = Sec = integer()</v>
<v>RFC1123Date = string()</v>
</type>
<desc>
- <marker id="rfc1123_date"></marker>
<p><c>rfc1123_date/0</c> returns the current date in RFC 1123
format. <c>rfc_date/1</c> converts the date in the Erlang format
to the RFC 1123 date format.</p>
-
- <marker id="split"></marker>
</desc>
</func>
<func>
<name>split(String,RegExp,N) -> SplitRes</name>
- <fsummary>Split a string in N chunks using a regular expression.</fsummary>
+ <fsummary>Splits a string in N chunks using a regular expression.</fsummary>
<type>
<v>String = RegExp = string()</v>
<v>SplitRes = {ok, FieldList} | {error, errordesc()}</v>
@@ -363,96 +304,86 @@
<v>N = integer</v>
</type>
<desc>
- <marker id="split"></marker>
- <p><c>split/3</c> splits the <c>String</c> in <c>N</c> chunks
- using the <c>RegExp</c>. <c>split/3</c> is is equivalent to
- <c>regexp:split/2</c> with one exception, that is <c>N</c>
- defines the number of maximum number of fields in the
+ <p><c>split/3</c> splits <c>String</c> in <c>N</c> chunks
+ using <c>RegExp</c>. <c>split/3</c> is equivalent to
+ <c>regexp:split/2</c> with the exception that <c>N</c>
+ defines the maximum number of fields in
<c>FieldList</c>.</p>
-
- <marker id="split_script_path"></marker>
</desc>
</func>
<func>
<name>split_script_path(RequestLine) -> Splitted</name>
- <fsummary>Split a <c>RequestLine</c>in a file reference to an executable and a<c>QueryString</c>or a <c>PathInfo</c>string.</fsummary>
+ <fsummary>Splits a <c>RequestLine</c> in a file reference to an executable,
+ and a <c>QueryString</c> or a <c>PathInfo</c>string.</fsummary>
<type>
<v>RequestLine = string()</v>
<v>Splitted = not_a_script | {Path, PathInfo, QueryString}</v>
<v>Path = QueryString = PathInfo = string()</v>
</type>
<desc>
- <marker id="split_script_path"></marker>
<p><c>split_script_path/1</c> is equivalent to
<c>split_path/1</c> with one exception. If the longest
- possible path is not a regular, accessible and executable
- file <c>not_a_script</c> is returned.</p>
-
- <marker id="split_path"></marker>
+ possible path is not a regular, accessible, and executable
+ file, then <c>not_a_script</c> is returned.</p>
</desc>
</func>
<func>
<name>split_path(RequestLine) -> {Path,QueryStringOrPathInfo}</name>
- <fsummary>Split a <c>RequestLine</c>in a file reference and a <c>QueryString</c>or a<c>PathInfo</c>string.</fsummary>
+ <fsummary>Splits a <c>RequestLine</c> in a file reference, and a
+ <c>QueryString</c> or a <c>PathInfo</c> string.</fsummary>
<type>
<v>RequestLine = Path = QueryStringOrPathInfo = string()</v>
</type>
<desc>
- <marker id="split_path"></marker>
- <p><c>split_path/1</c> splits the <c>RequestLine</c> in a file
- reference (<c>Path</c>) and a <c>QueryString</c> or a
- <c>PathInfo</c> string as specified in RFC 2616. A
- <c>QueryString</c> is isolated from the <c>Path</c> with a
+ <p><c>split_path/1</c> splits <c>RequestLine</c> in a file
+ reference (<c>Path</c>), and a <c>QueryString</c> or a
+ <c>PathInfo</c> string as specified in
+ <url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>.
+ A <c>QueryString</c> is isolated from <c>Path</c> with a
question mark (<c>?</c>) and <c>PathInfo</c> with a slash
(/). In the case of a <c>QueryString</c>, everything before
- the <c>?</c> is a <c>Path</c> and everything after a
- <c>QueryString</c>. In the case of a <c>PathInfo</c> the
+ <c>?</c> is a <c>Path</c> and everything after <c>?</c> is a
+ <c>QueryString</c>. In the case of a <c>PathInfo</c>,
<c>RequestLine</c> is scanned from left-to-right on the hunt
for longest possible <c>Path</c> being a file or a
directory. Everything after the longest possible
<c>Path</c>, isolated with a <c>/</c>, is regarded as
<c>PathInfo</c>. The resulting <c>Path</c> is decoded using
<c>decode_hex/1</c> before delivery.</p>
-
- <marker id="strip"></marker>
</desc>
</func>
<func>
<name>strip(String) -> Stripped</name>
- <fsummary>Returns String where the leading and trailing space and tabs has been removed.</fsummary>
+ <fsummary>Returns <c>String</c> where the leading and trailing space
+ tabs are removed.</fsummary>
<type>
<v>String = Stripped = string()</v>
</type>
<desc>
- <marker id="strip"></marker>
<p><c>strip/1</c> removes any leading or trailing linear white
- space from the string. Linear white space should be read as
+ space from the string. Linear white space is to be read as
horizontal tab or space.</p>
-
- <marker id="suffix"></marker>
</desc>
</func>
<func>
<name>suffix(FileName) -> Suffix</name>
- <fsummary>Extract the file suffix from a given filename.</fsummary>
+ <fsummary>Extracts the file suffix from a given filename.</fsummary>
<type>
<v>FileName = Suffix = string()</v>
</type>
<desc>
- <marker id="suffix"></marker>
<p><c>suffix/1</c> is equivalent to
- <c>filename:extension/1</c> with one exception, that is
- <c>Suffix</c> is returned without a leading dot (<c>.</c>). </p>
+ <c>filename:extension/1</c> with the exception that
+ <c>Suffix</c> is returned without a leading dot (<c>.</c>).</p>
</desc>
</func>
</funcs>
<section>
- <marker id="see_also"></marker>
<title>SEE ALSO</title>
<p><seealso marker="httpd">httpd(3)</seealso></p>
</section>
diff --git a/lib/inets/doc/src/inets.xml b/lib/inets/doc/src/inets.xml
index 079f60779d..137381cbe9 100644
--- a/lib/inets/doc/src/inets.xml
+++ b/lib/inets/doc/src/inets.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>2007</year><year>2011</year>
+ <year>2007</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
@@ -29,21 +30,21 @@
<rev></rev>
</header>
<module>inets</module>
- <modulesummary>The inets services API</modulesummary>
+ <modulesummary>The Inets services API.</modulesummary>
<description>
<p>This module provides the most basic API to the
- clients and servers, that are part of the Inets application,
- such as start and stop. </p>
+ clients and servers that are part of the <c>Inets</c> application,
+ such as start and stop.</p>
<marker id="common_data_types"></marker>
</description>
<section>
- <title>COMMON DATA TYPES </title>
+ <title>DATA TYPES</title>
<p>Type definitions that are used more than once in
- this module: </p>
- <p><c> service() = ftpc | tftp | httpc | httpd</c></p>
- <p><c> property() = atom() </c></p>
+ this module:</p>
+ <p><c>service() = ftpc | tftp | httpc | httpd</c></p>
+ <p><c>property() = atom()</c></p>
<marker id="functions"></marker>
<marker id="services"></marker>
</section>
@@ -51,7 +52,7 @@
<funcs>
<func>
<name>services() -> [{Service, Pid}]</name>
- <fsummary>Returns a list of currently running services. </fsummary>
+ <fsummary>Returns a list of currently running services.</fsummary>
<type>
<v>Service = service()</v>
<v>Pid = pid()</v>
@@ -59,7 +60,7 @@
<desc>
<p>Returns a list of currently running services.</p>
<note>
- <p>Services started as <c>stand_alone</c> will not be listed.</p>
+ <p>Services started as <c>stand_alone</c> are not listed.</p>
</note>
<marker id="services_info"></marker>
@@ -69,8 +70,8 @@
<func>
<name>services_info() -> [{Service, Pid, Info}]</name>
<fsummary>Returns a list of currently running services where
- each service is described by a [{Option, Value}]
- list. </fsummary>
+ each service is described by an <c>[{Option, Value}]</c>
+ list.</fsummary>
<type>
<v>Service = service()</v>
<v>Pid = pid()</v>
@@ -80,11 +81,10 @@
</type>
<desc>
<p>Returns a list of currently running services where each
- service is described by a [{Option, Value}] list. The
- information given in the list is specific for each service
- and it is probable that each service will have its own info
- function that gives you even more details about the
- service.</p>
+ service is described by an <c>[{Option, Value}]</c> list. The
+ information in the list is specific for each service
+ and each service has probably its own info
+ function that gives more details about the service.</p>
<marker id="service_names"></marker>
</desc>
@@ -106,59 +106,48 @@
<func>
<name>start() -> </name>
<name>start(Type) -> ok | {error, Reason}</name>
- <fsummary>Starts the Inets application. </fsummary>
+ <fsummary>Starts the <c>Inets</c> application.</fsummary>
<type>
<v>Type = permanent | transient | temporary</v>
</type>
<desc>
- <p>Starts the Inets application. Default type
- is temporary. See also
- <seealso marker="kernel:application">application(3)</seealso>. </p>
+ <p>Starts the <c>Inets</c> application. Default type
+ is <c>temporary</c>. See also
+ <seealso marker="kernel:application">application(3)</seealso>.</p>
<marker id="stop"></marker>
</desc>
</func>
<func>
- <name>stop() -> ok </name>
- <fsummary>Stops the inets application.</fsummary>
- <desc>
- <p>Stops the inets application. See also
- <seealso marker="kernel:application">application(3)</seealso>. </p>
-
- <marker id="start2"></marker>
- </desc>
- </func>
-
- <func>
<name>start(Service, ServiceConfig) -> {ok, Pid} | {error, Reason}</name>
<name>start(Service, ServiceConfig, How) -> {ok, Pid} | {error, Reason}</name>
- <fsummary>Dynamically starts an inets
- service after the inets application has been started. </fsummary>
+ <fsummary>Dynamically starts an <c>Inets</c>
+ service after the <c>Inets</c> application has been started.</fsummary>
<type>
<v>Service = service()</v>
<v>ServiceConfig = [{Option, Value}]</v>
<v>Option = property()</v>
<v>Value = term()</v>
- <v>How = inets | stand_alone - default is inets</v>
+ <v>How = inets | stand_alone - default is inets.</v>
</type>
<desc>
- <p>Dynamically starts an inets service after the inets
- application has been started. </p>
+ <p>Dynamically starts an <c>Inets</c> service after the <c>Inets</c>
+ application has been started.</p>
<note>
- <p>Dynamically started services will not be handled by
- application takeover and failover behavior when inets is
- run as a distributed application. Nor will they be
- automatically restarted when the inets application is
- restarted, but as long as the inets application is up and
- running they will be supervised and may be soft code
- upgraded. Services started as <c>stand_alone</c>,
- e.i. the service is not started as part of the inets
- application, will lose all OTP application benefits such
- as soft upgrade. The "stand_alone-service" will be linked to
- the process that started it. In most cases some of the
- supervision functionality will still be in place and in
- some sense the calling process has now become the top
+ <p>Dynamically started services are not handled by
+ application takeover and failover behavior when <c>Inets</c> is
+ run as a distributed application. Nor are they
+ automatically restarted when the <c>Inets</c> application is
+ restarted. As long as the <c>Inets</c> application is operational,
+ they are supervised and can be soft code upgraded.</p>
+ <p>A service started as <c>stand_alone</c>, that is, the service
+ is not started as part of the <c>Inets</c> application,
+ lose all OTP application benefits, such as soft upgrade.
+ The <c>stand_alone</c>-service is linked to
+ the process that started it. Usually some
+ supervision functionality is still in place and in
+ some sense the calling process becomes the top
supervisor.</p>
</note>
@@ -166,19 +155,30 @@
</desc>
</func>
+ <func>
+ <name>stop() -> ok </name>
+ <fsummary>Stops the <c>Inets</c> application.</fsummary>
+ <desc>
+ <p>Stops the <c>Inets</c> application. See also
+ <seealso marker="kernel:application">application(3)</seealso>.</p>
+
+ <marker id="start2"></marker>
+ </desc>
+ </func>
+
<func>
<name>stop(Service, Reference) -> ok | {error, Reason} </name>
- <fsummary>Stops a started service of the inets application or takes
- down a "stand_alone-service" gracefully.</fsummary>
+ <fsummary>Stops a started service of the <c>Inets</c> application or takes
+ down a <c>stand_alone </c>service gracefully.</fsummary>
<type>
<v>Service = service() | stand_alone</v>
- <v>Reference = pid() | term() - service specified reference</v>
+ <v>Reference = pid() | term() - service-specified reference</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Stops a started service of the inets application or takes
- down a "stand_alone-service" gracefully. When the
- <c>stand_alone</c> option is used in start,
+ <p>Stops a started service of the <c>Inets</c> application or takes
+ down a <c>stand_alone</c>-service gracefully. When option
+ <c>stand_alone</c> is used in start,
only the pid is a valid argument to stop.</p>
<marker id="see_also"></marker>
diff --git a/lib/inets/doc/src/inets_services.xml b/lib/inets/doc/src/inets_services.xml
index e282050b12..da60c01aa2 100644
--- a/lib/inets/doc/src/inets_services.xml
+++ b/lib/inets/doc/src/inets_services.xml
@@ -1,27 +1,28 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>1997</year><year>2012</year>
+ <year>1997</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
- <title>Introduction</title>
+ <title>Inets</title>
<prepared>Ingela Anderton Andin</prepared>
<responsible></responsible>
<docno></docno>
@@ -33,45 +34,26 @@
</header>
<section>
- <title>Purpose</title>
- <p>Inets is a container for Internet clients and
- servers. Currently, an <term id="HTTP"></term>client and server, a
- TFPT client and server, and a FTP client has been incorporated
- into Inets. The HTTP server and client is HTTP 1.1 compliant as
- defined in <term id="RFC"></term>2616.</p>
- </section>
-
- <section>
- <title>Prerequisites</title>
- <p>It is assumed that the reader is familiar with the Erlang
- programming language, concepts of OTP and has a basic
- understanding of the HTTP, TFTP and FTP protocols.</p>
- </section>
-
- <section>
- <title>The Service Concept</title>
- <p>Each client and server in inets is viewed as service. Services
- may be configured to be started at application startup or
- started dynamically in runtime. If you want to run inets as an
- distributed application that should handle application failover
- and takeover, services should be configured to be started at
- application startup. When starting the inets application
- the inets top supervisor will start a number of subsupervisors
- and worker processes for handling the different services
- provided. When starting services dynamically new children will
- be added to the supervision tree, unless the service is started
- with the stand alone option, in which case the service is linked
- to the calling process and all OTP application features such as
- soft upgrade are lost.</p>
- <p>Services that should be configured for startup at application
- startup time should be put into the erlang node configuration file
- on the form: </p>
+ <title>Service Concept</title>
+ <p>Each client and server in <c>Inets</c> is viewed as a service.
+ Services can be configured to be started at application startup or
+ dynamically in runtime. To run <c>Inets</c> as a distributed
+ application that handles application failover and takeover,
+ configure the services to be started at application startup.
+ When starting the <c>Inets</c> application, the <c>Inets</c>
+ top supervisor starts a number of subsupervisors and worker
+ processes for handling the provided services.
+ When starting services dynamically, new children are added to the
+ supervision tree, unless the service is started with the standalone
+ option. In this case the service is linked to the calling process
+ and all OTP application features, such as soft upgrade, are lost.</p>
+ <p>Services to be configured for startup at application startup are to
+ be put into the Erlang node configuration file
+ on the following form:</p>
<pre>
- [{inets, [{services, ListofConfiguredServices}]}].
- </pre>
- <p>For details of exactly what to put in the list of configured
- services see the documentation for the services that should be
- configured.</p>
+ [{inets, [{services, ListofConfiguredServices}]}].</pre>
+ <p>For details of what to put in the list of configured services,
+ see the documentation for the services to be configured.</p>
</section>
</chapter>
diff --git a/lib/inets/doc/src/introduction.xml b/lib/inets/doc/src/introduction.xml
new file mode 100644
index 0000000000..1af2ef5dae
--- /dev/null
+++ b/lib/inets/doc/src/introduction.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>1997</year><year>2016</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>Introduction</title>
+ <prepared>Ingela Anderton Andin</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2004-09-28</date>
+ <rev>A</rev>
+ <file>introduction.xml</file>
+ </header>
+
+ <section>
+ <title>Purpose</title>
+ <p><c>Inets</c> is a container for Internet clients and servers
+ including the following:</p>
+ <list type="bulleted">
+ <item>An FTP client</item>
+ <item>A TFTP client and server</item>
+ <item>An <term id="HTTP"></term> client and server</item>
+ </list>
+ <p>The HTTP client and server are HTTP 1.1 compliant as
+ defined in
+ <url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>.</p>
+ </section>
+
+ <section>
+ <title>Prerequisites</title>
+ <p>It is assumed that the reader is familiar with the Erlang
+ programming language, concepts of OTP, and has a basic
+ understanding of the FTP, TFTP, and HTTP protocols.</p>
+ </section>
+</chapter>
+
+
diff --git a/lib/inets/doc/src/mod_alias.xml b/lib/inets/doc/src/mod_alias.xml
index 265a1b8e76..6ae19700a5 100644
--- a/lib/inets/doc/src/mod_alias.xml
+++ b/lib/inets/doc/src/mod_alias.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>1997</year><year>2012</year>
+ <year>1997</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
@@ -31,8 +32,8 @@
<module>mod_alias</module>
<modulesummary>URL aliasing.</modulesummary>
<description>
- <p>Erlang Webserver Server internal API for handling of things
- such as interaction data exported by the mod_alias module.</p>
+ <p>Erlang web server internal API for handling of, for example,
+ interaction data exported by module <c>mod_alias</c>.</p>
<marker id="default_index"></marker>
</description>
@@ -40,7 +41,7 @@
<funcs>
<func>
<name>default_index(ConfigDB, Path) -> NewPath</name>
- <fsummary>Return a new path with the default resource or file appended.</fsummary>
+ <fsummary>Returns a new path with the default resource or file appended.</fsummary>
<type>
<v>ConfigDB = config_db()</v>
<v>Path = NewPath = string()</v>
@@ -49,14 +50,14 @@
<marker id="default_index"></marker>
<p>If <c>Path</c> is a directory, <c>default_index/2</c>, it starts
searching for resources or files that are specified in the config
- directive DirectoryIndex.
+ directive <c>DirectoryIndex</c>.
If an appropriate resource or file is found, it is appended to
the end of <c>Path</c> and then returned. <c>Path</c> is
- returned unaltered, if no appropriate
- file is found, or if <c>Path</c> is not a directory.
+ returned unaltered if no appropriate
+ file is found or if <c>Path</c> is not a directory.
<c>config_db()</c> is the server config file in ETS table format
as described in
- <seealso marker="http_server">Inets Users Guide.</seealso>.</p>
+ <seealso marker="http_server">Inets User's Guide</seealso>.</p>
<marker id="path"></marker>
</desc>
@@ -64,7 +65,7 @@
<func>
<name>path(PathData, ConfigDB, RequestURI) -> Path</name>
- <fsummary>Return the actual file path to a URL.</fsummary>
+ <fsummary>Returns the file path to a URL.</fsummary>
<type>
<v>PathData = interaction_data()</v>
<v>ConfigDB = config_db()</v>
@@ -72,15 +73,16 @@
</type>
<desc>
<marker id="path"></marker>
- <p><c>path/3</c> returns the actual file <c>Path</c> in the
- <c>RequestURI</c> (See RFC 1945). If the interaction data
- <c>{real_name,{Path,AfterPath}}</c> has been exported by
- mod_alias;
+ <p><c>path/3</c> returns the file <c>Path</c> in the
+ <c>RequestURI</c> (see
+ <url href="http://www.rfc-base.org/rfc-1945.html">RFC 1945</url>).
+ If the interaction data <c>{real_name,{Path,AfterPath}}</c>
+ has been exported by <c>mod_alias</c>,
<c>Path</c> is returned. If no interaction data has been
- exported, ServerRoot is used to
+ exported, <c>ServerRoot</c> is used to
generate a file <c>Path</c>. <c>config_db()</c> and
<c>interaction_data()</c> are as defined in
- <seealso marker="http_server">Inets Users Guide</seealso>.</p>
+ <seealso marker="http_server">Inets User's Guide</seealso>.</p>
<marker id="real_name"></marker>
</desc>
@@ -88,7 +90,7 @@
<func>
<name>real_name(ConfigDB, RequestURI, Aliases) -> Ret</name>
- <fsummary>Expand a request uri using Alias config directives.</fsummary>
+ <fsummary>Expands a request URI using <c>Aliases</c> config directives.</fsummary>
<type>
<v>ConfigDB = config_db()</v>
<v>RequestURI = string()</v>
@@ -100,26 +102,27 @@
<marker id="real_name"></marker>
<p><c>real_name/3</c> traverses <c>Aliases</c>, typically
extracted from <c>ConfigDB</c>, and matches each
- <c>FakeName</c> with <c>RequestURI</c>. If a match is found
+ <c>FakeName</c> with <c>RequestURI</c>. If a match is found,
<c>FakeName</c> is replaced with <c>RealName</c> in the
- match. The resulting path is split into two parts, that
- is <c>ShortPath</c> and <c>AfterPath</c> as defined in
- <seealso marker="httpd_util#split_path">httpd_util:split_path/1</seealso>.
- <c>Path</c> is generated from <c>ShortPath</c>, that is
+ match. The resulting path is split into two parts,
+ <c>ShortPath</c> and <c>AfterPath</c>, as defined in
+ <seealso marker="httpd_util#split_path-1">httpd_util:split_path/1</seealso>.
+ <c>Path</c> is generated from <c>ShortPath</c>, that is,
the result from
<seealso marker="#default_index">default_index/2</seealso> with
<c>ShortPath</c> as an argument.
<c>config_db()</c> is the server config file in ETS table
format as described in
- <seealso marker="http_server">Inets User Guide.</seealso>. </p>
+ <seealso marker="http_server">Inets User's Guide</seealso>.</p>
<marker id="real_script_name"></marker>
</desc>
</func>
<func>
- <name>real_script_name(ConfigDB,RequestURI,ScriptAliases) -> Ret</name>
- <fsummary>Expand a request uri using ScriptAlias config directives.</fsummary>
+ <name>real_script_name(ConfigDB, RequestURI, ScriptAliases) -> Ret</name>
+ <fsummary>Expands a request URI using <c>ScriptAliases</c>
+ config directives.</fsummary>
<type>
<v>ConfigDB = config_db()</v>
<v>RequestURI = string()</v>
@@ -129,16 +132,18 @@
</type>
<desc>
<marker id="real_script_name"></marker>
- <p><c>real_name/3</c> traverses <c>ScriptAliases</c>,
+ <p><c>real_script_name/3</c> traverses <c>ScriptAliases</c>,
typically extracted from <c>ConfigDB</c>, and matches each
- <c>FakeName</c> with <c>RequestURI</c>. If a match is found
+ <c>FakeName</c> with <c>RequestURI</c>. If a match is found,
<c>FakeName</c> is replaced with <c>RealName</c> in the
- match. If the resulting match is not an executable script
- <c>not_a_script</c> is returned. If it is a script the
- resulting script path is in two parts, that is
- <c>ShortPath</c> and <c>AfterPath</c> as defined in <seealso marker="httpd_util#split_script_path">httpd_util:split_script_path/1</seealso>.
+ match. If the resulting match is not an executable script,
+ <c>not_a_script</c> is returned. If it is a script, the
+ resulting script path is in two parts,
+ <c>ShortPath</c> and <c>AfterPath</c>, as defined in
+ <seealso marker="httpd_util#split_script_path-1">httpd_util:split_script_path/1</seealso>.
<c>config_db()</c> is the server config file in ETS table
- format as described in <seealso marker="http_server">Inets Users Guide.</seealso>.</p>
+ format as described in
+ <seealso marker="http_server">Inets User's Guide</seealso>.</p>
</desc>
</func>
</funcs>
diff --git a/lib/inets/doc/src/mod_auth.xml b/lib/inets/doc/src/mod_auth.xml
index a176242a72..4b7088b2c5 100644
--- a/lib/inets/doc/src/mod_auth.xml
+++ b/lib/inets/doc/src/mod_auth.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>1997</year><year>2012</year>
+ <year>1997</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
@@ -29,299 +30,267 @@
<file>mod_auth.sgml</file>
</header>
<module>mod_auth</module>
- <modulesummary>User authentication using text files, dets or mnesia database.</modulesummary>
+ <modulesummary>User authentication using text files, Dets, or Mnesia database.</modulesummary>
<description>
<p>This module provides for basic user authentication using
- textual files, dets databases as well as mnesia databases. </p>
-
- <marker id="add_user"></marker>
+ textual files, <c>Dets</c> databases, or <c>Mnesia</c> databases.</p>
</description>
<funcs>
+ <func>
+ <name>add_group_member(GroupName, UserName, Options) -> true | {error, Reason}</name>
+ <name>add_group_member(GroupName, UserName, Port, Dir) -> true | {error, Reason}</name>
+ <name>add_group_member(GroupName, UserName, Address, Port, Dir) -> true | {error, Reason}</name>
+ <fsummary>Adds a user to a group.</fsummary>
+ <type>
+ <v>GroupName = string()</v>
+ <v>UserName = string()</v>
+ <v>Options = [Option]</v>
+ <v>Option = {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
+ <v>Port = integer()</v>
+ <v>Address = {A,B,C,D} | string() | undefined</v>
+ <v>Dir = string()</v>
+ <v>AuthPassword = string()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p><c>add_group_member/3, add_group_member/4</c>, and
+ <c>add_group_member/5</c> each
+ adds a user to a group. If the group does not exist, it
+ is created and the user is added to the group. Upon successful
+ operation, this function returns <c>true</c>.
+ When <c>add_group_members/3</c>
+ is called, options <c>Port</c> and <c>Dir</c> are mandatory.</p>
+ </desc>
+ </func>
+
<func>
- <name>add_user(UserName, Options) -> true| {error, Reason}</name>
+ <name>add_user(UserName, Options) -> true| {error, Reason}</name>
<name>add_user(UserName, Password, UserData, Port, Dir) -> true | {error, Reason}</name>
<name>add_user(UserName, Password, UserData, Address, Port, Dir) -> true | {error, Reason}</name>
- <fsummary>Add a user to the user database.</fsummary>
+ <fsummary>Adds a user to the user database.</fsummary>
<type>
- <v>UserName = string()</v>
- <v>Options = [Option]</v>
- <v>Option = {password,Password} | {userData,UserData} | {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
- <v>Password = string()</v>
- <v>UserData = term()</v>
- <v>Port = integer()</v>
- <v>Address = {A,B,C,D} | string() | undefined</v>
- <v>Dir = string()</v>
+ <v>UserName = string()</v>
+ <v>Options = [Option]</v>
+ <v>Option = {password,Password} | {userData,UserData} | {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
+ <v>Password = string()</v>
+ <v>UserData = term()</v>
+ <v>Port = integer()</v>
+ <v>Address = {A,B,C,D} | string() | undefined</v>
+ <v>Dir = string()</v>
<v>AuthPassword =string()</v>
- <v>Reason = term()</v>
+ <v>Reason = term()</v>
</type>
<desc>
- <marker id="user_api"></marker>
- <marker id="add_user"></marker>
- <p><c>add_user/2, add_user/5</c> and <c>add_user/6</c> adds a
- user to the user
- database. If the operation is successful, this function returns
- <c>true</c>. If an error occurs, <c>{error,Reason}</c> is returned.
- When <c>add_user/2</c> is called the Password,
- UserData Port and Dir options is mandatory.</p>
-
- <marker id="delete_user"></marker>
+ <p><c>add_user/2, add_user/5</c>, and <c>add_user/6</c> each adds a
+ user to the user database. If the operation is successful,
+ this function returns <c>true</c>. If an error occurs,
+ <c>{error,Reason}</c> is returned.
+ When <c>add_user/2</c> is called, options <c>Password</c>,
+ <c>UserData</c>, <c>Port</c>, and <c>Dir</c> are mandatory.</p>
</desc>
</func>
- <func>
- <name>delete_user(UserName,Options) -> true | {error, Reason}</name>
- <name>delete_user(UserName, Port, Dir) -> true | {error, Reason}</name>
- <name>delete_user(UserName, Address, Port, Dir) -> true | {error, Reason}</name>
- <fsummary>Delete a user from the user database.</fsummary>
+ <func>
+ <name>delete_group(GroupName, Options) -> true | {error,Reason} &lt;name>delete_group(GroupName, Port, Dir) -> true | {error, Reason}</name>
+ <name>delete_group(GroupName, Address, Port, Dir) -> true | {error, Reason}</name>
+ <fsummary>Deletes a group.</fsummary>
<type>
- <v>UserName = string()</v>
- <v>Options = [Option]</v>
- <v>Option = {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
- <v>Port = integer()</v>
- <v>Address = {A,B,C,D} | string() | undefined</v>
- <v>Dir = string()</v>
+ <v>Options = [Option]</v>
+ <v>Option = {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
+ <v>Port = integer()</v>
+ <v>Address = {A,B,C,D} | string() | undefined</v>
+ <v>Dir = string()</v>
+ <v>GroupName = string()</v>
<v>AuthPassword = string()</v>
- <v>Reason = term()</v>
+ <v>Reason = term()</v>
</type>
<desc>
- <marker id="delete_user"></marker>
- <p><c>delete_user/2, delete_user/3</c> and <c>delete_user/4</c>
- deletes a user from the user database.
- If the operation is successful, this function returns <c>true</c>.
- If an error occurs, <c>{error,Reason}</c> is returned.
- When <c>delete_user/2</c> is called the Port and Dir options
- are mandatory.</p>
-
- <marker id="get_user"></marker>
+ <p><c>delete_group/2, delete_group/3</c>, and <c>delete_group/4</c>
+ each deletes the group specified and returns <c>true</c>.
+ If there is an error, <c>{error, Reason}</c> is returned.
+ When <c>delete_group/2</c> is called, option
+ <c>Port</c> and <c>Dir</c> are mandatory.</p>
</desc>
</func>
- <func>
- <name>get_user(UserName,Options) -> {ok, #httpd_user} |{error, Reason}</name>
- <name>get_user(UserName, Port, Dir) -> {ok, #httpd_user} | {error, Reason}</name>
- <name>get_user(UserName, Address, Port, Dir) -> {ok, #httpd_user} | {error, Reason}</name>
- <fsummary>Returns a user from the user database.</fsummary>
+ <func>
+ <name>delete_group_member(GroupName, UserName, Options) -> true | {error, Reason}</name>
+ <name>delete_group_member(GroupName, UserName, Port, Dir) -> true | {error, Reason}</name>
+ <name>delete_group_member(GroupName, UserName, Address, Port, Dir) -> true | {error, Reason}</name>
+ <fsummary>Removes a user from a group.</fsummary>
<type>
- <v>UserName = string()</v>
- <v>Options = [Option]</v>
- <v>Option = {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
- <v>Port = integer()</v>
- <v>Address = {A,B,C,D} | string() | undefined</v>
- <v>Dir = string()</v>
+ <v>GroupName = string()</v>
+ <v>UserName = string()</v>
+ <v>Options = [Option]</v>
+ <v>Option = {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
+ <v>Port = integer()</v>
+ <v>Address = {A,B,C,D} | string() | undefined</v>
+ <v>Dir = string()</v>
<v>AuthPassword = string()</v>
- <v>Reason = term()</v>
+ <v>Reason = term()</v>
</type>
<desc>
- <marker id="get_user"></marker>
- <p><c>get_user/2, get_user/3</c> and <c>get_user/4</c> returns a
- <c>httpd_user</c> record containing the userdata for a
- specific user. If the user cannot be found, <c>{error, Reason}</c>
- is returned. When <c>get_user/2</c> is called the Port and Dir
- options are mandatory.</p>
-
- <marker id="list_users"></marker>
+ <p><c>delete_group_member/3, delete_group_member/4</c>, and
+ <c>delete_group_member/5</c> each deletes a user from a group.
+ If the group or the user does not exist,
+ this function returns an error, otherwise <c>true</c>.
+ When <c>delete_group_member/3</c> is called, the options <c>Port</c>
+ and <c>Dir</c> are mandatory.</p>
</desc>
</func>
-
+
<func>
- <name>list_users(Options) -> {ok, Users} | {error, Reason}</name>
- <name>list_users(Port, Dir) -> {ok, Users} | {error, Reason}</name>
- <name>list_users(Address, Port, Dir) -> {ok, Users} | {error, Reason}</name>
- <fsummary>List users in the user database.</fsummary>
+ <name>delete_user(UserName,Options) -> true | {error, Reason}</name>
+ <name>delete_user(UserName, Port, Dir) -> true | {error, Reason}</name>
+ <name>delete_user(UserName, Address, Port, Dir) -> true | {error, Reason}</name>
+ <fsummary>Deletes a user from the user database.</fsummary>
<type>
- <v>Options = [Option]</v>
- <v>Option = {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
- <v>Port = integer()</v>
- <v>Address = {A,B,C,D} | string() | undefined</v>
- <v>Dir = string()</v>
- <v>Users = list()</v>
+ <v>UserName = string()</v>
+ <v>Options = [Option]</v>
+ <v>Option = {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
+ <v>Port = integer()</v>
+ <v>Address = {A,B,C,D} | string() | undefined</v>
+ <v>Dir = string()</v>
<v>AuthPassword = string()</v>
- <v>Reason = atom()</v>
+ <v>Reason = term()</v>
</type>
<desc>
- <marker id="list_users"></marker>
- <p><c>list_users/1, list_users/2</c> and <c>list_users/3</c>
- returns a list
- of users in the user database for a specific <c>Port/Dir</c>.
- When <c>list_users/1</c> is called the Port and Dir
- options are mandatory.</p>
-
- <marker id="add_group_member"></marker>
+ <p><c>delete_user/2, delete_user/3</c>, and <c>delete_user/4</c>
+ each deletes a user from the user database.
+ If the operation is successful, this function returns <c>true</c>.
+ If an error occurs, <c>{error,Reason}</c> is returned.
+ When <c>delete_user/2</c> is called, options <c>Port</c> and <c>Dir</c>
+ are mandatory.</p>
</desc>
</func>
<func>
- <name>add_group_member(GroupName, UserName, Options) -> true | {error, Reason}</name>
- <name>add_group_member(GroupName, UserName, Port, Dir) -> true | {error, Reason}</name>
- <name>add_group_member(GroupName, UserName, Address, Port, Dir) -> true | {error, Reason}</name>
- <fsummary>Add a user to a group.</fsummary>
+ <name>get_user(UserName,Options) -> {ok, #httpd_user} |{error, Reason}</name>
+ <name>get_user(UserName, Port, Dir) -> {ok, #httpd_user} | {error, Reason}</name>
+ <name>get_user(UserName, Address, Port, Dir) -> {ok, #httpd_user} | {error, Reason}</name>
+ <fsummary>Returns a user from the user database.</fsummary>
<type>
- <v>GroupName = string()</v>
- <v>UserName = string()</v>
- <v>Options = [Option]</v>
- <v>Option = {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
- <v>Port = integer()</v>
- <v>Address = {A,B,C,D} | string() | undefined</v>
- <v>Dir = string()</v>
+ <v>UserName = string()</v>
+ <v>Options = [Option]</v>
+ <v>Option = {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
+ <v>Port = integer()</v>
+ <v>Address = {A,B,C,D} | string() | undefined</v>
+ <v>Dir = string()</v>
<v>AuthPassword = string()</v>
- <v>Reason = term()</v>
+ <v>Reason = term()</v>
</type>
<desc>
- <marker id="add_group_member"></marker>
- <p><c>add_group_member/3, add_group_member/4</c> and
- <c>add_group_member/5</c>
- adds a user to a group. If the group does not exist, it
- is created and the user is added to the group. Upon successful
- operation, this function returns <c>true</c>.
- When <c>add_group_members/3</c>
- is called the Port and Dir options are mandatory.</p>
-
- <marker id="delete_group_member"></marker>
+ <p><c>get_user/2, get_user/3</c>, and <c>get_user/4</c> each
+ returns an <c>httpd_user</c> record containing the userdata for a
+ specific user. If the user cannot be found, <c>{error, Reason}</c>
+ is returned. When <c>get_user/2</c> is called, options <c>Port</c> and <c>Dir</c>
+ are mandatory.</p>
</desc>
</func>
- <func>
- <name>delete_group_member(GroupName, UserName, Options) -> true | {error, Reason}</name>
- <name>delete_group_member(GroupName, UserName, Port, Dir) -> true | {error, Reason}</name>
- <name>delete_group_member(GroupName, UserName, Address, Port, Dir) -> true | {error, Reason}</name>
- <fsummary>Remove a user from a group.</fsummary>
+ <func>
+ <name>list_groups(Options) -> {ok, Groups} | {error, Reason}</name>
+ <name>list_groups(Port, Dir) -> {ok, Groups} | {error, Reason}</name>
+ <name>list_groups(Address, Port, Dir) -> {ok, Groups} | {error, Reason}</name>
+ <fsummary>Lists all the groups.</fsummary>
<type>
- <v>GroupName = string()</v>
- <v>UserName = string()</v>
- <v>Options = [Option]</v>
- <v>Option = {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
- <v>Port = integer()</v>
- <v>Address = {A,B,C,D} | string() | undefined</v>
- <v>Dir = string()</v>
+ <v>Options = [Option]</v>
+ <v>Option = {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
+ <v>Port = integer()</v>
+ <v>Address = {A,B,C,D} | string() | undefined</v>
+ <v>Dir = string()</v>
+ <v>Groups = list()</v>
<v>AuthPassword = string()</v>
- <v>Reason = term()</v>
+ <v>Reason = term()</v>
</type>
<desc>
- <marker id="delete_group_member"></marker>
- <p><c>delete_group_member/3, delete_group_member/4</c> and
- <c>delete_group_member/5</c> deletes a user from a group.
- If the group or the user does not exist,
- this function returns an error, otherwise it returns <c>true</c>.
- When <c>delete_group_member/3</c> is called the Port and Dir options
- are mandatory.</p>
-
- <marker id="list_group_members"></marker>
+ <p><c>list_groups/1, list_groups/2</c>, and <c>list_groups/3</c>
+ each lists all the groups available.
+ If there is an error, <c>{error, Reason}</c> is returned.
+ When <c>list_groups/1</c> is called, options <c>Port</c>
+ and <c>Dir</c> are mandatory.</p>
</desc>
</func>
-
+
<func>
<name>list_group_members(GroupName, Options) -> {ok, Users} | {error, Reason}</name>
<name>list_group_members(GroupName, Port, Dir) -> {ok, Users} | {error, Reason}</name>
<name>list_group_members(GroupName, Address, Port, Dir) -> {ok, Users} | {error, Reason}</name>
- <fsummary>List the members of a group.</fsummary>
+ <fsummary>Lists the members of a group.</fsummary>
<type>
- <v>GroupName = string()</v>
- <v>Options = [Option]</v>
- <v>Option = {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
- <v>Port = integer()</v>
- <v>Address = {A,B,C,D} | string() | undefined</v>
- <v>Dir = string()</v>
- <v>Users = list()</v>
+ <v>GroupName = string()</v>
+ <v>Options = [Option]</v>
+ <v>Option = {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
+ <v>Port = integer()</v>
+ <v>Address = {A,B,C,D} | string() | undefined</v>
+ <v>Dir = string()</v>
+ <v>Users = list()</v>
<v>AuthPassword = string()</v>
- <v>Reason = term()</v>
+ <v>Reason = term()</v>
</type>
<desc>
- <marker id="list_group_members"></marker>
- <p><c>list_group_members/2, list_group_members/3</c> and
- <c>list_group_members/4</c>
+ <p><c>list_group_members/2, list_group_members/3</c>, and
+ <c>list_group_members/4</c> each
lists the members of a specified group. If the group does not
exist or there is an error, <c>{error, Reason}</c> is returned.
- When <c>list_group_members/2</c> is called the Port and Dir options
- are mandatory.</p>
-
- <marker id="list_groups"></marker>
+ When <c>list_group_members/2</c> is called, options <c>Port</c>
+ and <c>Dir</c> are mandatory.</p>
</desc>
</func>
- <func>
- <name>list_groups(Options) -> {ok, Groups} | {error, Reason}</name>
- <name>list_groups(Port, Dir) -> {ok, Groups} | {error, Reason}</name>
- <name>list_groups(Address, Port, Dir) -> {ok, Groups} | {error, Reason}</name>
- <fsummary>List all the groups.</fsummary>
+ <func>
+ <name>list_users(Options) -> {ok, Users} | {error, Reason}</name>
+ <name>list_users(Port, Dir) -> {ok, Users} | {error, Reason}</name>
+ <name>list_users(Address, Port, Dir) -> {ok, Users} | {error, Reason}</name>
+ <fsummary>Lists users in the user database.</fsummary>
<type>
- <v>Options = [Option]</v>
- <v>Option = {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
- <v>Port = integer()</v>
- <v>Address = {A,B,C,D} | string() | undefined</v>
- <v>Dir = string()</v>
- <v>Groups = list()</v>
+ <v>Options = [Option]</v>
+ <v>Option = {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
+ <v>Port = integer()</v>
+ <v>Address = {A,B,C,D} | string() | undefined</v>
+ <v>Dir = string()</v>
+ <v>Users = list()</v>
<v>AuthPassword = string()</v>
- <v>Reason = term()</v>
+ <v>Reason = atom()</v>
</type>
<desc>
- <marker id="list_groups"></marker>
- <p><c>list_groups/1, list_groups/2</c> and <c>list_groups/3</c>
- lists all the groups available.
- If there is an error, <c>{error, Reason}</c> is returned.
- When <c>list_groups/1</c> is called the Port and Dir options
+ <p><c>list_users/1, list_users/2</c>, and <c>list_users/3</c>
+ each returns a list
+ of users in the user database for a specific <c>Port/Dir</c>.
+ When <c>list_users/1</c> is called, options <c>Port</c> and <c>Dir</c>
are mandatory.</p>
-
- <marker id="delete_group"></marker>
</desc>
</func>
-
- <func>
- <name>delete_group(GroupName, Options) -> true | {error,Reason} &lt;name>delete_group(GroupName, Port, Dir) -> true | {error, Reason}</name>
- <name>delete_group(GroupName, Address, Port, Dir) -> true | {error, Reason}</name>
- <fsummary>Deletes a group</fsummary>
- <type>
- <v>Options = [Option]</v>
- <v>Option = {port,Port} | {addr,Address} | {dir,Directory} | {authPassword,AuthPassword}</v>
- <v>Port = integer()</v>
- <v>Address = {A,B,C,D} | string() | undefined</v>
- <v>Dir = string()</v>
- <v>GroupName = string()</v>
- <v>AuthPassword = string()</v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <marker id="delete_group"></marker>
- <p><c>delete_group/2, delete_group/3</c> and <c>delete_group/4</c>
- deletes the group specified and returns <c>true</c>.
- If there is an error, <c>{error, Reason}</c> is returned.
- When <c>delete_group/2</c> is called the
- Port and Dir options are mandatory.</p>
-
- <marker id="update_password"></marker>
- </desc>
- </func>
-
+
<func>
<name>update_password(Port, Dir, OldPassword, NewPassword, NewPassword) -> ok | {error, Reason}</name>
<name>update_password(Address,Port, Dir, OldPassword, NewPassword, NewPassword) -> ok | {error, Reason}</name>
- <fsummary>Change the AuthAcessPassword</fsummary>
+ <fsummary>Changes <c>AuthAcessPassword</c>.</fsummary>
<type>
- <v>Port = integer()</v>
- <v>Address = {A,B,C,D} | string() | undefined</v>
- <v>Dir = string()</v>
- <v>GroupName = string()</v>
- <v>OldPassword = string()</v>
- <v>NewPassword = string()</v>
- <v>Reason = term()</v>
+ <v>Port = integer()</v>
+ <v>Address = {A,B,C,D} | string() | undefined</v>
+ <v>Dir = string()</v>
+ <v>GroupName = string()</v>
+ <v>OldPassword = string()</v>
+ <v>NewPassword = string()</v>
+ <v>Reason = term()</v>
</type>
<desc>
- <marker id="update_password"></marker>
- <p><c>update_password/5</c> and <c>update_password/6</c>
- Updates the AuthAccessPassword for the specified directory.
- If NewPassword is equal to "NoPassword" no password is requires to
+ <p><c>update_password/5</c> and <c>update_password/6</c> each
+ updates <c>AuthAccessPassword</c> for the specified directory.
+ If <c>NewPassword</c> is equal to "NoPassword", no password is required to
change authorisation data.
- If NewPassword is equal to "DummyPassword" no changes can be done
+ If <c>NewPassword</c> is equal to "DummyPassword", no changes can be done
without changing the password first.</p>
</desc>
</func>
</funcs>
<section>
- <marker id="see_also"></marker>
<title>SEE ALSO</title>
<p><seealso marker="httpd">httpd(3)</seealso>,
- <seealso marker="mod_alias">mod_alias(3)</seealso>,</p>
+ <seealso marker="mod_alias">mod_alias(3)</seealso></p>
</section>
</erlref>
diff --git a/lib/inets/doc/src/mod_esi.xml b/lib/inets/doc/src/mod_esi.xml
index 9906ae0895..8279fdc824 100644
--- a/lib/inets/doc/src/mod_esi.xml
+++ b/lib/inets/doc/src/mod_esi.xml
@@ -1,130 +1,177 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>1997</year><year>2011</year>
+ <year>1997</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- 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/.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
- 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.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
<title>mod_esi</title>
- <prepared>Joakim Greben&ouml;</prepared>
- <docno></docno>
- <date>1997-10-14</date>
- <rev>2.2</rev>
<file>mod_esi.sgml</file>
</header>
<module>mod_esi</module>
- <modulesummary>Erlang Server Interface </modulesummary>
+ <modulesummary>Erlang Server Interface</modulesummary>
<description>
<p>This module defines the Erlang Server Interface (ESI) API.
- It is a more efficient way of writing erlang scripts
- for your Inets web server than writing them as common CGI scripts.</p>
+ It is a more efficient way of writing Erlang scripts
+ for your <c>Inets</c> web server than writing them as common CGI scripts.</p>
<marker id="deliver"></marker>
</description>
- <funcs>
- <func>
- <name>deliver(SessionID, Data) -> ok | {error, Reason}</name>
- <fsummary>Sends Data back to client.</fsummary>
- <type>
+ <section>
+ <title>DATA TYPES</title>
+ <p>The following data types are used in the functions for mod_esi:</p>
+
+ <taglist>
+ <tag><c>env() = </c></tag>
+ <item> <p><c>{EnvKey()::atom(), Value::term()}</c></p>
+
+ <p>Currently supported key value pairs</p>
+ <taglist>
+
+ <tag><c>{server_software, string()}</c></tag>
+ <item><p>Indicates the inets version.</p></item>
+
+ <tag><c>{server_name, string()}</c></tag>
+ <item><p>The local hostname. </p></item>
+
+ <tag><c>{gateway_interface, string()}</c></tag>
+ <item><p>Legacy string used in CGI, just ignore.</p> </item>
+
+ <tag><c>{server_protocol, string()}</c></tag>
+ <item><p> HTTP version, currently "HTTP/1.1"</p></item>
+
+ <tag><c>{server_port, integer()}</c></tag>
+ <item><p>Servers port number.</p></item>
+
+ <tag><c>{request_method, "GET | "PUT" | "DELETE | "POST" | "PATCH"}</c></tag>
+ <item><p>HTTP request method.</p></item>
+
+ <tag><c>{remote_adress, inet:ip_address()} </c></tag>
+ <item><p>The clients ip address.</p></item>
+
+ <tag><c>{peer_cert, undefined | no_peercert | DER:binary()</c></tag>
+ <item>
+ <p>For TLS connections where client certificates are used this will
+ be an ASN.1 DER-encoded X509-certificate as an Erlang binary.
+ If client certificates are not used the value will be <c>no_peercert</c>,
+ and if TLS is not used (HTTP or connection is lost due to network failure)
+ the value will be <c>undefined</c>.
+ </p></item>
+
+ <tag><c>{script_name, string()}</c></tag>
+ <item><p>Request URI</p></item>
+
+ <tag><c>{http_LowerCaseHTTPHeaderName, string()}</c></tag>
+ <item><p>example: {http_content_type, "text/html"}</p></item>
+ </taglist>
+ </item>
+ </taglist>
+ </section>
+
+ <funcs>
+ <func>
+ <name>deliver(SessionID, Data) -> ok | {error, Reason}</name>
+ <fsummary>Sends <c>Data</c> back to client.</fsummary>
+ <type>
<v>SessionID = term()</v>
<v>Data = string() | io_list() | binary()</v>
<v>Reason = term()</v>
- </type>
- <desc>
- <marker id="deliver"></marker>
- <p>This function is <em>only</em> intended to be used from
- functions called by the Erl Scheme interface to deliver
- parts of the content to the user.</p>
- <p>Sends data from a Erl Scheme script back to the client.</p>
+ </type>
+ <desc>
+ <marker id="deliver"></marker>
+ <p>This function is <em>only</em> intended to be used from
+ functions called by the Erl Scheme interface to deliver
+ parts of the content to the user.</p>
+ <p>Sends data from an Erl Scheme script back to the client.</p>
- <note>
- <p>Note that if any HTTP-header fields should be added by the
- script they must be in the first call to deliver/2 and the
- data in the call must be a string. Calls after the headers
- are complete may contain binary data to reduce copying
- overhead. Do not assume anything about the data type of
- SessionID, the SessionID must be the value given as input to
- the esi call back function that you implemented.</p>
- </note>
- </desc>
- </func>
- </funcs>
-
- <section>
- <title>ESI Callback Functions</title>
- </section>
- <funcs>
- <func>
- <name>Module:Function(SessionID, Env, Input)-> _ </name>
- <fsummary>Creates a dynamic web page and returns it chunk by chunk to the server process by calling mod_esi:deliver/2.</fsummary>
- <type>
- <v>SessionID = term()</v>
- <v>Env = [EnvironmentDirectives] ++ ParsedHeader</v>
- <v>EnvironmentDirectives = {Key,Value}</v>
- <v>Key = query_string | content_length | server_software | gateway_interface | server_protocol | server_port | request_method | remote_addr | script_name</v>
- <v>Input = string()</v>
- </type>
+ <note>
+ <p>If any HTTP header fields are added by the
+ script, they must be in the first call to <c>deliver/2</c>,
+ and the data in the call must be a string. Calls after the headers
+ are complete can contain binary data to reduce copying
+ overhead. Do not assume anything about the data type of
+ <c>SessionID</c>. <c>SessionID</c> must be the value given
+ as input to the ESI callback function that you implemented.</p>
+ </note>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>ESI Callback Functions</title>
+ </section>
+
+ <funcs>
+ <func>
+ <name>Module:Function(SessionID, Env, Input)-> _ </name>
+ <fsummary>Creates a dynamic web page and returns it chunk by chunk
+ to the server process by calling <c>mod_esi:deliver/2</c>.</fsummary>
+ <type>
+ <v>SessionID = term()</v>
+ <v>Env = env()</v>
+ <v>Input = string()</v>
+ </type>
<desc>
- <p>The <c>Module</c> must be found in the code path and export
- <c>Function</c> with an arity of three. An erlScriptAlias must
- also be set up in the configuration file for the Web server.</p>
- <p>If the HTTP request is a 'post' request and a body is sent
- then content_length will be the length of the posted
- data. If 'get' is used query_string will be the data after
- <em>?</em> in the url.</p>
- <p>ParsedHeader is the HTTP request as a key value tuple
- list. The keys in parsed header will be the in lower case.</p>
- <p>SessionID is a identifier
- the server uses when <c>deliver/2</c> is called; do not
- assume anything about the datatype.</p>
- <p>Use this callback function to dynamically generate dynamic web
- content. When a part of the page is generated send the
- data back to the client through <c>deliver/2</c>. Note
- that the first chunk of data sent to the client must at
- least contain all HTTP header fields that the response
- will generate. If the first chunk does not contain the
- <em>End of HTTP header</em>, that is <c>"\r\n\r\n",</c>
- the server will
- assume that no HTTP header fields will be generated.</p>
+ <p><c>Module</c> must be found in the code path and export
+ <c>Function</c> with an arity of three. An <c>erlScriptAlias</c> must
+ also be set up in the configuration file for the web server.</p>
+ <p>If the HTTP request is a 'post' request and a body is sent,
+ <c>content_length</c> is the length of the posted
+ data. If 'get' is used, <c>query_string</c> is the data after
+ <em>?</em> in the URL.</p>
+ <p><c>ParsedHeader</c> is the HTTP request as a key-value tuple
+ list. The keys in <c>ParsedHeader</c> are in lower case.</p>
+ <p><c>SessionID</c> is an identifier
+ the server uses when <c>deliver/2</c> is called. Do not
+ assume anything about the datatype.</p>
+ <p>Use this callback function to generate dynamic web
+ content dynamically. When a part of the page is generated, send the
+ data back to the client through <c>deliver/2</c>. Notice
+ that the first chunk of data sent to the client must at
+ least contain all HTTP header fields that the response
+ will generate. If the first chunk does not contain the
+ <em>end of HTTP header</em>, that is, <c>"\r\n\r\n",</c>
+ the server assumes that no HTTP header fields will be generated.</p>
</desc>
- </func>
- <func>
- <name>Module:Function(Env, Input)-> Response </name>
- <fsummary>Creates a dynamic web page and return it as a list. This functions is deprecated and only kept for backwards compatibility.</fsummary>
- <type>
- <v>Env = [EnvironmentDirectives] ++ ParsedHeader</v>
- <v>EnvironmentDirectives = {Key,Value}</v>
- <v>Key = query_string | content_length | server_software | gateway_interface | server_protocol | server_port | request_method | remote_addr | script_name.</v>
- <v>Input = string()</v>
- <v>Response = string()</v>
- </type>
+ </func>
+
+ <func>
+ <name>Module:Function(Env, Input)-> Response </name>
+ <fsummary>Creates a dynamic web page and returns it as a list.
+ This function is deprecated and is only kept for backwards compatibility.</fsummary>
+ <type>
+ <v>Env = env()</v>
+ <v>Input = string()</v>
+ <v>Response = string()</v>
+ </type>
<desc>
- <p>This callback format consumes a lot of memory since the
- whole response must be generated before it is sent to the
- user. This function is deprecated and only kept for backwards
- compatibility.
- For new development Module:Function/3 should be used.</p>
+ <p>This callback format consumes much memory, as the
+ whole response must be generated before it is sent to the
+ user. This function is deprecated and is only kept for backwards
+ compatibility.
+ For new development, use <c>Module:Function/3</c>.</p>
</desc>
- </func>
- </funcs>
-
+ </func>
+ </funcs>
+
</erlref>
diff --git a/lib/inets/doc/src/mod_security.xml b/lib/inets/doc/src/mod_security.xml
index a3c91dca5b..ec8d6ec42c 100644
--- a/lib/inets/doc/src/mod_security.xml
+++ b/lib/inets/doc/src/mod_security.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>1998</year><year>2011</year>
+ <year>1998</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- 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/.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
- 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.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
@@ -34,24 +35,45 @@
<p>Security Audit and Trailing Functionality</p>
</description>
<funcs>
+
+ <func>
+ <name>block_user(User, Port, Dir, Seconds) -> true | {error, Reason}</name>
+ <name>block_user(User, Address, Port, Dir, Seconds) -> true | {error, Reason}</name>
+ <fsummary>Blocks a user from access to a directory for a certain amount of time.</fsummary>
+ <type>
+ <v>User = string()</v>
+ <v>Port = integer()</v>
+ <v>Address = {A,B,C,D} | string() | undefined</v>
+ <v>Dir = string()</v>
+ <v>Seconds = integer() | infinity</v>
+ <v>Reason = no_such_directory</v>
+ </type>
+ <desc>
+ <p><c>block_user/4</c> and <c>block_user/5</c> each blocks the user
+ <c>User</c> from directory <c>Dir</c> for a specified
+ amount of time.</p>
+ </desc>
+ </func>
+
<func>
<name>list_auth_users(Port) -> Users | []</name>
<name>list_auth_users(Address, Port) -> Users | []</name>
<name>list_auth_users(Port, Dir) -> Users | []</name>
<name>list_auth_users(Address, Port, Dir) -> Users | []</name>
- <fsummary>List users that have authenticated within the SecurityAuthTimeout time for a given address (if specified), port number and directory (if specified).</fsummary>
+ <fsummary>Lists users that have authenticated within the <c>SecurityAuthTimeout</c>
+ time for a given address (if specified), port number, and directory
+ (if specified).</fsummary>
<type>
- <v>Port = integer()</v>
+ <v>Port = integer()</v>
<v>Address = {A,B,C,D} | string() | undefined</v>
- <v>Dir = string()</v>
- <v>Users = list() = [string()]</v>
+ <v>Dir = string()</v>
+ <v>Users = list() = [string()]</v>
</type>
<desc>
- <marker id="list_auth_users"></marker>
- <p><c>list_auth_users/1</c>, <c>list_auth_users/2</c> and
- <c>list_auth_users/3</c> returns a list of users that are
+ <p><c>list_auth_users/1</c>, <c>list_auth_users/2</c>, and
+ <c>list_auth_users/3</c> each returns a list of users that are
currently authenticated. Authentications are stored for
- SecurityAuthTimeout seconds, and are then discarded.</p>
+ <c>SecurityAuthTimeout</c> seconds, and then discarded.</p>
</desc>
</func>
<func>
@@ -59,96 +81,83 @@
<name>list_blocked_users(Address, Port) -> Users | []</name>
<name>list_blocked_users(Port, Dir) -> Users | []</name>
<name>list_blocked_users(Address, Port, Dir) -> Users | []</name>
- <fsummary>List users that are currently blocked from access to a specified port number, for a given address (if specified).</fsummary>
+ <fsummary>Lists users that are currently blocked from access to a
+ specified port number, for a given address (if specified).</fsummary>
<type>
- <v>Port = integer()</v>
+ <v>Port = integer()</v>
<v>Address = {A,B,C,D} | string() | undefined</v>
- <v>Dir = string()</v>
- <v>Users = list() = [string()]</v>
+ <v>Dir = string()</v>
+ <v>Users = list() = [string()]</v>
</type>
<desc>
- <marker id="list_blocked_users"></marker>
- <p><c>list_blocked_users/1</c>, <c>list_blocked_users/2</c> and
- <c>list_blocked_users/3</c> returns a list of users that are
+ <p><c>list_blocked_users/1</c>, <c>list_blocked_users/2</c>, and
+ <c>list_blocked_users/3</c> each returns a list of users that are
currently blocked from access.</p>
</desc>
</func>
+
<func>
- <name>block_user(User, Port, Dir, Seconds) -> true | {error, Reason}</name>
- <name>block_user(User, Address, Port, Dir, Seconds) -> true | {error, Reason}</name>
- <fsummary>Block user from access to a directory for a certain amount of time.</fsummary>
- <type>
- <v>User = string()</v>
- <v>Port = integer()</v>
- <v>Address = {A,B,C,D} | string() | undefined</v>
- <v>Dir = string()</v>
- <v>Seconds = integer() | infinity</v>
- <v>Reason = no_such_directory</v>
- </type>
- <desc>
- <marker id="block_user"></marker>
- <p><c>block_user/4</c> and <c>block_user/5</c> blocks the user
- <c>User</c> from the directory <c>Dir</c> for a specified
- amount of time.</p>
- </desc>
- </func>
- <func>
- <name>unblock_user(User, Port) -> true | {error, Reason}</name>
+ <name>unblock_user(User, Port) -> true | {error, Reason}</name>
<name>unblock_user(User, Address, Port) -> true | {error, Reason}</name>
- <name>unblock_user(User, Port, Dir) -> true | {error, Reason}</name>
+ <name>unblock_user(User, Port, Dir) -> true | {error, Reason}</name>
<name>unblock_user(User, Address, Port, Dir) -> true | {error, Reason}</name>
- <fsummary>Remove a blocked user from the block list</fsummary>
+ <fsummary>Removes a blocked user from the block list.</fsummary>
<type>
- <v>User = string()</v>
- <v>Port = integer()</v>
+ <v>User = string()</v>
+ <v>Port = integer()</v>
<v>Address = {A,B,C,D} | string() | undefined</v>
- <v>Dir = string()</v>
+ <v>Dir = string()</v>
<v>Reason = term()</v>
</type>
<desc>
- <marker id="unblock_user"></marker>
- <p><c>unblock_user/2</c>, <c>unblock_user/3</c> and
- <c>unblock_user/4</c> removes the user <c>User</c> from
- the list of blocked users for the Port (and Dir) specified.</p>
+ <p><c>unblock_user/2</c>, <c>unblock_user/3</c>, and
+ <c>unblock_user/4</c> each removes the user <c>User</c> from
+ the list of blocked users for <c>Port</c> (and <c>Dir</c>).</p>
</desc>
</func>
</funcs>
<section>
<marker id="callback_module"></marker>
- <title>The SecurityCallbackModule</title>
- <p>The SecurityCallbackModule is a user written module that can receive
- events from the mod_security Erlang Webserver API module.
- This module only exports the function(s),
- <seealso marker="#callback_module_event">event/4,5</seealso>,
- which are described below.
+ <title>SecurityCallbackModule</title>
+ <p>The <c>SecurityCallbackModule</c> is a user-written module that can receive
+ events from the <c>mod_security</c> Erlang web server API module.
+ This module only exports the functions event/[4,5]
+ which are described here.
</p>
</section>
<funcs>
<func>
- <name>event(What, Port, Dir, Data) -> ignored</name>
- <name>event(What, Address, Port, Dir, Data) -> ignored</name>
- <fsummary>This function is called whenever an event occurs in mod_security</fsummary>
+ <name>Module:event(What, Port, Dir, Data) -> ignored</name>
+ <name>Module:event(What, Address, Port, Dir, Data) -> ignored</name>
+ <fsummary>Called whenever an event occurs in <c>mod_security</c>.</fsummary>
<type>
- <v>What = atom()</v>
- <v>Port = integer()</v>
+ <v>What = atom()</v>
+ <v>Port = integer()</v>
<v>Address = {A,B,C,D} | string() &lt;v>Dir = string()</v>
- <v>Data = [Info]</v>
- <v>Info = {Name, Value}</v>
+ <v>Data = [Info]</v>
+ <v>Info = {Name, Value}</v>
</type>
<desc>
<marker id="callback_module_event"></marker>
- <p><c>event/4</c> or <c>event/4</c> is called whenever an event
- occurs in the mod_security Erlang Webserver API module (<c>event/4</c> is
- called if Address is undefined and <c>event/5</c> otherwise).
- The <c>What</c> argument specifies the type of event that has
- occurred, and should be one of the following reasons;
- <c>auth_fail</c> (a failed user authentication),
- <c>user_block</c> (a user is being blocked from access) or
- <c>user_unblock</c> (a user is being removed from the block list).</p>
+ <p><c>event/4</c> or <c>event/5</c> is called whenever an event
+ occurs in the <c>mod_security</c> Erlang web server API module.
+ (<c>event/4</c> is called if <c>Address</c> is undefined,
+ otherwise <c>event/5</c>.
+ Argument <c>What</c> specifies the type of event that has
+ occurred and is one of the following reasons:
+ </p>
+ <taglist>
+ <tag><c>auth_fail</c></tag>
+ <item><p>A failed user authentication.</p></item>
+ <tag><c>user_block</c></tag>
+ <item><p>A user is being blocked from access.</p></item>
+ <tag><c>user_unblock</c></tag>
+ <item><p>A user is being removed from the block list.</p></item>
+ </taglist>
<note>
- <p>Note that the <c>user_unblock</c> event is not triggered when
+ <p>The event <c>user_unblock</c> is not triggered when
a user is removed from the block list explicitly using the
<c>unblock_user</c> function.</p>
</note>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 72d67ddb4a..5321203511 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>2002</year><year>2013</year>
+ <year>2002</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
@@ -32,8 +33,908 @@
<file>notes.xml</file>
</header>
-
- <section><title>Inets 5.9.5</title>
+ <section><title>Inets 6.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Ftp client fixes: 1) Corrected a bug that the ftp client
+ gen_server crashed if the listening data socket was
+ closed.</p>
+ <p>
+ 2) Corrections of ftp client error codes so they are as
+ defined in the reference manual</p>
+ <p>
+ Own Id: OTP-13644</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Remove usage of erlang:now(). </p>
+ <p>
+ Own Id: OTP-12441</p>
+ </item>
+ <item>
+ <p> Add handling of DELETE Body to http client. </p>
+ <p>
+ Own Id: OTP-13383 Aux Id: PR-972 </p>
+ </item>
+ <item>
+ <p>
+ Removed references to mod_include and webtool from
+ examples and tests.</p>
+ <p>
+ Own Id: OTP-13445 Aux Id: PR-988 </p>
+ </item>
+ <item>
+ <p>
+ Remove module inets_regexp. Module re should be used
+ instead.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13561</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.2.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Handle multiple \t in mime types file</p>
+ <p>
+ Own Id: OTP-13663 Aux Id: seq13132 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.2.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Put back unused module inets_regexp and remove it in OTP
+ 19 instead as it is an incompatibility, although it is an
+ undocumented module and should not affect other
+ applications.</p>
+ <p>
+ Own Id: OTP-13533</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.2.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add environment information item peer_cert to mod_esi</p>
+ <p>
+ Own Id: OTP-13510</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Mend ipv6_host_with_brackets option in httpc</p>
+ <p>
+ Own Id: OTP-13417</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The TFTP client/server has been fixed to allow file sizes
+ larger than 32MB block by allowing the 16 bit block
+ counter to wrap. Since this is a commonly accepted
+ behavior we regard it as a bug fix.</p>
+ <p>
+ Own Id: OTP-13403</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Handle HTTP PATCH method in client.</p>
+ <p>
+ Own Id: OTP-13286</p>
+ </item>
+ <item>
+ <p>
+ Expected termination should not be logged as an
+ application error.</p>
+ <p>
+ Own Id: OTP-13389</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ mod_alias now traverses all aliases picking the longest
+ match and not the first match.</p>
+ <p>
+ Own Id: OTP-13248</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Replace obs-folds with spaces instead of failing</p>
+ <p>
+ Own Id: OTP-13069</p>
+ </item>
+ <item>
+ <p>
+ Add validation fun for URI scheme to http_uri API</p>
+ <p>
+ Own Id: OTP-13071</p>
+ </item>
+ <item>
+ <p>
+ Handle stream bodies as documented.</p>
+ <p>
+ Own Id: OTP-13093</p>
+ </item>
+ <item>
+ <p>
+ Correct error handling of mod_esi generated chunks. Send
+ warning headers in chunk trailers instead of generating
+ an unexpected additional 500 request response, when
+ problems, such as a timeout occurs.</p>
+ <p>
+ Own Id: OTP-13110</p>
+ </item>
+ <item>
+ <p>
+ HTTP client terminates gracefully when an invalid chunked
+ length header is encountered.</p>
+ <p>
+ Own Id: OTP-13117</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add default for SNI (Server Name Indication) when running
+ https using the inets HTTP-client.</p>
+ <p>
+ Own Id: OTP-12985</p>
+ </item>
+ <item>
+ <p>
+ Be forgiving to chunked sizes that have trailing
+ whitespaces as prior implementation was. Also some legacy
+ embedded devices does actually have trailing whitespaces
+ even though this in not according to the spec.</p>
+ <p>
+ Own Id: OTP-13116</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Improved error handling and gracfully termination when an
+ invalid chunked length header is encountered.</p>
+ <p>
+ Own Id: OTP-13061</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add possibility to set socket options, such as nodelay,
+ for httpd. Also phase out legacy option value inet6bf4
+ for the ipfamily option. This value will be translated to
+ the value inet.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13062</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Avoid crash in mod_auth_server and mod_security_server
+ due to using an atom instead of a string when creating a
+ name.</p>
+ <p>
+ Own Id: OTP-13022</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add function response_default_headers/0 to httpd
+ customize API, to allow user to specify default values
+ for HTTP response headers.</p>
+ <p>
+ Own Id: OTP-13013</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix broken socket feature, that is on Linux systems a
+ socket may be opened before starting Erlang and then
+ passed to Erlang's httpd daemon. This is useful as the
+ wrap program can open a privileged port and Erlang does
+ not have to be run as root.</p>
+ <p>
+ Own Id: OTP-12875 Aux Id: seq12878 </p>
+ </item>
+ <item>
+ <p>
+ Fix broken socket feature, that is on Linux systems a
+ socket may be opened before starting Erlang and then
+ passed to Erlangs tftp daemon. This is useful as the wrap
+ program can open a privileged port and Erlang does not
+ have to be run as root.</p>
+ <p>
+ Own Id: OTP-12898 Aux Id: seq12900 </p>
+ </item>
+ <item>
+ <p>
+ httpc_handler should react properly to cancel requests
+ even when the request to be canceled was already finished
+ but httpc_manager did not get notified about that yet.</p>
+ <p>
+ Own Id: OTP-12922</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Added format_status function to httpd process to avoid
+ sensitive information to be printed in supervisor logs.</p>
+ <p>
+ Own Id: OTP-12976</p>
+ </item>
+ <item>
+ <p>
+ Return meaningful error reason disregarding whether a
+ http proxy is used or not.</p>
+ <p>
+ Own Id: OTP-12984</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.0</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix race condition in httpc. If the socket is closed by
+ the peer do not try to close it again.</p>
+ <p>
+ Own Id: OTP-11845</p>
+ </item>
+ <item>
+ <p>
+ Avoid process leak by gracefully terminating httpc
+ request handler process when send operation fails.</p>
+ <p>
+ Own Id: OTP-12362</p>
+ </item>
+ <item>
+ <p>
+ Reject messages with a Content-Length less than 0</p>
+ <p>
+ Own Id: OTP-12739 Aux Id: seq12860 </p>
+ </item>
+ <item>
+ <p>
+ Let gen_tcp:controlling_process/2 and
+ inet_sctp:connect/[45] propagate prim_inet:setopt/3
+ errors instead of having them generate badmatch
+ exceptions.</p>
+ <p>
+ Own Id: OTP-12798</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Remove Server Side Include support from inets, as this is
+ an old technic that has security issues and was not well
+ tested.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-12156</p>
+ </item>
+ <item>
+ <p>
+ New value in <c>server_tokens</c> config for limiting
+ banner grabbing attempts. </p>
+ <p>
+ By setting <c>{server_tokens, none}</c> in
+ <c>ServiceConfig</c> for <c>inets:start(httpd,
+ ServiceConfig)</c>, the "Server:" header will not be set
+ in messages from the server.</p>
+ <p>
+ Own Id: OTP-12661 Aux Id: seq12840 </p>
+ </item>
+ <item>
+ <p>
+ To enable the HTTP server to run in a virtualized
+ environment, where there can be more that one server that
+ has the same ip-address and port, we add a new option
+ profile.</p>
+ <p>
+ Own Id: OTP-12674</p>
+ </item>
+ <item>
+ <p>
+ httpc: Fix implementation of gracefull shudown to work as
+ intended for keep alive connections not using pipelining.</p>
+ <p>
+ Own Id: OTP-12803</p>
+ </item>
+ <item>
+ <p>
+ Correct handling of proxy options when using persistent
+ connections.</p>
+ <p>
+ Own Id: OTP-12822</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.10.9</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add behaviour with optional callbacks to customize the
+ inets HTTP server.</p>
+ <p>
+ Own Id: OTP-12776</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.10.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Reject messages with a Content-Length less than 0</p>
+ <p>
+ Own Id: OTP-12739 Aux Id: seq12860 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.10.7</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ New value in <c>server_tokens</c> config for limiting
+ banner grabbing attempts. </p>
+ <p>
+ By setting <c>{server_tokens, none}</c> in
+ <c>ServiceConfig</c> for <c>inets:start(httpd,
+ ServiceConfig)</c>, the "Server:" header will not be set
+ in messages from the server.</p>
+ <p>
+ Own Id: OTP-12661 Aux Id: seq12840 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.10.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ inets: parse correctly 'Set-Cookie' header with empty
+ value</p>
+ <p>
+ httpc_cookie should parse cookies with empty values and
+ no attributes set in the 'Set-Cookie' headers.</p>
+ <p>
+ Own Id: OTP-12455</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add parsing of URI fragments to http_uri:parse</p>
+ <p>
+ This fixes a bug in httpc where redirection URIs could
+ lead to bad requests if they contained fragments.</p>
+ <p>
+ Own Id: OTP-12398</p>
+ </item>
+ <item>
+ <p>
+ httpc: http client now ignores invalid set-cookie headers</p>
+ <p>
+ Own Id: OTP-12430</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.10.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ mod_alias now handles https-URIs properly</p>
+ <p>
+ Consistent view of configuration parameter
+ keep_alive_timeout, should be presented in the
+ httpd:info/[1,2] function in the same unit as it is
+ inputted.</p>
+ <p>
+ Own Id: OTP-12436 Aux Id: seq12786 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Gracefully handle invalid content-lenght headers instead
+ of crashing in list_to_integer.</p>
+ <p>
+ Own Id: OTP-12429</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.10.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a spelling mistake in httpc documentation.</p>
+ <p>
+ Own Id: OTP-12221</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add option {ftp_extension, boolean} to enable use of
+ extended commands EPSV and EPRT, as specified in RFC
+ 2428, for IPv4 instead of using the legacy commands. Ipv6
+ can not be supported without the extended commands.</p>
+ <p>
+ Own Id: OTP-12255</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.10.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix some spelling mistakes in documentation</p>
+ <p>
+ Own Id: OTP-12152</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ httpd: Seperate timeout for TLS/SSL handshake from
+ keepalive timeout</p>
+ <p>
+ Own Id: OTP-12013</p>
+ </item>
+ <item>
+ <p>
+ Warning: this is experimental and may disappear or change
+ without previous warning.</p>
+ <p>
+ Experimental support for running Quickcheck and PropEr
+ tests from common_test suites is added to common_test.
+ See the reference manual for the new module
+ <c>ct_property_testing</c>.</p>
+ <p>
+ Experimental property tests are added under
+ <c>lib/{inet,ssh}/test/property_test</c>. They can be run
+ directly or from the commont_test suites
+ <c>inet/ftp_property_test_SUITE.erl</c> and
+ <c>ssh/test/ssh_property_test_SUITE.erl</c>.</p>
+ <p>
+ See the code in the <c>test</c> directories and the man
+ page for details.</p>
+ <p>
+ (Thanks to Tuncer Ayaz for a patch adding Triq)</p>
+ <p>
+ Own Id: OTP-12119</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.10.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ httpc: Fix streaming bugs when handling small responses</p>
+ <p>
+ Own Id: OTP-11992</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.10.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Correct distirbing mode for httpd:reload_config/2</p>
+ <p>
+ Own Id: OTP-11914</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improved handling of invalid strings in the HTTP request
+ line.</p>
+ <p>
+ Impact: May improve memory consumption</p>
+ <p>
+ Own Id: OTP-11925 Aux Id: Sequence 12601 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a spelling mistake in httpc doc (Thanks to Wasif
+ Riaz Malik)</p>
+ <p>
+ Own Id: OTP-11538</p>
+ </item>
+ <item>
+ <p>
+ Application upgrade (appup) files are corrected for the
+ following applications: </p>
+ <p>
+ <c>asn1, common_test, compiler, crypto, debugger,
+ dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe,
+ inets, observer, odbc, os_mon, otp_mibs, parsetools,
+ percept, public_key, reltool, runtime_tools, ssh,
+ syntax_tools, test_server, tools, typer, webtool, wx,
+ xmerl</c></p>
+ <p>
+ A new test utility for testing appup files is added to
+ test_server. This is now used by most applications in
+ OTP.</p>
+ <p>
+ (Thanks to Tobias Schlager)</p>
+ <p>
+ Own Id: OTP-11744</p>
+ </item>
+ <item>
+ <p>
+ ftp now sanitize file name, user name and passwords from
+ &lt;CR&gt; and &lt;LF&gt; tags (Thanks to Sergei Golovan)</p>
+ <p>
+ Own Id: OTP-11750</p>
+ </item>
+ <item>
+ <p>
+ Corrected error handling in the HTTP client, making it
+ behave more graceful.</p>
+ <p>
+ Thanks to Kirilll Zaborsky</p>
+ <p>
+ Own Id: OTP-11794</p>
+ </item>
+ <item>
+ <p>
+ Support identity transfer-encoding in httpc.</p>
+ <p>
+ Thanks to Anthony Ramine</p>
+ <p>
+ Own Id: OTP-11802</p>
+ </item>
+ <item>
+ <p>
+ Ignore empty Set-Cookie headers to increase
+ interoperability with servers that violate the RFC. </p>
+ <p>
+ Thanks to Kirilll Zaborsky</p>
+ <p>
+ Own Id: OTP-11803</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The commit 6189bc07 "inets: httpc improve pipelining" has
+ been reverted, as it turned out to break things rather
+ than improve pipelining utilization. It is instead up to
+ the user to configure httpc and use it wisely to be able
+ to get the most out of pipelining.</p>
+ <p>
+ Own Id: OTP-11756</p>
+ </item>
+ <item>
+ <p>
+ Handle all response codes in httpd_util:message/3</p>
+ <p>
+ Own Id: OTP-11838</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.9.8</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Mend max_clients check that was broken and avoid too
+ extensive logging that could cause memory problems.</p>
+ <p>
+ Own Id: OTP-11557 Aux Id: seq12478 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.9.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix httpd config option 'script_timeout' and fixed httpd
+ config option 'keep_alive_timeout'. Thanks to Johannes
+ Weissl.</p>
+ <p>
+ Own Id: OTP-11276</p>
+ </item>
+ <item>
+ <p>
+ Make httpc:request_cancel/[1,2] asynchronous. Previously
+ these functions tried to guarantee request answer would
+ not reach the client, which only worked for some of the
+ use cases. Now these functions are totally asynchronous
+ which makes it the clients responsibility to disregard
+ possible answers to canceled requests. </p>
+ <p>
+ Also pipelining implementation has been changed to
+ improve the utilization factor. Further investigation of
+ possible enhancements in this area are planned for later.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-11312</p>
+ </item>
+ <item>
+ <p>
+ [httpd] Add handling of new response for mod_head
+ (otherwise causing case_clause crash). Also updated
+ logging: Removed logging for keep-alive connections
+ timeout (this is a normal occurrence and not an error)
+ and some access-log body size corrections.</p>
+ <p>
+ Own Id: OTP-11328</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The ftp client now supports ftp over tls (ftps).</p>
+ <p>
+ Own Id: OTP-11037</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.9.6</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ httpc: Allow content body in DELETE requests. Thanks to
+ James Wheare.</p>
+ <p>
+ Own Id: OTP-11190</p>
+ </item>
+ <item>
+ <p>
+ Add missing brackets to report formatting on ftp_progress
+ process exit. Thanks to Artur Wilniewczyc.</p>
+ <p>
+ Own Id: OTP-11202</p>
+ </item>
+ <item>
+ <p>
+ Fix some errors in the inets documentation. Thanks to
+ Johannes Weissl.</p>
+ <p>
+ Own Id: OTP-11210</p>
+ </item>
+ <item>
+ <p>
+ Fix various typos in httpd, inets. Thanks to Tomohiko
+ Aono.</p>
+ <p>
+ Own Id: OTP-11226</p>
+ </item>
+ <item>
+ <p>
+ Fix httpd config option 'erl_script_nocache'. Thanks to
+ Johannes Weissl.</p>
+ <p>
+ Own Id: OTP-11260</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.9.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -46,8 +947,6 @@
</item>
</list>
</section>
-
-
<section><title>Improvements and New Features</title>
<list>
<item>
@@ -93,7 +992,6 @@
</section>
<section><title>Inets 5.9.4</title>
-
<section><title>Improvements and New Features</title>
<list>
<item>
@@ -160,6 +1058,32 @@
</section>
</section>
+<section><title>Inets 5.9.2.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Make log_alert configurable as option in ssl, SSLLogLevel
+ added as option to inets conf file</p>
+ <p>
+ Own Id: OTP-11259</p>
+ </item>
+ </list>
+ </section>
+</section>
+<section><title>Inets 5.9.2.1</title>
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fixed obsolete error report in inets.</p>
+ <p>
+ Own Id: OTP-11185 Aux Id: seq12357 </p>
+ </item>
+ </list>
+ </section>
+</section>
<section><title>Inets 5.9.2</title>
@@ -195,9 +1119,9 @@
<p>Better handling of errorI(s) during update of the session
database. </p>
<p>Also added and updated some debugging functions
- <seealso marker="httpc#which_sessions">which_sessions/10,1</seealso>
+ <seealso marker="httpc#which_sessions-0">which_sessions/[0,1]</seealso>
and
- <seealso marker="httpc#info">info/0</seealso>. </p>
+ <seealso marker="httpc#info-0">info/0</seealso>. </p>
<p>Own Id: OTP-10093</p>
<p>Aux Id: Seq 12062</p>
</item>
@@ -291,7 +1215,7 @@
<item>
<p>[httpc] Add function for retrieving current options,
- <seealso marker="httpc#get_options">get_options/1,2</seealso>. </p>
+ <seealso marker="httpc#get_options-1">get_options/[1,2]</seealso>. </p>
<p>Own Id: OTP-9979</p>
</item>
@@ -468,15 +1392,11 @@
<section>
<title>Incompatibilities</title>
-<!--
- <p>-</p>
--->
-
<list>
<item>
<p>[httpc] Deprecated interface module <c>http</c> has been removed.
It has (long) been replaced by http client interface module
- <seealso marker="httpc#">httpc</seealso>. </p>
+ <seealso marker="httpc">httpc</seealso>. </p>
<p>Own Id: OTP-9359</p>
</item>
@@ -594,7 +1514,7 @@
<p>[httpd] Fix httpd directory traversal on Windows.
Directory traversal was possible on Windows where
backward slash is used as directory separator. </p>
- <p>Andr�s Veres-Szentkir�lyi.</p>
+ <p>András Veres-Szentkirályi.</p>
<p>Own Id: OTP-9561</p>
</item>
@@ -664,15 +1584,13 @@
<section><title>Inets 5.6</title>
<section><title>Improvements and New Features</title>
-<!--
- <p>-</p>
--->
+
<list>
<item>
<p>[httpc] Add support for upload body streaming (PUT and POST).</p>
<p>For more info,
see the definition of the <c>Body</c> argument of the
- <seealso marker="httpc#request2">request/4,5</seealso>
+ <seealso marker="httpc#request-4">request/[4,5]</seealso>
function. </p>
<p>Filipe David Manana</p>
<p>Own Id: OTP-9094</p>
@@ -685,7 +1603,7 @@
<item>
<p>[httpd]
- <seealso marker="mod_esi#deliver">mod_esi:deliver/2</seealso>
+ <seealso marker="mod_esi#deliver-2">mod_esi:deliver/2</seealso>
made to accept binary data. </p>
<p>Bernard Duggan</p>
<p>Own Id: OTP-9123</p>
@@ -702,7 +1620,7 @@
<item>
<p>[httpd] Improved error messages. </p>
- <p>Ricardo Catalinas Jim�nez</p>
+ <p>Ricardo Catalinas Jiménez</p>
<p>Own Id: OTP-9157</p>
</item>
@@ -713,7 +1631,7 @@
for using file descriptors has been improved.
It is now possible to add the file descriptor to the config
(option fd) when calling the
- <seealso marker="inets#start2">inets:start(httpd, ...)</seealso>
+ <seealso marker="inets#start-2">inets:start(httpd, ...)</seealso>
function. </p>
<p>Attila Rajmund Nohl</p>
<p>Own Id: OTP-9202</p>
@@ -727,7 +1645,7 @@
<p>See the httpd
<seealso marker="httpd#props_comm">socket_type</seealso>
communication property or the httpc
- <seealso marker="httpc#request2">request/4,5</seealso> function
+ <seealso marker="httpc#request-4">request/[4,5]</seealso> function
for more info. </p>
<p>Own Id: OTP-9230</p>
<p>*** POTENTIAL INCOMPATIBILITY ***</p>
@@ -927,7 +1845,7 @@
<p>[httpc|httpd] - Now allow the use of the "new" ssl, by using
the <c>essl</c> tag instead. </p>
<p>See the <c>http_option</c> option in the
- <seealso marker="httpc#request2">request/4,5</seealso> or
+ <seealso marker="httpc#request-4">request/[4,5]</seealso> or
the <seealso marker="httpd#props_comm">socket-type</seealso>
section of the Communication properties chapter for more info, </p>
<p>Own Id: OTP-7907</p>
@@ -1122,23 +2040,21 @@
<item>
<p>[httpd] - Issues with ESI erl_script_timeout. </p>
- <p>
- <list type="bulleted">
- <item>
- <p>The <c>erl_script_timeout</c> config option is ducumented
- as a number of seconds. But when parsing the config, in the
- new format (not a config file), it was handled as if in
- number of milliseconds. </p>
- </item>
- <item>
- <p>When the erl-script-timeout time was exceeded, the server
- incorrectly marked the answer as sent, thereby leaving
- client hanging (with an incomplete answer).
- This has been changed, so that now the socket will be
- closed. </p>
- </item>
- </list>
- </p>
+ <list type="bulleted">
+ <item>
+ <p>The <c>erl_script_timeout</c> config option is ducumented
+ as a number of seconds. But when parsing the config, in the
+ new format (not a config file), it was handled as if in
+ number of milliseconds. </p>
+ </item>
+ <item>
+ <p>When the erl-script-timeout time was exceeded, the server
+ incorrectly marked the answer as sent, thereby leaving
+ client hanging (with an incomplete answer).
+ This has been changed, so that now the socket will be
+ closed. </p>
+ </item>
+ </list>
<p>Own Id: OTP-8509</p>
</item>
</list>
@@ -1159,8 +2075,8 @@
<p>[httpc] - Allow users to pass socket options to the transport
module when making requests. </p>
<p>See the <c>socket_opts</c> option in the
- <seealso marker="httpc#request2">request/4</seealso> or
- <seealso marker="httpc#set_options">set_options/1,2</seealso>
+ <seealso marker="httpc#request-4">request/4</seealso> or
+ <seealso marker="httpc#set_options-1">set_options/[1,2]</seealso>
for more info, </p>
<p>Own Id: OTP-8352</p>
</item>
@@ -1201,7 +2117,7 @@
deliver an async reply to more receivers then the calling
process. </p>
<p>See the
- <seealso marker="httpc#request2">receiver</seealso>
+ <seealso marker="httpc#request-2">receiver</seealso>
option for more info, </p>
<p>Own Id: OTP-8106</p>
</item>
@@ -1214,20 +2130,19 @@
<item>
<p>[httpc] Several more or less critical fixes:</p>
- <p>
- <list type="bulleted">
- <item>
- <p>Initial call between the httpc manager and request
- handler was synchronous. </p>
- <p>When the manager starts a new request handler,
- this is no longer a synchronous operation. Previously,
- the new request handler made the connection to the
- server and issuing of the first request (the reason
- for starting it) in the gen_server init function.
- If the connection for some reason "took some time",
- the manager hanged, leaving all other activities by
- that manager also hanging. </p>
- </item>
+ <list type="bulleted">
+ <item>
+ <p>Initial call between the httpc manager and request
+ handler was synchronous. </p>
+ <p>When the manager starts a new request handler,
+ this is no longer a synchronous operation. Previously,
+ the new request handler made the connection to the
+ server and issuing of the first request (the reason
+ for starting it) in the gen_server init function.
+ If the connection for some reason "took some time",
+ the manager hanged, leaving all other activities by
+ that manager also hanging. </p>
+ </item>
<!--
<item>
<p>Copying of data between processes</p>
@@ -1238,8 +2153,7 @@
<p>TBD</p>
</item>
-->
- </list>
- </p>
+ </list>
<p>As a side-effect of these changes, some modules was also
renamed, and a new api module,
<seealso marker="httpc">httpc</seealso>, has been introduced
@@ -1470,7 +2384,7 @@
request, when the client connects to the server. Default
value is that of the <c>timeout</c> option. </p>
<p>See the
- <seealso marker="httpc#request2">request/4,5</seealso>
+ <seealso marker="httpc#request-4">request/[4,5]</seealso>
function for more info. </p>
<p>Own Id: OTP-7298</p>
<!-- <p>Aux Id: seq11086</p> -->
@@ -1577,7 +2491,7 @@
the client connects to the server. </p>
<p>As a side-effect of this, the option <c>ipv6</c> has been
removed and replaced by the <c>ipfamily</c> option. </p>
- <p>See <seealso marker="httpc#set_options">http:set_options/1,2</seealso>
+ <p>See <seealso marker="httpc#set_options-1">http:set_options/[1,2]</seealso>
for more info. </p>
<p>*** POTENTIAL INCOMPATIBILITY ***</p>
<p>Own Id: OTP-8004</p>
diff --git a/lib/inets/doc/src/notes_history.xml b/lib/inets/doc/src/notes_history.xml
index bd59c1ba47..c12899e614 100644
--- a/lib/inets/doc/src/notes_history.xml
+++ b/lib/inets/doc/src/notes_history.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>2004</year><year>2012</year>
+ <year>2004</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
@@ -834,7 +835,7 @@
<list type="bulleted">
<item>
<p>[ftp, client] - A new option {progress, {CBmodule,
- CBFunction, InitProgressTerm} has been added to allow
+ CBFunction, InitProgressTerm}} has been added to allow
users to create things such as progress bars in there
GUI's. The option affects ftp:send/[3,4] and
ftp:recv/[3,4].</p>
diff --git a/lib/inets/doc/src/part.xml b/lib/inets/doc/src/part.xml
index 3b6734a9b8..f777481b5c 100644
--- a/lib/inets/doc/src/part.xml
+++ b/lib/inets/doc/src/part.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE part SYSTEM "part.dtd">
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>2004</year><year>2012</year>
+ <year>2004</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
@@ -29,10 +30,18 @@
<file>part.sgml</file>
</header>
<description>
- <p>The <em>Inets Application </em> provides a set of Internet
- related services. Currently supported are a HTTP client, a HTTP
- server a FTP client and a TFTP client and server.</p>
+ <p>The <c>Inets</c> application provides a set of
+ Internet-related services as follows:</p>
+ <list type="bulleted">
+ <item>An FTP client</item>
+ <item>A TFTP client and server</item>
+ <item>An <term id="HTTP"></term> client and server</item>
+ </list>
+ <p>The HTTP client and server are HTTP 1.1 compliant as
+ defined in
+ <url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>.</p>
</description>
+ <xi:include href="introduction.xml"/>
<xi:include href="inets_services.xml"/>
<xi:include href="ftp_client.xml"/>
<xi:include href="http_client.xml"/>
diff --git a/lib/inets/doc/src/part_notes.xml b/lib/inets/doc/src/part_notes.xml
index 81b0dedbfa..d10c829f4a 100644
--- a/lib/inets/doc/src/part_notes.xml
+++ b/lib/inets/doc/src/part_notes.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE part SYSTEM "part.dtd">
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>2002</year><year>2012</year>
+ <year>2002</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
diff --git a/lib/inets/doc/src/part_notes_history.xml b/lib/inets/doc/src/part_notes_history.xml
index f714a6d2e3..66e3307f69 100644
--- a/lib/inets/doc/src/part_notes_history.xml
+++ b/lib/inets/doc/src/part_notes_history.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE part SYSTEM "part.dtd">
<part>
<header>
<copyright>
- <year>2004</year><year>2012</year>
+ <year>2004</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
diff --git a/lib/inets/doc/src/ref_man.xml b/lib/inets/doc/src/ref_man.xml
index e44829827c..27021ea09a 100644
--- a/lib/inets/doc/src/ref_man.xml
+++ b/lib/inets/doc/src/ref_man.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE application SYSTEM "application.dtd">
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1997</year><year>2012</year>
+ <year>1997</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
@@ -29,16 +30,16 @@
<file>ref_man.xml</file>
</header>
<description>
- <p>Inets is a container for Internet clients and
- servers. Currently a FTP client, a HTTP client and server, and
- a tftp client and server has been incorporated in Inets.</p>
+ <p><c>Inets</c> is a container for Internet clients and
+ servers. An FTP client, an HTTP client and server, and
+ a TFTP client and server are incorporated in <c>Inets</c>.</p>
</description>
<xi:include href="inets.xml"/>
<xi:include href="ftp.xml"/>
<xi:include href="tftp.xml"/>
<xi:include href="httpc.xml"/>
<xi:include href="httpd.xml"/>
- <xi:include href="httpd_conf.xml"/>
+ <xi:include href="httpd_custom_api.xml"/>
<xi:include href="httpd_socket.xml"/>
<xi:include href="httpd_util.xml"/>
<xi:include href="mod_alias.xml"/>
diff --git a/lib/inets/doc/src/tftp.xml b/lib/inets/doc/src/tftp.xml
index 0b3e93a153..10398f5088 100644
--- a/lib/inets/doc/src/tftp.xml
+++ b/lib/inets/doc/src/tftp.xml
@@ -1,23 +1,24 @@
-<?xml version="1.0" encoding="iso-8859-1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>2006</year><year>2012</year>
+ <year>2006</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
</legalnotice>
@@ -28,173 +29,170 @@
<rev></rev>
</header>
<module>tftp</module>
- <modulesummary>Trivial FTP</modulesummary>
+ <modulesummary>Trivial FTP.</modulesummary>
<description>
<p>This is a complete implementation of the following IETF standards:</p>
<list type="bulleted">
- <item>RFC 1350, The TFTP Protocol (revision 2).</item>
- <item>RFC 2347, TFTP Option Extension.</item>
- <item>RFC 2348, TFTP Blocksize Option.</item>
- <item>RFC 2349, TFTP Timeout Interval and Transfer Size Options.</item>
+ <item>RFC 1350, The TFTP Protocol (revision 2)</item>
+ <item>RFC 2347, TFTP Option Extension</item>
+ <item>RFC 2348, TFTP Blocksize Option</item>
+ <item>RFC 2349, TFTP Timeout Interval and Transfer Size Options</item>
</list>
- <p>The only feature that not is implemented in this release is
+ <p>The only feature that not is implemented is
the "netascii" transfer mode.</p>
<p>The <seealso marker="#start/1">start/1</seealso> function starts
- a daemon process which listens for UDP packets on a port. When it
- receives a request for read or write it spawns a temporary server
- process which handles the actual transfer of the file.</p>
- <p>On the client side
- the <seealso marker="#read_file/3">read_file/3</seealso>
+ a daemon process listening for UDP packets on a port. When it
+ receives a request for read or write, it spawns a temporary server
+ process handling the transfer.</p>
+ <p>On the client side,
+ function <seealso marker="#read_file/3">read_file/3</seealso>
and <seealso marker="#write_file/3">write_file/3</seealso>
- functions spawns a temporary client process which establishes
- contact with a TFTP daemon and performs the actual transfer of
- the file.</p>
- <p><c>tftp</c> uses a callback module to handle the actual file
+ spawn a temporary client process establishing
+ contact with a TFTP daemon and perform the file transfer.</p>
+ <p><c>tftp</c> uses a callback module to handle the file
transfer. Two such callback modules are provided,
<c>tftp_binary</c> and <c>tftp_file</c>. See
<seealso marker="#read_file/3">read_file/3</seealso> and
- <seealso marker="#write_file/3">write_file/3</seealso> for
- more information about these. The user can also implement own
- callback modules, see <seealso marker="#tftp_callback">CALLBACK FUNCTIONS</seealso> below. A callback module provided by
- the user is registered using the <c>callback</c> option, see
- <seealso marker="#options">DATA TYPES</seealso> below.</p>
+ <seealso marker="#write_file/3">write_file/3</seealso> for details.
+ You can also implement your own callback modules, see
+ <seealso marker="#tftp_callback">CALLBACK FUNCTIONS</seealso>.
+ A callback module provided by
+ the user is registered using option <c>callback</c>, see
+ <seealso marker="#options">DATA TYPES</seealso>.</p>
</description>
<section>
- <title>TFTP SERVER SERVICE START/STOP </title>
+ <title>TFTP SERVER SERVICE START/STOP</title>
<p>A TFTP server can be configured to start statically when starting
- the Inets application. Alternatively it can be started dynamically
- (when Inets already is started) by calling the Inets application API
- <c>inets:start(tftpd, ServiceConfig)</c>, or
+ the <c>Inets</c> application. Alternatively, it can be started dynamically
+ (when <c>Inets</c> is already started) by calling the <c>Inets</c> application
+ API <c>inets:start(tftpd, ServiceConfig)</c> or
<c>inets:start(tftpd, ServiceConfig, How)</c>,
see <seealso marker="inets">inets(3)</seealso> for details.
- The <c>ServiceConfig</c> for TFTP is described below in
- the <seealso marker="#options">COMMON DATA TYPES</seealso>
+ The <c>ServiceConfig</c> for TFTP is described in
+ the <seealso marker="#options">DATA TYPES</seealso>
section.</p>
<p>The TFTP server can be stopped using <c>inets:stop(tftpd, Pid)</c>,
see <seealso marker="inets">inets(3)</seealso> for details.</p>
<p>The TPFT client is of such a temporary nature that it is not
- handled as a service in the Inets service framework.</p>
+ handled as a service in the <c>Inets</c> service framework.</p>
</section>
<section>
<marker id="options"></marker>
- <title>COMMON DATA TYPES</title>
- <pre>
- ServiceConfig = Options
- Options = [option()]
- option() -- see below
- </pre>
+ <title>DATA TYPES</title>
+ <p><c>ServiceConfig = Options</c></p>
+ <p><c>Options = [option()]</c></p>
<p>Most of the options are common for both the client and the server
- side, but some of them differs a little. Here are the available
- options:</p>
+ side, but some of them differs a little.
+ The available <c>option()</c>s are as follows:</p>
<taglist>
<tag><c>{debug, Level}</c></tag>
<item>
<p><c>Level = none | error | warning | brief | normal | verbose | all</c></p>
- <p>Controls the level of debug printouts. The default is
- <c>none</c>.</p>
+ <p>Controls the level of debug printouts.
+ Default is <c>none</c>.</p>
</item>
<tag><c>{host, Host}</c></tag>
<item>
- <p><c>Host = hostname()</c> see
- <seealso marker="kernel:inet">inet(3)</seealso></p>
+ <p><c>Host = hostname()</c>, see
+ <seealso marker="kernel:inet">inet(3)</seealso>.</p>
<p>The name or IP address of the host where the TFTP daemon
resides. This option is only used by the client.</p>
</item>
<tag><c>{port, Port}</c></tag>
<item>
<p><c>Port = int()</c></p>
- <p>The TFTP port where the daemon listens. It defaults to
- the standardized number 69. On the server side it may
- sometimes make sense to set it to 0, which means that
- the daemon just will pick a free port (which one is
- returned by the <c>info/1</c> function).</p>
- <p>If a socket has somehow already has been connected, the
- {udp, [{fd, integer()}]} option can be used to pass the
- open file descriptor to gen_udp. This can be automated
- a bit by using a command line argument stating the
+ <p>The TFTP port where the daemon listens. Defaults is
+ the standardized number 69. On the server side, it can
+ sometimes make sense to set it to 0, meaning that
+ the daemon just picks a free port (which one is
+ returned by function <c>info/1</c>).</p>
+ <p>If a socket is connected already, option
+ <c>{udp, [{fd, integer()}]}</c> can be used to pass the
+ open file descriptor to <c>gen_udp</c>. This can be automated
+ by using a command-line argument stating the
prebound file descriptor number. For example, if the
- Port is 69 and the file descriptor 22 has been opened by
- setuid_socket_wrap. Then the command line argument
- "-tftpd_69 22" will trigger the prebound file
+ port is 69 and file descriptor 22 is opened by
+ <c>setuid_socket_wrap</c>, the command-line argument
+ "-tftpd_69 22" triggers the prebound file
descriptor 22 to be used instead of opening port 69.
- The UDP option {udp, [{fd, 22}]} automatically be added.
- See init:get_argument/ about command line arguments and
- gen_udp:open/2 about UDP options.</p>
+ The UDP option <c>{udp, [{fd, 22}]}</c> is automatically added.
+ See <c>init:get_argument/</c> about command-line arguments and
+ <c>gen_udp:open/2</c> about UDP options.</p>
</item>
<tag><c>{port_policy, Policy}</c></tag>
<item>
- <p><c>Policy = random | Port | {range, MinPort, MaxPort}</c> <br></br>
-<c>Port = MinPort = MaxPort = int()</c></p>
- <p>Policy for the selection of the temporary port which is used
- by the server/client during the file transfer. It defaults to
- <c>random</c> which is the standardized policy. With this
- policy a randomized free port used. A single port or a range
- of ports can be useful if the protocol should pass through a
+ <p><c>Policy = random | Port | {range, MinPort, MaxPort}</c></p>
+ <p><c>Port = MinPort = MaxPort = int()</c></p>
+ <p>Policy for the selection of the temporary port that is used
+ by the server/client during the file transfer. Default is
+ <c>random</c>, which is the standardized policy. With this
+ policy a randomized free port is used. A single port or a range
+ of ports can be useful if the protocol passes through a
firewall.</p>
</item>
<tag><c>{udp, Options}</c></tag>
<item>
- <p><c>Options = [Opt]</c> see
- <seealso marker="kernel:gen_udp#open/1">gen_udp:open/2</seealso></p>
+ <p><c>Options = [Opt]</c>, see
+ <seealso marker="kernel:gen_udp#open/1">gen_udp:open/2</seealso>.</p>
</item>
<tag><c>{use_tsize, Bool}</c></tag>
<item>
<p><c>Bool = bool()</c></p>
- <p>Flag for automated usage of the <c>tsize</c> option. With
- this set to true, the <c>write_file/3</c> client will
- determine the filesize and send it to the server as
+ <p>Flag for automated use of option <c>tsize</c>. With
+ this set to <c>true</c>, the <c>write_file/3</c> client
+ determines the filesize and sends it to the server as
the standardized <c>tsize</c> option. A <c>read_file/3</c>
- client will just acquire filesize from the server by sending
+ client acquires only a filesize from the server by sending
a zero <c>tsize</c>.</p>
</item>
<tag><c>{max_tsize, MaxTsize}</c></tag>
<item>
<p><c>MaxTsize = int() | infinity</c></p>
<p>Threshold for the maximal filesize in bytes. The transfer
- will be aborted if the limit is exceeded. It defaults to
- <c>infinity</c>.</p>
+ is aborted if the limit is exceeded.
+ Default is <c>infinity</c>.</p>
</item>
<tag><c>{max_conn, MaxConn}</c></tag>
<item>
<p><c>MaxConn = int() | infinity</c></p>
<p>Threshold for the maximal number of active connections.
- The daemon will reject the setup of new connections if
- the limit is exceeded. It defaults to <c>infinity</c>.</p>
+ The daemon rejects the setup of new connections if
+ the limit is exceeded. Default is <c>infinity</c>.</p>
</item>
<tag><c>{TftpKey, TftpVal}</c></tag>
<item>
<p><c>TftpKey = string()</c> <br></br>
<c>TftpVal = string()</c></p>
- <p>The name and value of a TFTP option.</p>
+ <p>Name and value of a TFTP option.</p>
</item>
<tag><c>{reject, Feature}</c></tag>
<item>
<p><c>Feature = Mode | TftpKey</c> <br></br>
<c>&nbsp;Mode = read | write</c> <br></br>
<c>&nbsp;TftpKey = string()</c></p>
- <p>Control which features that should be rejected. This is
- mostly useful for the server as it may restrict usage of
- certain TFTP options or read/write access.</p>
+ <p>Controls which features to reject. This is
+ mostly useful for the server as it can restrict the use
+ of certain TFTP options or read/write access.</p>
</item>
<tag><c>{callback, {RegExp, Module, State}}</c></tag>
<item>
<p><c>RegExp = string()</c> <br></br>
<c>Module = atom()</c> <br></br>
-<c>State = term()</c></p>
+<c>State = term()</c></p>
<p>Registration of a callback module. When a file is to be
- transferred, its local filename will be matched to the regular
+ transferred, its local filename is matched to the regular
expressions of the registered callbacks. The first matching
- callback will be used the during the transfer. See
+ callback is used during the transfer. See
<seealso marker="#read_file/3">read_file/3</seealso> and
<seealso marker="#write_file/3">write_file/3</seealso>.
</p>
- <p>The callback module must implement the <c>tftp</c> behavior,
+ <p>The callback module must implement the <c>tftp</c> behavior, see
<seealso marker="#tftp_callback">CALLBACK FUNCTIONS</seealso>.</p>
</item>
@@ -202,9 +200,9 @@
<item>
<p><c>Module = module()()</c></p>
- <p>Callback module for customized logging of error, warning and
- info messages. >The callback module must implement the
- <c>tftp_logger</c> behavior,
+ <p>Callback module for customized logging of errors, warnings, and
+ info messages. The callback module must implement the
+ <c>tftp_logger</c> behavior, see
<seealso marker="#tftp_logger">LOGGER FUNCTIONS</seealso>.
The default module is <c>tftp_logger</c>.</p>
</item>
@@ -214,199 +212,169 @@
<p><c>MaxRetries = int()</c></p>
<p>Threshold for the maximal number of retries. By default
- the server/client will try to resend a message up to
- <c>5</c> times when the timeout expires.</p>
+ the server/client tries to resend a message up to
+ five times when the time-out expires.</p>
</item>
</taglist>
-
- <marker id="start1"></marker>
</section>
<funcs>
<func>
- <name>start(Options) -> {ok, Pid} | {error, Reason}</name>
- <fsummary>Start a daemon process</fsummary>
+ <name>change_config(daemons, Options) -> [{Pid, Result}]</name>
+ <fsummary>Changes configuration for all daemons.
+ </fsummary>
<type>
<v>Options = [option()]</v>
<v>Pid = pid()</v>
+ <v>Result = ok | {error, Reason}</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Starts a daemon process which listens for udp packets on a
- port. When it receives a request for read or write it spawns
- a temporary server process which handles the actual transfer
- of the (virtual) file.</p>
-
- <marker id="read_file"></marker>
+ <p>Changes configuration for all TFTP daemon processes. </p>
</desc>
</func>
<func>
- <name>read_file(RemoteFilename, LocalFilename, Options) -> {ok, LastCallbackState} | {error, Reason}</name>
- <fsummary>Read a (virtual) file from a TFTP server</fsummary>
+ <name>change_config(servers, Options) -> [{Pid, Result}]</name>
+ <fsummary>Changes configuration for all servers.
+ </fsummary>
<type>
- <v>RemoteFilename = string()</v>
- <v>LocalFilename = binary | string()</v>
<v>Options = [option()]</v>
- <v>LastCallbackState = term()</v>
+ <v>Pid = pid()</v>
+ <v>Result = ok | {error, Reason}</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Reads a (virtual) file <c>RemoteFilename</c> from a TFTP
- server.</p>
- <p>If <c>LocalFilename</c> is the atom <c>binary</c>,
- <c>tftp_binary</c> is used as callback module. It concatenates
- all transferred blocks and returns them as one single binary
- in <c>LastCallbackState</c>.</p>
- <p>If <c>LocalFilename</c> is a string and there are no
- registered callback modules, <c>tftp_file</c> is used as
- callback module. It writes each transferred block to the file
- named <c>LocalFilename</c> and returns the number of
- transferred bytes in <c>LastCallbackState</c>.</p>
- <p>If <c>LocalFilename</c> is a string and there are registered
- callback modules, <c>LocalFilename</c> is tested against
- the regexps of these and the callback module corresponding to
- the first match is used, or an error tuple is returned if no
- matching regexp is found.</p>
+ <p>Changes configuration for all TFTP server processes.</p>
</desc>
-
- <marker id="write_file"></marker>
</func>
<func>
- <name>write_file(RemoteFilename, LocalFilename, Options) -> {ok, LastCallbackState} | {error, Reason}</name>
- <fsummary>Write a (virtual) file to a TFTP server</fsummary>
+ <name>change_config(Pid, Options) -> Result</name>
+ <fsummary>Changes configuration for a TFTP daemon, server,
+ or client process.</fsummary>
<type>
- <v>RemoteFilename = string()</v>
- <v>LocalFilename = binary() | string()</v>
+ <v>Pid = pid()</v>
<v>Options = [option()]</v>
- <v>LastCallbackState = term()</v>
+ <v>Result = ok | {error, Reason}</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Writes a (virtual) file <c>RemoteFilename</c> to a TFTP
- server.</p>
- <p>If <c>LocalFilename</c> is a binary, <c>tftp_binary</c> is
- used as callback module. The binary is transferred block by
- block and the number of transferred bytes is returned in
- <c>LastCallbackState</c>.</p>
- <p>If <c>LocalFilename</c> is a string and there are no
- registered callback modules, <c>tftp_file</c> is used as
- callback module. It reads the file named <c>LocalFilename</c>
- block by block and returns the number of transferred bytes
- in <c>LastCallbackState</c>.</p>
- <p>If <c>LocalFilename</c> is a string and there are registered
- callback modules, <c>LocalFilename</c> is tested against
- the regexps of these and the callback module corresponding to
- the first match is used, or an error tuple is returned if no
- matching regexp is found.</p>
-
- <marker id="info_daemons"></marker>
+ <p>Changes configuration for a TFTP daemon, server, or client process.</p>
</desc>
</func>
-
+
<func>
<name>info(daemons) -> [{Pid, Options}]</name>
- <fsummary>Return information about all daemons</fsummary>
+ <fsummary>Returns information about all daemons.</fsummary>
<type>
<v>Pid = [pid()()]</v>
<v>Options = [option()]</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Returns info about all TFTP daemon processes. </p>
-
- <marker id="info_servers"></marker>
+ <p>Returns information about all TFTP daemon processes.</p>
</desc>
</func>
<func>
<name>info(servers) -> [{Pid, Options}]</name>
- <fsummary>Return information about all servers</fsummary>
+ <fsummary>Returns information about all servers.</fsummary>
<type>
<v>Pid = [pid()()]</v>
<v>Options = [option()]</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Returns info about all TFTP server processes. </p>
-
- <marker id="info_pid"></marker>
+ <p>Returns information about all TFTP server processes. </p>
</desc>
</func>
<func>
<name>info(Pid) -> {ok, Options} | {error, Reason}</name>
- <fsummary>Return information about a daemon, server or client process</fsummary>
+ <fsummary>Returns information about a daemon, server, or client process.</fsummary>
<type>
<v>Options = [option()]</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Returns info about a TFTP daemon, server or client process.</p>
-
- <marker id="change_config_daemons"></marker>
+ <p>Returns information about a TFTP daemon, server, or client process.</p>
</desc>
</func>
-
- <func>
- <name>change_config(daemons, Options) -> [{Pid, Result}]</name>
- <fsummary>Changes config for all daemons
- </fsummary>
+
+ <func>
+ <name>read_file(RemoteFilename, LocalFilename, Options) -> {ok, LastCallbackState} | {error, Reason}</name>
+ <fsummary>Reads a (virtual) file from a TFTP server.</fsummary>
<type>
+ <v>RemoteFilename = string()</v>
+ <v>LocalFilename = binary | string()</v>
<v>Options = [option()]</v>
- <v>Pid = pid()</v>
- <v>Result = ok | {error, Reason}</v>
+ <v>LastCallbackState = term()</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Changes config for all TFTP daemon processes. </p>
-
- <marker id="change_config_servers"></marker>
- </desc>
+ <p>Reads a (virtual) file <c>RemoteFilename</c> from a TFTP
+ server.</p>
+ <p>If <c>LocalFilename</c> is the atom <c>binary</c>,
+ <c>tftp_binary</c> is used as callback module. It concatenates
+ all transferred blocks and returns them as one single binary
+ in <c>LastCallbackState</c>.</p>
+ <p>If <c>LocalFilename</c> is a string and there are no
+ registered callback modules, <c>tftp_file</c> is used as
+ callback module. It writes each transferred block to the file
+ named <c>LocalFilename</c> and returns the number of
+ transferred bytes in <c>LastCallbackState</c>.</p>
+ <p>If <c>LocalFilename</c> is a string and there are registered
+ callback modules, <c>LocalFilename</c> is tested against
+ the regexps of these and the callback module corresponding to
+ the first match is used, or an error tuple is returned if no
+ matching regexp is found.</p>
+ </desc>
</func>
-
+
<func>
- <name>change_config(servers, Options) -> [{Pid, Result}]</name>
- <fsummary>Changes config for all servers
- </fsummary>
+ <name>start(Options) -> {ok, Pid} | {error, Reason}</name>
+ <fsummary>Starts a daemon process.</fsummary>
<type>
<v>Options = [option()]</v>
<v>Pid = pid()</v>
- <v>Result = ok | {error, Reason}</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Changes config for all TFTP server processes. </p>
-
- <marker id="change_config_pid"></marker>
+ <p>Starts a daemon process listening for UDP packets on a
+ port. When it receives a request for read or write, it spawns
+ a temporary server process handling the actual transfer
+ of the (virtual) file.</p>
</desc>
</func>
<func>
- <name>change_config(Pid, Options) -> Result</name>
- <fsummary>Changes config for a TFTP daemon, server or client process</fsummary>
+ <name>write_file(RemoteFilename, LocalFilename, Options) -> {ok, LastCallbackState} | {error, Reason}</name>
+ <fsummary>Writes a (virtual) file to a TFTP server.</fsummary>
<type>
- <v>Pid = pid()</v>
+ <v>RemoteFilename = string()</v>
+ <v>LocalFilename = binary() | string()</v>
<v>Options = [option()]</v>
- <v>Result = ok | {error, Reason}</v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>Changes config for a TFTP daemon, server or client process</p>
-
- <marker id="start2"></marker>
- </desc>
- </func>
-
- <func>
- <name>start() -> ok | {error, Reason}</name>
- <fsummary>Start the Inets application</fsummary>
- <type>
+ <v>LastCallbackState = term()</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Starts the Inets application.</p>
+ <p>Writes a (virtual) file <c>RemoteFilename</c> to a TFTP
+ server.</p>
+ <p>If <c>LocalFilename</c> is a binary, <c>tftp_binary</c> is
+ used as callback module. The binary is transferred block by
+ block and the number of transferred bytes is returned in
+ <c>LastCallbackState</c>.</p>
+ <p>If <c>LocalFilename</c> is a string and there are no
+ registered callback modules, <c>tftp_file</c> is used as
+ callback module. It reads the file named <c>LocalFilename</c>
+ block by block and returns the number of transferred bytes
+ in <c>LastCallbackState</c>.</p>
+ <p>If <c>LocalFilename</c> is a string and there are registered
+ callback modules, <c>LocalFilename</c> is tested against
+ the regexps of these and the callback module corresponding to
+ the first match is used, or an error tuple is returned if no
+ matching regexp is found.</p>
</desc>
</func>
</funcs>
@@ -414,40 +382,41 @@
<section>
<marker id="tftp_callback"></marker>
<title>CALLBACK FUNCTIONS</title>
- <p>A <c>tftp</c> callback module should be implemented as a
- <c>tftp</c> behavior and export the functions listed below.</p>
- <p>On the server side the callback interaction starts with a call to
+ <p>A <c>tftp</c> callback module is to be implemented as a
+ <c>tftp</c> behavior and export the functions listed
+ in the following.</p>
+ <p>On the server side, the callback interaction starts with a call to
<c>open/5</c> with the registered initial callback state.
<c>open/5</c> is expected to open the (virtual) file. Then either
- the <c>read/1</c> or <c>write/2</c> functions are invoked
- repeatedly, once per transferred block. At each function call
+ function <c>read/1</c> or <c>write/2</c> is invoked
+ repeatedly, once per transferred block. At each function call,
the state returned from the previous call is obtained. When
- the last block has been encountered the <c>read/1</c> or
- <c>write/2</c> functions is expected to close the (virtual) file
- and return its last state. The <c>abort/3</c> function is only
- used in error situations. <c>prepare/5</c> is not used on
+ the last block is encountered, function <c>read/1</c> or
+ <c>write/2</c> is expected to close the (virtual) file
+ and return its last state. Function <c>abort/3</c> is only
+ used in error situations. Function <c>prepare/5</c> is not used on
the server side.</p>
- <p>On the client side the callback interaction is the same, but it
+ <p>On the client side, the callback interaction is the same, but it
starts and ends a bit differently. It starts with a call to
<c>prepare/5</c> with the same arguments as <c>open/5</c> takes.
- <c>prepare/5</c> is expected to validate the TFTP options,
- suggested by the user and return the subset of them that it
- accepts. Then the options is sent to the server which will perform
+ <c>prepare/5</c> is expected to validate the TFTP options
+ suggested by the user and to return the subset of them that it
+ accepts. Then the options are sent to the server, which performs
the same TFTP option negotiation procedure. The options that are
- accepted by the server are forwarded to the <c>open/5</c> function
- on the client side. On the client side the <c>open/5</c> function
- must accept all option as is or reject the transfer. Then
+ accepted by the server are forwarded to function <c>open/5</c>
+ on the client side. On the client side, function <c>open/5</c>
+ must accept all option as-is or reject the transfer. Then
the callback interaction follows the same pattern as described
- above for the server side. When the last block is encountered in
- <c>read/1</c> or <c>write/2</c> the returned state is forwarded to
+ for the server side. When the last block is encountered in
+ <c>read/1</c> or <c>write/2</c>, the returned state is forwarded to
the user and returned from <c>read_file</c>/3 or
<c>write_file/3</c>.</p>
- <p> If a callback (which performs the file access
+ <p> If a callback (performing the file access
in the TFTP server) takes too long time (more than
- the double TFTP timeout), the server will abort the
- connection and send an error reply to the client.
- This implies that the server will release resources
+ the double TFTP time-out), the server aborts the
+ connection and sends an error reply to the client.
+ This implies that the server releases resources
attached to the connection faster than before. The
server simply assumes that the client has given
up.</p>
@@ -455,21 +424,45 @@
<p>If the TFTP server receives yet another request from
the same client (same host and port) while it
already has an active connection to the client, it
- will simply ignore the new request if the request is
- equal with the first one (same filename and options).
+ ignores the new request if the request is
+ equal to the first one (same filename and options).
This implies that the (new) client will be served
by the already ongoing connection on the server
side. By not setting up yet another connection, in
- parallel with the ongoing one, the server will
- consumer lesser resources. </p>
+ parallel with the ongoing one, the server
+ consumes less resources.</p>
<marker id="prepare"></marker>
</section>
<funcs>
- <func>
- <name>prepare(Peer, Access, Filename, Mode, SuggestedOptions, InitialState) -> {ok, AcceptedOptions, NewState} | {error, {Code, Text}}</name>
- <fsummary>Prepare to open a file on the client side</fsummary>
+ <func>
+ <name>Module:abort(Code, Text, State) -> ok</name>
+ <fsummary>Aborts the file transfer.</fsummary>
+ <type>
+ <v>Code = undef | enoent | eacces | enospc</v>
+ <v>&nbsp;&nbsp;| badop | eexist | baduser | badopt</v>
+ <v>&nbsp;&nbsp;| int()</v>
+ <v>Text = string()</v>
+ <v>State = term()</v>
+ </type>
+ <desc>
+ <p>Invoked when the file transfer is aborted.</p>
+ <p>The callback function is expected to clean
+ up its used resources after the aborted file
+ transfer, such as closing open file
+ descriptors and so on. The function is not
+ invoked if any of the other callback
+ functions returns an error, as it is
+ expected that they already have cleaned up
+ the necessary resources. However, it is
+ invoked if the functions fail (crash).</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:open(Peer, Access, Filename, Mode, SuggestedOptions, State) -> {ok, AcceptedOptions, NewState} | {error, {Code, Text}}</name>
+ <fsummary>Opens a file for read or write access.</fsummary>
<type>
<v>Peer = {PeerType, PeerHost, PeerPort}</v>
<v>PeerType = inet | inet6</v>
@@ -480,7 +473,8 @@
<v>Mode = string()</v>
<v>SuggestedOptions = AcceptedOptions = [{Key, Value}]</v>
<v>&nbsp;Key = Value = string()</v>
- <v>InitialState = [] | [{root_dir, string()}]</v>
+ <v>State = InitialState | term()</v>
+ <v>&nbsp;InitialState = [] | [{root_dir, string()}]</v>
<v>NewState = term()</v>
<v>Code = undef | enoent | eacces | enospc</v>
<v>&nbsp;&nbsp;| badop | eexist | baduser | badopt</v>
@@ -488,23 +482,22 @@
<v>Text = string()</v>
</type>
<desc>
- <p>Prepares to open a file on the client side.</p>
- <p>No new options may be added, but the ones that are present in
- <c>SuggestedOptions</c> may be omitted or replaced with new
- values in <c>AcceptedOptions</c>.</p>
- <p>Will be followed by a call to <c>open/4</c> before any
- read/write access is performed. <c>AcceptedOptions</c> is
- sent to the server which replies with those options that it
- accepts. These will be forwarded to <c>open/4</c> as
- <c>SuggestedOptions</c>.</p>
+ <p>Opens a file for read or write access.</p>
+ <p>On the client side, where the <c>open/5</c> call has been
+ preceded by a call to <c>prepare/5</c>, all options must be
+ accepted or rejected.</p>
+ <p>On the server side, where there is no preceding
+ <c>prepare/5</c> call, no new options can be added, but
+ those present in <c>SuggestedOptions</c> can be
+ omitted or replaced with new values in <c>AcceptedOptions</c>.</p>
- <marker id="open"></marker>
+ <marker id="read"></marker>
</desc>
</func>
-
+
<func>
- <name>open(Peer, Access, Filename, Mode, SuggestedOptions, State) -> {ok, AcceptedOptions, NewState} | {error, {Code, Text}}</name>
- <fsummary>Open a file for read or write access</fsummary>
+ <name>Module:prepare(Peer, Access, Filename, Mode, SuggestedOptions, InitialState) -> {ok, AcceptedOptions, NewState} | {error, {Code, Text}}</name>
+ <fsummary>Prepares to open a file on the client side.</fsummary>
<type>
<v>Peer = {PeerType, PeerHost, PeerPort}</v>
<v>PeerType = inet | inet6</v>
@@ -515,8 +508,7 @@
<v>Mode = string()</v>
<v>SuggestedOptions = AcceptedOptions = [{Key, Value}]</v>
<v>&nbsp;Key = Value = string()</v>
- <v>State = InitialState | term()</v>
- <v>&nbsp;InitialState = [] | [{root_dir, string()}]</v>
+ <v>InitialState = [] | [{root_dir, string()}]</v>
<v>NewState = term()</v>
<v>Code = undef | enoent | eacces | enospc</v>
<v>&nbsp;&nbsp;| badop | eexist | baduser | badopt</v>
@@ -524,22 +516,23 @@
<v>Text = string()</v>
</type>
<desc>
- <p>Opens a file for read or write access.</p>
- <p>On the client side where the <c>open/5</c> call has been
- preceded by a call to <c>prepare/5</c>, all options must be
- accepted or rejected.</p>
- <p>On the server side, where there is no preceding
- <c>prepare/5</c> call, no new options may be added, but
- the ones that are present in <c>SuggestedOptions</c> may be
- omitted or replaced with new values in <c>AcceptedOptions</c>.</p>
+ <p>Prepares to open a file on the client side.</p>
+ <p>No new options can be added, but those present in
+ <c>SuggestedOptions</c> can be omitted or replaced with new
+ values in <c>AcceptedOptions</c>.</p>
+ <p>This is followed by a call to <c>open/4</c> before any
+ read/write access is performed. <c>AcceptedOptions</c> is
+ sent to the server, which replies with the options that it
+ accepts. These are then forwarded to <c>open/4</c> as
+ <c>SuggestedOptions</c>.</p>
- <marker id="read"></marker>
+ <marker id="open"></marker>
</desc>
</func>
<func>
- <name>read(State) -> {more, Bin, NewState} | {last, Bin, FileSize} | {error, {Code, Text}}</name>
- <fsummary>Read a chunk from the file</fsummary>
+ <name>Module:read(State) -> {more, Bin, NewState} | {last, Bin, FileSize} | {error, {Code, Text}}</name>
+ <fsummary>Reads a chunk from the file.</fsummary>
<type>
<v>State = NewState = term()</v>
<v>Bin = binary()</v>
@@ -550,13 +543,13 @@
<v>Text = string()</v>
</type>
<desc>
- <p>Read a chunk from the file.</p>
+ <p>Reads a chunk from the file.</p>
<p>The callback function is expected to close
the file when the last file chunk is
- encountered. When an error is encountered
+ encountered. When an error is encountered,
the callback function is expected to clean
up after the aborted file transfer, such as
- closing open file descriptors etc. In both
+ closing open file descriptors, and so on. In both
cases there will be no more calls to any of
the callback functions.</p>
@@ -565,8 +558,8 @@
</func>
<func>
- <name>write(Bin, State) -> {more, NewState} | {last, FileSize} | {error, {Code, Text}}</name>
- <fsummary>Write a chunk to the file</fsummary>
+ <name>Module:write(Bin, State) -> {more, NewState} | {last, FileSize} | {error, {Code, Text}}</name>
+ <fsummary>Writes a chunk to the file.</fsummary>
<type>
<v>Bin = binary()</v>
<v>State = NewState = term()</v>
@@ -577,99 +570,75 @@
<v>Text = string()</v>
</type>
<desc>
- <p>Write a chunk to the file.</p>
+ <p>Writes a chunk to the file.</p>
<p>The callback function is expected to close
the file when the last file chunk is
- encountered. When an error is encountered
+ encountered. When an error is encountered,
the callback function is expected to clean
up after the aborted file transfer, such as
- closing open file descriptors etc. In both
+ closing open file descriptors, and so on. In both
cases there will be no more calls to any of
the callback functions.</p>
<marker id="abort"></marker>
</desc>
</func>
-
- <func>
- <name>abort(Code, Text, State) -> ok</name>
- <fsummary>Abort the file transfer</fsummary>
- <type>
- <v>Code = undef | enoent | eacces | enospc</v>
- <v>&nbsp;&nbsp;| badop | eexist | baduser | badopt</v>
- <v>&nbsp;&nbsp;| int()</v>
- <v>Text = string()</v>
- <v>State = term()</v>
- </type>
- <desc>
- <p>Invoked when the file transfer is aborted.</p>
- <p>The callback function is expected to clean
- up its used resources after the aborted file
- transfer, such as closing open file
- descriptors etc. The function will not be
- invoked if any of the other callback
- functions returns an error, as it is
- expected that they already have cleaned up
- the necessary resources. It will however be
- invoked if the functions fails (crashes).</p>
- </desc>
- </func>
</funcs>
<section>
<marker id="tftp_logger"></marker>
<title>LOGGER FUNCTIONS</title>
- <p>A <c>tftp_logger</c> callback module should be implemented as a
- <c>tftp_logger</c> behavior and export the functions listed below.</p>
+ <p>A <c>tftp_logger</c> callback module is to be implemented as a
+ <c>tftp_logger</c> behavior and export the following functions:</p>
<marker id="error_msg"></marker>
</section>
<funcs>
<func>
- <name>error_msg(Format, Data) -> ok | exit(Reason)</name>
- <fsummary>Log an error message</fsummary>
+ <name>Logger:error_msg(Format, Data) -> ok | exit(Reason)</name>
+ <fsummary>Logs an error message.</fsummary>
<type>
<v>Format = string()</v>
<v>Data = [term()]</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Log an error message.
- See <c>error_logger:error_msg/2 for details.</c> </p>
+ <p>Logs an error message.
+ See <c>error_logger:error_msg/2</c> for details.</p>
<marker id="warning_msg"></marker>
</desc>
</func>
<func>
- <name>warning_msg(Format, Data) -> ok | exit(Reason)</name>
- <fsummary>Log an error message</fsummary>
+ <name>Logger:info_msg(Format, Data) -> ok | exit(Reason)</name>
+ <fsummary>Logs an info message.</fsummary>
<type>
<v>Format = string()</v>
<v>Data = [term()]</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Log a warning message.
- See <c>error_logger:warning_msg/2 for details.</c> </p>
-
- <marker id="info_msg"></marker>
+ <p>Logs an info message.
+ See <c>error_logger:info_msg/2</c> for details.</p>
</desc>
</func>
-
+
<func>
- <name>info_msg(Format, Data) -> ok | exit(Reason)</name>
- <fsummary>Log an error message</fsummary>
+ <name>Logger:warning_msg(Format, Data) -> ok | exit(Reason)</name>
+ <fsummary>Logs a warning message.</fsummary>
<type>
<v>Format = string()</v>
<v>Data = [term()]</v>
<v>Reason = term()</v>
</type>
<desc>
- <p>Log an info message.
- See <c>error_logger:info_msg/2 for details.</c> </p>
+ <p>Logs a warning message.
+ See <c>error_logger:warning_msg/2</c> for details.</p>
+
+ <marker id="info_msg"></marker>
</desc>
</func>
</funcs>
diff --git a/lib/inets/examples/Makefile b/lib/inets/examples/Makefile
index 775c449062..7892185bd4 100644
--- a/lib/inets/examples/Makefile
+++ b/lib/inets/examples/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010. All Rights Reserved.
-#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
+# Copyright Ericsson AB 2010-2016. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
diff --git a/lib/inets/examples/httpd_load_test/Makefile b/lib/inets/examples/httpd_load_test/Makefile
index abd2d8c35d..8b1b654593 100644
--- a/lib/inets/examples/httpd_load_test/Makefile
+++ b/lib/inets/examples/httpd_load_test/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2012. All Rights Reserved.
+# Copyright Ericsson AB 2010-2016. 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/.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# 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.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
diff --git a/lib/inets/examples/httpd_load_test/hdlt.erl b/lib/inets/examples/httpd_load_test/hdlt.erl
index 18d8c34ccf..f4b81c8102 100644
--- a/lib/inets/examples/httpd_load_test/hdlt.erl
+++ b/lib/inets/examples/httpd_load_test/hdlt.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/examples/httpd_load_test/hdlt.sh.skel b/lib/inets/examples/httpd_load_test/hdlt.sh.skel
index a250bad9c5..1797a53d1d 100644
--- a/lib/inets/examples/httpd_load_test/hdlt.sh.skel
+++ b/lib/inets/examples/httpd_load_test/hdlt.sh.skel
@@ -3,18 +3,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010. All Rights Reserved.
+# Copyright Ericsson AB 2010-2016. 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/.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# 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.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
diff --git a/lib/inets/examples/httpd_load_test/hdlt_client.erl b/lib/inets/examples/httpd_load_test/hdlt_client.erl
index d65ac5a885..8e929fbf2b 100644
--- a/lib/inets/examples/httpd_load_test/hdlt_client.erl
+++ b/lib/inets/examples/httpd_load_test/hdlt_client.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/examples/httpd_load_test/hdlt_ctrl.erl b/lib/inets/examples/httpd_load_test/hdlt_ctrl.erl
index 950d2632f7..fcf5b4505f 100644
--- a/lib/inets/examples/httpd_load_test/hdlt_ctrl.erl
+++ b/lib/inets/examples/httpd_load_test/hdlt_ctrl.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/examples/httpd_load_test/hdlt_logger.erl b/lib/inets/examples/httpd_load_test/hdlt_logger.erl
index b0c7eab2d1..29fbaab3ae 100644
--- a/lib/inets/examples/httpd_load_test/hdlt_logger.erl
+++ b/lib/inets/examples/httpd_load_test/hdlt_logger.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/examples/httpd_load_test/hdlt_logger.hrl b/lib/inets/examples/httpd_load_test/hdlt_logger.hrl
index aa94babc48..ac03df2e22 100644
--- a/lib/inets/examples/httpd_load_test/hdlt_logger.hrl
+++ b/lib/inets/examples/httpd_load_test/hdlt_logger.hrl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/examples/httpd_load_test/hdlt_random_html.erl b/lib/inets/examples/httpd_load_test/hdlt_random_html.erl
index e3a572c61f..3b72d27f06 100644
--- a/lib/inets/examples/httpd_load_test/hdlt_random_html.erl
+++ b/lib/inets/examples/httpd_load_test/hdlt_random_html.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -48,7 +49,10 @@ stop() ->
".
content(WorkSim, SzSim) ->
- {A, B, C} = now(),
+ {A, B, C} = {erlang:phash2([node()]),
+ inets_time_compat:monotonic_time(),
+ inets_time_compat:unique_integer()},
+
random:seed(A, B, C),
lists:sort([random:uniform(X) || X <- lists:seq(1, WorkSim)]),
lists:flatten(lists:duplicate(SzSim, "Dummy data ")).
diff --git a/lib/inets/examples/httpd_load_test/hdlt_server.erl b/lib/inets/examples/httpd_load_test/hdlt_server.erl
index 3e5a849d5b..cd454c82cd 100644
--- a/lib/inets/examples/httpd_load_test/hdlt_server.erl
+++ b/lib/inets/examples/httpd_load_test/hdlt_server.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/examples/httpd_load_test/hdlt_slave.erl b/lib/inets/examples/httpd_load_test/hdlt_slave.erl
index 52af9b5b90..5ee005629d 100644
--- a/lib/inets/examples/httpd_load_test/hdlt_slave.erl
+++ b/lib/inets/examples/httpd_load_test/hdlt_slave.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -180,7 +181,7 @@ ssh_slave_start(Host, ErlCmd) ->
?DEBUG("ssh_exec_erl -> done", []),
{ok, Connection, Channel};
Error3 ->
- ?LOG("failed exec comand: ~p", [Error3]),
+ ?LOG("failed exec command: ~p", [Error3]),
throw({error, {ssh_exec_failed, Error3}})
end.
diff --git a/lib/inets/examples/httpd_load_test/modules.mk b/lib/inets/examples/httpd_load_test/modules.mk
index 9d0d7103d5..80974819e9 100644
--- a/lib/inets/examples/httpd_load_test/modules.mk
+++ b/lib/inets/examples/httpd_load_test/modules.mk
@@ -2,18 +2,19 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010. All Rights Reserved.
+# Copyright Ericsson AB 2010-2016. 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/.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# 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.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
diff --git a/lib/inets/examples/server_root/Makefile b/lib/inets/examples/server_root/Makefile
index 7a0ee78ecf..d11389f0a7 100644
--- a/lib/inets/examples/server_root/Makefile
+++ b/lib/inets/examples/server_root/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. 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/.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# 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.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
diff --git a/lib/inets/examples/server_root/conf/8080.conf b/lib/inets/examples/server_root/conf/8080.conf
index 48e66f0114..7b1b4a15b2 100644
--- a/lib/inets/examples/server_root/conf/8080.conf
+++ b/lib/inets/examples/server_root/conf/8080.conf
@@ -1,7 +1,7 @@
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
+Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log
ServerAdmin [email protected]
ServerRoot /var/tmp/server_root
ErrorLog logs/error_log_8080
diff --git a/lib/inets/examples/server_root/conf/8888.conf b/lib/inets/examples/server_root/conf/8888.conf
index 79bb7fcca4..042779fcd0 100644
--- a/lib/inets/examples/server_root/conf/8888.conf
+++ b/lib/inets/examples/server_root/conf/8888.conf
@@ -1,7 +1,7 @@
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
+Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log
ServerAdmin [email protected]
ServerRoot /var/tmp/server_root
ErrorLog logs/error_log_8888
diff --git a/lib/inets/examples/server_root/conf/httpd.conf b/lib/inets/examples/server_root/conf/httpd.conf
index 8a74ed1afd..3f9fde03b5 100644
--- a/lib/inets/examples/server_root/conf/httpd.conf
+++ b/lib/inets/examples/server_root/conf/httpd.conf
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
@@ -63,7 +64,7 @@ SocketType ip_comm
# 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
+Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_responsecontrol mod_trace mod_range mod_head mod_dir mod_get mod_log mod_disk_log
# ServerAdmin: Your address, where problems with the server should be
# e-mailed.
diff --git a/lib/inets/examples/server_root/conf/ssl.conf b/lib/inets/examples/server_root/conf/ssl.conf
index 8b8c57a98b..de49ceafd0 100644
--- a/lib/inets/examples/server_root/conf/ssl.conf
+++ b/lib/inets/examples/server_root/conf/ssl.conf
@@ -1,7 +1,7 @@
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
+Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log
ServerAdmin [email protected]
ServerRoot /var/tmp/server_root
ErrorLog logs/error_log_8088
diff --git a/lib/inets/include/httpd.hrl b/lib/inets/include/httpd.hrl
index a7e63ca670..fb338d5c68 100644
--- a/lib/inets/include/httpd.hrl
+++ b/lib/inets/include/httpd.hrl
@@ -1,18 +1,19 @@
%%
%% %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.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/include/mod_auth.hrl b/lib/inets/include/mod_auth.hrl
index cf931e681a..224b2edb5e 100644
--- a/lib/inets/include/mod_auth.hrl
+++ b/lib/inets/include/mod_auth.hrl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-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.
+%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/priv/Makefile b/lib/inets/priv/Makefile
index acca20f076..ddd35e8625 100644
--- a/lib/inets/priv/Makefile
+++ b/lib/inets/priv/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2012. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
diff --git a/lib/inets/src/Makefile b/lib/inets/src/Makefile
index dd18e92107..281dd388cb 100644
--- a/lib/inets/src/Makefile
+++ b/lib/inets/src/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
diff --git a/lib/inets/src/ftp/Makefile b/lib/inets/src/ftp/Makefile
index 2c3d2b6d13..6b99694ea7 100644
--- a/lib/inets/src/ftp/Makefile
+++ b/lib/inets/src/ftp/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2012. All Rights Reserved.
+# Copyright Ericsson AB 2005-2016. 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/.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# 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.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl
index 132a384a49..bbf25f8e90 100644
--- a/lib/inets/src/ftp/ftp.erl
+++ b/lib/inets/src/ftp/ftp.erl
@@ -1,24 +1,25 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
%% Description: This module implements an ftp client, RFC 959.
-%% It also supports ipv6 RFC 2428.
+%% It also supports ipv6 RFC 2428 and starttls RFC 4217.
-module(ftp).
@@ -39,7 +40,8 @@
send_chunk_start/2, send_chunk/2, send_chunk_end/1,
type/2, user/3, user/4, account/2,
append/3, append/2, append_bin/3,
- append_chunk/2, append_chunk_end/1, append_chunk_start/2, info/1]).
+ append_chunk/2, append_chunk_end/1, append_chunk_start/2,
+ info/1, latest_ctrl_response/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2,
@@ -54,11 +56,12 @@
-include("ftp_internal.hrl").
-%% Constante used in internal state definition
+%% Constants used in internal state definition
-define(CONNECTION_TIMEOUT, 60*1000).
-define(DATA_ACCEPT_TIMEOUT, infinity).
-define(DEFAULT_MODE, passive).
-define(PROGRESS_DEFAULT, ignore).
+-define(FTP_EXT_DEFAULT, false).
%% Internal Constants
-define(FTP_PORT, 21).
@@ -67,7 +70,8 @@
%% Internal state
-record(state, {
csock = undefined, % socket() - Control connection socket
- dsock = undefined, % socket() - Data connection socket
+ dsock = undefined, % socket() - Data connection socket
+ tls_options = undefined, % list()
verbose = false, % boolean()
ldir = undefined, % string() - Current local directory
type = ftp_server_default, % atom() - binary | ascii
@@ -83,6 +87,7 @@
%% and hence the end of the response is reached!
ctrl_data = {<<>>, [], start}, % {binary(), [bytes()], LineStatus}
%% pid() - Client pid (note not the same as "From")
+ latest_ctrl_response = "",
owner = undefined,
client = undefined, % "From" to be used in gen_server:reply/2
%% Function that activated a connection and maybe some
@@ -90,7 +95,9 @@
caller = undefined, % term()
ipfamily, % inet | inet6 | inet6fb4
progress = ignore, % ignore | pid()
- dtimeout = ?DATA_ACCEPT_TIMEOUT % non_neg_integer() | infinity
+ dtimeout = ?DATA_ACCEPT_TIMEOUT, % non_neg_integer() | infinity
+ tls_upgrading_data_connection = false,
+ ftp_extension = ?FTP_EXT_DEFAULT
}).
@@ -99,6 +106,8 @@
-type common_reason() :: 'econn' | 'eclosed' | term().
-type file_write_error_reason() :: term(). % See file:write for more info
+%%-define(DBG(F,A), 'n/a').
+-define(DBG(F,A), io:format(F,A)).
%%%=========================================================================
%%% API - CLIENT FUNCTIONS
@@ -154,8 +163,7 @@ open(Host, Opts) when is_list(Opts) ->
?fcrt("open", [{open_options, OpenOptions}]),
case start_link(StartOptions, []) of
{ok, Pid} ->
- ?fcrt("open - ok", [{pid, Pid}]),
- call(Pid, {open, ip_comm, OpenOptions}, plain);
+ do_open(Pid, OpenOptions, tls_options(Opts));
Error1 ->
?fcrt("open - error", [{error1, Error1}]),
Error1
@@ -166,7 +174,13 @@ open(Host, Opts) when is_list(Opts) ->
Error2
end.
-
+do_open(Pid, OpenOptions, TLSOpts) ->
+ case call(Pid, {open, ip_comm, OpenOptions}, plain) of
+ {ok, Pid} ->
+ maybe_tls_upgrade(Pid, TLSOpts);
+ Error ->
+ Error
+ end.
%%--------------------------------------------------------------------------
%% user(Pid, User, Pass, <Acc>) -> ok | {error, euser} | {error, econn}
%% | {error, eacct}
@@ -181,7 +195,12 @@ open(Host, Opts) when is_list(Opts) ->
'ok' | {'error', Reason :: 'euser' | common_reason()}.
user(Pid, User, Pass) ->
- call(Pid, {user, User, Pass}, atom).
+ case {is_name_sane(User), is_name_sane(Pass)} of
+ {true, true} ->
+ call(Pid, {user, User, Pass}, atom);
+ _ ->
+ {error, euser}
+ end.
-spec user(Pid :: pid(),
User :: string(),
@@ -190,7 +209,12 @@ user(Pid, User, Pass) ->
'ok' | {'error', Reason :: 'euser' | common_reason()}.
user(Pid, User, Pass, Acc) ->
- call(Pid, {user, User, Pass, Acc}, atom).
+ case {is_name_sane(User), is_name_sane(Pass), is_name_sane(Acc)} of
+ {true, true, true} ->
+ call(Pid, {user, User, Pass, Acc}, atom);
+ _ ->
+ {error, euser}
+ end.
%%--------------------------------------------------------------------------
@@ -205,7 +229,12 @@ user(Pid, User, Pass, Acc) ->
'ok' | {'error', Reason :: 'eacct' | common_reason()}.
account(Pid, Acc) ->
- call(Pid, {account, Acc}, atom).
+ case is_name_sane(Acc) of
+ true ->
+ call(Pid, {account, Acc}, atom);
+ _ ->
+ {error, eacct}
+ end.
%%--------------------------------------------------------------------------
@@ -251,7 +280,12 @@ lpwd(Pid) ->
'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
cd(Pid, Dir) ->
- call(Pid, {cd, Dir}, atom).
+ case is_name_sane(Dir) of
+ true ->
+ call(Pid, {cd, Dir}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -294,7 +328,12 @@ ls(Pid) ->
{'error', Reason :: restriction_reason() | common_reason()}.
ls(Pid, Dir) ->
- call(Pid, {dir, long, Dir}, string).
+ case is_name_sane(Dir) of
+ true ->
+ call(Pid, {dir, long, Dir}, string);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -322,7 +361,12 @@ nlist(Pid) ->
{'error', Reason :: restriction_reason() | common_reason()}.
nlist(Pid, Dir) ->
- call(Pid, {dir, short, Dir}, string).
+ case is_name_sane(Dir) of
+ true ->
+ call(Pid, {dir, short, Dir}, string);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -338,7 +382,12 @@ nlist(Pid, Dir) ->
'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
rename(Pid, Old, New) ->
- call(Pid, {rename, Old, New}, string).
+ case {is_name_sane(Old), is_name_sane(New)} of
+ {true, true} ->
+ call(Pid, {rename, Old, New}, string);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -354,7 +403,12 @@ rename(Pid, Old, New) ->
'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
delete(Pid, File) ->
- call(Pid, {delete, File}, string).
+ case is_name_sane(File) of
+ true ->
+ call(Pid, {delete, File}, string);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -369,7 +423,12 @@ delete(Pid, File) ->
'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
mkdir(Pid, Dir) ->
- call(Pid, {mkdir, Dir}, atom).
+ case is_name_sane(Dir) of
+ true ->
+ call(Pid, {mkdir, Dir}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -384,7 +443,12 @@ mkdir(Pid, Dir) ->
'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
rmdir(Pid, Dir) ->
- call(Pid, {rmdir, Dir}, atom).
+ case is_name_sane(Dir) of
+ true ->
+ call(Pid, {rmdir, Dir}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -426,7 +490,12 @@ recv(Pid, RemotFileName) ->
'ok' | {'error', Reason :: term()}.
recv(Pid, RemotFileName, LocalFileName) ->
- call(Pid, {recv, RemotFileName, LocalFileName}, atom).
+ case is_name_sane(RemotFileName) of
+ true ->
+ call(Pid, {recv, RemotFileName, LocalFileName}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -445,7 +514,12 @@ recv(Pid, RemotFileName, LocalFileName) ->
{'error', Reason :: restriction_reason() | common_reason()}.
recv_bin(Pid, RemoteFile) ->
- call(Pid, {recv_bin, RemoteFile}, bin).
+ case is_name_sane(RemoteFile) of
+ true ->
+ call(Pid, {recv_bin, RemoteFile}, bin);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -462,7 +536,12 @@ recv_bin(Pid, RemoteFile) ->
'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
recv_chunk_start(Pid, RemoteFile) ->
- call(Pid, {recv_chunk_start, RemoteFile}, atom).
+ case is_name_sane(RemoteFile) of
+ true ->
+ call(Pid, {recv_chunk_start, RemoteFile}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -510,7 +589,12 @@ send(Pid, LocalFileName) ->
shortage_reason()}.
send(Pid, LocalFileName, RemotFileName) ->
- call(Pid, {send, LocalFileName, RemotFileName}, atom).
+ case is_name_sane(RemotFileName) of
+ true ->
+ call(Pid, {send, LocalFileName, RemotFileName}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -530,7 +614,12 @@ send(Pid, LocalFileName, RemotFileName) ->
shortage_reason()}.
send_bin(Pid, Bin, RemoteFile) when is_binary(Bin) ->
- call(Pid, {send_bin, Bin, RemoteFile}, atom);
+ case is_name_sane(RemoteFile) of
+ true ->
+ call(Pid, {send_bin, Bin, RemoteFile}, atom);
+ _ ->
+ {error, efnamena}
+ end;
send_bin(_Pid, _Bin, _RemoteFile) ->
{error, enotbinary}.
@@ -548,7 +637,12 @@ send_bin(_Pid, _Bin, _RemoteFile) ->
'ok' | {'error', Reason :: restriction_reason() | common_reason()}.
send_chunk_start(Pid, RemoteFile) ->
- call(Pid, {send_chunk_start, RemoteFile}, atom).
+ case is_name_sane(RemoteFile) of
+ true ->
+ call(Pid, {send_chunk_start, RemoteFile}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -564,7 +658,12 @@ send_chunk_start(Pid, RemoteFile) ->
'ok' | {'error', Reason :: term()}.
append_chunk_start(Pid, RemoteFile) ->
- call(Pid, {append_chunk_start, RemoteFile}, atom).
+ case is_name_sane(RemoteFile) of
+ true ->
+ call(Pid, {append_chunk_start, RemoteFile}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -672,7 +771,12 @@ append(Pid, LocalFileName) ->
'ok' | {'error', Reason :: term()}.
append(Pid, LocalFileName, RemotFileName) ->
- call(Pid, {append, LocalFileName, RemotFileName}, atom).
+ case is_name_sane(RemotFileName) of
+ true ->
+ call(Pid, {append, LocalFileName, RemotFileName}, atom);
+ _ ->
+ {error, efnamena}
+ end.
%%--------------------------------------------------------------------------
@@ -694,7 +798,12 @@ append(Pid, LocalFileName, RemotFileName) ->
shortage_reason()}.
append_bin(Pid, Bin, RemoteFile) when is_binary(Bin) ->
- call(Pid, {append_bin, Bin, RemoteFile}, atom);
+ case is_name_sane(RemoteFile) of
+ true ->
+ call(Pid, {append_bin, Bin, RemoteFile}, atom);
+ _ ->
+ {error, efnamena}
+ end;
append_bin(_Pid, _Bin, _RemoteFile) ->
{error, enotbinary}.
@@ -744,6 +853,18 @@ info(Pid) ->
call(Pid, info, list).
+%%--------------------------------------------------------------------------
+%% latest_ctrl_response(Pid) -> string()
+%% Pid = pid()
+%%
+%% Description: The latest received response from the server
+%%--------------------------------------------------------------------------
+
+-spec latest_ctrl_response(Pid :: pid()) -> string().
+
+latest_ctrl_response(Pid) ->
+ call(Pid, latest_ctrl_response, string).
+
%%%========================================================================
%%% Behavior callbacks
%%%========================================================================
@@ -851,6 +972,8 @@ start_options(Options) ->
%% timeout
%% dtimeout
%% progress
+%% ftp_extension
+
open_options(Options) ->
?fcrt("open_options", [{options, Options}]),
ValidateMode =
@@ -895,6 +1018,11 @@ open_options(Options) ->
(_) ->
false
end,
+ ValidateFtpExtension =
+ fun(true) -> true;
+ (false) -> true;
+ (_) -> false
+ end,
ValidOptions =
[{mode, ValidateMode, false, ?DEFAULT_MODE},
{host, ValidateHost, true, ehost},
@@ -902,9 +1030,14 @@ open_options(Options) ->
{ipfamily, ValidateIpFamily, false, inet},
{timeout, ValidateTimeout, false, ?CONNECTION_TIMEOUT},
{dtimeout, ValidateDTimeout, false, ?DATA_ACCEPT_TIMEOUT},
- {progress, ValidateProgress, false, ?PROGRESS_DEFAULT}],
+ {progress, ValidateProgress, false, ?PROGRESS_DEFAULT},
+ {ftp_extension, ValidateFtpExtension, false, ?FTP_EXT_DEFAULT}],
validate_options(Options, ValidOptions, []).
+tls_options(Options) ->
+ %% Options will be validated by ssl application
+ proplists:get_value(tls, Options, undefined).
+
validate_options([], [], Acc) ->
?fcrt("validate_options -> done", [{acc, Acc}]),
{ok, lists:reverse(Acc)};
@@ -1021,8 +1154,8 @@ handle_call({_, info}, _, #state{verbose = Verbose,
ipfamily = IpFamily,
csock = Socket,
progress = Progress} = State) ->
- {ok, {_, LocalPort}} = inet:sockname(Socket),
- {ok, {Address, Port}} = inet:peername(Socket),
+ {ok, {_, LocalPort}} = sockname(Socket),
+ {ok, {Address, Port}} = peername(Socket),
Options = [{verbose, Verbose},
{ipfamily, IpFamily},
{mode, Mode},
@@ -1033,6 +1166,9 @@ handle_call({_, info}, _, #state{verbose = Verbose,
{progress, Progress}],
{reply, {ok, Options}, State};
+handle_call({_,latest_ctrl_response}, _, #state{latest_ctrl_response=Resp} = State) ->
+ {reply, {ok,Resp}, State};
+
%% But everything else must come from the owner
handle_call({Pid, _}, _, #state{owner = Owner} = State) when Owner =/= Pid ->
{reply, {error, not_connection_owner}, State};
@@ -1049,12 +1185,14 @@ handle_call({_, {open, ip_comm, Opts}}, From, State) ->
DTimeout = key_search(dtimeout, Opts, ?DATA_ACCEPT_TIMEOUT),
Progress = key_search(progress, Opts, ignore),
IpFamily = key_search(ipfamily, Opts, inet),
+ FtpExt = key_search(ftp_extension, Opts, ?FTP_EXT_DEFAULT),
State2 = State#state{client = From,
mode = Mode,
progress = progress(Progress),
ipfamily = IpFamily,
- dtimeout = DTimeout},
+ dtimeout = DTimeout,
+ ftp_extension = FtpExt},
?fcrd("handle_call(open) -> setup ctrl connection with",
[{host, Host}, {port, Port}, {timeout, Timeout}]),
@@ -1077,11 +1215,13 @@ handle_call({_, {open, ip_comm, Host, Opts}}, From, State) ->
Timeout = key_search(timeout, Opts, ?CONNECTION_TIMEOUT),
DTimeout = key_search(dtimeout, Opts, ?DATA_ACCEPT_TIMEOUT),
Progress = key_search(progress, Opts, ignore),
+ FtpExt = key_search(ftp_extension, Opts, ?FTP_EXT_DEFAULT),
State2 = State#state{client = From,
mode = Mode,
progress = progress(Progress),
- dtimeout = DTimeout},
+ dtimeout = DTimeout,
+ ftp_extension = FtpExt},
case setup_ctrl_connection(Host, Port, Timeout, State2) of
{ok, State3, WaitTimeout} ->
@@ -1091,6 +1231,11 @@ handle_call({_, {open, ip_comm, Host, Opts}}, From, State) ->
{stop, normal, State2#state{client = undefined}}
end;
+handle_call({_, {open, tls_upgrade, TLSOptions}}, From, State) ->
+ send_ctrl_message(State, mk_cmd("AUTH TLS", [])),
+ activate_ctrl_connection(State),
+ {noreply, State#state{client = From, caller = open, tls_options = TLSOptions}};
+
handle_call({_, {user, User, Password}}, From,
#state{csock = CSock} = State) when (CSock =/= undefined) ->
handle_user(User, Password, "", State#state{client = From});
@@ -1196,8 +1341,8 @@ handle_call({_,{recv_chunk_start, RemoteFile}}, From, #state{chunk = false}
handle_call({_, recv_chunk}, _, #state{chunk = false} = State) ->
{reply, {error, "ftp:recv_chunk_start/2 not called"}, State};
-handle_call({_, recv_chunk}, From, #state{chunk = true} = State) ->
- activate_data_connection(State),
+handle_call({_, recv_chunk}, From, #state{chunk = true} = State0) ->
+ State = activate_data_connection(State0),
{noreply, State#state{client = From, caller = recv_chunk}};
handle_call({_, {send, LocalFile, RemoteFile}}, From,
@@ -1238,12 +1383,18 @@ handle_call({_, {transfer_chunk, Bin}}, _, #state{chunk = true} = State) ->
send_data_message(State, Bin),
{reply, ok, State};
+handle_call({_, {transfer_chunk, _}}, _, #state{chunk = false} = State) ->
+ {reply, {error, echunk}, State};
+
handle_call({_, chunk_end}, From, #state{chunk = true} = State) ->
close_data_connection(State),
activate_ctrl_connection(State),
{noreply, State#state{client = From, dsock = undefined,
caller = end_chunk_transfer, chunk = false}};
+handle_call({_, chunk_end}, _, #state{chunk = false} = State) ->
+ {reply, {error, echunk}, State};
+
handle_call({_, {quote, Cmd}}, From, #state{chunk = false} = State) ->
send_ctrl_message(State, mk_cmd(Cmd, [])),
activate_ctrl_connection(State),
@@ -1299,71 +1450,77 @@ handle_info(timeout, State) ->
{noreply, State};
%%% Data socket messages %%%
-handle_info({tcp, Socket, Data},
- #state{dsock = Socket,
- caller = {recv_file, Fd}} = State) ->
+handle_info({Trpt, Socket, Data},
+ #state{dsock = {Trpt,Socket},
+ caller = {recv_file, Fd}} = State0) when Trpt==tcp;Trpt==ssl ->
+ ?DBG('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State0]),
file_write(binary_to_list(Data), Fd),
- progress_report({binary, Data}, State),
- activate_data_connection(State),
+ progress_report({binary, Data}, State0),
+ State = activate_data_connection(State0),
{noreply, State};
-handle_info({tcp, Socket, Data}, #state{dsock = Socket, client = From,
+handle_info({Trpt, Socket, Data}, #state{dsock = {Trpt,Socket}, client = From,
caller = recv_chunk}
- = State) ->
+ = State) when Trpt==tcp;Trpt==ssl ->
+ ?DBG('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State]),
gen_server:reply(From, {ok, Data}),
{noreply, State#state{client = undefined, data = <<>>}};
-handle_info({tcp, Socket, Data}, #state{dsock = Socket} = State) ->
- activate_data_connection(State),
+handle_info({Trpt, Socket, Data}, #state{dsock = {Trpt,Socket}} = State0) when Trpt==tcp;Trpt==ssl ->
+ ?DBG('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State0]),
+ State = activate_data_connection(State0),
{noreply, State#state{data = <<(State#state.data)/binary,
Data/binary>>}};
-handle_info({tcp_closed, Socket}, #state{dsock = Socket,
+handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket},
caller = {recv_file, Fd}}
- = State) ->
+ = State) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
file_close(Fd),
progress_report({transfer_size, 0}, State),
activate_ctrl_connection(State),
{noreply, State#state{dsock = undefined, data = <<>>}};
-handle_info({tcp_closed, Socket}, #state{dsock = Socket, client = From,
+handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, client = From,
caller = recv_chunk}
- = State) ->
+ = State) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
gen_server:reply(From, ok),
{noreply, State#state{dsock = undefined, client = undefined,
data = <<>>, caller = undefined,
chunk = false}};
-handle_info({tcp_closed, Socket}, #state{dsock = Socket, caller = recv_bin,
- data = Data} = State) ->
+handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, caller = recv_bin,
+ data = Data} = State)
+ when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
activate_ctrl_connection(State),
{noreply, State#state{dsock = undefined, data = <<>>,
caller = {recv_bin, Data}}};
-handle_info({tcp_closed, Socket}, #state{dsock = Socket, data = Data,
+handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, data = Data,
caller = {handle_dir_result, Dir}}
- = State) ->
+ = State) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
activate_ctrl_connection(State),
{noreply, State#state{dsock = undefined,
caller = {handle_dir_result, Dir, Data},
% data = <<?CR,?LF>>}};
data = <<>>}};
-
-handle_info({tcp_error, Socket, Reason}, #state{dsock = Socket,
- client = From} = State) ->
+
+handle_info({Err, Socket, Reason}, #state{dsock = {Trpt,Socket},
+ client = From} = State)
+ when {Err,Trpt}=={tcp_error,tcp} ; {Err,Trpt}=={ssl_error,ssl} ->
gen_server:reply(From, {error, Reason}),
close_data_connection(State),
{noreply, State#state{dsock = undefined, client = undefined,
data = <<>>, caller = undefined, chunk = false}};
%%% Ctrl socket messages %%%
-handle_info({tcp, Socket, Data}, #state{csock = Socket,
- verbose = Verbose,
- caller = Caller,
- client = From,
- ctrl_data = {CtrlData, AccLines,
- LineStatus}}
+handle_info({Transport, Socket, Data}, #state{csock = {Transport, Socket},
+ verbose = Verbose,
+ caller = Caller,
+ client = From,
+ ctrl_data = {CtrlData, AccLines,
+ LineStatus}}
= State) ->
+ ?DBG('--ctrl ~p ----> ~s~p~n',[Socket,<<CtrlData/binary, Data/binary>>,State]),
case ftp_response:parse_lines(<<CtrlData/binary, Data/binary>>,
AccLines, LineStatus) of
{ok, Lines, NextMsgData} ->
@@ -1374,27 +1531,32 @@ handle_info({tcp, Socket, Data}, #state{csock = Socket,
gen_server:reply(From, string:tokens(Lines, [?CR, ?LF])),
{noreply, State#state{client = undefined,
caller = undefined,
+ latest_ctrl_response = Lines,
ctrl_data = {NextMsgData, [],
start}}};
_ ->
+ ?DBG(' ...handle_ctrl_result(~p,...) ctrl_data=~p~n',[CtrlResult,{NextMsgData, [], start}]),
handle_ctrl_result(CtrlResult,
- State#state{ctrl_data =
- {NextMsgData, [], start}})
+ State#state{latest_ctrl_response = Lines,
+ ctrl_data =
+ {NextMsgData, [], start}})
end;
{continue, NewCtrlData} ->
+ ?DBG(' ...Continue... ctrl_data=~p~n',[NewCtrlData]),
activate_ctrl_connection(State),
{noreply, State#state{ctrl_data = NewCtrlData}}
end;
-handle_info({tcp_closed, Socket}, #state{csock = Socket}) ->
- %% If the server closes the control channel it is
- %% the expected behavior that connection process terminates.
+%% If the server closes the control channel it is
+%% the expected behavior that connection process terminates.
+handle_info({Cls, Socket}, #state{csock = {Trpt, Socket}})
+ when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} ->
exit(normal); %% User will get error message from terminate/2
-handle_info({tcp_error, Socket, Reason}, _) ->
+handle_info({Err, Socket, Reason}, _) when Err==tcp_error ; Err==ssl_error ->
Report =
- io_lib:format("tcp_error on socket: ~p for reason: ~p~n",
- [Socket, Reason]),
+ io_lib:format("~p on socket: ~p for reason: ~p~n",
+ [Err, Socket, Reason]),
error_logger:error_report(Report),
%% If tcp does not work the only option is to terminate,
%% this is the expected behavior under these circumstances.
@@ -1417,7 +1579,7 @@ handle_info({'DOWN', _Ref, _Type, Process, Reason}, State) ->
handle_info({'EXIT', Pid, Reason}, #state{progress = Pid} = State) ->
Report = io_lib:format("Progress reporting stopped for reason ~p~n",
- Reason),
+ [Reason]),
error_logger:info_report(Report),
{noreply, State#state{progress = ignore}};
@@ -1425,8 +1587,8 @@ handle_info({'EXIT', Pid, Reason}, #state{progress = Pid} = State) ->
%% so we do not want to crash, but we make a log entry as it is an
%% unwanted behaviour.)
handle_info(Info, State) ->
- Report = io_lib:format("ftp : ~p : Unexpected message: ~p\n",
- [self(), Info]),
+ Report = io_lib:format("ftp : ~p : Unexpected message: ~p~nState: ~p~n",
+ [self(), Info, State]),
error_logger:info_report(Report),
{noreply, State}.
@@ -1566,8 +1728,37 @@ handle_user_account(Acc, State) ->
%%--------------------------------------------------------------------------
%% handle_ctrl_result
%%--------------------------------------------------------------------------
-%%--------------------------------------------------------------------------
-%% Handling of control connection setup
+handle_ctrl_result({tls_upgrade, _}, #state{csock = {tcp, Socket},
+ tls_options = TLSOptions,
+ timeout = Timeout,
+ caller = open, client = From}
+ = State0) ->
+ ?DBG('<--ctrl ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State0]),
+ case ssl:connect(Socket, TLSOptions, Timeout) of
+ {ok, TLSSocket} ->
+ State = State0#state{csock = {ssl,TLSSocket}},
+ send_ctrl_message(State, mk_cmd("PBSZ 0", [])),
+ activate_ctrl_connection(State),
+ {noreply, State#state{tls_upgrading_data_connection = {true, pbsz}} };
+ {error, _} = Error ->
+ gen_server:reply(From, {Error, self()}),
+ {stop, normal, State0#state{client = undefined,
+ caller = undefined,
+ tls_upgrading_data_connection = false}}
+ end;
+
+handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, pbsz}} = State) ->
+ send_ctrl_message(State, mk_cmd("PROT P", [])),
+ activate_ctrl_connection(State),
+ {noreply, State#state{tls_upgrading_data_connection = {true, prot}}};
+
+handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, prot},
+ client = From} = State) ->
+ gen_server:reply(From, {ok, self()}),
+ {noreply, State#state{client = undefined,
+ caller = undefined,
+ tls_upgrading_data_connection = false}};
+
handle_ctrl_result({pos_compl, _}, #state{caller = open, client = From}
= State) ->
gen_server:reply(From, {ok, self()}),
@@ -1584,12 +1775,12 @@ handle_ctrl_result({pos_compl, _Lines},
{LSock, Caller}}} = State) ->
handle_caller(State#state{caller = Caller, dsock = {lsock, LSock}});
-handle_ctrl_result({Status, Lines},
+handle_ctrl_result({Status, _Lines},
#state{mode = active,
caller = {setup_data_connection, {LSock, _}}}
= State) ->
- close_connection(LSock),
- ctrl_result_response(Status, State, {error, Lines});
+ close_connection({tcp,LSock}),
+ ctrl_result_response(Status, State, {error, Status});
%% Data connection setup passive mode
handle_ctrl_result({pos_compl, Lines},
@@ -1601,10 +1792,10 @@ handle_ctrl_result({pos_compl, Lines},
timeout = Timeout}
= State) ->
[_, PortStr | _] = lists:reverse(string:tokens(Lines, "|")),
- {ok, {IP, _}} = inet:peername(CSock),
+ {ok, {IP, _}} = peername(CSock),
case connect(IP, list_to_integer(PortStr), Timeout, State) of
{ok, _, Socket} ->
- handle_caller(State#state{caller = Caller, dsock = Socket});
+ handle_caller(State#state{caller = Caller, dsock = {tcp, Socket}});
{error, _Reason} = Error ->
gen_server:reply(From, Error),
{noreply, State#state{client = undefined, caller = undefined}}
@@ -1614,8 +1805,9 @@ handle_ctrl_result({pos_compl, Lines},
#state{mode = passive,
ipfamily = inet,
client = From,
- caller = {setup_data_connection, Caller},
- timeout = Timeout} = State) ->
+ caller = {setup_data_connection, Caller},
+ timeout = Timeout,
+ ftp_extension = false} = State) ->
{_, [?LEFT_PAREN | Rest]} =
lists:splitwith(fun(?LEFT_PAREN) -> false; (_) -> true end, Lines),
@@ -1626,14 +1818,38 @@ handle_ctrl_result({pos_compl, Lines},
string:tokens(NewPortAddr, [$,])),
IP = {A1, A2, A3, A4},
Port = (P1 * 256) + P2,
+
+ ?DBG('<--data tcp connect to ~p:~p, Caller=~p~n',[IP,Port,Caller]),
case connect(IP, Port, Timeout, State) of
- {ok, _, Socket} ->
- handle_caller(State#state{caller = Caller, dsock = Socket});
+ {ok, _, Socket} ->
+ handle_caller(State#state{caller = Caller, dsock = {tcp,Socket}});
{error, _Reason} = Error ->
gen_server:reply(From, Error),
{noreply,State#state{client = undefined, caller = undefined}}
end;
+handle_ctrl_result({pos_compl, Lines},
+ #state{mode = passive,
+ ipfamily = inet,
+ client = From,
+ caller = {setup_data_connection, Caller},
+ csock = CSock,
+ timeout = Timeout,
+ ftp_extension = true} = State) ->
+
+ [_, PortStr | _] = lists:reverse(string:tokens(Lines, "|")),
+ {ok, {IP, _}} = peername(CSock),
+
+ ?DBG('<--data tcp connect to ~p:~p, Caller=~p~n',[IP,PortStr,Caller]),
+ case connect(IP, list_to_integer(PortStr), Timeout, State) of
+ {ok, _, Socket} ->
+ handle_caller(State#state{caller = Caller, dsock = {tcp, Socket}});
+ {error, _Reason} = Error ->
+ gen_server:reply(From, Error),
+ {noreply, State#state{client = undefined, caller = undefined}}
+ end;
+
+
%% FTP server does not support passive mode: try to fallback on active mode
handle_ctrl_result(_,
#state{mode = passive,
@@ -1669,18 +1885,18 @@ handle_ctrl_result({pos_compl, Lines},
%%--------------------------------------------------------------------------
%% Directory listing
-handle_ctrl_result({pos_prel, _}, #state{caller = {dir, Dir}} = State) ->
- case accept_data_connection(State) of
- {ok, NewState} ->
- activate_data_connection(NewState),
- {noreply, NewState#state{caller = {handle_dir_result, Dir}}};
+handle_ctrl_result({pos_prel, _}, #state{caller = {dir, Dir}} = State0) ->
+ case accept_data_connection(State0) of
+ {ok, State1} ->
+ State = activate_data_connection(State1),
+ {noreply, State#state{caller = {handle_dir_result, Dir}}};
{error, _Reason} = ERROR ->
- case State#state.client of
+ case State0#state.client of
undefined ->
- {stop, ERROR, State};
+ {stop, ERROR, State0};
From ->
gen_server:reply(From, ERROR),
- {stop, normal, State#state{client = undefined}}
+ {stop, normal, State0#state{client = undefined}}
end
end;
@@ -1755,7 +1971,7 @@ handle_ctrl_result(_, #state{caller = {handle_dir_data_third_phase, DirData},
{noreply, State#state{client = undefined, caller = undefined}};
handle_ctrl_result({Status, _}, #state{caller = cd} = State) ->
- ctrl_result_response(Status, State, {error, epath});
+ ctrl_result_response(Status, State, {error, Status});
handle_ctrl_result(Status={epath, _}, #state{caller = {dir,_}} = State) ->
ctrl_result_response(Status, State, {error, epath});
@@ -1770,26 +1986,26 @@ handle_ctrl_result({pos_interm, _}, #state{caller = {rename, NewFile}}
handle_ctrl_result({Status, _},
#state{caller = {rename, _}} = State) ->
- ctrl_result_response(Status, State, {error, epath});
+ ctrl_result_response(Status, State, {error, Status});
handle_ctrl_result({Status, _},
#state{caller = rename_second_phase} = State) ->
- ctrl_result_response(Status, State, {error, epath});
+ ctrl_result_response(Status, State, {error, Status});
%%--------------------------------------------------------------------------
%% File handling - recv_bin
-handle_ctrl_result({pos_prel, _}, #state{caller = recv_bin} = State) ->
- case accept_data_connection(State) of
- {ok, NewState} ->
- activate_data_connection(NewState),
- {noreply, NewState};
+handle_ctrl_result({pos_prel, _}, #state{caller = recv_bin} = State0) ->
+ case accept_data_connection(State0) of
+ {ok, State1} ->
+ State = activate_data_connection(State1),
+ {noreply, State};
{error, _Reason} = ERROR ->
- case State#state.client of
+ case State0#state.client of
undefined ->
- {stop, ERROR, State};
+ {stop, ERROR, State0};
From ->
gen_server:reply(From, ERROR),
- {stop, normal, State#state{client = undefined}}
+ {stop, normal, State0#state{client = undefined}}
end
end;
@@ -1812,36 +2028,35 @@ handle_ctrl_result({Status, _}, #state{caller = {recv_bin, _}} = State) ->
%% File handling - start_chunk_transfer
handle_ctrl_result({pos_prel, _}, #state{client = From,
caller = start_chunk_transfer}
- = State) ->
- case accept_data_connection(State) of
- {ok, NewState} ->
- gen_server:reply(From, ok),
- {noreply, NewState#state{chunk = true, client = undefined,
- caller = undefined}};
+ = State0) ->
+ case accept_data_connection(State0) of
+ {ok, State1} ->
+ State = start_chunk(State1),
+ {noreply, State};
{error, _Reason} = ERROR ->
- case State#state.client of
+ case State0#state.client of
undefined ->
- {stop, ERROR, State};
+ {stop, ERROR, State0};
From ->
gen_server:reply(From, ERROR),
- {stop, normal, State#state{client = undefined}}
+ {stop, normal, State0#state{client = undefined}}
end
end;
%%--------------------------------------------------------------------------
%% File handling - recv_file
-handle_ctrl_result({pos_prel, _}, #state{caller = {recv_file, _}} = State) ->
- case accept_data_connection(State) of
- {ok, NewState} ->
- activate_data_connection(NewState),
- {noreply, NewState};
+handle_ctrl_result({pos_prel, _}, #state{caller = {recv_file, _}} = State0) ->
+ case accept_data_connection(State0) of
+ {ok, State1} ->
+ State = activate_data_connection(State1),
+ {noreply, State};
{error, _Reason} = ERROR ->
- case State#state.client of
+ case State0#state.client of
undefined ->
- {stop, ERROR, State};
+ {stop, ERROR, State0};
From ->
gen_server:reply(From, ERROR),
- {stop, normal, State#state{client = undefined}}
+ {stop, normal, State0#state{client = undefined}}
end
end;
@@ -1853,36 +2068,32 @@ handle_ctrl_result({Status, _}, #state{caller = {recv_file, Fd}} = State) ->
%%--------------------------------------------------------------------------
%% File handling - transfer_*
handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_file, Fd}}
- = State) ->
- case accept_data_connection(State) of
- {ok, NewState} ->
- send_file(Fd, NewState);
+ = State0) ->
+ case accept_data_connection(State0) of
+ {ok, State1} ->
+ send_file(State1, Fd);
{error, _Reason} = ERROR ->
- case State#state.client of
+ case State0#state.client of
undefined ->
- {stop, ERROR, State};
+ {stop, ERROR, State0};
From ->
gen_server:reply(From, ERROR),
- {stop, normal, State#state{client = undefined}}
+ {stop, normal, State0#state{client = undefined}}
end
end;
handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_data, Bin}}
- = State) ->
- case accept_data_connection(State) of
- {ok, NewState} ->
- send_data_message(NewState, Bin),
- close_data_connection(NewState),
- activate_ctrl_connection(NewState),
- {noreply, NewState#state{caller = transfer_data_second_phase,
- dsock = undefined}};
+ = State0) ->
+ case accept_data_connection(State0) of
+ {ok, State} ->
+ send_bin(State, Bin);
{error, _Reason} = ERROR ->
- case State#state.client of
+ case State0#state.client of
undefined ->
- {stop, ERROR, State};
+ {stop, ERROR, State0};
From ->
gen_server:reply(From, ERROR),
- {stop, normal, State#state{client = undefined}}
+ {stop, normal, State0#state{client = undefined}}
end
end;
@@ -1890,7 +2101,7 @@ handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_data, Bin}}
%% Default
handle_ctrl_result({Status, Lines}, #state{client = From} = State)
when From =/= undefined ->
- ctrl_result_response(Status, State, {error, Lines}).
+ ctrl_result_response(Status, State, {error, Status}).
%%--------------------------------------------------------------------------
%% Help functions to handle_ctrl_result
@@ -1908,7 +2119,6 @@ ctrl_result_response(Status, #state{client = From} = State, _)
(Status =:= epnospc) orelse
(Status =:= efnamena) orelse
(Status =:= econn) ->
-%Status == etnospc; Status == epnospc; Status == econn ->
gen_server:reply(From, {error, Status}),
%% {stop, normal, {error, Status}, State#state{client = undefined}};
{stop, normal, State#state{client = undefined}};
@@ -1972,16 +2182,16 @@ handle_caller(#state{caller = {transfer_data, {Cmd, Bin, RemoteFile}}} =
%% Connect to FTP server at Host (default is TCP port 21)
%% in order to establish a control connection.
setup_ctrl_connection(Host, Port, Timeout, State) ->
- MsTime = millisec_time(),
+ MsTime = erlang:monotonic_time(),
case connect(Host, Port, Timeout, State) of
{ok, IpFam, CSock} ->
- NewState = State#state{csock = CSock, ipfamily = IpFam},
+ NewState = State#state{csock = {tcp, CSock}, ipfamily = IpFam},
activate_ctrl_connection(NewState),
- case Timeout - (millisec_time() - MsTime) of
+ case Timeout - inets_lib:millisec_passed(MsTime) of
Timeout2 when (Timeout2 >= 0) ->
{ok, NewState#state{caller = open}, Timeout2};
_ ->
- %% Oups: Simulate timeout
+ %% Oups: Simulate timeout
{ok, NewState#state{caller = open}, 0}
end;
Error ->
@@ -1990,13 +2200,14 @@ setup_ctrl_connection(Host, Port, Timeout, State) ->
setup_data_connection(#state{mode = active,
caller = Caller,
- csock = CSock} = State) ->
- case (catch inet:sockname(CSock)) of
+ csock = CSock,
+ ftp_extension = FtpExt} = State) ->
+ case (catch sockname(CSock)) of
{ok, {{_, _, _, _, _, _, _, _} = IP, _}} ->
{ok, LSock} =
gen_tcp:listen(0, [{ip, IP}, {active, false},
inet6, binary, {packet, 0}]),
- {ok, Port} = inet:port(LSock),
+ {ok, {_, Port}} = sockname({tcp,LSock}),
IpAddress = inet_parse:ntoa(IP),
Cmd = mk_cmd("EPRT |2|~s|~p|", [IpAddress, Port]),
send_ctrl_message(State, Cmd),
@@ -2007,11 +2218,18 @@ setup_data_connection(#state{mode = active,
{ok, LSock} = gen_tcp:listen(0, [{ip, IP}, {active, false},
binary, {packet, 0}]),
{ok, Port} = inet:port(LSock),
- {IP1, IP2, IP3, IP4} = IP,
- {Port1, Port2} = {Port div 256, Port rem 256},
- send_ctrl_message(State,
- mk_cmd("PORT ~w,~w,~w,~w,~w,~w",
- [IP1, IP2, IP3, IP4, Port1, Port2])),
+ case FtpExt of
+ false ->
+ {IP1, IP2, IP3, IP4} = IP,
+ {Port1, Port2} = {Port div 256, Port rem 256},
+ send_ctrl_message(State,
+ mk_cmd("PORT ~w,~w,~w,~w,~w,~w",
+ [IP1, IP2, IP3, IP4, Port1, Port2]));
+ true ->
+ IpAddress = inet_parse:ntoa(IP),
+ Cmd = mk_cmd("EPRT |1|~s|~p|", [IpAddress, Port]),
+ send_ctrl_message(State, Cmd)
+ end,
activate_ctrl_connection(State),
{noreply, State#state{caller = {setup_data_connection,
{LSock, Caller}}}}
@@ -2024,24 +2242,18 @@ setup_data_connection(#state{mode = passive, ipfamily = inet6,
{noreply, State#state{caller = {setup_data_connection, Caller}}};
setup_data_connection(#state{mode = passive, ipfamily = inet,
- caller = Caller} = State) ->
+ caller = Caller,
+ ftp_extension = false} = State) ->
send_ctrl_message(State, mk_cmd("PASV", [])),
activate_ctrl_connection(State),
- {noreply, State#state{caller = {setup_data_connection, Caller}}}.
-
-
-%% setup_data_connection(#state{mode = passive, ip_v6_disabled = false,
-%% caller = Caller} = State) ->
-%% send_ctrl_message(State, mk_cmd("EPSV", [])),
-%% activate_ctrl_connection(State),
-%% {noreply, State#state{caller = {setup_data_connection, Caller}}};
-
-%% setup_data_connection(#state{mode = passive, ip_v6_disabled = true,
-%% caller = Caller} = State) ->
-%% send_ctrl_message(State, mk_cmd("PASV", [])),
-%% activate_ctrl_connection(State),
-%% {noreply, State#state{caller = {setup_data_connection, Caller}}}.
+ {noreply, State#state{caller = {setup_data_connection, Caller}}};
+setup_data_connection(#state{mode = passive, ipfamily = inet,
+ caller = Caller,
+ ftp_extension = true} = State) ->
+ send_ctrl_message(State, mk_cmd("EPSV", [])),
+ activate_ctrl_connection(State),
+ {noreply, State#state{caller = {setup_data_connection, Caller}}}.
connect(Host, Port, Timeout, #state{ipfamily = inet = IpFam}) ->
connect2(Host, Port, IpFam, Timeout);
@@ -2088,75 +2300,102 @@ connect2(Host, Port, IpFam, Timeout) ->
accept_data_connection(#state{mode = active,
dtimeout = DTimeout,
- dsock = {lsock, LSock}} = State) ->
+ tls_options = TLSOptions,
+ dsock = {lsock, LSock}} = State0) ->
case gen_tcp:accept(LSock, DTimeout) of
+ {ok, Socket} when is_list(TLSOptions) ->
+ gen_tcp:close(LSock),
+ ?DBG('<--data ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State0]),
+ case ssl:connect(Socket, TLSOptions, DTimeout) of
+ {ok, TLSSocket} ->
+ {ok, State0#state{dsock={ssl,TLSSocket}}};
+ {error, Reason} ->
+ {error, {ssl_connect_failed, Reason}}
+ end;
{ok, Socket} ->
gen_tcp:close(LSock),
- {ok, State#state{dsock = Socket}};
+ {ok, State0#state{dsock={tcp,Socket}}};
{error, Reason} ->
{error, {data_connect_failed, Reason}}
end;
+accept_data_connection(#state{mode = passive,
+ dtimeout = DTimeout,
+ dsock = {tcp,Socket},
+ tls_options = TLSOptions} = State) when is_list(TLSOptions) ->
+ ?DBG('<--data ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State]),
+ case ssl:connect(Socket, TLSOptions, DTimeout) of
+ {ok, TLSSocket} ->
+ {ok, State#state{dsock={ssl,TLSSocket}}};
+ {error, Reason} ->
+ {error, {ssl_connect_failed, Reason}}
+ end;
accept_data_connection(#state{mode = passive} = State) ->
- {ok, State}.
+ {ok,State}.
+
-send_ctrl_message(#state{csock = Socket, verbose = Verbose}, Message) ->
- %% io:format("send control message: ~n~p~n", [lists:flatten(Message)]),
+send_ctrl_message(_S=#state{csock = Socket, verbose = Verbose}, Message) ->
verbose(lists:flatten(Message),Verbose,send),
+ ?DBG('<--ctrl ~p ---- ~s~p~n',[Socket,Message,_S]),
send_message(Socket, Message).
-send_data_message(#state{dsock = Socket}, Message) ->
- send_message(Socket, Message).
-
-send_message(Socket, Message) ->
- case gen_tcp:send(Socket, Message) of
+send_data_message(_S=#state{dsock = Socket}, Message) ->
+ ?DBG('<==data ~p ==== ~s~n~p~n',[Socket,Message,_S]),
+ case send_message(Socket, Message) of
ok ->
ok;
{error, Reason} ->
- Report = io_lib:format("gen_tcp:send/2 failed for "
- "reason ~p~n", [Reason]),
+ Report = io_lib:format("send/2 for socket ~p failed with "
+ "reason ~p~n", [Socket, Reason]),
error_logger:error_report(Report),
- %% If tcp does not work the only option is to terminate,
+ %% If tcp/ssl does not work the only option is to terminate,
%% this is the expected behavior under these circumstances.
exit(normal) %% User will get error message from terminate/2
end.
+send_message({tcp, Socket}, Message) ->
+ gen_tcp:send(Socket, Message);
+send_message({ssl, Socket}, Message) ->
+ ssl:send(Socket, Message).
+
activate_ctrl_connection(#state{csock = Socket, ctrl_data = {<<>>, _, _}}) ->
activate_connection(Socket);
activate_ctrl_connection(#state{csock = Socket}) ->
%% We have already received at least part of the next control message,
%% that has been saved in ctrl_data, process this first.
- self() ! {tcp, Socket, <<>>}.
+ self() ! {tcp, unwrap_socket(Socket), <<>>}.
-activate_data_connection(#state{dsock = Socket}) ->
- activate_connection(Socket).
+unwrap_socket({tcp,Socket}) -> Socket;
+unwrap_socket({ssl,Socket}) -> Socket;
+unwrap_socket(Socket) -> Socket.
+
-activate_connection(Socket) ->
- inet:setopts(Socket, [{active, once}]).
+activate_data_connection(#state{dsock = Socket} = State) ->
+ activate_connection(Socket),
+ State.
-close_ctrl_connection(#state{csock = undefined}) ->
- ok;
-close_ctrl_connection(#state{csock = Socket}) ->
- close_connection(Socket).
+activate_connection({tcp, Socket}) -> inet:setopts(Socket, [{active, once}]);
+activate_connection({ssl, Socket}) -> ssl:setopts(Socket, [{active, once}]).
-close_data_connection(#state{dsock = undefined}) ->
- ok;
-close_data_connection(#state{dsock = {lsock, Socket}}) ->
- close_connection(Socket);
-close_data_connection(#state{dsock = Socket}) ->
- close_connection(Socket).
+close_ctrl_connection(#state{csock = undefined}) -> ok;
+close_ctrl_connection(#state{csock = Socket}) -> close_connection(Socket).
-close_connection(Socket) ->
- gen_tcp:close(Socket).
+close_data_connection(#state{dsock = undefined}) -> ok;
+close_data_connection(#state{dsock = Socket}) -> close_connection(Socket).
-%% ------------ FILE HANDELING ----------------------------------------
+close_connection({lsock,Socket}) -> gen_tcp:close(Socket);
+close_connection({tcp, Socket}) -> gen_tcp:close(Socket);
+close_connection({ssl, Socket}) -> ssl:close(Socket).
-send_file(Fd, State) ->
+%% ------------ FILE HANDELING ----------------------------------------
+send_file(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State, Fd) ->
+ {noreply, State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, send_file, Fd}}};
+send_file(State, Fd) ->
case file_read(Fd) of
{ok, N, Bin} when N > 0->
send_data_message(State, Bin),
progress_report({binary, Bin}, State),
- send_file(Fd, State);
+ send_file(State, Fd);
{ok, _, _} ->
file_close(Fd),
close_data_connection(State),
@@ -2206,9 +2445,27 @@ call(GenServer, Msg, Format, Timeout) ->
cast(GenServer, Msg) ->
gen_server:cast(GenServer, {self(), Msg}).
+send_bin(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State, Bin) ->
+ State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, send_bin, Bin}};
+send_bin(State, Bin) ->
+ send_data_message(State, Bin),
+ close_data_connection(State),
+ activate_ctrl_connection(State),
+ {noreply, State#state{caller = transfer_data_second_phase,
+ dsock = undefined}}.
+
mk_cmd(Fmt, Args) ->
[io_lib:format(Fmt, Args)| [?CR, ?LF]]. % Deep list ok.
+is_name_sane([]) ->
+ true;
+is_name_sane([?CR| _]) ->
+ false;
+is_name_sane([?LF| _]) ->
+ false;
+is_name_sane([_| Rest]) ->
+ is_name_sane(Rest).
+
pwd_result(Lines) ->
{_, [?DOUBLE_QUOTE | Rest]} =
lists:splitwith(fun(?DOUBLE_QUOTE) -> false; (_) -> true end, Lines),
@@ -2216,20 +2473,6 @@ pwd_result(Lines) ->
lists:splitwith(fun(?DOUBLE_QUOTE) -> false; (_) -> true end, Rest),
Dir.
-%% is_verbose(Params) ->
-%% check_param(verbose, Params).
-
-%% is_debug(Flags) ->
-%% check_param(debug, Flags).
-
-%% is_trace(Flags) ->
-%% check_param(trace, Flags).
-
-%% is_ipv6_disabled(Flags) ->
-%% check_param(ip_v6_disabled, Flags).
-
-%% check_param(Param, Params) ->
-%% lists:member(Param, Params).
key_search(Key, List, Default) ->
case lists:keysearch(Key, 1, List) of
@@ -2239,14 +2482,6 @@ key_search(Key, List, Default) ->
Default
end.
-%% check_option(Pred, Value, Default) ->
-%% case Pred(Value) of
-%% true ->
-%% Value;
-%% false ->
-%% Default
-%% end.
-
verbose(Lines, true, Direction) ->
DirStr =
case Direction of
@@ -2273,6 +2508,22 @@ progress_report(Report, #state{progress = ProgressPid}) ->
ftp_progress:report(ProgressPid, Report).
-millisec_time() ->
- {A,B,C} = erlang:now(),
- A*1000000000+B*1000+(C div 1000).
+peername({tcp, Socket}) -> inet:peername(Socket);
+peername({ssl, Socket}) -> ssl:peername(Socket).
+
+sockname({tcp, Socket}) -> inet:sockname(Socket);
+sockname({ssl, Socket}) -> ssl:sockname(Socket).
+
+maybe_tls_upgrade(Pid, undefined) ->
+ {ok, Pid};
+maybe_tls_upgrade(Pid, TLSOptions) ->
+ catch ssl:start(),
+ call(Pid, {open, tls_upgrade, TLSOptions}, plain).
+
+start_chunk(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State) ->
+ State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, start_chunk, undefined}};
+start_chunk(#state{client = From} = State) ->
+ gen_server:reply(From, ok),
+ State#state{chunk = true,
+ client = undefined,
+ caller = undefined}.
diff --git a/lib/inets/src/ftp/ftp_internal.hrl b/lib/inets/src/ftp/ftp_internal.hrl
index 148f8217ba..f29bb4a099 100644
--- a/lib/inets/src/ftp/ftp_internal.hrl
+++ b/lib/inets/src/ftp/ftp_internal.hrl
@@ -1,18 +1,19 @@
%%
%% %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.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/ftp/ftp_progress.erl b/lib/inets/src/ftp/ftp_progress.erl
index 39f4d05bc2..68185a222d 100644
--- a/lib/inets/src/ftp/ftp_progress.erl
+++ b/lib/inets/src/ftp/ftp_progress.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/ftp/ftp_response.erl b/lib/inets/src/ftp/ftp_response.erl
index 4bf788e946..7533bc4550 100644
--- a/lib/inets/src/ftp/ftp_response.erl
+++ b/lib/inets/src/ftp/ftp_response.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -175,6 +176,8 @@ error_string(Reason) ->
%% Positive Preleminary Reply
interpret_status(?POS_PREL,_,_) -> pos_prel;
+%%FIXME ??? 3??? interpret_status(?POS_COMPL, ?AUTH_ACC, 3) -> tls_upgrade;
+interpret_status(?POS_COMPL, ?AUTH_ACC, 4) -> tls_upgrade;
%% Positive Completion Reply
interpret_status(?POS_COMPL,_,_) -> pos_compl;
%% Positive Intermediate Reply nedd account
@@ -191,5 +194,6 @@ interpret_status(?TRANS_NEG_COMPL,_,_) -> trans_neg_compl;
interpret_status(?PERM_NEG_COMPL,?FILE_SYSTEM,0) -> epath;
interpret_status(?PERM_NEG_COMPL,?FILE_SYSTEM,2) -> epnospc;
interpret_status(?PERM_NEG_COMPL,?FILE_SYSTEM,3) -> efnamena;
+interpret_status(?PERM_NEG_COMPL,?AUTH_ACC,0) -> elogin;
interpret_status(?PERM_NEG_COMPL,_,_) -> perm_neg_compl.
diff --git a/lib/inets/src/ftp/ftp_sup.erl b/lib/inets/src/ftp/ftp_sup.erl
index 547170b671..21dcfb6ab2 100644
--- a/lib/inets/src/ftp/ftp_sup.erl
+++ b/lib/inets/src/ftp/ftp_sup.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_client/Makefile b/lib/inets/src/http_client/Makefile
index f0d4ce139e..a1c1f36b70 100644
--- a/lib/inets/src/http_client/Makefile
+++ b/lib/inets/src/http_client/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2012. All Rights Reserved.
+# Copyright Ericsson AB 2005-2016. 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/.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# 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.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl
index 41bba7995e..91d87289a2 100644
--- a/lib/inets/src/http_client/httpc.erl
+++ b/lib/inets/src/http_client/httpc.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -100,7 +101,8 @@ request(Url, Profile) ->
%% {ok, {StatusLine, Headers, Body}} | {ok, {Status, Body}} |
%% {ok, RequestId} | {error,Reason} | {ok, {saved_as, FilePath}
%%
-%% Method - atom() = head | get | put | post | trace | options| delete
+%% Method - atom() = head | get | put | patch | post | trace |
+%% options | delete
%% Request - {Url, Headers} | {Url, Headers, ContentType, Body}
%% Url - string()
%% HTTPOptions - [HttpOption]
@@ -175,8 +177,8 @@ request(Method,
request(Method,
{Url, Headers, ContentType, Body},
HTTPOptions, Options, Profile)
- when ((Method =:= post) orelse (Method =:= put)) andalso
- (is_atom(Profile) orelse is_pid(Profile)) ->
+ when ((Method =:= post) orelse (Method =:= patch) orelse (Method =:= put) orelse
+ (Method =:= delete)) andalso (is_atom(Profile) orelse is_pid(Profile)) ->
?hcrt("request", [{method, Method},
{url, Url},
{headers, Headers},
@@ -208,16 +210,8 @@ cancel_request(RequestId) ->
cancel_request(RequestId, Profile)
when is_atom(Profile) orelse is_pid(Profile) ->
?hcrt("cancel request", [{request_id, RequestId}, {profile, Profile}]),
- ok = httpc_manager:cancel_request(RequestId, profile_name(Profile)),
- receive
- %% If the request was already fulfilled throw away the
- %% answer as the request has been canceled.
- {http, {RequestId, _}} ->
- ok
- after 0 ->
- ok
- end.
-
+ httpc_manager:cancel_request(RequestId, profile_name(Profile)).
+
%%--------------------------------------------------------------------------
%% set_options(Options) -> ok | {error, Reason}
@@ -241,14 +235,7 @@ set_options(Options, Profile) when is_atom(Profile) orelse is_pid(Profile) ->
?hcrt("set options", [{options, Options}, {profile, Profile}]),
case validate_options(Options) of
{ok, Opts} ->
- try
- begin
- httpc_manager:set_options(Opts, profile_name(Profile))
- end
- catch
- exit:{noproc, _} ->
- {error, inets_not_started}
- end;
+ httpc_manager:set_options(Opts, profile_name(Profile));
{error, Reason} ->
{error, Reason}
end.
@@ -343,8 +330,6 @@ store_cookies(SetCookieHeaders, Url, Profile)
ok
end
catch
- exit:{noproc, _} ->
- {error, {not_started, Profile}};
error:{badmatch, Bad} ->
{error, {parse_failed, Bad}}
end.
@@ -571,7 +556,7 @@ handle_request(Method, Url,
Request = #request{from = Receiver,
scheme = Scheme,
- address = {Host, Port},
+ address = {host_address(Host, BracketedHost), Port},
path = MaybeEscPath,
pquery = MaybeEscQuery,
method = Method,
@@ -1283,3 +1268,7 @@ child_name(Pid, [_ | Children]) ->
%% d(_, _, _) ->
%% ok.
+host_address(Host, false) ->
+ Host;
+host_address(Host, true) ->
+ string:strip(string:strip(Host, right, $]), left, $[).
diff --git a/lib/inets/src/http_client/httpc_cookie.erl b/lib/inets/src/http_client/httpc_cookie.erl
index 69900bae65..cbf428ab3e 100644
--- a/lib/inets/src/http_client/httpc_cookie.erl
+++ b/lib/inets/src/http_client/httpc_cookie.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -115,8 +116,8 @@ maybe_dets_close(Db) ->
%%--------------------------------------------------------------------
-%% Func: insert(CookieDb) -> ok
-%% Purpose: Close the cookie db
+%% Func: insert(CookieDb, Cookie) -> ok
+%% Purpose: insert cookies into the cookie db
%%--------------------------------------------------------------------
%% If no persistent cookie database is defined we
@@ -334,8 +335,23 @@ add_domain(Str, #http_cookie{domain_default = true}) ->
add_domain(Str, #http_cookie{domain = Domain}) ->
Str ++ "; $Domain=" ++ Domain.
+is_set_cookie_valid("") ->
+ %% an empty Set-Cookie header is not valid
+ false;
+is_set_cookie_valid([$=|_]) ->
+ %% a Set-Cookie header without name is not valid
+ false;
+is_set_cookie_valid(SetCookieHeader) ->
+ %% a Set-Cookie header without name/value is not valid
+ case string:chr(SetCookieHeader, $=) of
+ 0 -> false;
+ _ -> true
+ end.
+
parse_set_cookies(CookieHeaders, DefaultPathDomain) ->
- SetCookieHeaders = [Value || {"set-cookie", Value} <- CookieHeaders],
+ %% filter invalid Set-Cookie headers
+ SetCookieHeaders = [Value || {"set-cookie", Value} <- CookieHeaders,
+ is_set_cookie_valid(Value)],
Cookies = [parse_set_cookie(SetCookieHeader, DefaultPathDomain) ||
SetCookieHeader <- SetCookieHeaders],
%% print_cookies("Parsed Cookies", Cookies),
@@ -347,6 +363,8 @@ parse_set_cookie(CookieHeader, {DefaultPath, DefaultDomain}) ->
Name = string:substr(CookieHeader, 1, Pos - 1),
{Value, Attrs} =
case string:substr(CookieHeader, Pos + 1) of
+ [] ->
+ {"", ""};
[$;|ValueAndAttrs] ->
{"", string:tokens(ValueAndAttrs, ";")};
ValueAndAttrs ->
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index 55794f57dc..d1c52dcc78 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -25,6 +26,7 @@
-include_lib("inets/src/http_lib/http_internal.hrl").
-include("httpc_internal.hrl").
+-define(IS_STREAMED(Code), ((Code =:= 200) orelse (Code =:= 206))).
%%--------------------------------------------------------------------
%% Internal Application API
@@ -32,7 +34,7 @@
start_link/4,
%% connect_and_send/2,
send/2,
- cancel/3,
+ cancel/2,
stream_next/1,
info/1
]).
@@ -55,8 +57,8 @@
headers, % #http_response_h{}
body, % binary()
mfa, % {Module, Function, Args}
- pipeline = queue:new(), % queue()
- keep_alive = queue:new(), % queue()
+ pipeline = queue:new(), % queue:queue()
+ keep_alive = queue:new(), % queue:queue()
status, % undefined | new | pipeline | keep_alive | close | {ssl_tunnel, Request}
canceled = [], % [RequestId]
max_header_size = nolimit, % nolimit | integer()
@@ -87,7 +89,7 @@
%% block the httpc manager process in odd cases such as trying to call
%% a server that does not exist. (See OTP-6735) The only API function
%% sending messages to the handler process that can be called before
-%% init has compleated is cancel and that is not a problem! (Send and
+%% init has completed is cancel and that is not a problem! (Send and
%% stream will not be called before the first request has been sent and
%% the reply or part of it has arrived.)
%%--------------------------------------------------------------------
@@ -117,8 +119,8 @@ send(Request, Pid) ->
%% Description: Cancels a request. Intended to be called by the httpc
%% manager process.
%%--------------------------------------------------------------------
-cancel(RequestId, Pid, From) ->
- cast({cancel, RequestId, From}, Pid).
+cancel(RequestId, Pid) ->
+ cast({cancel, RequestId}, Pid).
%%--------------------------------------------------------------------
@@ -162,22 +164,22 @@ info(Pid) ->
%% Request should not be streamed
stream(BodyPart, #request{stream = none} = Request, _) ->
?hcrt("stream - none", []),
- {BodyPart, Request};
+ {false, BodyPart, Request};
%% Stream to caller
stream(BodyPart, #request{stream = Self} = Request, Code)
- when ((Code =:= 200) orelse (Code =:= 206)) andalso
+ when ?IS_STREAMED(Code) andalso
((Self =:= self) orelse (Self =:= {self, once})) ->
?hcrt("stream - self", [{stream, Self}, {code, Code}]),
httpc_response:send(Request#request.from,
{Request#request.id, stream, BodyPart}),
- {<<>>, Request};
+ {true, <<>>, Request};
%% Stream to file
%% This has been moved to start_stream/3
%% We keep this for backward compatibillity...
stream(BodyPart, #request{stream = Filename} = Request, Code)
- when ((Code =:= 200) orelse (Code =:= 206)) andalso is_list(Filename) ->
+ when ?IS_STREAMED(Code) andalso is_list(Filename) ->
?hcrt("stream - filename", [{stream, Filename}, {code, Code}]),
case file:open(Filename, [write, raw, append, delayed_write]) of
{ok, Fd} ->
@@ -189,18 +191,18 @@ stream(BodyPart, #request{stream = Filename} = Request, Code)
%% Stream to file
stream(BodyPart, #request{stream = Fd} = Request, Code)
- when ((Code =:= 200) orelse (Code =:= 206)) ->
+ when ?IS_STREAMED(Code) ->
?hcrt("stream to file", [{stream, Fd}, {code, Code}]),
case file:write(Fd, BodyPart) of
ok ->
- {<<>>, Request};
+ {true, <<>>, Request};
{error, Reason} ->
exit({stream_to_file_failed, Reason})
end;
stream(BodyPart, Request,_) -> % only 200 and 206 responses can be streamed
?hcrt("stream - ignore", [{request, Request}]),
- {BodyPart, Request}.
+ {false, BodyPart, Request}.
%%====================================================================
@@ -316,8 +318,9 @@ handle_call(#request{address = Addr} = Request, _,
{reply, ok, State}
end;
{error, Reason} ->
- ?hcri("failed sending request", [{reason, Reason}]),
- {reply, {pipeline_failed, Reason}, State0}
+ ?hcri("failed sending request", [{reason, Reason}]),
+ NewPipeline = queue:in(Request, State0#state.pipeline),
+ {stop, shutdown, {pipeline_failed, Reason}, State0#state{pipeline = NewPipeline}}
end;
handle_call(#request{address = Addr} = Request, _,
@@ -350,30 +353,30 @@ handle_call(#request{address = Addr} = Request, _,
{reply, ok, State0#state{keep_alive = NewKeepAlive,
session = NewSession}};
undefined ->
- %% Note: tcp-message reciving has already been
+ %% Note: tcp-message receiving has already been
%% activated by handle_pipeline/2.
?hcrd("no current request", []),
cancel_timer(Timers#timers.queue_timer,
timeout_queue),
+ NewTimers = Timers#timers{queue_timer = undefined},
+ State1 = State0#state{timers = NewTimers},
Address = handle_proxy(Addr, Proxy),
case httpc_request:send(Address, Session, Request) of
ok ->
?hcrd("request sent", []),
%% Activate the request time out for the new request
- State1 =
- activate_request_timeout(State0#state{request = Request}),
- NewTimers = State1#state.timers,
+ State2 =
+ activate_request_timeout(State1#state{request = Request}),
NewSession =
Session#session{queue_length = 1,
client_close = ClientClose},
insert_session(NewSession, ProfileName),
- State = init_wait_for_response_state(Request, State1#state{session = NewSession,
- timers = NewTimers}),
+ State = init_wait_for_response_state(Request, State2#state{session = NewSession}),
{reply, ok, State};
{error, Reason} ->
?hcri("failed sending request", [{reason, Reason}]),
- {reply, {request_failed, Reason}, State0}
+ {stop, shutdown, {keepalive_failed, Reason}, State1}
end
end;
@@ -391,7 +394,7 @@ handle_call(info, _, State) ->
%% When the request in process has been canceled the handler process is
%% stopped and the pipelined requests will be reissued or remaining
%% requests will be sent on a new connection. This is is
-%% based on the assumption that it is proably cheaper to reissue the
+%% based on the assumption that it is probably cheaper to reissue the
%% requests than to wait for a potentiall large response that we then
%% only throw away. This of course is not always true maybe we could
%% do something smarter here?! If the request canceled is not
@@ -400,19 +403,17 @@ handle_call(info, _, State) ->
%% handle_keep_alive_queue/2 on the other hand will just skip the
%% request as if it was never issued as in this case the request will
%% not have been sent.
-handle_cast({cancel, RequestId, From},
+handle_cast({cancel, RequestId},
#state{request = #request{id = RequestId} = Request,
profile_name = ProfileName,
canceled = Canceled} = State) ->
?hcrv("cancel current request", [{request_id, RequestId},
{profile, ProfileName},
{canceled, Canceled}]),
- httpc_manager:request_canceled(RequestId, ProfileName, From),
- ?hcrv("canceled", []),
{stop, normal,
State#state{canceled = [RequestId | Canceled],
request = Request#request{from = answer_sent}}};
-handle_cast({cancel, RequestId, From},
+handle_cast({cancel, RequestId},
#state{profile_name = ProfileName,
request = #request{id = CurrId},
canceled = Canceled} = State) ->
@@ -420,9 +421,17 @@ handle_cast({cancel, RequestId, From},
{curr_req_id, CurrId},
{profile, ProfileName},
{canceled, Canceled}]),
- httpc_manager:request_canceled(RequestId, ProfileName, From),
- ?hcrv("canceled", []),
{noreply, State#state{canceled = [RequestId | Canceled]}};
+handle_cast({cancel, RequestId},
+ #state{profile_name = ProfileName,
+ request = undefined,
+ canceled = Canceled} = State) ->
+ ?hcrv("cancel", [{request_id, RequestId},
+ {curr_req_id, undefined},
+ {profile, ProfileName},
+ {canceled, Canceled}]),
+ {noreply, State};
+
handle_cast(stream_next, #state{session = Session} = State) ->
activate_once(Session),
@@ -466,18 +475,18 @@ handle_info({Proto, _Socket, Data},
{Module, whole_body, [Body, Length]} ->
?hcrd("data processed - whole body", [{length, Length}]),
{_, Code, _} = StatusLine,
- {NewBody, NewRequest} = stream(Body, Request, Code),
+ {Streamed, NewBody, NewRequest} = stream(Body, Request, Code),
%% When we stream we will not keep the already
%% streamed data, that would be a waste of memory.
NewLength =
- case Stream of
- none ->
+ case Streamed of
+ false ->
Length;
- _ ->
+ true ->
Length - size(Body)
end,
- NewState = next_body_chunk(State),
+ NewState = next_body_chunk(State, Code),
NewMFA = {Module, whole_body, [NewBody, NewLength]},
{noreply, NewState#state{mfa = NewMFA,
request = NewRequest}};
@@ -489,8 +498,8 @@ handle_info({Proto, _Socket, Data},
%% The response body is chunk-encoded. Steal decoded
%% chunks as much as possible to stream.
{_, Code, _} = StatusLine,
- {NewBody, NewRequest} = stream(BodySoFar, Request, Code),
- NewState = next_body_chunk(State),
+ {_, NewBody, NewRequest} = stream(BodySoFar, Request, Code),
+ NewState = next_body_chunk(State, Code),
NewMFA = {Module, decode_size,
[TotalChunk, HexList,
{MaxBodySize, NewBody, AccLength, MaxHeaderSize}]},
@@ -509,8 +518,8 @@ handle_info({Proto, _Socket, Data},
NewChunkSize = ChunkSize - ChunkSizeToSteal,
{_, Code, _} = StatusLine,
- {NewBody, NewRequest} = stream(StolenBody, Request, Code),
- NewState = next_body_chunk(State),
+ {_, NewBody, NewRequest} = stream(StolenBody, Request, Code),
+ NewState = next_body_chunk(State, Code),
NewMFA = {Module, decode_data,
[NewChunkSize, NewTotalChunk,
{MaxBodySize, NewBody, AccLength, MaxHeaderSize}]},
@@ -521,19 +530,12 @@ handle_info({Proto, _Socket, Data},
activate_once(Session),
{noreply, State#state{mfa = NewMFA}}
catch
- exit:_Exit ->
- ?hcrd("data processing exit", [{exit, _Exit}]),
+ _:_Reason ->
+ ?hcrd("data processing exit", [{exit, _Reason}]),
ClientReason = {could_not_parse_as_http, Data},
ClientErrMsg = httpc_response:error(Request, ClientReason),
NewState = answer_request(Request, ClientErrMsg, State),
- {stop, normal, NewState};
- error:_Error ->
- ?hcrd("data processing error", [{error, _Error}]),
- ClientReason = {could_not_parse_as_http, Data},
- ClientErrMsg = httpc_response:error(Request, ClientReason),
- NewState = answer_request(Request, ClientErrMsg, State),
{stop, normal, NewState}
-
end,
?hcri("data processed", [{final_result, FinalResult}]),
FinalResult;
@@ -643,7 +645,7 @@ handle_info({timeout, RequestId},
handle_info(timeout_queue, State = #state{request = undefined}) ->
{stop, normal, State};
-%% Timing was such as the pipeline_timout was not canceled!
+%% Timing was such as the queue_timeout was not canceled!
handle_info(timeout_queue, #state{timers = Timers} = State) ->
{noreply, State#state{timers =
Timers#timers{queue_timer = undefined}}};
@@ -1070,13 +1072,13 @@ handle_http_msg({ChunkedHeaders, Body},
?hcrt("handle_http_msg",
[{chunked_headers, ChunkedHeaders}, {headers, Headers}]),
NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders),
- {NewBody, NewRequest} = stream(Body, State#state.request, Code),
+ {_, NewBody, NewRequest} = stream(Body, State#state.request, Code),
handle_response(State#state{headers = NewHeaders,
body = NewBody,
request = NewRequest});
handle_http_msg(Body, #state{status_line = {_,Code, _}} = State) ->
?hcrt("handle_http_msg", [{code, Code}]),
- {NewBody, NewRequest} = stream(Body, State#state.request, Code),
+ {_, NewBody, NewRequest} = stream(Body, State#state.request, Code),
handle_response(State#state{body = NewBody, request = NewRequest}).
handle_http_body(_, #state{status = {ssl_tunnel, _},
@@ -1111,14 +1113,14 @@ handle_http_body(Body, #state{headers = Headers,
case case_insensitive_header(TransferEnc) of
"chunked" ->
?hcrt("handle_http_body - chunked", []),
- case http_chunk:decode(Body, State#state.max_body_size,
- State#state.max_header_size) of
+ try http_chunk:decode(Body, State#state.max_body_size,
+ State#state.max_header_size) of
{Module, Function, Args} ->
?hcrt("handle_http_body - new mfa",
[{module, Module},
{function, Function},
{args, Args}]),
- NewState = next_body_chunk(State),
+ NewState = next_body_chunk(State, Code),
{noreply, NewState#state{mfa =
{Module, Function, Args}}};
{ok, {ChunkedHeaders, NewBody}} ->
@@ -1127,30 +1129,38 @@ handle_http_body(Body, #state{headers = Headers,
{new_body, NewBody}]),
NewHeaders = http_chunk:handle_headers(Headers,
ChunkedHeaders),
- handle_response(State#state{headers = NewHeaders,
- body = NewBody})
+ case Body of
+ <<>> ->
+ handle_response(State#state{headers = NewHeaders,
+ body = NewBody});
+ _ ->
+ {_, NewBody2, _} =
+ stream(NewBody, Request, Code),
+ handle_response(State#state{headers = NewHeaders,
+ body = NewBody2})
+ end
+ catch throw:{error, Reason} ->
+ NewState =
+ answer_request(Request,
+ httpc_response:error(Request,
+ Reason),
+ State),
+ {stop, normal, NewState}
end;
- Encoding when is_list(Encoding) ->
- ?hcrt("handle_http_body - encoding", [{encoding, Encoding}]),
- NewState = answer_request(Request,
- httpc_response:error(Request,
- unknown_encoding),
- State),
- {stop, normal, NewState};
- _ ->
- ?hcrt("handle_http_body - other", []),
+ Enc when Enc =:= "identity"; Enc =:= undefined ->
+ ?hcrt("handle_http_body - identity", []),
Length =
list_to_integer(Headers#http_response_h.'content-length'),
case ((Length =< MaxBodySize) orelse (MaxBodySize =:= nolimit)) of
true ->
case httpc_response:whole_body(Body, Length) of
{ok, Body} ->
- {NewBody, NewRequest} =
+ {_, NewBody, NewRequest} =
stream(Body, Request, Code),
handle_response(State#state{body = NewBody,
request = NewRequest});
MFA ->
- NewState = next_body_chunk(State),
+ NewState = next_body_chunk(State, Code),
{noreply, NewState#state{mfa = MFA}}
end;
false ->
@@ -1160,7 +1170,14 @@ handle_http_body(Body, #state{headers = Headers,
body_too_big),
State),
{stop, normal, NewState}
- end
+ end;
+ Encoding when is_list(Encoding) ->
+ ?hcrt("handle_http_body - other", [{encoding, Encoding}]),
+ NewState = answer_request(Request,
+ httpc_response:error(Request,
+ unknown_encoding),
+ State),
+ {stop, normal, NewState}
end.
handle_response(#state{status = new} = State) ->
@@ -1229,6 +1246,7 @@ handle_response(#state{request = Request,
handle_queue(State#state{request = undefined}, Data);
{ok, Msg, Data} ->
?hcrd("handle response - ok", []),
+ stream_remaining_body(Body, Request, StatusLine),
end_stream(StatusLine, Request),
NewState = maybe_send_answer(Request, Msg, State),
handle_queue(NewState, Data);
@@ -1303,7 +1321,8 @@ handle_pipeline(#state{status = pipeline,
handle_keep_alive_queue(#state{status = keep_alive,
session = Session,
profile_name = ProfileName,
- options = #options{keep_alive_timeout = TimeOut}} = State,
+ options = #options{keep_alive_timeout = TimeOut,
+ proxy = Proxy}} = State,
Data) ->
?hcrd("handle keep_alive", [{profile, ProfileName},
@@ -1324,14 +1343,15 @@ handle_keep_alive_queue(#state{status = keep_alive,
State#state{keep_alive = KeepAlive}, Data);
false ->
?hcrv("next request", [{request, NextRequest}]),
- #request{address = Address} = NextRequest,
+ #request{address = Addr} = NextRequest,
+ Address = handle_proxy(Addr, Proxy),
case httpc_request:send(Address, Session, NextRequest) of
ok ->
receive_response(NextRequest,
Session, <<>>,
State#state{keep_alive = KeepAlive});
{error, Reason} ->
- {reply, {keep_alive_failed, Reason}, State}
+ {stop, {shutdown, {keepalive_failed, Reason}}, State}
end
end
end.
@@ -1346,7 +1366,7 @@ handle_empty_queue(Session, ProfileName, TimeOut, State) ->
%% closed by the server, the client may want to close it.
NewState = activate_queue_timeout(TimeOut, State),
update_session(ProfileName, Session, #session.queue_length, 0),
- %% Note mfa will be initilized when a new request
+ %% Note mfa will be initialized when a new request
%% arrives.
{noreply,
NewState#state{request = undefined,
@@ -1389,6 +1409,8 @@ case_insensitive_header(Str) ->
activate_once(#session{socket = Socket, socket_type = SocketType}) ->
http_transport:setopts(SocketType, Socket, [{active, once}]).
+close_socket(#session{socket = {remote_close,_}}) ->
+ ok;
close_socket(#session{socket = Socket, socket_type = SocketType}) ->
http_transport:close(SocketType, Socket).
@@ -1632,21 +1654,21 @@ start_stream({_Version, _Code, _ReasonPhrase}, _Headers,
{ok, Request};
start_stream({_Version, Code, _ReasonPhrase}, Headers,
#request{stream = self} = Request)
- when (Code =:= 200) orelse (Code =:= 206) ->
+ when ?IS_STREAMED(Code) ->
?hcrt("start stream - self", [{code, Code}]),
Msg = httpc_response:stream_start(Headers, Request, ignore),
httpc_response:send(Request#request.from, Msg),
{ok, Request};
start_stream({_Version, Code, _ReasonPhrase}, Headers,
#request{stream = {self, once}} = Request)
- when (Code =:= 200) orelse (Code =:= 206) ->
+ when ?IS_STREAMED(Code) ->
?hcrt("start stream - self:once", [{code, Code}]),
Msg = httpc_response:stream_start(Headers, Request, self()),
httpc_response:send(Request#request.from, Msg),
{ok, Request};
start_stream({_Version, Code, _ReasonPhrase}, _Headers,
#request{stream = Filename} = Request)
- when ((Code =:= 200) orelse (Code =:= 206)) andalso is_list(Filename) ->
+ when ?IS_STREAMED(Code) andalso is_list(Filename) ->
?hcrt("start stream", [{code, Code}, {filename, Filename}]),
case file:open(Filename, [write, raw, append, delayed_write]) of
{ok, Fd} ->
@@ -1659,6 +1681,10 @@ start_stream(_StatusLine, _Headers, Request) ->
?hcrt("start stream - no op", []),
{ok, Request}.
+stream_remaining_body(<<>>, _, _) ->
+ ok;
+stream_remaining_body(Body, Request, {_, Code, _}) ->
+ stream(Body, Request, Code).
%% Note the end stream message is handled by httpc_response and will
%% be sent by answer_request
@@ -1694,13 +1720,15 @@ end_stream(SL, R) ->
next_body_chunk(#state{request = #request{stream = {self, once}},
once = once,
- session = Session} = State) ->
+ session = Session} = State,
+ Code) when ?IS_STREAMED(Code) ->
activate_once(Session),
State#state{once = inactive};
next_body_chunk(#state{request = #request{stream = {self, once}},
- once = inactive} = State) ->
+ once = inactive} = State,
+ Code) when ?IS_STREAMED(Code) ->
State; %% Wait for user to call stream_next
-next_body_chunk(#state{session = Session} = State) ->
+next_body_chunk(#state{session = Session} = State, _) ->
activate_once(Session),
State.
@@ -1791,7 +1819,7 @@ tls_tunnel_request(#request{headers = Headers,
host_header(#http_request_h{host = Host}, _) ->
Host;
-%% Handles header_as_is
+%% Handles headers_as_is
host_header(_, URI) ->
{ok, {_, _, Host, _, _, _}} = http_uri:parse(URI),
Host.
@@ -1799,13 +1827,15 @@ host_header(_, URI) ->
tls_upgrade(#state{status =
{ssl_tunnel,
#request{settings =
- #http_options{ssl = {_, TLSOptions} = SocketType}} = Request},
+ #http_options{ssl = {_, TLSOptions0} = SocketType},
+ address = {Host, _} = Address} = Request},
session = #session{socket = TCPSocket} = Session0,
options = Options} = State) ->
+ TLSOptions = maybe_add_sni(Host, TLSOptions0),
+
case ssl:connect(TCPSocket, TLSOptions) of
{ok, TLSSocket} ->
- Address = Request#request.address,
ClientClose = httpc_request:is_client_closing(Request#request.headers),
SessionType = httpc_manager:session_type(Options),
Session = Session0#session{
@@ -1826,10 +1856,23 @@ tls_upgrade(#state{status =
status = new
},
{noreply, activate_request_timeout(NewState)};
- {error, _Reason} ->
+ {error, Reason} ->
+ Error = httpc_response:error(Request, {failed_connect,
+ [{to_address, Address},
+ {tls, TLSOptions, Reason}]}),
+ maybe_send_answer(Request, Error, State),
{stop, normal, State#state{request = Request}}
end.
+maybe_add_sni(Host, Options) ->
+ case http_util:is_hostname(Host) andalso
+ not lists:keymember(server_name_indication, 1, Options) of
+ true ->
+ [{server_name_indication, Host} | Options];
+ false ->
+ Options
+ end.
+
%% ---------------------------------------------------------------------
%% Session wrappers
%% ---------------------------------------------------------------------
@@ -1848,6 +1891,7 @@ update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) ->
Session2 = erlang:setelement(Pos, Session, Value),
insert_session(Session2, ProfileName);
T:E ->
+ Stacktrace = erlang:get_stacktrace(),
error_logger:error_msg("Failed updating session: "
"~n ProfileName: ~p"
"~n SessionId: ~p"
@@ -1862,7 +1906,7 @@ update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) ->
[ProfileName, SessionId, Pos, Value,
(catch httpc_manager:which_session_info(ProfileName)),
Session,
- (catch httpc_manager:lookup_session(ProfileName, SessionId)),
+ (catch httpc_manager:lookup_session(SessionId, ProfileName)),
T, E]),
exit({failed_updating_session,
[{profile, ProfileName},
@@ -1871,7 +1915,7 @@ update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) ->
{value, Value},
{etype, T},
{error, E},
- {stacktrace, erlang:get_stacktrace()}]})
+ {stacktrace, Stacktrace}]})
end.
diff --git a/lib/inets/src/http_client/httpc_handler_sup.erl b/lib/inets/src/http_client/httpc_handler_sup.erl
index f7a0b014b3..ad70afe2ae 100644
--- a/lib/inets/src/http_client/httpc_handler_sup.erl
+++ b/lib/inets/src/http_client/httpc_handler_sup.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_client/httpc_internal.hrl b/lib/inets/src/http_client/httpc_internal.hrl
index 30e2742e9d..f4e69cc1fa 100644
--- a/lib/inets/src/http_client/httpc_internal.hrl
+++ b/lib/inets/src/http_client/httpc_internal.hrl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl
index c45dcab802..4cb6f005ad 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -29,7 +30,6 @@
start_link/3,
request/2,
cancel_request/2,
- request_canceled/3,
request_done/2,
retry_request/2,
redirect_request/2,
@@ -144,22 +144,7 @@ redirect_request(Request, ProfileName) ->
%%--------------------------------------------------------------------
cancel_request(RequestId, ProfileName) ->
- call(ProfileName, {cancel_request, RequestId}).
-
-
-%%--------------------------------------------------------------------
-%% Function: request_canceled(RequestId, ProfileName) -> ok
-%% RequestId - ref()
-%% ProfileName = atom()
-%%
-%% Description: Confirms that a request has been canceld. Intended to
-%% be called by the httpc handler process.
-%%--------------------------------------------------------------------
-
-request_canceled(RequestId, ProfileName, From) ->
- gen_server:reply(From, ok),
- cast(ProfileName, {request_canceled, RequestId}).
-
+ cast(ProfileName, {cancel_request, RequestId}).
%%--------------------------------------------------------------------
%% Function: request_done(RequestId, ProfileName) -> ok
@@ -474,26 +459,6 @@ handle_call({request, Request}, _, State) ->
{stop, Error, httpc_response:error(Request, Error), State}
end;
-handle_call({cancel_request, RequestId}, From,
- #state{handler_db = HandlerDb} = State) ->
- ?hcri("cancel_request", [{request_id, RequestId}]),
- case ets:lookup(HandlerDb, RequestId) of
- [] ->
- %% The request has allready compleated make sure
- %% it is deliverd to the client process queue so
- %% it can be thrown away by httpc:cancel_request
- %% This delay is hopfully a temporary workaround.
- %% Note that it will not not delay the manager,
- %% only the client that called httpc:cancel_request
- timer:apply_after(?DELAY, gen_server, reply, [From, ok]),
- {noreply, State};
- [{_, Pid, _}] ->
- httpc_handler:cancel(RequestId, Pid, From),
- {noreply,
- State#state{cancel =
- [{RequestId, Pid, From} | State#state.cancel]}}
- end;
-
handle_call(reset_cookies, _, #state{cookie_db = CookieDb} = State) ->
?hcrv("reset cookies", []),
httpc_cookie:reset_db(CookieDb),
@@ -555,19 +520,19 @@ handle_cast({retry_or_redirect_request, Request}, State) ->
{stop, Error, State}
end;
-handle_cast({request_canceled, RequestId}, State) ->
- ?hcrv("request canceled", [{request_id, RequestId}]),
- ets:delete(State#state.handler_db, RequestId),
- case lists:keysearch(RequestId, 1, State#state.cancel) of
- {value, Entry = {RequestId, _, From}} ->
- ?hcrt("found in cancel", [{from, From}]),
- {noreply,
- State#state{cancel = lists:delete(Entry, State#state.cancel)}};
- Else ->
- ?hcrt("not found in cancel", [{else, Else}]),
- {noreply, State}
+handle_cast({cancel_request, RequestId},
+ #state{handler_db = HandlerDb} = State) ->
+ case ets:lookup(HandlerDb, RequestId) of
+ [] ->
+ %% Request already compleated nothing to
+ %% cancel
+ {noreply, State};
+ [{_, Pid, _}] ->
+ httpc_handler:cancel(RequestId, Pid),
+ ets:delete(State#state.handler_db, RequestId),
+ {noreply, State}
end;
-
+
handle_cast({request_done, RequestId}, State) ->
?hcrv("request done", [{request_id, RequestId}]),
ets:delete(State#state.handler_db, RequestId),
@@ -629,22 +594,8 @@ handle_info({'EXIT', _, _}, State) ->
%% Handled in DOWN
{noreply, State};
handle_info({'DOWN', _, _, Pid, _}, State) ->
- ets:match_delete(State#state.handler_db, {'_', Pid, '_'}),
-
- %% If there where any canceled request, handled by the
- %% the process that now has terminated, the
- %% cancelation can be viewed as sucessfull!
- NewCanceldList =
- lists:foldl(fun(Entry = {_, HandlerPid, From}, Acc) ->
- case HandlerPid of
- Pid ->
- gen_server:reply(From, ok),
- lists:delete(Entry, Acc);
- _ ->
- Acc
- end
- end, State#state.cancel, State#state.cancel),
- {noreply, State#state{cancel = NewCanceldList}};
+ ets:match_delete(State#state.handler_db, {'_', Pid, '_'}),
+ {noreply, State};
handle_info(Info, State) ->
Report = io_lib:format("Unknown message in "
"httpc_manager:handle_info ~p~n", [Info]),
@@ -869,7 +820,7 @@ select_session(Method, HostPort, Scheme, SessionType,
?hcrd("select session", [{host_port, HostPort},
{scheme, Scheme},
{type, SessionType},
- {candidates, Candidates}]),
+ {candidates, Candidates}]),
select_session(Candidates, MaxKeepAlive, MaxPipe, SessionType);
false ->
no_connection
diff --git a/lib/inets/src/http_client/httpc_profile_sup.erl b/lib/inets/src/http_client/httpc_profile_sup.erl
index 29f86aa373..334f2eb1c7 100644
--- a/lib/inets/src/http_client/httpc_profile_sup.erl
+++ b/lib/inets/src/http_client/httpc_profile_sup.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_client/httpc_request.erl b/lib/inets/src/http_client/httpc_request.erl
index 879053f0f2..e8d020c165 100644
--- a/lib/inets/src/http_client/httpc_request.erl
+++ b/lib/inets/src/http_client/httpc_request.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -185,15 +186,19 @@ is_client_closing(Headers) ->
%%%========================================================================
%%% Internal functions
%%%========================================================================
-post_data(Method, Headers, {ContentType, Body}, HeadersAsIs)
- when (Method =:= post) orelse (Method =:= put) ->
+post_data(Method, Headers, {ContentType, Body}, HeadersAsIs)
+ when (Method =:= post)
+ orelse (Method =:= put)
+ orelse (Method =:= patch)
+ orelse (Method =:= delete) ->
+
NewBody = case Headers#http_request_h.expect of
- "100-continue" ->
- "";
- _ ->
- Body
- end,
-
+ "100-continue" ->
+ "";
+ _ ->
+ Body
+ end,
+
NewHeaders = case HeadersAsIs of
[] ->
Headers#http_request_h{
@@ -211,7 +216,7 @@ post_data(Method, Headers, {ContentType, Body}, HeadersAsIs)
_ ->
HeadersAsIs
end,
-
+
{NewHeaders, NewBody};
post_data(_, Headers, _, []) ->
diff --git a/lib/inets/src/http_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl
index 9107dfbf05..91256fa6a2 100644
--- a/lib/inets/src/http_client/httpc_response.erl
+++ b/lib/inets/src/http_client/httpc_response.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -327,7 +328,7 @@ status_service_unavailable(Response = {_, Headers, _}, Request) ->
undefined ->
status_server_error_50x(Response, Request);
Time when (length(Time) < 3) -> % Wait only 99 s or less
- NewTime = list_to_integer(Time) * 100, % time in ms
+ NewTime = list_to_integer(Time) * 1000, % time in ms
{_, Data} = format_response(Response),
{retry, {NewTime, Request}, Data};
_ ->
diff --git a/lib/inets/src/http_client/httpc_sup.erl b/lib/inets/src/http_client/httpc_sup.erl
index 152a57d32d..b2d944c834 100644
--- a/lib/inets/src/http_client/httpc_sup.erl
+++ b/lib/inets/src/http_client/httpc_sup.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_lib/Makefile b/lib/inets/src/http_lib/Makefile
index 51167b34fa..8248e37c44 100644
--- a/lib/inets/src/http_lib/Makefile
+++ b/lib/inets/src/http_lib/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2012. All Rights Reserved.
+# Copyright Ericsson AB 2005-2016. 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/.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# 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.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
diff --git a/lib/inets/src/http_lib/http_chunk.erl b/lib/inets/src/http_lib/http_chunk.erl
index d15d7ba49a..742d634577 100644
--- a/lib/inets/src/http_lib/http_chunk.erl
+++ b/lib/inets/src/http_lib/http_chunk.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -24,7 +25,7 @@
-include("http_internal.hrl").
%% API
--export([decode/3, encode/1, encode_last/0, handle_headers/2]).
+-export([decode/3, encode/1, encode_last/0, encode_last/1, handle_headers/2]).
%% Callback API - used for example if the chunkedbody is received a
%% little at a time on a socket.
-export([decode_size/1, ignore_extensions/1, decode_data/1, decode_trailer/1]).
@@ -56,7 +57,7 @@
%%-------------------------------------------------------------------------
decode(ChunkedBody, MaxBodySize, MaxHeaderSize) ->
%% Note decode_size will call decode_data.
- decode_size([ChunkedBody, <<>>, [],
+ decode_size([ChunkedBody, <<>>, [], 0,
{MaxBodySize, <<>>, 0, MaxHeaderSize}]).
%%-------------------------------------------------------------------------
@@ -84,6 +85,11 @@ encode(Chunk) when is_list(Chunk)->
encode_last() ->
<<$0, ?CR, ?LF, ?CR, ?LF >>.
+encode_last([]) ->
+ encode_last();
+encode_last(Trailers0) ->
+ Trailers = list_to_binary(encode_trailers(Trailers0)),
+ <<$0, ?CR, ?LF, Trailers/binary>>.
%%-------------------------------------------------------------------------
%% handle_headers(HeaderRecord, ChunkedHeaders) -> NewHeaderRecord
@@ -119,65 +125,80 @@ handle_headers(ResponseHeaderRecord = #http_response_h{}, ChunkedHeaders) ->
%% Functions that may be returned during the decoding process
%% if the input data is incompleate.
-decode_size([Bin, Rest, HexList, Info]) ->
- decode_size(<<Rest/binary, Bin/binary>>, HexList, Info).
+decode_size([Bin, Rest, HexList, AccSize, Info]) ->
+ decode_size(<<Rest/binary, Bin/binary>>, HexList, AccSize, Info).
-ignore_extensions([Bin, Rest, NextFunction]) ->
- ignore_extensions(<<Rest/binary, Bin/binary>>, NextFunction).
+ignore_extensions([Bin, Rest, RemainingSize, TotalMaxHeaderSize, NextFunction]) ->
+ ignore_extensions(<<Rest/binary, Bin/binary>>, RemainingSize, TotalMaxHeaderSize, NextFunction).
decode_data([Bin, ChunkSize, TotalChunk, Info]) ->
decode_data(ChunkSize, <<TotalChunk/binary, Bin/binary>>, Info).
-decode_trailer([Bin, Rest, Header, Headers, MaxHeaderSize, Body,
- BodyLength]) ->
+decode_trailer([Bin, Rest, Header, Headers, Body,
+ BodyLength, RemainingSize, TotalMaxHeaderSize]) ->
decode_trailer(<<Rest/binary, Bin/binary>>,
- Header, Headers, MaxHeaderSize, Body, BodyLength).
+ Header, Headers, Body, BodyLength, RemainingSize, TotalMaxHeaderSize).
%%%========================================================================
%%% Internal functions
%%%========================================================================
-decode_size(<<>>, HexList, Info) ->
- {?MODULE, decode_size, [<<>>, HexList, Info]};
-decode_size(Data = <<?CR, ?LF, ChunkRest/binary>>, HexList,
+decode_size(_, _, AccHeaderSize, {_,_,_, MaxHeaderSize}) when
+ AccHeaderSize > MaxHeaderSize ->
+ throw({error, {header_too_long, {max, MaxHeaderSize}}});
+
+decode_size(<<>>, HexList, AccHeaderSize, Info) ->
+ {?MODULE, decode_size, [<<>>, HexList, AccHeaderSize, Info]};
+decode_size(Data = <<?CR, ?LF, ChunkRest/binary>>, HexList, AccHeaderSize,
{MaxBodySize, Body,
AccLength,
MaxHeaderSize}) ->
- ChunkSize = http_util:hexlist_to_integer(lists:reverse(HexList)),
- case ChunkSize of
+ try http_util:hexlist_to_integer(lists:reverse(string:strip(HexList, left))) of
0 -> % Last chunk, there was no data
- ignore_extensions(Data, {?MODULE, decode_trailer,
- [<<>>, [],[], MaxHeaderSize,
- Body,
- integer_to_list(AccLength)]});
- _ ->
+ ignore_extensions(Data, remaing_size(MaxHeaderSize, AccHeaderSize), MaxHeaderSize,
+ {?MODULE, decode_trailer,
+ [<<>>, [],[],
+ Body,
+ integer_to_list(AccLength)]});
+ ChunkSize ->
%% Note decode_data may call decode_size again if there
%% is more than one chunk, hence here is where the last parameter
%% to this function comes in.
decode_data(ChunkSize, ChunkRest, {MaxBodySize, Body,
- ChunkSize + AccLength ,
+ ChunkSize + AccLength,
MaxHeaderSize})
+ catch
+ _:_ ->
+ throw({error, {chunk_size, lists:reverse(HexList)}})
end;
-decode_size(<<";", Rest/binary>>, HexList, Info) ->
+decode_size(<<";", Rest/binary>>, HexList, AccHeaderSize, {_,_,_, MaxHeaderSize} = Info) ->
%% Note ignore_extensions will call decode_size/1 again when
%% it ignored all extensions.
- ignore_extensions(Rest, {?MODULE, decode_size, [<<>>, HexList, Info]});
-decode_size(<<?CR>> = Data, HexList, Info) ->
- {?MODULE, decode_size, [Data, HexList, Info]};
-decode_size(<<Octet, Rest/binary>>, HexList, Info) ->
- decode_size(Rest, [Octet | HexList], Info).
+ ignore_extensions(Rest, remaing_size(MaxHeaderSize, AccHeaderSize), MaxHeaderSize,
+ {?MODULE, decode_size, [<<>>, HexList, AccHeaderSize, Info]});
+decode_size(<<?CR>> = Data, HexList, AccHeaderSize, Info) ->
+ {?MODULE, decode_size, [Data, HexList, AccHeaderSize, Info]};
+decode_size(<<Octet, Rest/binary>>, HexList, AccHeaderSize, Info) ->
+ decode_size(Rest, [Octet | HexList], AccHeaderSize + 1, Info).
%% "All applications MUST ignore chunk-extension extensions they
%% do not understand.", see RFC 2616 Section 3.6.1 We don't
%% understand any extension...
-ignore_extensions(<<>>, NextFunction) ->
- {?MODULE, ignore_extensions, [<<>>, NextFunction]};
-ignore_extensions(Data = <<?CR, ?LF, _ChunkRest/binary>>,
+ignore_extensions(_, 0, TotalMaxHeaderSize, _) ->
+ throw({error, {header_too_long, {max, TotalMaxHeaderSize}}});
+ignore_extensions(<<>>, RemainingSize, TotalMaxHeaderSize, NextFunction) ->
+ {?MODULE, ignore_extensions, [<<>>, RemainingSize, TotalMaxHeaderSize, NextFunction]};
+ignore_extensions(Data = <<?CR, ?LF, _ChunkRest/binary>>, RemainingSize, TotalMaxHeaderSize,
{Module, Function, Args}) ->
- Module:Function([Data | Args]);
-ignore_extensions(<<?CR>> = Data, NextFunction) ->
- {?MODULE, ignore_extensions, [Data, NextFunction]};
-ignore_extensions(<<_Octet, Rest/binary>>, NextFunction) ->
- ignore_extensions(Rest, NextFunction).
+ case Function of
+ decode_trailer ->
+ Module:Function([Data | Args ++ [RemainingSize, TotalMaxHeaderSize]]);
+ _ ->
+ Module:Function([Data | Args])
+ end;
+ignore_extensions(<<?CR>> = Data, RemainingSize, TotalMaxHeaderSize, NextFunction) ->
+ {?MODULE, ignore_extensions, [Data, RemainingSize, TotalMaxHeaderSize, NextFunction]};
+ignore_extensions(<<_Octet, Rest/binary>>, RemainingSize, TotalMaxHeaderSize, NextFunction) ->
+ ignore_extensions(Rest, remaing_size(RemainingSize, 1), TotalMaxHeaderSize, NextFunction).
decode_data(ChunkSize, TotalChunk,
Info = {MaxBodySize, BodySoFar, AccLength, MaxHeaderSize})
@@ -189,83 +210,89 @@ decode_data(ChunkSize, TotalChunk,
%% once it ignored all extensions.
{?MODULE, ignore_extensions,
[<<>>,
- {?MODULE, decode_trailer, [<<>>, [],[], MaxHeaderSize,
+ {?MODULE, decode_trailer, [<<>>, [],[],
<<BodySoFar/binary, Data/binary>>,
integer_to_list(AccLength)]}]};
<<Data:ChunkSize/binary, ?CR, ?LF, "0", ";", Rest/binary>> ->
%% Note ignore_extensions will call decode_trailer/1
%% once it ignored all extensions.
- ignore_extensions(Rest, {?MODULE, decode_trailer,
- [<<>>, [],[], MaxHeaderSize,
+ ignore_extensions(Rest, MaxHeaderSize, MaxHeaderSize,
+ {?MODULE, decode_trailer,
+ [<<>>, [],[],
<<BodySoFar/binary, Data/binary>>,
integer_to_list(AccLength)]});
<<Data:ChunkSize/binary, ?CR, ?LF, "0", ?CR, ?LF>> ->
- {?MODULE, decode_trailer, [<<?CR, ?LF>>, [],[], MaxHeaderSize,
+ {?MODULE, decode_trailer, [<<?CR, ?LF>>, [],[],
<<BodySoFar/binary, Data/binary>>,
- integer_to_list(AccLength)]};
+ integer_to_list(AccLength), MaxHeaderSize, MaxHeaderSize]};
<<Data:ChunkSize/binary, ?CR, ?LF, "0", ?CR, ?LF, Rest/binary>> ->
- decode_trailer(<<?CR, ?LF, Rest/binary>>, [],[], MaxHeaderSize,
+ decode_trailer(<<?CR, ?LF, Rest/binary>>, [],[],
<<BodySoFar/binary, Data/binary>>,
- integer_to_list(AccLength));
- %% There are more chunks, so here we go agin...
+ integer_to_list(AccLength), MaxHeaderSize, MaxHeaderSize);
+ %% There are more chunks, so here we go again...
<<Data:ChunkSize/binary, ?CR, ?LF>> ->
NewBody = <<BodySoFar/binary, Data/binary>>,
- {?MODULE, decode_size, [<<>>, [], {MaxBodySize, NewBody, AccLength, MaxHeaderSize}]};
+ {?MODULE, decode_size, [<<>>, [], 0, {MaxBodySize, NewBody, AccLength, MaxHeaderSize}]};
<<Data:ChunkSize/binary, ?CR, ?LF, Rest/binary>>
when (AccLength < MaxBodySize) or (MaxBodySize == nolimit) ->
- decode_size(Rest, [],
+ decode_size(Rest, [], 0,
{MaxBodySize, <<BodySoFar/binary, Data/binary>>,
AccLength, MaxHeaderSize});
<<_:ChunkSize/binary, ?CR, ?LF, _/binary>> ->
- throw({error, body_too_big});
+ throw({error, {body_too_big, {max, MaxBodySize}}});
_ ->
{?MODULE, decode_data, [ChunkSize, TotalChunk, Info]}
end;
decode_data(ChunkSize, TotalChunk, Info) ->
{?MODULE, decode_data, [ChunkSize, TotalChunk, Info]}.
-decode_trailer(<<>>, Header, Headers, MaxHeaderSize, Body, BodyLength) ->
- {?MODULE, decode_trailer, [<<>>, Header, Headers, MaxHeaderSize, Body,
- BodyLength]};
-
+decode_trailer(_,_,_,_,_, 0, TotalMaxHeaderSize) ->
+ throw({error, {header_too_long, {max, TotalMaxHeaderSize}}});
+decode_trailer(<<>>, Header, Headers, Body, BodyLength, RemainingSize, TotalMaxHeaderSize) ->
+ {?MODULE, decode_trailer, [<<>>, Header, Headers, Body,
+ BodyLength, RemainingSize, TotalMaxHeaderSize]};
%% Note: If Bin is not empty it is part of a pipelined request/response.
-decode_trailer(<<?CR,?LF,?CR,?LF, Bin/binary>>, [], [], _, Body, BodyLength) ->
+decode_trailer(<<?CR,?LF,?CR,?LF, Bin/binary>>, [], [], Body, BodyLength, _, _) ->
{ok, {["content-length:" ++ BodyLength], <<Body/binary, Bin/binary>>}};
decode_trailer(<<?CR,?LF,?CR,?LF, Bin/binary>>,
- Header, Headers, MaxHeaderSize, Body, BodyLength) ->
+ Header, Headers, Body, BodyLength, _, _) ->
NewHeaders = case Header of
[] ->
Headers;
_ ->
[lists:reverse(Header) | Headers]
end,
- Length = length(NewHeaders),
- case Length > MaxHeaderSize of
- true ->
- throw({error, {header_too_long, MaxHeaderSize,
- MaxHeaderSize-Length}});
- false ->
- {ok, {["content-length:" ++ BodyLength | NewHeaders],
- <<Body/binary, Bin/binary>>}}
- end;
-decode_trailer(<<?CR,?LF,?CR>> = Data, Header, Headers, MaxHeaderSize,
- Body, BodyLength) ->
- {?MODULE, decode_trailer, [Data, Header, Headers, MaxHeaderSize, Body,
- BodyLength]};
-decode_trailer(<<?CR,?LF>> = Data, Header, Headers, MaxHeaderSize,
- Body, BodyLength) ->
- {?MODULE, decode_trailer, [Data, Header, Headers, MaxHeaderSize, Body,
- BodyLength]};
-decode_trailer(<<?CR>> = Data, Header, Headers, MaxHeaderSize,
- Body, BodyLength) ->
- {?MODULE, decode_trailer, [Data, Header, Headers, MaxHeaderSize, Body,
- BodyLength]};
-decode_trailer(<<?CR, ?LF, Rest/binary>>, Header, Headers,
- MaxHeaderSize, Body, BodyLength) ->
+ {ok, {["content-length:" ++ BodyLength | NewHeaders],
+ <<Body/binary, Bin/binary>>}};
+decode_trailer(<<?CR,?LF,?CR>> = Data, Header, Headers,
+ Body, BodyLength, RemainingSize, TotalMaxHeaderSize) ->
+ {?MODULE, decode_trailer, [Data, Header, Headers, Body,
+ BodyLength, RemainingSize, TotalMaxHeaderSize]};
+decode_trailer(<<?CR,?LF>> = Data, Header, Headers,
+ Body, BodyLength, RemainingSize, TotalMaxHeaderSize) ->
+ {?MODULE, decode_trailer, [Data, Header, Headers, Body,
+ BodyLength, RemainingSize, TotalMaxHeaderSize]};
+decode_trailer(<<?CR>> = Data, Header, Headers,
+ Body, BodyLength, RemainingSize, TotalMaxHeaderSize) ->
+ {?MODULE, decode_trailer, [Data, Header, Headers, Body,
+ BodyLength, RemainingSize, TotalMaxHeaderSize]};
+decode_trailer(<<?CR, ?LF, Rest/binary>>, Header, Headers, Body, BodyLength, RemainingSize, TotalMaxHeaderSize) ->
decode_trailer(Rest, [], [lists:reverse(Header) | Headers],
- MaxHeaderSize, Body, BodyLength);
+ Body, BodyLength, RemainingSize, TotalMaxHeaderSize);
+decode_trailer(<<Octet, Rest/binary>>, Header, Headers, Body,
+ BodyLength, RemainingSize, TotalMaxHeaderSize) ->
+ decode_trailer(Rest, [Octet | Header], Headers,
+ Body, BodyLength, remaing_size(RemainingSize, 1), TotalMaxHeaderSize).
+
+remaing_size(nolimit, _) ->
+ nolimit;
+remaing_size(Total, Consumed) ->
+ Total - Consumed.
-decode_trailer(<<Octet, Rest/binary>>, Header, Headers, MaxHeaderSize, Body,
- BodyLength) ->
- decode_trailer(Rest, [Octet | Header], Headers, MaxHeaderSize,
- Body, BodyLength).
+encode_trailers(Trailers) ->
+ encode_trailers(Trailers, "").
+
+encode_trailers([], Acc) ->
+ Acc ++ ?CRLF ++ ?CRLF;
+encode_trailers([{Header, Value} | Rest], Acc) ->
+ encode_trailers(Rest, Header ++ ":" ++ Value ++ ?CRLF ++ Acc).
diff --git a/lib/inets/src/http_lib/http_internal.hrl b/lib/inets/src/http_lib/http_internal.hrl
index 97cf474ab9..ae92b5df8f 100644
--- a/lib/inets/src/http_lib/http_internal.hrl
+++ b/lib/inets/src/http_lib/http_internal.hrl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -26,6 +27,9 @@
-define(HTTP_MAX_BODY_SIZE, nolimit).
-define(HTTP_MAX_HEADER_SIZE, 10240).
-define(HTTP_MAX_URI_SIZE, nolimit).
+-define(HTTP_MAX_VERSION_STRING, 8).
+-define(HTTP_MAX_METHOD_STRING, 20).
+-define(HTTP_MAX_CONTENT_LENGTH, 100000000).
-ifndef(HTTP_DEFAULT_SSL_KIND).
-define(HTTP_DEFAULT_SSL_KIND, essl).
diff --git a/lib/inets/src/http_lib/http_request.erl b/lib/inets/src/http_lib/http_request.erl
index aa9f9f6774..c77b616f0d 100644
--- a/lib/inets/src/http_lib/http_request.erl
+++ b/lib/inets/src/http_lib/http_request.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -21,8 +22,16 @@
-include("http_internal.hrl").
--export([headers/2, http_headers/1, is_absolut_uri/1]).
+-export([headers/2, http_headers/1, is_absolut_uri/1, key_value/1]).
+
+key_value(KeyValueStr) ->
+ case lists:splitwith(fun($:) -> false; (_) -> true end, KeyValueStr) of
+ {Key, [$: | Value]} ->
+ {http_util:to_lower(string:strip(Key)), string:strip(Value)};
+ {_, []} ->
+ undefined
+ end.
%%-------------------------------------------------------------------------
%% headers(HeaderList, #http_request_h{}) -> #http_request_h{}
%% HeaderList - ["HeaderField:Value"]
@@ -34,17 +43,12 @@
%%-------------------------------------------------------------------------
headers([], Headers) ->
Headers;
-headers([Header | Tail], Headers) ->
- case lists:splitwith(fun($:) -> false; (_) -> true end, Header) of
- {Key, [$: | Value]} ->
- headers(Tail, headers(http_util:to_lower(string:strip(Key)),
- string:strip(Value), Headers));
- {_, []} ->
- Report = io_lib:format("Ignored invalid HTTP-header: ~p~n",
- [Header]),
- error_logger:error_report(Report),
- headers(Tail, Headers)
- end.
+headers([{Key, Value} | Tail], Headers) ->
+ headers(Tail, headers(Key, Value, Headers));
+headers([undefined], Headers) ->
+ Headers;
+headers(KeyValues, Headers) ->
+ headers([key_value(KeyValue) || KeyValue <- KeyValues], Headers).
%%-------------------------------------------------------------------------
%% headers(#http_request_h{}) -> HeaderList
diff --git a/lib/inets/src/http_lib/http_response.erl b/lib/inets/src/http_lib/http_response.erl
index b1e7f1e647..cbdea82523 100644
--- a/lib/inets/src/http_lib/http_response.erl
+++ b/lib/inets/src/http_lib/http_response.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -30,16 +31,11 @@
%% Value - string()
%%
%% Description: Creates a http_response_h-record used internally to
-%% handle http-headers.
+%% handle http-headers, assumes reversed list of headers
+%% to unfold multiline headers with obs-folds
%%-------------------------------------------------------------------------
-headers([], Headers) ->
- Headers;
-
-headers([Header | Tail], Headers) ->
- {Key, [$: | Value]} =
- lists:splitwith(fun($:) -> false; (_) -> true end, Header),
- headers(Tail, headers(http_util:to_lower(string:strip(Key)),
- string:strip(Value), Headers)).
+headers(RevLines, Headers) ->
+ fill_headers(RevLines, [], Headers).
%%-------------------------------------------------------------------------
%% headers(#http_response_h{}) -> HeaderList
@@ -67,6 +63,25 @@ header_list(Headers) ->
%%%========================================================================
%%% Internal functions
%%%========================================================================
+fill_headers([], _, Headers) ->
+ Headers;
+fill_headers([[]], _, Headers) ->
+ Headers;
+fill_headers([[Ch|HeaderFold]|Tail], Folded, Headers)
+ when Ch == $\t; Ch == $\s ->
+ fill_headers(Tail, [HeaderFold|Folded], Headers);
+fill_headers([Header | Tail], Folded, Headers) ->
+ Unfolded = unfold([Header|Folded]),
+ {Key, [$: | Value]} =
+ lists:splitwith(fun($:) -> false; (_) -> true end, Unfolded),
+ fill_headers(Tail, [], headers(http_util:to_lower(string:strip(Key)),
+ string:strip(Value), Headers)).
+
+unfold([L]) ->
+ L;
+unfold(Folded) ->
+ string:join(Folded, " ").
+
headers("cache-control", Value, Headers) ->
Headers#http_response_h{'cache-control'= Value};
headers("connection", Value, Headers) ->
diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl
index df58fa1b81..2e3e099e5b 100644
--- a/lib/inets/src/http_lib/http_transport.erl
+++ b/lib/inets/src/http_lib/http_transport.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -39,12 +40,6 @@
-include_lib("inets/src/inets_app/inets_internal.hrl").
-include("http_internal.hrl").
--define(SERVICE, httpl).
--define(hlri(Label, Content), ?report_important(Label, ?SERVICE, Content)).
--define(hlrv(Label, Content), ?report_verbose(Label, ?SERVICE, Content)).
--define(hlrd(Label, Content), ?report_debug(Label, ?SERVICE, Content)).
--define(hlrt(Label, Content), ?report_trace(Label, ?SERVICE, Content)).
-
%%%=========================================================================
%%% Internal application API
@@ -54,38 +49,27 @@
%% start(SocketType) -> ok | {error, Reason}
%% SocketType = ip_comm | {ssl, _}
%%
-%% Description: Makes sure inet_db or ssl is started.
+%% Description: Makes sure ssl is started.
%%-------------------------------------------------------------------------
start(ip_comm) ->
- do_start_ip_comm();
-
-%% This is just for backward compatibillity
+ ok;
+start({ip_comm, _}) ->
+ ok;
start({ssl, _}) ->
do_start_ssl();
start({essl, _}) ->
do_start_ssl().
-
-do_start_ip_comm() ->
- case inet_db:start() of
- {ok, _} ->
- ok;
- {error, {already_started, _}} ->
- ok;
- Error ->
- Error
- end.
-
do_start_ssl() ->
- case ssl:start() of
- ok ->
- ok;
- {error, {already_started,_}} ->
- ok;
- Error ->
- Error
+ try lists:foreach(fun(App) ->
+ ok = application:ensure_started(App)
+ end,
+ [crypto, asn1, public_key, ssl])
+ catch
+ _:Reason ->
+ {error, Reason}
end.
-
+
%%-------------------------------------------------------------------------
%% connect(SocketType, Address, Options, Timeout) ->
@@ -102,12 +86,8 @@ do_start_ssl() ->
connect(SocketType, Address, Opts) ->
connect(SocketType, Address, Opts, infinity).
-
-connect(ip_comm = _SocketType, {Host, Port}, Opts0, Timeout)
- when is_list(Opts0) ->
- Opts = [binary, {packet, 0}, {active, false}, {reuseaddr, true} | Opts0],
- ?hlrt("connect using gen_tcp",
- [{host, Host}, {port, Port}, {opts, Opts}, {timeout, Timeout}]),
+connect(ip_comm, {Host, Port}, Opts0, Timeout) ->
+ Opts = [binary, {packet, 0}, {active, false}, {reuseaddr, true} | Opts0 ],
try gen_tcp:connect(Host, Port, Opts, Timeout) of
{ok, _} = OK ->
OK;
@@ -126,11 +106,6 @@ connect({ssl, SslConfig}, Address, Opts, Timeout) ->
connect({essl, SslConfig}, {Host, Port}, Opts0, Timeout) ->
Opts = [binary, {active, false}, {ssl_imp, new} | Opts0] ++ SslConfig,
- ?hlrt("connect using essl",
- [{host, Host},
- {port, Port},
- {ssl_config, SslConfig},
- {timeout, Timeout}]),
case (catch ssl:connect(Host, Port, Opts, Timeout)) of
{'EXIT', Reason} ->
{error, {eoptions, Reason}};
@@ -155,115 +130,61 @@ connect({essl, SslConfig}, {Host, Port}, Opts0, Timeout) ->
%% reason for this to enable a HTTP-server not running as root to use
%% port 80.
%%-------------------------------------------------------------------------
-listen(ip_comm = _SocketType, Addr, Port, Fd, IpFamily) ->
- listen_ip_comm(Addr, Port, Fd, IpFamily);
-
+listen(ip_comm, Addr, Port, Fd, IpFamily) ->
+ listen_ip_comm(Addr, Port, [], Fd, IpFamily);
+
+listen({ip_comm, SockOpts}, Addr, Port, Fd, IpFamily) ->
+ listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily);
+
listen({essl, SSLConfig}, Addr, Port, Fd, IpFamily) ->
- listen_ssl(Addr, Port, Fd, SSLConfig, IpFamily).
+ listen_ssl(Addr, Port, Fd, SSLConfig, IpFamily, []).
-listen(ip_comm = _SocketType, Addr, Port, IpFamily) ->
- listen_ip_comm(Addr, Port, undefined, IpFamily);
+listen(ip_comm, Addr, Port, IpFamily) ->
+ listen_ip_comm(Addr, Port, [], undefined, IpFamily);
%% Wrapper for backaward compatibillity
listen({ssl, SSLConfig}, Addr, Port, IpFamily) ->
- ?hlrt("listen (wrapper)",
- [{addr, Addr},
- {port, Port},
- {ssl_config, SSLConfig}]),
listen({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Addr, Port, IpFamily);
-
listen({essl, SSLConfig}, Addr, Port, IpFamily) ->
- ?hlrt("listen (essl)",
- [{addr, Addr},
- {port, Port},
- {ssl_config, SSLConfig}]),
- listen_ssl(Addr, Port, undefined, SSLConfig, IpFamily).
-
-listen_ip_comm(Addr, Port, Fd, IpFamily) ->
- case (catch do_listen_ip_comm(Addr, Port, Fd, IpFamily)) of
+ {SSLConfig2, ExtraOpts} = case proplists:get_value(log_alert, SSLConfig, undefined) of
+ undefined ->
+ {SSLConfig, []};
+ LogAlert ->
+ {proplists:delete(log_alert, SSLConfig), [{log_alert, LogAlert}]}
+ end,
+ listen_ssl(Addr, Port, undefined, SSLConfig2, IpFamily, ExtraOpts).
+
+listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily) ->
+ case (catch do_listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily)) of
{'EXIT', Reason} ->
{error, {exit, Reason}};
Else ->
Else
end.
-do_listen_ip_comm(Addr, Port, Fd, IpFamily) ->
- {NewPort, Opts} = get_socket_info(Addr, Port, Fd),
- case IpFamily of
- inet6fb4 ->
- Opts2 = [inet6 | Opts],
- ?hlrt("try ipv6 listen", [{port, NewPort}, {opts, Opts2}]),
- case (catch gen_tcp:listen(NewPort, Opts2)) of
- {error, Reason} when ((Reason =:= nxdomain) orelse
- (Reason =:= eafnosupport)) ->
- Opts3 = [inet | Opts],
- ?hlrt("ipv6 listen failed - try ipv4 instead",
- [{reason, Reason}, {port, NewPort}, {opts, Opts3}]),
- gen_tcp:listen(NewPort, Opts3);
-
- %% This is when a given hostname has resolved to a
- %% IPv4-address. The inet6-option together with a
- %% {ip, IPv4} option results in badarg
- {'EXIT', Reason} ->
- Opts3 = [inet | Opts],
- ?hlrt("ipv6 listen exit - try ipv4 instead",
- [{reason, Reason}, {port, NewPort}, {opts, Opts3}]),
- gen_tcp:listen(NewPort, Opts3);
-
- Other ->
- ?hlrt("ipv6 listen done", [{other, Other}]),
- Other
- end;
- _ ->
- Opts2 = [IpFamily | Opts],
- ?hlrt("listen", [{port, NewPort}, {opts, Opts2}]),
- gen_tcp:listen(NewPort, Opts2)
- end.
-
-
-listen_ssl(Addr, Port, Fd, Opts0, IpFamily) ->
- {NewPort, SockOpt} = get_socket_info(Addr, Port, Fd),
+do_listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily) ->
+ Backlog = proplists:get_value(backlog, SockOpts, 128),
+ {NewPort, Opts} = get_socket_info(Addr, Port, Fd,
+ [{backlog, Backlog}, {reuseaddr, true} | SockOpts]),
+ Opts2 = [IpFamily | Opts],
+ gen_tcp:listen(NewPort, Opts2).
+
+listen_ssl(Addr, Port, Fd, Opts0, IpFamily, ExtraOpts) ->
+ Backlog = proplists:get_value(backlog, Opts0, 128),
+ {NewPort, SockOpt} = get_socket_info(Addr, Port, Fd,
+ [{backlog, Backlog}, {reuseaddr, true}]),
Opts = SockOpt ++ Opts0,
- case IpFamily of
- inet6fb4 ->
- Opts2 = [inet6 | Opts],
- ?hlrt("try ipv6 listen", [{opts, Opts2}]),
- case (catch ssl:listen(Port, Opts2)) of
- {error, Reason} when ((Reason =:= nxdomain) orelse
- (Reason =:= eafnosupport)) ->
- Opts3 = [inet | Opts],
- ?hlrt("ipv6 listen failed - try ipv4 instead",
- [{reason, Reason}, {opts, Opts3}]),
- ssl:listen(NewPort, Opts3);
-
- {'EXIT', Reason} ->
- Opts3 = [inet | Opts],
- ?hlrt("ipv6 listen exit - try ipv4 instead",
- [{reason, Reason}, {opts, Opts3}]),
- ssl:listen(NewPort, Opts3);
-
- Other ->
- ?hlrt("ipv6 listen done", [{other, Other}]),
- Other
- end;
-
- _ ->
- Opts2 = [IpFamily | Opts],
- ?hlrt("listen", [{opts, Opts2}]),
- ssl:listen(NewPort, Opts2)
- end.
-
+ Opts2 = [IpFamily | Opts],
+ ssl:listen(NewPort, Opts2 ++ ExtraOpts).
-
-get_socket_info(Addr, Port, Fd) ->
- BaseOpts = [{backlog, 128}, {reuseaddr, true}],
+get_socket_info(Addr, Port, Fd, BaseOpts) ->
%% The presence of a file descriptor takes precedence
case Fd of
undefined ->
{Port, sock_opts(Addr, BaseOpts)};
Fd ->
- {0, sock_opts(Addr, [{fd, Fd} | BaseOpts])}
+ {0, sock_opts([{fd, Fd} | BaseOpts])}
end.
%%-------------------------------------------------------------------------
@@ -282,6 +203,8 @@ accept(SocketType, ListenSocket) ->
accept(ip_comm, ListenSocket, Timeout) ->
gen_tcp:accept(ListenSocket, Timeout);
+accept({ip_comm, _}, ListenSocket, Timeout) ->
+ gen_tcp:accept(ListenSocket, Timeout);
%% Wrapper for backaward compatibillity
accept({ssl, SSLConfig}, ListenSocket, Timeout) ->
@@ -301,6 +224,8 @@ accept({essl, _SSLConfig}, ListenSocket, Timeout) ->
%%-------------------------------------------------------------------------
controlling_process(ip_comm, Socket, NewOwner) ->
gen_tcp:controlling_process(Socket, NewOwner);
+controlling_process({ip_comm, _}, Socket, NewOwner) ->
+ gen_tcp:controlling_process(Socket, NewOwner);
%% Wrapper for backaward compatibillity
controlling_process({ssl, SSLConfig}, Socket, NewOwner) ->
@@ -319,7 +244,8 @@ controlling_process({essl, _}, Socket, NewOwner) ->
%% gen_tcp or ssl.
%%-------------------------------------------------------------------------
setopts(ip_comm, Socket, Options) ->
- ?hlrt("ip_comm setopts", [{socket, Socket}, {options, Options}]),
+ inet:setopts(Socket, Options);
+setopts({ip_comm, _}, Socket, Options) ->
inet:setopts(Socket, Options);
%% Wrapper for backaward compatibillity
@@ -327,10 +253,7 @@ setopts({ssl, SSLConfig}, Socket, Options) ->
setopts({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Options);
setopts({essl, _}, Socket, Options) ->
- ?hlrt("[e]ssl setopts", [{socket, Socket}, {options, Options}]),
- Reason = (catch ssl:setopts(Socket, Options)),
- ?hlrt("[e]ssl setopts result", [{reason, Reason}]),
- Reason.
+ (catch ssl:setopts(Socket, Options)).
%%-------------------------------------------------------------------------
@@ -344,8 +267,10 @@ getopts(SocketType, Socket) ->
Opts = [packet, packet_size, recbuf, sndbuf, priority, tos, send_timeout],
getopts(SocketType, Socket, Opts).
+getopts({ip_comm, _}, Socket, Options) ->
+ getopts(ip_comm, Socket, Options);
+
getopts(ip_comm, Socket, Options) ->
- ?hlrt("ip_comm getopts", [{socket, Socket}, {options, Options}]),
case inet:getopts(Socket, Options) of
{ok, SocketOpts} ->
SocketOpts;
@@ -358,7 +283,6 @@ getopts({ssl, SSLConfig}, Socket, Options) ->
getopts({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Options);
getopts({essl, _}, Socket, Options) ->
- ?hlrt("essl getopts", [{socket, Socket}, {options, Options}]),
getopts_ssl(Socket, Options).
getopts_ssl(Socket, Options) ->
@@ -378,7 +302,6 @@ getopts_ssl(Socket, Options) ->
%% Description: Gets the socket stats values for the socket
%%-------------------------------------------------------------------------
getstat(ip_comm = _SocketType, Socket) ->
- ?hlrt("ip_comm getstat", [{socket, Socket}]),
case inet:getstat(Socket) of
{ok, Stats} ->
Stats;
@@ -403,6 +326,8 @@ getstat({essl, _} = _SocketType, _Socket) ->
%%-------------------------------------------------------------------------
send(ip_comm, Socket, Message) ->
gen_tcp:send(Socket, Message);
+send({ip_comm, _}, Socket, Message) ->
+ gen_tcp:send(Socket, Message);
%% Wrapper for backaward compatibillity
send({ssl, SSLConfig}, Socket, Message) ->
@@ -411,7 +336,6 @@ send({ssl, SSLConfig}, Socket, Message) ->
send({essl, _}, Socket, Message) ->
ssl:send(Socket, Message).
-
%%-------------------------------------------------------------------------
%% close(SocketType, Socket) -> ok | {error, Reason}
%% SocketType = ip_comm | {ssl, _}
@@ -421,6 +345,8 @@ send({essl, _}, Socket, Message) ->
%%-------------------------------------------------------------------------
close(ip_comm, Socket) ->
gen_tcp:close(Socket);
+close({ip_comm, []}, Socket) ->
+ gen_tcp:close(Socket);
%% Wrapper for backaward compatibillity
close({ssl, SSLConfig}, Socket) ->
@@ -442,6 +368,8 @@ close({essl, _}, Socket) ->
%%-------------------------------------------------------------------------
peername(ip_comm, Socket) ->
do_peername(inet:peername(Socket));
+peername({ip_comm, _}, Socket) ->
+ do_peername(inet:peername(Socket));
%% Wrapper for backaward compatibillity
peername({ssl, SSLConfig}, Socket) ->
@@ -474,7 +402,8 @@ do_peername({error, _}) ->
%%-------------------------------------------------------------------------
sockname(ip_comm, Socket) ->
do_sockname(inet:sockname(Socket));
-
+sockname({ip_comm, _}, Socket) ->
+ do_sockname(inet:sockname(Socket));
%% Wrapper for backaward compatibillity
sockname({ssl, SSLConfig}, Socket) ->
sockname({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
@@ -549,28 +478,13 @@ sock_opts(Opts) ->
%% -- negotiate --
negotiate(ip_comm,_,_) ->
- ?hlrt("negotiate(ip_comm)", []),
+ ok;
+negotiate({ip_comm, _},_,_) ->
ok;
negotiate({ssl, SSLConfig}, Socket, Timeout) ->
- ?hlrt("negotiate(ssl)", []),
negotiate({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Timeout);
negotiate({essl, _}, Socket, Timeout) ->
- ?hlrt("negotiate(essl)", []),
negotiate_ssl(Socket, Timeout).
negotiate_ssl(Socket, Timeout) ->
- ?hlrt("negotiate_ssl", [{socket, Socket}, {timeout, Timeout}]),
- case ssl:ssl_accept(Socket, Timeout) of
- ok ->
- ok;
- {error, Reason} ->
- ?hlrd("negotiate_ssl - accept failed", [{reason, Reason}]),
- %% Look for "valid" error reasons
- ValidReasons = [timeout, econnreset, esslaccept, esslerrssl],
- case lists:member(Reason, ValidReasons) of
- true ->
- {error, normal};
- false ->
- {error, Reason}
- end
- end.
+ ssl:ssl_accept(Socket, Timeout).
diff --git a/lib/inets/src/http_lib/http_uri.erl b/lib/inets/src/http_lib/http_uri.erl
index 5962001c3a..cb3e107ccf 100644
--- a/lib/inets/src/http_lib/http_uri.erl
+++ b/lib/inets/src/http_lib/http_uri.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -90,8 +91,8 @@ parse(AbsURI, Opts) ->
{error, Reason};
{Scheme, DefaultPort, Rest} ->
case (catch parse_uri_rest(Scheme, DefaultPort, Rest, Opts)) of
- {ok, {UserInfo, Host, Port, Path, Query}} ->
- {ok, {Scheme, UserInfo, Host, Port, Path, Query}};
+ {ok, Result} ->
+ {ok, Result};
{error, Reason} ->
{error, {Reason, Scheme, AbsURI}};
_ ->
@@ -137,38 +138,50 @@ parse_scheme(AbsURI, Opts) ->
{error, no_scheme} ->
{error, no_scheme};
{SchemeStr, Rest} ->
- Scheme = list_to_atom(http_util:to_lower(SchemeStr)),
- SchemeDefaults = which_scheme_defaults(Opts),
- case lists:keysearch(Scheme, 1, SchemeDefaults) of
- {value, {Scheme, DefaultPort}} ->
- {Scheme, DefaultPort, Rest};
- false ->
- {Scheme, no_default_port, Rest}
+ case extract_scheme(SchemeStr, Opts) of
+ {error, Error} ->
+ {error, Error};
+ {ok, Scheme} ->
+ SchemeDefaults = which_scheme_defaults(Opts),
+ case lists:keysearch(Scheme, 1, SchemeDefaults) of
+ {value, {Scheme, DefaultPort}} ->
+ {Scheme, DefaultPort, Rest};
+ false ->
+ {Scheme, no_default_port, Rest}
+ end
end
end.
+extract_scheme(Str, Opts) ->
+ case lists:keysearch(scheme_validation_fun, 1, Opts) of
+ {value, {scheme_validation_fun, Fun}} when is_function(Fun) ->
+ case Fun(Str) of
+ valid ->
+ {ok, list_to_atom(http_util:to_lower(Str))};
+ {error, Error} ->
+ {error, Error}
+ end;
+ _ ->
+ {ok, list_to_atom(http_util:to_lower(Str))}
+ end.
+
parse_uri_rest(Scheme, DefaultPort, "//" ++ URIPart, Opts) ->
- {Authority, PathQuery} =
- case split_uri(URIPart, "/", URIPart, 1, 0) of
- Split = {_, _} ->
- Split;
- URIPart ->
- case split_uri(URIPart, "\\?", URIPart, 1, 0) of
- Split = {_, _} ->
- Split;
- URIPart ->
- {URIPart,""}
- end
- end,
+ {Authority, PathQueryFragment} =
+ split_uri(URIPart, "[/?#]", {URIPart, ""}, 1, 0),
+ {RawPath, QueryFragment} =
+ split_uri(PathQueryFragment, "[?#]", {PathQueryFragment, ""}, 1, 0),
+ {Query, Fragment} =
+ split_uri(QueryFragment, "#", {QueryFragment, ""}, 1, 0),
{UserInfo, HostPort} = split_uri(Authority, "@", {"", Authority}, 1, 1),
{Host, Port} = parse_host_port(Scheme, DefaultPort, HostPort, Opts),
- {Path, Query} = parse_path_query(PathQuery),
- {ok, {UserInfo, Host, Port, Path, Query}}.
-
+ Path = path(RawPath),
+ case lists:keyfind(fragment, 1, Opts) of
+ {fragment, true} ->
+ {ok, {Scheme, UserInfo, Host, Port, Path, Query, Fragment}};
+ _ ->
+ {ok, {Scheme, UserInfo, Host, Port, Path, Query}}
+ end.
-parse_path_query(PathQuery) ->
- {Path, Query} = split_uri(PathQuery, "\\?", {PathQuery, ""}, 1, 0),
- {path(Path), Query}.
%% In this version of the function, we no longer need
%% the Scheme argument, but just in case...
@@ -183,10 +196,10 @@ parse_host_port(_Scheme, DefaultPort, HostPort, _Opts) ->
{Host, int_port(Port)}.
split_uri(UriPart, SplitChar, NoMatchResult, SkipLeft, SkipRight) ->
- case inets_regexp:first_match(UriPart, SplitChar) of
- {match, Match, _} ->
- {string:substr(UriPart, 1, Match - SkipLeft),
- string:substr(UriPart, Match + SkipRight, length(UriPart))};
+ case re:run(UriPart, SplitChar, [{capture, first}]) of
+ {match, [{Match, _}]} ->
+ {string:substr(UriPart, 1, Match + 1 - SkipLeft),
+ string:substr(UriPart, Match + 1 + SkipRight, length(UriPart))};
nomatch ->
NoMatchResult
end.
diff --git a/lib/inets/src/http_lib/http_util.erl b/lib/inets/src/http_lib/http_util.erl
index 5b21170b78..20c342dd66 100644
--- a/lib/inets/src/http_lib/http_util.erl
+++ b/lib/inets/src/http_lib/http_util.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -151,27 +152,11 @@ convert_netscapecookie_date([_D,_A,_Y, _SP,
Sec=list_to_integer([S1,S2]),
{{Year,Month,Day},{Hour,Min,Sec}}.
-hexlist_to_integer([]) ->
- empty;
-%%When the string only contains one value its eaasy done.
-%% 0-9
-hexlist_to_integer([Size]) when (Size >= 48) andalso (Size =< 57) ->
- Size - 48;
-%% A-F
-hexlist_to_integer([Size]) when (Size >= 65) andalso (Size =< 70) ->
- Size - 55;
-%% a-f
-hexlist_to_integer([Size]) when (Size >= 97) andalso (Size =< 102) ->
- Size - 87;
-hexlist_to_integer([_Size]) ->
- not_a_num;
-
-hexlist_to_integer(Size) ->
- Len = string:span(Size, "1234567890abcdefABCDEF"),
- hexlist_to_integer2(Size, 16 bsl (4 *(Len-2)),0).
+hexlist_to_integer(List) ->
+ list_to_integer(List, 16).
-integer_to_hexlist(Num)->
- integer_to_hexlist(Num, get_size(Num), []).
+integer_to_hexlist(Int) ->
+ integer_to_list(Int, 16).
convert_month("Jan") -> 1;
convert_month("Feb") -> 2;
@@ -212,51 +197,6 @@ html_encode(Chars) ->
%%%========================================================================
%%% Internal functions
%%%========================================================================
-hexlist_to_integer2([],_Pos,Sum)->
- Sum;
-hexlist_to_integer2([HexVal | HexString], Pos, Sum)
- when HexVal >= 48, HexVal =< 57 ->
- hexlist_to_integer2(HexString, Pos bsr 4, Sum + ((HexVal-48) * Pos));
-
-hexlist_to_integer2([HexVal | HexString], Pos, Sum)
- when HexVal >= 65, HexVal =<70 ->
- hexlist_to_integer2(HexString, Pos bsr 4, Sum + ((HexVal-55) * Pos));
-
-hexlist_to_integer2([HexVal | HexString], Pos, Sum)
- when HexVal>=97, HexVal=<102 ->
- hexlist_to_integer2(HexString, Pos bsr 4, Sum + ((HexVal-87) * Pos));
-
-hexlist_to_integer2(_AfterHexString, _Pos, Sum)->
- Sum.
-
-integer_to_hexlist(Num, Pot, Res) when Pot < 0 ->
- convert_to_ascii([Num | Res]);
-
-integer_to_hexlist(Num,Pot,Res) ->
- Position = (16 bsl (Pot*4)),
- PosVal = Num div Position,
- integer_to_hexlist(Num - (PosVal*Position), Pot-1, [PosVal | Res]).
-
-get_size(Num)->
- get_size(Num, 0).
-
-get_size(Num, Pot) when Num < (16 bsl(Pot *4)) ->
- Pot-1;
-
-get_size(Num, Pot) ->
- get_size(Num, Pot+1).
-
-convert_to_ascii(RevesedNum) ->
- convert_to_ascii(RevesedNum, []).
-
-convert_to_ascii([], Num)->
- Num;
-convert_to_ascii([Num | Reversed], Number)
- when (Num > -1) andalso (Num < 10) ->
- convert_to_ascii(Reversed, [Num + 48 | Number]);
-convert_to_ascii([Num | Reversed], Number)
- when (Num > 9) andalso (Num < 16) ->
- convert_to_ascii(Reversed, [Num + 55 | Number]).
char_to_html_entity(Char, Reserved) ->
case sets:is_element(Char, Reserved) of
diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile
index 67555d5f1c..1c05d454a5 100644
--- a/lib/inets/src/http_server/Makefile
+++ b/lib/inets/src/http_server/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2012. All Rights Reserved.
+# Copyright Ericsson AB 2005-2015. All Rights Reserved.
#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# 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.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
@@ -39,12 +40,18 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
+
+BEHAVIOUR_MODULES= \
+ httpd_custom_api
+
MODULES = \
httpd \
httpd_acceptor \
httpd_acceptor_sup \
+ httpd_connection_sup\
httpd_cgi \
httpd_conf \
+ httpd_custom \
httpd_example \
httpd_esi \
httpd_file\
@@ -74,7 +81,6 @@ MODULES = \
mod_get \
mod_head \
mod_htaccess \
- mod_include \
mod_log \
mod_range \
mod_responsecontrol \
@@ -84,10 +90,13 @@ MODULES = \
HRL_FILES = httpd.hrl httpd_internal.hrl mod_auth.hrl
-ERL_FILES = $(MODULES:%=%.erl)
+ERL_FILES = $(MODULES:%=%.erl)\
+ $(BEHAVIOUR_MODULES:%=%.erl)
TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+BEHAVIOUR_TARGET_FILES= $(BEHAVIOUR_MODULES:%=$(EBIN)/%.$(EMULATOR))
+
INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"'
@@ -107,11 +116,12 @@ ERL_COMPILE_FLAGS += \
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
+$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES)
debug opt: $(TARGET_FILES)
clean:
- rm -f $(TARGET_FILES)
+ rm -f $(TARGET_FILES) $(BEHAVIOUR_TARGET_FILES)
rm -f core
docs:
@@ -127,7 +137,7 @@ release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/src/http_server"
$(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/http_server"
$(INSTALL_DIR) "$(RELSYSDIR)/ebin"
- $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin"
+ $(INSTALL_DATA) $(TARGET_FILES) $(BEHAVIOUR_TARGET_FILES) "$(RELSYSDIR)/ebin"
release_docs_spec:
diff --git a/lib/inets/src/http_server/httpd.erl b/lib/inets/src/http_server/httpd.erl
index 93608dbf96..0b632d24e3 100644
--- a/lib/inets/src/http_server/httpd.erl
+++ b/lib/inets/src/http_server/httpd.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -23,6 +24,7 @@
-behaviour(inets_service).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
%% Behavior callbacks
-export([
@@ -36,19 +38,12 @@
%% API
-export([parse_query/1, reload_config/2, info/1, info/2, info/3]).
-%% Internal debugging and status info stuff...
--export([
- get_status/1, get_status/2, get_status/3,
- get_admin_state/0, get_admin_state/1, get_admin_state/2,
- get_usage_state/0, get_usage_state/1, get_usage_state/2
- ]).
-
%%%========================================================================
%%% API
%%%========================================================================
parse_query(String) ->
- {ok, SplitString} = inets_regexp:split(String,"[&;]"),
+ SplitString = re:split(String,"[&;]", [{return, list}]),
foreach(SplitString).
reload_config(Config = [Value| _], Mode) when is_tuple(Value) ->
@@ -68,18 +63,27 @@ info(Pid, Properties) when is_pid(Pid) andalso is_list(Properties) ->
{ok, ServiceInfo} = service_info(Pid),
Address = proplists:get_value(bind_address, ServiceInfo),
Port = proplists:get_value(port, ServiceInfo),
+ Profile = proplists:get_value(profile, ServiceInfo, default),
case Properties of
[] ->
- info(Address, Port);
+ info(Address, Port, Profile);
_ ->
- info(Address, Port, Properties)
+ info(Address, Port, Profile, Properties)
end;
+
info(Address, Port) when is_integer(Port) ->
- httpd_conf:get_config(Address, Port).
+ info(Address, Port, default).
+
+info(Address, Port, Profile) when is_integer(Port), is_atom(Profile) ->
+ httpd_conf:get_config(Address, Port, Profile);
info(Address, Port, Properties) when is_integer(Port) andalso
is_list(Properties) ->
- httpd_conf:get_config(Address, Port, Properties).
+ httpd_conf:get_config(Address, Port, default, Properties).
+
+info(Address, Port, Profile, Properties) when is_integer(Port) andalso
+ is_atom(Profile) andalso is_list(Properties) ->
+ httpd_conf:get_config(Address, Port, Profile, Properties).
%%%========================================================================
@@ -93,14 +97,16 @@ start_service(Conf) ->
httpd_sup:start_child(Conf).
stop_service({Address, Port}) ->
- httpd_sup:stop_child(Address, Port);
-
+ stop_service({Address, Port, ?DEFAULT_PROFILE});
+stop_service({Address, Port, Profile}) ->
+ httpd_sup:stop_child(Address, Port, Profile);
stop_service(Pid) when is_pid(Pid) ->
case service_info(Pid) of
{ok, Info} ->
Address = proplists:get_value(bind_address, Info),
Port = proplists:get_value(port, Info),
- stop_service({Address, Port});
+ Profile = proplists:get_value(profile, Info, ?DEFAULT_PROFILE),
+ stop_service({Address, Port, Profile});
Error ->
Error
end.
@@ -108,7 +114,6 @@ stop_service(Pid) when is_pid(Pid) ->
services() ->
[{httpd, ChildPid} || {_, ChildPid, _, _} <-
supervisor:which_children(httpd_sup)].
-
service_info(Pid) ->
try
[{ChildName, ChildPid} ||
@@ -121,7 +126,6 @@ service_info(Pid) ->
{error, service_not_available}
end.
-
%%%--------------------------------------------------------------
%%% Internal functions
%%%--------------------------------------------------------------------
@@ -135,12 +139,12 @@ child_name(Pid, [_ | Children]) ->
child_name2info(undefined) ->
{error, no_such_service};
-child_name2info({httpd_instance_sup, any, Port}) ->
+child_name2info({httpd_instance_sup, any, Port, Profile}) ->
{ok, Host} = inet:gethostname(),
- Info = info(any, Port, [server_name]),
+ Info = info(any, Port, Profile, [server_name]),
{ok, [{bind_address, any}, {host, Host}, {port, Port} | Info]};
-child_name2info({httpd_instance_sup, Address, Port}) ->
- Info = info(Address, Port, [server_name]),
+child_name2info({httpd_instance_sup, Address, Port, Profile}) ->
+ Info = info(Address, Port, Profile, [server_name]),
case inet:gethostbyaddr(Address) of
{ok, {_, Host, _, _,_, _}} ->
{ok, [{bind_address, Address},
@@ -150,8 +154,8 @@ child_name2info({httpd_instance_sup, Address, Port}) ->
end.
-reload(Config, Address, Port) ->
- Name = make_name(Address,Port),
+reload(Config, Address, Port, Profile) ->
+ Name = make_name(Address,Port, Profile),
case whereis(Name) of
Pid when is_pid(Pid) ->
httpd_manager:reload(Pid, Config);
@@ -198,51 +202,19 @@ reload(Config, Address, Port) ->
%%% Timeout -> integer()
%%%
-block(Addr, Port, disturbing) when is_integer(Port) ->
- do_block(Addr, Port, disturbing);
-block(Addr, Port, non_disturbing) when is_integer(Port) ->
- do_block(Addr, Port, non_disturbing);
-
-block(ConfigFile, Mode, Timeout)
- when is_list(ConfigFile) andalso
- is_atom(Mode) andalso
- is_integer(Timeout) ->
- case get_addr_and_port(ConfigFile) of
- {ok, Addr, Port} ->
- block(Addr, Port, Mode, Timeout);
- Error ->
- Error
- end.
-
-
-block(Addr, Port, non_disturbing, Timeout)
- when is_integer(Port) andalso is_integer(Timeout) ->
- do_block(Addr, Port, non_disturbing, Timeout);
-block(Addr,Port,disturbing,Timeout)
- when is_integer(Port) andalso is_integer(Timeout) ->
- do_block(Addr, Port, disturbing, Timeout).
-
-do_block(Addr, Port, Mode) when is_integer(Port) andalso is_atom(Mode) ->
- Name = make_name(Addr,Port),
+block(Addr, Port, Profile, disturbing) when is_integer(Port) ->
+ do_block(Addr, Port, Profile, disturbing);
+block(Addr, Port, Profile, non_disturbing) when is_integer(Port) ->
+ do_block(Addr, Port, Profile, non_disturbing).
+do_block(Addr, Port, Profile, Mode) when is_integer(Port) andalso is_atom(Mode) ->
+ Name = make_name(Addr, Port, Profile),
case whereis(Name) of
Pid when is_pid(Pid) ->
- httpd_manager:block(Pid,Mode);
+ httpd_manager:block(Pid, Mode);
_ ->
{error,not_started}
end.
-
-do_block(Addr, Port, Mode, Timeout)
- when is_integer(Port) andalso is_atom(Mode) ->
- Name = make_name(Addr,Port),
- case whereis(Name) of
- Pid when is_pid(Pid) ->
- httpd_manager:block(Pid,Mode,Timeout);
- _ ->
- {error,not_started}
- end.
-
-
%%% =========================================================
%%% Function: unblock/2
%%% unblock(Addr, Port)
@@ -255,8 +227,8 @@ do_block(Addr, Port, Mode, Timeout)
%%% ConfigFile -> string()
%%%
-unblock(Addr, Port) when is_integer(Port) ->
- Name = make_name(Addr,Port),
+unblock(Addr, Port, Profile) when is_integer(Port) ->
+ Name = make_name(Addr,Port, Profile),
case whereis(Name) of
Pid when is_pid(Pid) ->
httpd_manager:unblock(Pid);
@@ -267,256 +239,37 @@ unblock(Addr, Port) when is_integer(Port) ->
foreach([]) ->
[];
foreach([KeyValue|Rest]) ->
- {ok, Plus2Space, _} = inets_regexp:gsub(KeyValue,"[\+]"," "),
- case inets_regexp:split(Plus2Space,"=") of
- {ok,[Key|Value]} ->
- [{http_uri:decode(Key),
- http_uri:decode(lists:flatten(Value))}|foreach(Rest)];
- {ok,_} ->
- foreach(Rest)
- end.
-
-get_addr_and_port(ConfigFile) ->
- case httpd_conf:load(ConfigFile) of
- {ok, ConfigList} ->
- case (catch httpd_conf:validate_properties(ConfigList)) of
- {ok, Config} ->
- Address = proplists:get_value(bind_address, Config, any),
- Port = proplists:get_value(port, Config, 80),
- {ok, Address, Port};
- Error ->
- Error
- end;
- Error ->
- Error
- end.
-
-
-make_name(Addr, Port) ->
- httpd_util:make_name("httpd", Addr, Port).
-
-
-%%%--------------------------------------------------------------
-%%% Internal debug functions - Do we want these functions here!?
-%%%--------------------------------------------------------------------
-
-%%% =========================================================
-%%% Function: get_admin_state/0, get_admin_state/1, get_admin_state/2
-%%% get_admin_state()
-%%% get_admin_state(Port)
-%%% get_admin_state(Addr,Port)
-%%%
-%%% Returns: {ok,State} | {error,Reason}
-%%%
-%%% Description: This function is used to retrieve the administrative
-%%% state of the HTTP server.
-%%%
-%%% Types: Port -> integer()
-%%% Addr -> {A,B,C,D} | string() | undefined
-%%% State -> unblocked | shutting_down | blocked
-%%% Reason -> term()
-%%%
-get_admin_state() -> get_admin_state(undefined,8888).
-get_admin_state(Port) when is_integer(Port) -> get_admin_state(undefined,Port);
-
-get_admin_state(ConfigFile) when is_list(ConfigFile) ->
- case get_addr_and_port(ConfigFile) of
- {ok,Addr,Port} ->
- unblock(Addr,Port);
- Error ->
- Error
- end.
-
-get_admin_state(Addr,Port) when is_integer(Port) ->
- Name = make_name(Addr,Port),
- case whereis(Name) of
- Pid when is_pid(Pid) ->
- httpd_manager:get_admin_state(Pid);
- _ ->
- {error,not_started}
- end.
-
-
-
-%%% =========================================================
-%%% Function: get_usage_state/0, get_usage_state/1, get_usage_state/2
-%%% get_usage_state()
-%%% get_usage_state(Port)
-%%% get_usage_state(Addr,Port)
-%%%
-%%% Returns: {ok,State} | {error,Reason}
-%%%
-%%% Description: This function is used to retrieve the usage
-%%% state of the HTTP server.
-%%%
-%%% Types: Port -> integer()
-%%% Addr -> {A,B,C,D} | string() | undefined
-%%% State -> idle | active | busy
-%%% Reason -> term()
-%%%
-get_usage_state() -> get_usage_state(undefined,8888).
-get_usage_state(Port) when is_integer(Port) -> get_usage_state(undefined,Port);
-
-get_usage_state(ConfigFile) when is_list(ConfigFile) ->
- case get_addr_and_port(ConfigFile) of
- {ok,Addr,Port} ->
- unblock(Addr,Port);
- Error ->
- Error
- end.
-
-get_usage_state(Addr,Port) when is_integer(Port) ->
- Name = make_name(Addr,Port),
- case whereis(Name) of
- Pid when is_pid(Pid) ->
- httpd_manager:get_usage_state(Pid);
+ Plus2Space = re:replace(KeyValue,"[\+]"," ", [{return,list}, global]),
+ case re:split(Plus2Space,"=", [{return, list}]) of
+ [Key|Value] ->
+ [{http_uri:decode(Key),
+ http_uri:decode(lists:flatten(Value))}|foreach(Rest)];
_ ->
- {error,not_started}
+ foreach(Rest)
end.
+make_name(Addr, Port, Profile) ->
+ httpd_util:make_name("httpd", Addr, Port, Profile).
-%%% =========================================================
-%% Function: get_status(ConfigFile) -> Status
-%% get_status(Port) -> Status
-%% get_status(Addr,Port) -> Status
-%% get_status(Port,Timeout) -> Status
-%% get_status(Addr,Port,Timeout) -> Status
-%%
-%% Arguments: ConfigFile -> string()
-%% Configuration file from which Port and
-%% BindAddress will be extracted.
-%% Addr -> {A,B,C,D} | string()
-%% Bind Address of the http server
-%% Port -> integer()
-%% Port number of the http server
-%% Timeout -> integer()
-%% Timeout time for the call
-%%
-%% Returns: Status -> list()
-%%
-%% Description: This function is used when the caller runs in the
-%% same node as the http server or if calling with a
-%% program such as erl_call (see erl_interface).
-%%
-
-get_status(ConfigFile) when is_list(ConfigFile) ->
- case get_addr_and_port(ConfigFile) of
- {ok,Addr,Port} ->
- get_status(Addr,Port);
- Error ->
- Error
- end;
-
-get_status(Port) when is_integer(Port) ->
- get_status(undefined,Port,5000).
-
-get_status(Port,Timeout) when is_integer(Port) andalso is_integer(Timeout) ->
- get_status(undefined,Port,Timeout);
-
-get_status(Addr,Port) ->
- get_status(Addr,Port,5000).
-
-get_status(Addr,Port,Timeout) when is_integer(Port) ->
- Name = make_name(Addr,Port),
- case whereis(Name) of
- Pid when is_pid(Pid) ->
- httpd_manager:get_status(Pid,Timeout);
- _ ->
- not_started
- end.
do_reload_config(ConfigList, Mode) ->
case (catch httpd_conf:validate_properties(ConfigList)) of
{ok, Config} ->
Address = proplists:get_value(bind_address, Config, any),
Port = proplists:get_value(port, Config, 80),
- block(Address, Port, Mode),
- reload(Config, Address, Port),
- unblock(Address, Port);
+ Profile = proplists:get_value(profile, Config, default),
+ case block(Address, Port, Profile, Mode) of
+ ok ->
+ reload(Config, Address, Port, Profile),
+ unblock(Address, Port, Profile);
+ Error ->
+ Error
+ end;
Error ->
Error
end.
-
%%%--------------------------------------------------------------
%%% Deprecated
%%%--------------------------------------------------------------
-
-%% start() ->
-%% start("/var/tmp/server_root/conf/8888.conf").
-
-%% start(ConfigFile) ->
-%% {ok, Pid} = inets:start(httpd, ConfigFile, stand_alone),
-%% unlink(Pid),
-%% {ok, Pid}.
-
-%% start_link() ->
-%% start("/var/tmp/server_root/conf/8888.conf").
-
-%% start_link(ConfigFile) when is_list(ConfigFile) ->
-%% inets:start(httpd, ConfigFile, stand_alone).
-
-%% stop() ->
-%% stop(8888).
-
-%% stop(Port) when is_integer(Port) ->
-%% stop(undefined, Port);
-%% stop(Pid) when is_pid(Pid) ->
-%% old_stop(Pid);
-%% stop(ConfigFile) when is_list(ConfigFile) ->
-%% old_stop(ConfigFile).
-
-%% stop(Addr, Port) when is_integer(Port) ->
-%% old_stop(Addr, Port).
-
-%% start_child() ->
-%% start_child("/var/tmp/server_root/conf/8888.conf").
-
-%% start_child(ConfigFile) ->
-%% httpd_sup:start_child(ConfigFile).
-
-%% stop_child() ->
-%% stop_child(8888).
-
-%% stop_child(Port) ->
-%% stop_child(undefined, Port).
-
-%% stop_child(Addr, Port) when is_integer(Port) ->
-%% httpd_sup:stop_child(Addr, Port).
-
-%% restart() -> reload(undefined, 8888).
-
-%% restart(Port) when is_integer(Port) ->
-%% reload(undefined, Port).
-%% restart(Addr, Port) ->
-%% reload(Addr, Port).
-
-%% old_stop(Pid) when is_pid(Pid) ->
-%% do_stop(Pid);
-%% old_stop(ConfigFile) when is_list(ConfigFile) ->
-%% case get_addr_and_port(ConfigFile) of
-%% {ok, Addr, Port} ->
-%% old_stop(Addr, Port);
-
-%% Error ->
-%% Error
-%% end;
-%% old_stop(_StartArgs) ->
-%% ok.
-
-%% old_stop(Addr, Port) when is_integer(Port) ->
-%% Name = old_make_name(Addr, Port),
-%% case whereis(Name) of
-%% Pid when is_pid(Pid) ->
-%% do_stop(Pid),
-%% ok;
-%% _ ->
-%% not_started
-%% end.
-
-%% do_stop(Pid) ->
-%% exit(Pid, shutdown).
-
-%% old_make_name(Addr,Port) ->
-%% httpd_util:make_name("httpd_instance_sup",Addr,Port).
diff --git a/lib/inets/src/http_server/httpd.hrl b/lib/inets/src/http_server/httpd.hrl
index 4eba833e2c..751264ae0a 100644
--- a/lib/inets/src/http_server/httpd.hrl
+++ b/lib/inets/src/http_server/httpd.hrl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_server/httpd_acceptor.erl b/lib/inets/src/http_server/httpd_acceptor.erl
index 1bffcc1f12..447faec12f 100644
--- a/lib/inets/src/http_server/httpd_acceptor.erl
+++ b/lib/inets/src/http_server/httpd_acceptor.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -21,13 +22,13 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
--include("inets_internal.hrl").
+%%-include("inets_internal.hrl").
%% Internal application API
--export([start_link/6, start_link/7]).
+-export([start_link/7, start_link/8]).
%% Other exports (for spawn's etc.)
--export([acceptor_init/7, acceptor_init/8, acceptor_loop/6]).
+-export([acceptor_init/8, acceptor_init/9, acceptor_loop/8]).
%%
%% External API
@@ -36,51 +37,52 @@
%% start_link
start_link(Manager, SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout) ->
- ?hdrd("start link",
- [{manager, Manager},
- {socket_type, SocketType},
- {address, Addr},
- {port, Port},
- {timeout, AcceptTimeout}]),
+ %% ?hdrd("start link",
+ %% [{manager, Manager},
+ %% {socket_type, SocketType},
+ %% {address, Addr},
+ %% {port, Port},
+ %% {timeout, AcceptTimeout}]),
Args = [self(), Manager, SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout],
proc_lib:start_link(?MODULE, acceptor_init, Args).
-start_link(Manager, SocketType, ListenSocket, IpFamily, ConfigDb, AcceptTimeout) ->
- ?hdrd("start link",
- [{manager, Manager},
- {socket_type, SocketType},
- {listen_socket, ListenSocket},
- {timeout, AcceptTimeout}]),
- Args = [self(), Manager, SocketType, ListenSocket, IpFamily,
+start_link(Manager, SocketType, Addr, Port, ListenSocket, IpFamily, ConfigDb, AcceptTimeout) ->
+ %% ?hdrd("start link",
+ %% [{manager, Manager},
+ %% {socket_type, SocketType},
+ %% {listen_socket, ListenSocket},
+ %% {timeout, AcceptTimeout}]),
+ Args = [self(), Manager, SocketType, Addr, Port, ListenSocket, IpFamily,
ConfigDb, AcceptTimeout],
proc_lib:start_link(?MODULE, acceptor_init, Args).
-acceptor_init(Parent, Manager, SocketType, {ListenOwner, ListenSocket}, IpFamily,
+acceptor_init(Parent, Manager, SocketType, Addr, Port, {ListenOwner, ListenSocket}, IpFamily,
ConfigDb, AcceptTimeout) ->
- ?hdrd("acceptor init",
- [{parent, Parent},
- {manager, Manager},
- {socket_type, SocketType},
- {listen_owner, ListenOwner},
- {listen_socket, ListenSocket},
- {timeout, AcceptTimeout}]),
+ %% ?hdrd("acceptor init",
+ %% [{parent, Parent},
+ %% {manager, Manager},
+ %% {socket_type, SocketType},
+ %% {listen_owner, ListenOwner},
+ %% {listen_socket, ListenSocket},
+ %% {timeout, AcceptTimeout}]),
link(ListenOwner),
proc_lib:init_ack(Parent, {ok, self()}),
- acceptor_loop(Manager, SocketType, ListenSocket, IpFamily, ConfigDb, AcceptTimeout).
+ acceptor_loop(Manager, SocketType, Addr, Port,
+ ListenSocket, IpFamily, ConfigDb, AcceptTimeout).
acceptor_init(Parent, Manager, SocketType, Addr, Port, IpFamily,
ConfigDb, AcceptTimeout) ->
- ?hdrd("acceptor init",
- [{parent, Parent},
- {manager, Manager},
- {socket_type, SocketType},
- {address, Addr},
- {port, Port},
- {timeout, AcceptTimeout}]),
+ %% ?hdrd("acceptor init",
+ %% [{parent, Parent},
+ %% {manager, Manager},
+ %% {socket_type, SocketType},
+ %% {address, Addr},
+ %% {port, Port},
+ %% {timeout, AcceptTimeout}]),
case (catch do_init(SocketType, Addr, Port, IpFamily)) of
{ok, ListenSocket} ->
proc_lib:init_ack(Parent, {ok, self()}),
- acceptor_loop(Manager, SocketType,
+ acceptor_loop(Manager, SocketType, Addr, Port,
ListenSocket, IpFamily,ConfigDb, AcceptTimeout);
Error ->
proc_lib:init_ack(Parent, Error),
@@ -88,67 +90,68 @@ acceptor_init(Parent, Manager, SocketType, Addr, Port, IpFamily,
end.
do_init(SocketType, Addr, Port, IpFamily) ->
- ?hdrt("do init", []),
+ %% ?hdrt("do init", []),
do_socket_start(SocketType),
ListenSocket = do_socket_listen(SocketType, Addr, Port, IpFamily),
{ok, ListenSocket}.
do_socket_start(SocketType) ->
- ?hdrt("do socket start", []),
+ %% ?hdrt("do socket start", []),
case http_transport:start(SocketType) of
ok ->
ok;
{error, Reason} ->
- ?hdrv("failed starting transport", [{reason, Reason}]),
+ %% ?hdrv("failed starting transport", [{reason, Reason}]),
throw({error, {socket_start_failed, Reason}})
end.
do_socket_listen(SocketType, Addr, Port, IpFamily) ->
- ?hdrt("do socket listen", []),
+ %% ?hdrt("do socket listen", []),
case http_transport:listen(SocketType, Addr, Port, IpFamily) of
{ok, ListenSocket} ->
ListenSocket;
{error, Reason} ->
- ?hdrv("listen failed", [{reason, Reason},
- {socket_type, SocketType},
- {addr, Addr},
- {port, Port}]),
+ %% ?hdrv("listen failed", [{reason, Reason},
+ %% {socket_type, SocketType},
+ %% {addr, Addr},
+ %% {port, Port}]),
throw({error, {listen, Reason}})
end.
%% acceptor
-acceptor_loop(Manager, SocketType, ListenSocket, IpFamily, ConfigDb, AcceptTimeout) ->
- ?hdrd("awaiting accept",
- [{manager, Manager},
- {socket_type, SocketType},
- {listen_socket, ListenSocket},
- {timeout, AcceptTimeout}]),
+acceptor_loop(Manager, SocketType, Addr, Port, ListenSocket, IpFamily, ConfigDb, AcceptTimeout) ->
+ %% ?hdrd("awaiting accept",
+ %% [{manager, Manager},
+ %% {socket_type, SocketType},
+ %% {listen_socket, ListenSocket},
+ %% {timeout, AcceptTimeout}]),
case (catch http_transport:accept(SocketType, ListenSocket, 50000)) of
{ok, Socket} ->
- ?hdrv("accepted", [{socket, Socket}]),
- handle_connection(Manager, ConfigDb, AcceptTimeout,
+ %% ?hdrv("accepted", [{socket, Socket}]),
+ handle_connection(Addr, Port, Manager, ConfigDb, AcceptTimeout,
SocketType, Socket),
- ?MODULE:acceptor_loop(Manager, SocketType,
+ ?MODULE:acceptor_loop(Manager, SocketType, Addr, Port,
ListenSocket, IpFamily, ConfigDb,AcceptTimeout);
{error, Reason} ->
- ?hdri("accept failed", [{reason, Reason}]),
+ %% ?hdri("accept failed", [{reason, Reason}]),
handle_error(Reason, ConfigDb),
- ?MODULE:acceptor_loop(Manager, SocketType, ListenSocket,
+ ?MODULE:acceptor_loop(Manager, SocketType, Addr, Port, ListenSocket,
IpFamily, ConfigDb, AcceptTimeout);
{'EXIT', Reason} ->
- ?hdri("accept exited", [{reason, Reason}]),
+ %% ?hdri("accept exited", [{reason, Reason}]),
ReasonString =
lists:flatten(io_lib:format("Accept exit: ~p", [Reason])),
accept_failed(ConfigDb, ReasonString)
end.
-handle_connection(Manager, ConfigDb, AcceptTimeout, SocketType, Socket) ->
- {ok, Pid} = httpd_request_handler:start(Manager, ConfigDb, AcceptTimeout),
+handle_connection(Address, Port, Manager, ConfigDb, AcceptTimeout, SocketType, Socket) ->
+ Sup = httpd_connection_sup:connection_sup(Address, Port),
+ {ok, Pid} = httpd_connection_sup:start_child(Sup, [Manager, ConfigDb, AcceptTimeout]),
http_transport:controlling_process(SocketType, Socket, Pid),
httpd_request_handler:socket_ownership_transfered(Pid, SocketType, Socket).
diff --git a/lib/inets/src/http_server/httpd_acceptor_sup.erl b/lib/inets/src/http_server/httpd_acceptor_sup.erl
index df837b5a24..09d81a66d0 100644
--- a/lib/inets/src/http_server/httpd_acceptor_sup.erl
+++ b/lib/inets/src/http_server/httpd_acceptor_sup.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -26,8 +27,11 @@
-behaviour(supervisor).
+-include("httpd_internal.hrl").
+
%% API
--export([start_link/2, start_acceptor/6, start_acceptor/7, stop_acceptor/2]).
+-export([start_link/1]).
+%%, start_acceptor/6, start_acceptor/7, stop_acceptor/2]).
%% Supervisor callback
-export([init/1]).
@@ -35,63 +39,52 @@
%%%=========================================================================
%%% API
%%%=========================================================================
-start_link(Addr, Port) ->
- SupName = make_name(Addr, Port),
- supervisor:start_link({local, SupName}, ?MODULE, []).
-
-%%----------------------------------------------------------------------
-%% Function: [start|stop]_acceptor/5
-%% Description: Starts/stops an [auth | security] worker (child) process
-%%----------------------------------------------------------------------
-start_acceptor(SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout) ->
- start_worker(httpd_acceptor, SocketType, Addr, Port, IpFamily,
- ConfigDb, AcceptTimeout, self(), []).
-start_acceptor(SocketType, Addr, Port, IpFamily, ConfigDb, AcceptTimeout, ListenSocket) ->
- start_worker(httpd_acceptor, SocketType, Addr, Port, IpFamily,
- ConfigDb, AcceptTimeout, ListenSocket, self(), []).
-
-
-stop_acceptor(Addr, Port) ->
- stop_worker(httpd_acceptor, Addr, Port).
+start_link([Addr, Port, Config| _] = Args) ->
+ Profile = proplists:get_value(profile, Config, ?DEFAULT_PROFILE),
+ SupName = make_name(Addr, Port, Profile),
+ supervisor:start_link({local, SupName}, ?MODULE, [Args]).
%%%=========================================================================
%%% Supervisor callback
%%%=========================================================================
-init(_) ->
- Flags = {one_for_one, 500, 100},
- Workers = [],
- {ok, {Flags, Workers}}.
+init([Args]) ->
+ RestartStrategy = one_for_one,
+ MaxR = 10,
+ MaxT = 3600,
+ Children = [child_spec(Args)],
+ {ok, {{RestartStrategy, MaxR, MaxT}, Children}}.
%%%=========================================================================
%%% Internal functions
%%%=========================================================================
+child_spec([Address, Port, ConfigList, AcceptTimeout, ListenInfo]) ->
+ Profile = proplists:get_value(profile, ConfigList, ?DEFAULT_PROFILE),
+ Name = id(Address, Port, Profile),
+ Manager = httpd_util:make_name("httpd", Address, Port, Profile),
+ SockType = proplists:get_value(socket_type, ConfigList, ip_comm),
+ IpFamily = proplists:get_value(ipfamily, ConfigList, inet),
+ StartFunc = case ListenInfo of
+ undefined ->
+ {httpd_acceptor, start_link,
+ [Manager, SockType, Address, Port, IpFamily,
+ httpd_util:make_name("httpd_conf", Address, Port, Profile),
+ AcceptTimeout]};
+ _ ->
+ {httpd_acceptor, start_link,
+ [Manager, SockType, Address, Port, ListenInfo,
+ IpFamily,
+ httpd_util:make_name("httpd_conf", Address, Port, Profile),
+ AcceptTimeout]}
+ end,
+ Restart = transient,
+ Shutdown = brutal_kill,
+ Modules = [httpd_acceptor],
+ Type = worker,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
-make_name(Addr,Port) ->
- httpd_util:make_name("httpd_acc_sup", Addr, Port).
-
-start_worker(M, SocketType, Addr, Port, IpFamily, ConfigDB, AcceptTimeout, Manager, Modules) ->
- SupName = make_name(Addr, Port),
- Args = [Manager, SocketType, Addr, Port, IpFamily, ConfigDB, AcceptTimeout],
- Spec = {{M, Addr, Port},
- {M, start_link, Args},
- permanent, timer:seconds(1), worker, [M] ++ Modules},
- supervisor:start_child(SupName, Spec).
+id(Address, Port, Profile) ->
+ {httpd_acceptor_sup, Address, Port, Profile}.
-start_worker(M, SocketType, Addr, Port, IpFamily, ConfigDB, AcceptTimeout, ListenSocket,
- Manager, Modules) ->
- SupName = make_name(Addr, Port),
- Args = [Manager, SocketType, ListenSocket, IpFamily, ConfigDB, AcceptTimeout],
- Spec = {{M, Addr, Port},
- {M, start_link, Args},
- permanent, timer:seconds(1), worker, [M] ++ Modules},
- supervisor:start_child(SupName, Spec).
+make_name(Addr, Port, Profile) ->
+ httpd_util:make_name("httpd_acceptor_sup", Addr, Port, Profile).
-stop_worker(M, Addr, Port) ->
- SupName = make_name(Addr, Port),
- Name = {M, Addr, Port},
- case supervisor:terminate_child(SupName, Name) of
- ok ->
- supervisor:delete_child(SupName, Name);
- Error ->
- Error
- end.
diff --git a/lib/inets/src/http_server/httpd_cgi.erl b/lib/inets/src/http_server/httpd_cgi.erl
index c06a06aad3..583c0be985 100644
--- a/lib/inets/src/http_server/httpd_cgi.erl
+++ b/lib/inets/src/http_server/httpd_cgi.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl
index d45f3c0048..e5182ca23c 100644
--- a/lib/inets/src/http_server/httpd_conf.erl
+++ b/lib/inets/src/http_server/httpd_conf.erl
@@ -1,159 +1,48 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(httpd_conf).
-%% EWSAPI
--export([is_directory/1, is_file/1, make_integer/1, clean/1,
- custom_clean/3, check_enum/2]).
-
%% Application internal API
-export([load/1, load/2, load_mime_types/1, store/1, store/2,
- remove/1, remove_all/1, get_config/2, get_config/3,
+ remove/1, remove_all/1, get_config/3, get_config/4,
lookup_socket_type/1,
lookup/2, lookup/3, lookup/4,
- validate_properties/1]).
+ validate_properties/1, white_space_clean/1]).
+
+%% Deprecated
+-export([is_directory/1, is_file/1, make_integer/1, clean/1,
+ custom_clean/3, check_enum/2]).
+
+-deprecated({is_directory, 1, next_major_release}).
+-deprecated({is_file, 1, next_major_release}).
+-deprecated({make_integer, 1, next_major_release}).
+-deprecated({clean, 1, next_major_release}).
+-deprecated({custom_clean, 3, next_major_release}).
+-deprecated({check_enum, 2, next_major_release}).
-define(VMODULE,"CONF").
-include("httpd_internal.hrl").
-include("httpd.hrl").
-include_lib("inets/src/http_lib/http_internal.hrl").
-
-%%%=========================================================================
-%%% EWSAPI
-%%%=========================================================================
-%%-------------------------------------------------------------------------
-%% is_directory(FilePath) -> Result
-%% FilePath = string()
-%% Result = {ok,Directory} | {error,Reason}
-%% Directory = string()
-%% Reason = string() | enoent | eaccess | enotdir | FileInfo
-%% FileInfo = File info record
-%%
-%% Description: Checks if FilePath is a directory in which case it is
-%% returned.
-%%-------------------------------------------------------------------------
-is_directory(Directory) ->
- case file:read_file_info(Directory) of
- {ok,FileInfo} ->
- #file_info{type = Type, access = Access} = FileInfo,
- is_directory(Type,Access,FileInfo,Directory);
- {error,Reason} ->
- {error,Reason}
- end.
-is_directory(directory,read,_FileInfo,Directory) ->
- {ok,Directory};
-is_directory(directory,read_write,_FileInfo,Directory) ->
- {ok,Directory};
-is_directory(_Type,_Access,FileInfo,_Directory) ->
- {error,FileInfo}.
-
-
-%%-------------------------------------------------------------------------
-%% is_file(FilePath) -> Result
-%% FilePath = string()
-%% Result = {ok,File} | {error,Reason}
-%% File = string()
-%% Reason = string() | enoent | eaccess | enotdir | FileInfo
-%% FileInfo = File info record
-%%
-%% Description: Checks if FilePath is a regular file in which case it
-%% is returned.
-%%-------------------------------------------------------------------------
-is_file(File) ->
- case file:read_file_info(File) of
- {ok,FileInfo} ->
- #file_info{type = Type, access = Access} = FileInfo,
- is_file(Type,Access,FileInfo,File);
- {error,Reason} ->
- {error,Reason}
- end.
-is_file(regular,read,_FileInfo,File) ->
- {ok,File};
-is_file(regular,read_write,_FileInfo,File) ->
- {ok,File};
-is_file(_Type,_Access,FileInfo,_File) ->
- {error,FileInfo}.
-
-
-%%-------------------------------------------------------------------------
-%% make_integer(String) -> Result
-%% String = string()
-%% Result = {ok,integer()} | {error,nomatch}
-%%
-%% Description: make_integer/1 returns an integer representation of String.
-%%-------------------------------------------------------------------------
-make_integer(String) ->
- case inets_regexp:match(clean(String),"[0-9]+") of
- {match, _, _} ->
- {ok, list_to_integer(clean(String))};
- nomatch ->
- {error, nomatch}
- end.
-
-
-%%-------------------------------------------------------------------------
-%% clean(String) -> Stripped
-%% String = Stripped = string()
-%%
-%% Description:clean/1 removes leading and/or trailing white spaces
-%% from String.
-%%-------------------------------------------------------------------------
-clean(String) ->
- {ok,CleanedString,_} =
- inets_regexp:gsub(String, "^[ \t\n\r\f]*|[ \t\n\r\f]*\$",""),
- CleanedString.
-
-
-%%-------------------------------------------------------------------------
-%% custom_clean(String,Before,After) -> Stripped
-%% Before = After = regexp()
-%% String = Stripped = string()
-%%
-%% Description: custom_clean/3 removes leading and/or trailing white
-%% spaces and custom characters from String.
-%%-------------------------------------------------------------------------
-custom_clean(String,MoreBefore,MoreAfter) ->
- {ok,CleanedString,_} = inets_regexp:gsub(String,"^[ \t\n\r\f"++MoreBefore++
- "]*|[ \t\n\r\f"++MoreAfter++"]*\$",""),
- CleanedString.
-
-
-%%-------------------------------------------------------------------------
-%% check_enum(EnumString,ValidEnumStrings) -> Result
-%% EnumString = string()
-%% ValidEnumStrings = [string()]
-%% Result = {ok,atom()} | {error,not_valid}
-%%
-%% Description: check_enum/2 checks if EnumString is a valid
-%% enumeration of ValidEnumStrings in which case it is returned as an
-%% atom.
-%%-------------------------------------------------------------------------
-check_enum(_Enum,[]) ->
- {error, not_valid};
-check_enum(Enum,[Enum|_Rest]) ->
- {ok, list_to_atom(Enum)};
-check_enum(Enum, [_NotValid|Rest]) ->
- check_enum(Enum, Rest).
-
-
%%%=========================================================================
%%% Application internal API
%%%=========================================================================
@@ -192,7 +81,7 @@ load("MaxHeaderSize " ++ MaxHeaderSize, []) ->
{ok, Integer} ->
{ok, [], {max_header_size,Integer}};
{error, _} ->
- {error, ?NICE(clean(MaxHeaderSize)++
+ {error, ?NICE(string:strip(MaxHeaderSize)++
" is an invalid number of MaxHeaderSize")}
end;
@@ -201,50 +90,50 @@ load("MaxURISize " ++ MaxHeaderSize, []) ->
{ok, Integer} ->
{ok, [], {max_uri_size, Integer}};
{error, _} ->
- {error, ?NICE(clean(MaxHeaderSize)++
+ {error, ?NICE(string:strip(MaxHeaderSize)++
" is an invalid number of MaxHeaderSize")}
end;
-load("MaxBodySize " ++ MaxBodySize, []) ->
- case make_integer(MaxBodySize) of
+load("MaxContentLength " ++ Max, []) ->
+ case make_integer(Max) of
{ok, Integer} ->
- {ok, [], {max_body_size,Integer}};
+ {ok, [], {max_content_length, Integer}};
{error, _} ->
- {error, ?NICE(clean(MaxBodySize) ++
- " is an invalid number of MaxBodySize")}
+ {error, ?NICE(string:strip(Max) ++
+ " is an invalid number of MaxContentLength")}
end;
load("ServerName " ++ ServerName, []) ->
- {ok,[], {server_name, clean(ServerName)}};
+ {ok,[], {server_name, string:strip(ServerName)}};
load("ServerTokens " ++ ServerTokens, []) ->
%% These are the valid *plain* server tokens:
- %% sprod, major, minor, minimum, os, full
+ %% none, prod, major, minor, minimum, os, full
%% It can also be a "private" server token: private:<any string>
case string:tokens(ServerTokens, [$:]) of
["private", Private] ->
- {ok,[], {server_tokens, clean(Private)}};
+ {ok,[], {server_tokens, string:strip(Private)}};
[TokStr] ->
- Tok = list_to_atom(clean(TokStr)),
- case lists:member(Tok, [prod, major, minor, minimum, os, full]) of
+ Tok = list_to_atom(string:strip(TokStr)),
+ case lists:member(Tok, [none, prod, major, minor, minimum, os, full]) of
true ->
{ok,[], {server_tokens, Tok}};
false ->
- {error, ?NICE(clean(ServerTokens) ++
+ {error, ?NICE(string:strip(ServerTokens) ++
" is an invalid ServerTokens")}
end;
_ ->
- {error, ?NICE(clean(ServerTokens) ++ " is an invalid ServerTokens")}
+ {error, ?NICE(string:strip(ServerTokens) ++ " is an invalid ServerTokens")}
end;
load("SocketType " ++ SocketType, []) ->
%% ssl is the same as HTTP_DEFAULT_SSL_KIND
%% essl is the pure Erlang-based ssl (the "new" ssl)
- case check_enum(clean(SocketType), ["ssl", "essl", "ip_comm"]) of
+ case check_enum(string:strip(SocketType), ["ssl", "essl", "ip_comm"]) of
{ok, ValidSocketType} ->
{ok, [], {socket_type, ValidSocketType}};
{error,_} ->
- {error, ?NICE(clean(SocketType) ++ " is an invalid SocketType")}
+ {error, ?NICE(string:strip(SocketType) ++ " is an invalid SocketType")}
end;
load("Port " ++ Port, []) ->
@@ -252,7 +141,7 @@ load("Port " ++ Port, []) ->
{ok, Integer} ->
{ok, [], {port, Integer}};
{error, _} ->
- {error, ?NICE(clean(Port)++" is an invalid Port")}
+ {error, ?NICE(string:strip(Port)++" is an invalid Port")}
end;
load("BindAddress " ++ Address0, []) ->
@@ -267,7 +156,7 @@ load("BindAddress " ++ Address0, []) ->
case string:tokens(Address0, [$|]) of
[Address1] ->
?hdrv("load BindAddress", [{address1, Address1}]),
- {clean_address(Address1), inet6fb4};
+ {clean_address(Address1), inet};
[Address1, IpFamilyStr] ->
?hdrv("load BindAddress",
[{address1, Address1},
@@ -308,7 +197,7 @@ load("BindAddress " ++ Address0, []) ->
end;
load("KeepAlive " ++ OnorOff, []) ->
- case list_to_atom(clean(OnorOff)) of
+ case list_to_atom(string:strip(OnorOff)) of
off ->
{ok, [], {keep_alive, false}};
_ ->
@@ -320,7 +209,7 @@ load("MaxKeepAliveRequests " ++ MaxRequests, []) ->
{ok, Integer} ->
{ok, [], {max_keep_alive_request, Integer}};
{error, _} ->
- {error, ?NICE(clean(MaxRequests) ++
+ {error, ?NICE(string:strip(MaxRequests) ++
" is an invalid MaxKeepAliveRequests")}
end;
@@ -330,35 +219,35 @@ load("MaxKeepAliveRequest " ++ MaxRequests, []) ->
{ok, Integer} ->
{ok, [], {max_keep_alive_request, Integer}};
{error, _} ->
- {error, ?NICE(clean(MaxRequests) ++
+ {error, ?NICE(string:strip(MaxRequests) ++
" is an invalid MaxKeepAliveRequest")}
end;
load("KeepAliveTimeout " ++ Timeout, []) ->
case make_integer(Timeout) of
{ok, Integer} ->
- {ok, [], {keep_alive_timeout, Integer*1000}};
+ {ok, [], {keep_alive_timeout, Integer}};
{error, _} ->
- {error, ?NICE(clean(Timeout)++" is an invalid KeepAliveTimeout")}
+ {error, ?NICE(string:strip(Timeout)++" is an invalid KeepAliveTimeout")}
end;
load("Modules " ++ Modules, []) ->
- {ok, ModuleList} = inets_regexp:split(Modules," "),
+ ModuleList = re:split(Modules," ", [{return, list}]),
{ok, [], {modules,[list_to_atom(X) || X <- ModuleList]}};
load("ServerAdmin " ++ ServerAdmin, []) ->
- {ok, [], {server_admin,clean(ServerAdmin)}};
+ {ok, [], {server_admin,string:strip(ServerAdmin)}};
load("ServerRoot " ++ ServerRoot, []) ->
- case is_directory(clean(ServerRoot)) of
+ case is_directory(string:strip(ServerRoot)) of
{ok, Directory} ->
{ok, [], [{server_root,string:strip(Directory,right,$/)}]};
{error, _} ->
- {error, ?NICE(clean(ServerRoot)++" is an invalid ServerRoot")}
+ {error, ?NICE(string:strip(ServerRoot)++" is an invalid ServerRoot")}
end;
load("MimeTypes " ++ MimeTypes, []) ->
- case load_mime_types(clean(MimeTypes)) of
+ case load_mime_types(white_space_clean(MimeTypes)) of
{ok, MimeTypesList} ->
{ok, [], [{mime_types, MimeTypesList}]};
{error, Reason} ->
@@ -370,101 +259,115 @@ load("MaxClients " ++ MaxClients, []) ->
{ok, Integer} ->
{ok, [], {max_clients,Integer}};
{error, _} ->
- {error, ?NICE(clean(MaxClients) ++
+ {error, ?NICE(string:strip(MaxClients) ++
" is an invalid number of MaxClients")}
end;
load("DocumentRoot " ++ DocumentRoot,[]) ->
- case is_directory(clean(DocumentRoot)) of
+ case is_directory(string:strip(DocumentRoot)) of
{ok, Directory} ->
{ok, [], {document_root,string:strip(Directory,right,$/)}};
{error, _} ->
- {error, ?NICE(clean(DocumentRoot)++" is an invalid DocumentRoot")}
+ {error, ?NICE(string:strip(DocumentRoot)++" is an invalid DocumentRoot")}
end;
load("DefaultType " ++ DefaultType, []) ->
- {ok, [], {default_type,clean(DefaultType)}};
+ {ok, [], {default_type,string:strip(DefaultType)}};
load("SSLCertificateFile " ++ SSLCertificateFile, []) ->
- case is_file(clean(SSLCertificateFile)) of
+ case is_file(string:strip(SSLCertificateFile)) of
{ok, File} ->
{ok, [], {ssl_certificate_file,File}};
{error, _} ->
- {error, ?NICE(clean(SSLCertificateFile)++
+ {error, ?NICE(string:strip(SSLCertificateFile)++
" is an invalid SSLCertificateFile")}
end;
+load("SSLLogLevel " ++ SSLLogAlert, []) ->
+ case SSLLogAlert of
+ "none" ->
+ {ok, [], {ssl_log_alert, false}};
+ _ ->
+ {ok, [], {ssl_log_alert, true}}
+ end;
load("SSLCertificateKeyFile " ++ SSLCertificateKeyFile, []) ->
- case is_file(clean(SSLCertificateKeyFile)) of
+ case is_file(string:strip(SSLCertificateKeyFile)) of
{ok, File} ->
{ok, [], {ssl_certificate_key_file,File}};
{error, _} ->
- {error, ?NICE(clean(SSLCertificateKeyFile)++
+ {error, ?NICE(string:strip(SSLCertificateKeyFile)++
" is an invalid SSLCertificateKeyFile")}
end;
load("SSLVerifyClient " ++ SSLVerifyClient, []) ->
- case make_integer(clean(SSLVerifyClient)) of
+ case make_integer(string:strip(SSLVerifyClient)) of
{ok, Integer} when (Integer >=0) andalso (Integer =< 2) ->
{ok, [], {ssl_verify_client,Integer}};
{ok, _Integer} ->
- {error,?NICE(clean(SSLVerifyClient) ++
+ {error,?NICE(string:strip(SSLVerifyClient) ++
" is an invalid SSLVerifyClient")};
{error, nomatch} ->
- {error,?NICE(clean(SSLVerifyClient) ++
+ {error,?NICE(string:strip(SSLVerifyClient) ++
" is an invalid SSLVerifyClient")}
end;
load("SSLVerifyDepth " ++ SSLVerifyDepth, []) ->
- case make_integer(clean(SSLVerifyDepth)) of
+ case make_integer(string:strip(SSLVerifyDepth)) of
{ok, Integer} when Integer > 0 ->
{ok, [], {ssl_verify_client_depth,Integer}};
{ok, _Integer} ->
- {error,?NICE(clean(SSLVerifyDepth) ++
+ {error,?NICE(string:strip(SSLVerifyDepth) ++
" is an invalid SSLVerifyDepth")};
{error, nomatch} ->
- {error,?NICE(clean(SSLVerifyDepth) ++
+ {error,?NICE(string:strip(SSLVerifyDepth) ++
" is an invalid SSLVerifyDepth")}
end;
load("SSLCiphers " ++ SSLCiphers, []) ->
- {ok, [], {ssl_ciphers, clean(SSLCiphers)}};
+ {ok, [], {ssl_ciphers, string:strip(SSLCiphers)}};
load("SSLCACertificateFile " ++ SSLCACertificateFile, []) ->
- case is_file(clean(SSLCACertificateFile)) of
+ case is_file(string:strip(SSLCACertificateFile)) of
{ok, File} ->
{ok, [], {ssl_ca_certificate_file,File}};
{error, _} ->
- {error, ?NICE(clean(SSLCACertificateFile)++
+ {error, ?NICE(string:strip(SSLCACertificateFile)++
" is an invalid SSLCACertificateFile")}
end;
load("SSLPasswordCallbackModule " ++ SSLPasswordCallbackModule, []) ->
{ok, [], {ssl_password_callback_module,
- list_to_atom(clean(SSLPasswordCallbackModule))}};
+ list_to_atom(string:strip(SSLPasswordCallbackModule))}};
load("SSLPasswordCallbackFunction " ++ SSLPasswordCallbackFunction, []) ->
{ok, [], {ssl_password_callback_function,
- list_to_atom(clean(SSLPasswordCallbackFunction))}};
+ list_to_atom(string:strip(SSLPasswordCallbackFunction))}};
load("SSLPasswordCallbackArguments " ++ SSLPasswordCallbackArguments, []) ->
{ok, [], {ssl_password_callback_arguments,
SSLPasswordCallbackArguments}};
load("DisableChunkedTransferEncodingSend " ++ TrueOrFalse, []) ->
- case list_to_atom(clean(TrueOrFalse)) of
+ case list_to_atom(string:strip(TrueOrFalse)) of
true ->
{ok, [], {disable_chunked_transfer_encoding_send, true}};
_ ->
{ok, [], {disable_chunked_transfer_encoding_send, false}}
end;
load("LogFormat " ++ LogFormat, []) ->
- {ok,[],{log_format, list_to_atom(httpd_conf:clean(LogFormat))}};
+ {ok,[],{log_format, list_to_atom(string:strip(LogFormat))}};
load("ErrorLogFormat " ++ LogFormat, []) ->
- {ok,[],{error_log_format, list_to_atom(httpd_conf:clean(LogFormat))}}.
+ {ok,[],{error_log_format, list_to_atom(string:strip(LogFormat))}}.
clean_address(Addr) ->
- string:strip(string:strip(clean(Addr), left, $[), right, $]).
+ string:strip(string:strip(string:strip(Addr), left, $[), right, $]).
make_ipfamily(IpFamilyStr) ->
- IpFamily = list_to_atom(IpFamilyStr),
- case lists:member(IpFamily, [inet, inet6, inet6fb4]) of
- true ->
- IpFamily;
- false ->
- throw({error, {bad_ipfamily, IpFamilyStr}})
- end.
-
+ validate_ipfamily(list_to_atom(IpFamilyStr)).
+
+validate_ipfamily(inet) ->
+ inet;
+validate_ipfamily(inet6) ->
+ inet6;
+%% Backwards compatibility wrapper,
+%% fallback to the default, IPV4,
+%% as it will most proably work.
+%% IPv6 standard moved away from
+%% beeing able to fallback to ipv4
+validate_ipfamily(inet6fb4) ->
+ inet;
+validate_ipfamily(IpFamilyStr) ->
+ throw({error, {bad_ipfamily, IpFamilyStr}}).
%%
%% load_mime_types/1 -> {ok, MimeTypes} | {error, Reason}
@@ -497,20 +400,16 @@ validate_properties2(Properties) ->
undefined ->
case proplists:get_value(sock_type, Properties, ip_comm) of
ip_comm ->
- case proplists:get_value(ipfamily, Properties) of
- undefined ->
- [{bind_address, any},
- {ipfamily, inet6fb4} | Properties];
- _ ->
- [{bind_address, any} | Properties]
- end;
+ add_inet_defaults(Properties);
+ {ip_comm, _} ->
+ add_inet_defaults(Properties);
_ ->
[{bind_address, any} | Properties]
end;
any ->
Properties;
Address0 ->
- IpFamily = proplists:get_value(ipfamily, Properties, inet6fb4),
+ IpFamily = proplists:get_value(ipfamily, Properties, inet),
case httpd_util:ip_address(Address0, IpFamily) of
{ok, Address} ->
Properties1 = proplists:delete(bind_address, Properties),
@@ -522,6 +421,16 @@ validate_properties2(Properties) ->
throw(Error)
end
end.
+
+add_inet_defaults(Properties) ->
+ case proplists:get_value(ipfamily, Properties) of
+ undefined ->
+ [{bind_address, any},
+ {ipfamily, inet} | Properties];
+ _ ->
+ [{bind_address, any} | Properties]
+ end.
+
check_minimum_bytes_per_second(Properties) ->
case proplists:get_value(minimum_bytes_per_second, Properties, false) of
false ->
@@ -562,6 +471,12 @@ validate_config_params([{max_body_size, Value} | Rest])
validate_config_params([{max_body_size, Value} | _]) ->
throw({max_body_size, Value});
+validate_config_params([{max_content_length, Value} | Rest])
+ when is_integer(Value) andalso (Value > 0) ->
+ validate_config_params(Rest);
+validate_config_params([{max_content_length, Value} | _]) ->
+ throw({max_content_length, Value});
+
validate_config_params([{server_name, Value} | Rest])
when is_list(Value) ->
validate_config_params(Rest);
@@ -585,12 +500,11 @@ validate_config_params([{server_tokens, Value} | _]) ->
validate_config_params([{socket_type, ip_comm} | Rest]) ->
validate_config_params(Rest);
-validate_config_params([{socket_type, Value} | Rest])
- when Value == ssl; Value == essl ->
- validate_config_params(Rest);
-
-validate_config_params([{socket_type, {Value, _}} | Rest])
- when Value == essl orelse Value == ssl ->
+validate_config_params([{socket_type, {Value, Opts}} | Rest]) when Value == ip_comm;
+ Value == ssl;
+ Value == essl ->
+ %% Make sure not to set socket values used internaly
+ validate_config_params(Opts),
validate_config_params(Rest);
validate_config_params([{socket_type, Value} | _]) ->
@@ -628,7 +542,7 @@ validate_config_params([{max_keep_alive_request, Value} | Rest])
when is_integer(Value) andalso (Value > 0) ->
validate_config_params(Rest);
validate_config_params([{max_keep_alive_request, Value} | _]) ->
- throw({max_header_size, Value});
+ throw({max_keep_alive_request, Value});
validate_config_params([{keep_alive_timeout, Value} | Rest])
when is_integer(Value) andalso (Value >= 0) ->
@@ -720,21 +634,32 @@ validate_config_params([{disable_chunked_transfer_encoding_send, Value} |
validate_config_params([{disable_chunked_transfer_encoding_send, Value} |
_ ]) ->
throw({disable_chunked_transfer_encoding_send, Value});
+validate_config_params([{Name, _} = Opt | _]) when Name == packet;
+ Name == mode;
+ Name == active;
+ Name == reuseaddr ->
+ throw({internaly_handled_opt_can_not_be_set, Opt});
validate_config_params([_| Rest]) ->
validate_config_params(Rest).
-%% It is actually pointless to check bind_address in this way since
-%% we need ipfamily to do it properly...
is_bind_address(any) ->
true;
is_bind_address(Value) ->
- case httpd_util:ip_address(Value, inet6fb4) of
+ case is_bind_address(Value, inet) of
+ false ->
+ is_bind_address(Value, inet6);
+ True ->
+ True
+ end.
+
+is_bind_address(Value, IpFamily) ->
+ case httpd_util:ip_address(Value, IpFamily) of
{ok, _} ->
true;
_ ->
false
end.
-
+
store(ConfigList0) ->
?hdrd("store", []),
try validate_config_params(ConfigList0) of
@@ -744,8 +669,9 @@ store(ConfigList0) ->
?hdrt("store", [{modules, Modules}]),
Port = proplists:get_value(port, ConfigList0),
Addr = proplists:get_value(bind_address, ConfigList0, any),
+ Profile = proplists:get_value(profile, ConfigList0, default),
ConfigList = fix_mime_types(ConfigList0),
- Name = httpd_util:make_name("httpd_conf", Addr, Port),
+ Name = httpd_util:make_name("httpd_conf", Addr, Port, Profile),
ConfigDB = ets:new(Name, [named_table, bag, protected]),
store(ConfigDB, ConfigList,
lists:append(Modules, [?MODULE]),
@@ -772,8 +698,15 @@ fix_mime_types(ConfigList0) ->
[{"html","text/html"},{"htm","text/html"}]}
| ConfigList0]
end;
- _ ->
- ConfigList0
+ MimeTypes ->
+ case filelib:is_file(MimeTypes) of
+ true ->
+ {ok, MimeTypesList} = load_mime_types(MimeTypes),
+ ConfigList = proplists:delete(mime_types, ConfigList0),
+ [{mime_types, MimeTypesList} | ConfigList];
+ false ->
+ ConfigList0
+ end
end.
store({mime_types,MimeTypesList},ConfigList) ->
@@ -791,6 +724,8 @@ store({log_format, LogFormat}, _ConfigList)
store({server_tokens, ServerTokens} = Entry, _ConfigList) ->
Server = server(ServerTokens),
{ok, [Entry, {server, Server}]};
+store({keep_alive_timeout, KeepAliveTimeout}, _ConfigList) ->
+ {ok, {keep_alive_timeout, KeepAliveTimeout}};
store(ConfigListEntry, _ConfigList) ->
{ok, ConfigListEntry}.
@@ -835,6 +770,8 @@ server(full = _ServerTokens) ->
OS = os_info(full),
lists:flatten(
io_lib:format("~s ~s OTP/~s", [?SERVER_SOFTWARE, OS, OTPRelease]));
+server(none = _ServerTokens) ->
+ "";
server({private, Server} = _ServerTokens) when is_list(Server) ->
%% The user provide its own
Server;
@@ -862,38 +799,16 @@ remove(ConfigDB) ->
ets:delete(ConfigDB),
ok.
-%% config(ConfigDB) ->
-%% case httpd_util:lookup(ConfigDB, socket_type, ip_comm) of
-%% ssl ->
-%% case ssl_certificate_file(ConfigDB) of
-%% undefined ->
-%% {error,
-%% "Directive SSLCertificateFile "
-%% "not found in the config file"};
-%% SSLCertificateFile ->
-%% {ssl,
-%% SSLCertificateFile++
-%% ssl_certificate_key_file(ConfigDB)++
-%% ssl_verify_client(ConfigDB)++
-%% ssl_ciphers(ConfigDB)++
-%% ssl_password(ConfigDB)++
-%% ssl_verify_depth(ConfigDB)++
-%% ssl_ca_certificate_file(ConfigDB)}
-%% end;
-%% ip_comm ->
-%% ip_comm
-%% end.
-
-
-get_config(Address, Port) ->
- Tab = httpd_util:make_name("httpd_conf", Address, Port),
+
+get_config(Address, Port, Profile) ->
+ Tab = httpd_util:make_name("httpd_conf", Address, Port, Profile),
Properties = ets:tab2list(Tab),
MimeTab = proplists:get_value(mime_types, Properties),
NewProperties = proplists:delete(mime_types, Properties),
[{mime_types, ets:tab2list(MimeTab)} | NewProperties].
-get_config(Address, Port, Properties) ->
- Tab = httpd_util:make_name("httpd_conf", Address, Port),
+get_config(Address, Port, Profile, Properties) ->
+ Tab = httpd_util:make_name("httpd_conf", Address, Port, Profile),
Config =
lists:map(fun(Prop) -> {Prop, httpd_util:lookup(Tab, Prop)} end,
Properties),
@@ -922,6 +837,8 @@ lookup_socket_type(ConfigDB) ->
case httpd_util:lookup(ConfigDB, socket_type, ip_comm) of
ip_comm ->
ip_comm;
+ {ip_comm, _} = Type ->
+ Type;
{Tag, Conf} ->
{Tag, Conf};
SSL when (SSL =:= ssl) orelse (SSL =:= essl) ->
@@ -948,7 +865,8 @@ ssl_config(ConfigDB) ->
ssl_ciphers(ConfigDB) ++
ssl_password(ConfigDB) ++
ssl_verify_depth(ConfigDB) ++
- ssl_ca_certificate_file(ConfigDB).
+ ssl_ca_certificate_file(ConfigDB) ++
+ ssl_log_level(ConfigDB).
@@ -961,7 +879,7 @@ bootstrap([]) ->
bootstrap([Line|Config]) ->
case Line of
"Modules " ++ Modules ->
- {ok, ModuleList} = inets_regexp:split(Modules," "),
+ ModuleList = re:split(Modules," ", [{return, list}]),
TheMods = [list_to_atom(X) || X <- ModuleList],
case verify_modules(TheMods) of
ok ->
@@ -1060,7 +978,7 @@ verify_modules([]) ->
verify_modules([Mod|Rest]) ->
case code:which(Mod) of
non_existing ->
- {error, ?NICE(atom_to_list(Mod)++" does not exist")};
+ {error, ?NICE(string:strip(atom_to_list(Mod), right, $\n) ++" does not exist")};
_Path ->
verify_modules(Rest)
end.
@@ -1086,7 +1004,8 @@ read_config_file(Stream, SoFar) ->
%% Ignore commented lines for efficiency later ..
read_config_file(Stream, SoFar);
Line ->
- {ok, NewLine, _}=inets_regexp:sub(clean(Line),"[\t\r\f ]"," "),
+ NewLine = re:replace(white_space_clean(Line),
+ "[\t\r\f ]"," ", [{return,list}, global]),
case NewLine of
[] ->
%% Also ignore empty lines ..
@@ -1102,7 +1021,7 @@ parse_mime_types(Stream,MimeTypesList) ->
eof ->
eof;
String ->
- clean(String)
+ re:replace(white_space_clean(String), "[\t\r\f ]"," ", [{return,list}, global])
end,
parse_mime_types(Stream, MimeTypesList, Line).
parse_mime_types(Stream, MimeTypesList, eof) ->
@@ -1113,17 +1032,19 @@ parse_mime_types(Stream, MimeTypesList, "") ->
parse_mime_types(Stream, MimeTypesList, [$#|_]) ->
parse_mime_types(Stream, MimeTypesList);
parse_mime_types(Stream, MimeTypesList, Line) ->
- case inets_regexp:split(Line, " ") of
- {ok, [NewMimeType|Suffixes]} ->
+ case re:split(Line, " ", [{return, list}]) of
+ [NewMimeType|Suffixes] ->
parse_mime_types(Stream,
lists:append(suffixes(NewMimeType,Suffixes),
MimeTypesList));
- {ok, _} ->
+ _ ->
{error, ?NICE(Line)}
end.
suffixes(_MimeType,[]) ->
[];
+suffixes(MimeType,[""|Rest]) ->
+ suffixes(MimeType, Rest);
suffixes(MimeType,[Suffix|Rest]) ->
[{Suffix,MimeType}|suffixes(MimeType,Rest)].
@@ -1214,6 +1135,14 @@ ssl_certificate_key_file(ConfigDB) ->
[{keyfile,SSLCertificateKeyFile}]
end.
+ssl_log_level(ConfigDB) ->
+ case httpd_util:lookup(ConfigDB,ssl_log_alert) of
+ undefined ->
+ [];
+ SSLLogLevel ->
+ [{log_alert,SSLLogLevel}]
+ end.
+
ssl_verify_client(ConfigDB) ->
case httpd_util:lookup(ConfigDB,ssl_verify_client) of
undefined ->
@@ -1275,9 +1204,73 @@ ssl_ca_certificate_file(ConfigDB) ->
end.
plain_server_tokens() ->
- [prod, major, minor, minimum, os, full].
+ [none, prod, major, minor, minimum, os, full].
error_report(Where,M,F,Error) ->
error_logger:error_report([{?MODULE, Where},
{apply, {M, F, []}}, Error]).
+white_space_clean(String) ->
+ re:replace(String, "^[ \t\n\r\f]*|[ \t\n\r\f]*\$","",
+ [{return,list}, global]).
+
+
+%%%=========================================================================
+%%% Deprecated remove in 19
+%%%=========================================================================
+is_directory(Directory) ->
+ case file:read_file_info(Directory) of
+ {ok,FileInfo} ->
+ #file_info{type = Type, access = Access} = FileInfo,
+ is_directory(Type,Access,FileInfo,Directory);
+ {error,Reason} ->
+ {error,Reason}
+ end.
+is_directory(directory,read,_FileInfo,Directory) ->
+ {ok,Directory};
+is_directory(directory,read_write,_FileInfo,Directory) ->
+ {ok,Directory};
+is_directory(_Type,_Access,FileInfo,_Directory) ->
+ {error,FileInfo}.
+
+is_file(File) ->
+ case file:read_file_info(File) of
+ {ok,FileInfo} ->
+ #file_info{type = Type, access = Access} = FileInfo,
+ is_file(Type,Access,FileInfo,File);
+ {error,Reason} ->
+ {error,Reason}
+ end.
+is_file(regular,read,_FileInfo,File) ->
+ {ok,File};
+is_file(regular,read_write,_FileInfo,File) ->
+ {ok,File};
+is_file(_Type,_Access,FileInfo,_File) ->
+ {error,FileInfo}.
+
+make_integer(String) ->
+ case re:run(string:strip(String),"[0-9]+", [{capture, none}]) of
+ match ->
+ {ok, list_to_integer(string:strip(String))};
+ nomatch ->
+ {error, nomatch}
+ end.
+
+clean(String) ->
+ re:replace(String, "^[ \t\n\r\f]*|[ \t\n\r\f]*\$","",
+ [{return,list}, global]).
+
+custom_clean(String,MoreBefore,MoreAfter) ->
+ re:replace(String,
+ "^[ \t\n\r\f"++MoreBefore++
+ "]*|[ \t\n\r\f"++MoreAfter++"]*\$","",
+ [{return,list}, global]).
+
+
+check_enum(_Enum,[]) ->
+ {error, not_valid};
+check_enum(Enum,[Enum|_Rest]) ->
+ {ok, list_to_atom(Enum)};
+check_enum(Enum, [_NotValid|Rest]) ->
+ check_enum(Enum, Rest).
+
diff --git a/lib/inets/src/http_server/httpd_connection_sup.erl b/lib/inets/src/http_server/httpd_connection_sup.erl
new file mode 100644
index 0000000000..f7b3bef245
--- /dev/null
+++ b/lib/inets/src/http_server/httpd_connection_sup.erl
@@ -0,0 +1,69 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Ssh connection supervisor.
+%%----------------------------------------------------------------------
+
+-module(httpd_connection_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/1]).
+-export([start_child/2, connection_sup/2]).
+
+%% Supervisor callback
+-export([init/1]).
+
+%%%=========================================================================
+%%% API
+%%%=========================================================================
+start_link(Args) ->
+ supervisor:start_link(?MODULE, [Args]).
+
+start_child(Sup, Args) ->
+ supervisor:start_child(Sup, Args).
+
+connection_sup(Addr, Port) ->
+ httpd_util:make_name("httpd_connection_sup", Addr, Port).
+
+%%%=========================================================================
+%%% Supervisor callback
+%%%=========================================================================
+init([[Addr, Port]]) ->
+ RegName = connection_sup(Addr, Port),
+ register(RegName, self()),
+ RestartStrategy = simple_one_for_one,
+ MaxR = 0,
+ MaxT = 3600,
+
+ Name = undefined, % As simple_one_for_one is used.
+ StartFunc = {httpd_request_handler, start_link, []},
+ Restart = temporary, % E.g. should not be restarted
+ Shutdown = 4000,
+ Modules = [httpd_request_handler],
+ Type = worker,
+
+ ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules},
+ {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}.
+
+
+
diff --git a/lib/inets/src/http_server/httpd_custom.erl b/lib/inets/src/http_server/httpd_custom.erl
new file mode 100644
index 0000000000..2b9701ef75
--- /dev/null
+++ b/lib/inets/src/http_server/httpd_custom.erl
@@ -0,0 +1,95 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015-2015. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(httpd_custom).
+
+-export([response_header/1, request_header/1, response_default_headers/0]).
+-export([customize_headers/3, response_default_headers/1]).
+
+-include("../inets_app/inets_internal.hrl").
+
+-behaviour(httpd_custom_api).
+
+%%--------------------------------------------------------------------
+%% Behavior API -----------------------------------
+%%--------------------------------------------------------------------
+
+response_header(Header) ->
+ {true, httpify(Header)}.
+request_header(Header) ->
+ {true, Header}.
+response_default_headers() ->
+ [].
+
+%%--------------------------------------------------------------------
+%% Internal API -----------------------------------
+%%--------------------------------------------------------------------
+customize_headers(?MODULE, Function, Arg) ->
+ ?MODULE:Function(Arg);
+customize_headers(Module, Function, Arg) ->
+ try Module:Function(Arg) of
+ {true, Value} ->
+ ?MODULE:Function(Value);
+ false ->
+ false
+ catch
+ _:_ ->
+ ?MODULE:Function(Arg)
+ end.
+
+response_default_headers(?MODULE) ->
+ response_default_headers();
+response_default_headers(Module) ->
+ try Module:response_default_headers() of
+ Defaults ->
+ [{http_util:to_lower(Key), Value} || {Key, Value} <- Defaults,
+ is_list(Key), is_list(Value)]
+ catch
+ _:_ ->
+ ?MODULE:response_default_headers()
+ end.
+%%--------------------------------------------------------------------
+%% Internal functions -----------------------------------
+%%--------------------------------------------------------------------
+httpify({Key0, Value}) ->
+ %% make sure first letter is capital (defacto standard)
+ Words1 = string:tokens(Key0, "-"),
+ Words2 = upify(Words1, []),
+ Key = new_key(Words2),
+ Key ++ ": " ++ Value ++ ?CRLF .
+
+new_key([]) ->
+ "";
+new_key([W]) ->
+ W;
+new_key([W1,W2]) ->
+ W1 ++ "-" ++ W2;
+new_key([W|R]) ->
+ W ++ "-" ++ new_key(R).
+
+upify([], Acc) ->
+ lists:reverse(Acc);
+upify([Key|Rest], Acc) ->
+ upify(Rest, [upify2(Key)|Acc]).
+
+upify2([C|Rest]) when (C >= $a) andalso (C =< $z) ->
+ [C-($a-$A)|Rest];
+upify2(Str) ->
+ Str.
diff --git a/lib/inets/src/http_server/httpd_custom_api.erl b/lib/inets/src/http_server/httpd_custom_api.erl
new file mode 100644
index 0000000000..d5a6fa8715
--- /dev/null
+++ b/lib/inets/src/http_server/httpd_custom_api.erl
@@ -0,0 +1,32 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015-2015. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(httpd_custom_api).
+
+-callback response_default_headers() ->
+ [{Key::string(), Value::string()}].
+-callback response_header({Key::string(), Value::string()}) ->
+ {true, {Key::string(), Value::string()}} | false |
+ {true, string()}. %% Used internally to avoid traversing headers twice
+-callback request_header({Key::string(), Value::string()}) ->
+ {true, {Key::string(), Value::string()}} | false.
+
+-optional_callbacks([response_default_headers/0, response_header/1,
+ request_header/1]).
diff --git a/lib/inets/src/http_server/httpd_esi.erl b/lib/inets/src/http_server/httpd_esi.erl
index 000874d0a3..9406b47802 100644
--- a/lib/inets/src/http_server/httpd_esi.erl
+++ b/lib/inets/src/http_server/httpd_esi.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_server/httpd_example.erl b/lib/inets/src/http_server/httpd_example.erl
index 16a080f8e2..424d269859 100644
--- a/lib/inets/src/http_server/httpd_example.erl
+++ b/lib/inets/src/http_server/httpd_example.erl
@@ -1,29 +1,30 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(httpd_example).
-export([print/1]).
--export([get/2, post/2, yahoo/2, test1/2, get_bin/2]).
+-export([get/2, post/2, yahoo/2, test1/2, get_bin/2, peer/2]).
-export([newformat/3]).
%% These are used by the inets test-suite
--export([delay/1]).
+-export([delay/1, chunk_timeout/3]).
print(String) ->
@@ -66,7 +67,7 @@ get_bin(_Env,_Input) ->
<INPUT TYPE=\"text\" NAME=\"input2\">
<INPUT TYPE=\"submit\"><BR>
</FORM>" ++ "\n"),
- footer()].
+ list_to_binary(footer())].
post(_Env,[]) ->
[header(),
@@ -93,10 +94,26 @@ default(Env,Input) ->
io_lib:format("~p",[httpd:parse_query(Input)]),"\n",
footer()].
+peer(Env, Input) ->
+ Header =
+ case proplists:get_value(peer_cert, Env) of
+ undefined ->
+ header("text/html", "Peer-Cert-Exist:false");
+ _ ->
+ header("text/html", "Peer-Cert-Exist:true")
+ end,
+ [Header,
+ top("Test peer_cert environment option"),
+ "<B>Peer cert:</B> ",
+ io_lib:format("~p",[proplists:get_value(peer_cert, Env)]),"\n",
+ footer()].
+
header() ->
header("text/html").
header(MimeType) ->
"Content-type: " ++ MimeType ++ "\r\n\r\n".
+header(MimeType, Other) ->
+ "Content-type: " ++ MimeType ++ "\r\n" ++ Other ++ "\r\n\r\n".
top(Title) ->
"<HTML>
@@ -126,9 +143,7 @@ delay(Time) when is_integer(Time) ->
i("httpd_example:delay(~p) -> done, now reply",[Time]),
delay_reply("delay ok");
delay(Time) when is_list(Time) ->
- delay(httpd_conf:make_integer(Time));
-delay({ok,Time}) when is_integer(Time) ->
- delay(Time);
+ delay(list_to_integer(Time));
delay({error,_Reason}) ->
i("delay -> called with invalid time"),
delay_reply("delay failed: invalid delay time").
@@ -143,3 +158,11 @@ i(F) -> i(F,[]).
i(F,A) -> io:format(F ++ "~n",A).
sleep(T) -> receive after T -> ok end.
+
+%% ------------------------------------------------------
+
+chunk_timeout(SessionID, _, StrInt) ->
+ mod_esi:deliver(SessionID, "Tranfer-Encoding:chunked/html\r\n\r\n"),
+ mod_esi:deliver(SessionID, top("Test chunk encoding timeout")),
+ timer:sleep(20000),
+ mod_esi:deliver(SessionID, footer()).
diff --git a/lib/inets/src/http_server/httpd_file.erl b/lib/inets/src/http_server/httpd_file.erl
index f2ba33099e..4d419172d0 100644
--- a/lib/inets/src/http_server/httpd_file.erl
+++ b/lib/inets/src/http_server/httpd_file.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_server/httpd_instance_sup.erl b/lib/inets/src/http_server/httpd_instance_sup.erl
index baa60d318c..b77aa174ca 100644
--- a/lib/inets/src/http_server/httpd_instance_sup.erl
+++ b/lib/inets/src/http_server/httpd_instance_sup.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -27,6 +28,8 @@
-behaviour(supervisor).
+-include("httpd_internal.hrl").
+
%% Internal application API
-export([start_link/3, start_link/4]).
@@ -41,7 +44,8 @@ start_link([{_, _}| _] = Config, AcceptTimeout, Debug) ->
{ok, Config2} ->
Address = proplists:get_value(bind_address, Config2),
Port = proplists:get_value(port, Config2),
- Name = make_name(Address, Port),
+ Profile = proplists:get_value(profile, Config2, ?DEFAULT_PROFILE),
+ Name = make_name(Address, Port, Profile),
SupName = {local, Name},
supervisor:start_link(SupName, ?MODULE,
[undefined, Config2, AcceptTimeout,
@@ -54,7 +58,8 @@ start_link([{_, _}| _] = Config, AcceptTimeout, Debug) ->
start_link(ConfigFile, AcceptTimeout, Debug) ->
case file_2_config(ConfigFile) of
{ok, ConfigList, Address, Port} ->
- Name = make_name(Address, Port),
+ Profile = proplists:get_value(profile, ConfigList, ?DEFAULT_PROFILE),
+ Name = make_name(Address, Port, Profile),
SupName = {local, Name},
supervisor:start_link(SupName, ?MODULE,
[ConfigFile, ConfigList, AcceptTimeout,
@@ -70,7 +75,8 @@ start_link([{_, _}| _] = Config, AcceptTimeout, ListenInfo, Debug) ->
{ok, Config2} ->
Address = proplists:get_value(bind_address, Config2),
Port = proplists:get_value(port, Config2),
- Name = make_name(Address, Port),
+ Profile = proplists:get_value(profile, Config2, ?DEFAULT_PROFILE),
+ Name = make_name(Address, Port, Profile),
SupName = {local, Name},
supervisor:start_link(SupName, ?MODULE,
[undefined, Config2, AcceptTimeout,
@@ -83,7 +89,8 @@ start_link([{_, _}| _] = Config, AcceptTimeout, ListenInfo, Debug) ->
start_link(ConfigFile, AcceptTimeout, ListenInfo, Debug) ->
case file_2_config(ConfigFile) of
{ok, ConfigList, Address, Port} ->
- Name = make_name(Address, Port),
+ Profile = proplists:get_value(profile, ConfigList, ?DEFAULT_PROFILE),
+ Name = make_name(Address, Port, Profile),
SupName = {local, Name},
supervisor:start_link(SupName, ?MODULE,
[ConfigFile, ConfigList, AcceptTimeout,
@@ -99,18 +106,24 @@ start_link(ConfigFile, AcceptTimeout, ListenInfo, Debug) ->
%%%=========================================================================
init([ConfigFile, ConfigList, AcceptTimeout, Debug, Address, Port]) ->
httpd_util:enable_debug(Debug),
+ Profile = proplists:get_value(profile, ConfigList, ?DEFAULT_PROFILE),
Flags = {one_for_one, 0, 1},
- Children = [sup_spec(httpd_acceptor_sup, Address, Port),
- sup_spec(httpd_misc_sup, Address, Port),
- worker_spec(httpd_manager, Address, Port,
+ Children = [httpd_connection_sup_spec(Address, Port, Profile),
+ httpd_acceptor_sup_spec(Address, Port, Profile, ConfigList, AcceptTimeout,
+ undefined),
+ sup_spec(httpd_misc_sup, Address, Port, Profile),
+ worker_spec(httpd_manager, Address, Port, Profile,
ConfigFile, ConfigList,AcceptTimeout)],
{ok, {Flags, Children}};
init([ConfigFile, ConfigList, AcceptTimeout, Debug, Address, Port, ListenInfo]) ->
httpd_util:enable_debug(Debug),
+ Profile = proplists:get_value(profile, ConfigList, ?DEFAULT_PROFILE),
Flags = {one_for_one, 0, 1},
- Children = [sup_spec(httpd_acceptor_sup, Address, Port),
- sup_spec(httpd_misc_sup, Address, Port),
- worker_spec(httpd_manager, Address, Port, ListenInfo,
+ Children = [httpd_connection_sup_spec(Address, Port, Profile),
+ httpd_acceptor_sup_spec(Address, Port, Profile, ConfigList, AcceptTimeout,
+ ListenInfo),
+ sup_spec(httpd_misc_sup, Address, Port, Profile),
+ worker_spec(httpd_manager, Address, Port, Profile, ListenInfo,
ConfigFile, ConfigList, AcceptTimeout)],
{ok, {Flags, Children}}.
@@ -118,18 +131,36 @@ init([ConfigFile, ConfigList, AcceptTimeout, Debug, Address, Port, ListenInfo])
%%%=========================================================================
%%% Internal functions
%%%=========================================================================
-sup_spec(SupModule, Address, Port) ->
- Name = {SupModule, Address, Port},
- StartFunc = {SupModule, start_link, [Address, Port]},
+httpd_connection_sup_spec(Address, Port, Profile) ->
+ Name = {httpd_connection_sup, Address, Port, Profile},
+ StartFunc = {httpd_connection_sup, start_link, [[Address, Port]]},
+ Restart = permanent,
+ Shutdown = 5000,
+ Modules = [httpd_connection_sup],
+ Type = supervisor,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
+httpd_acceptor_sup_spec(Address, Port, Profile, ConfigList, AcceptTimeout, ListenInfo) ->
+ Name = {httpd_acceptor_sup, Address, Port, Profile},
+ StartFunc = {httpd_acceptor_sup, start_link, [[Address, Port, ConfigList, AcceptTimeout, ListenInfo]]},
+ Restart = permanent,
+ Shutdown = infinity,
+ Modules = [httpd_acceptor_sup],
+ Type = supervisor,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
+sup_spec(SupModule, Address, Port, Profile) ->
+ Name = {SupModule, Address, Port, Profile},
+ StartFunc = {SupModule, start_link, [Address, Port, Profile]},
Restart = permanent,
Shutdown = infinity,
Modules = [SupModule],
Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
-worker_spec(WorkerModule, Address, Port, ConfigFile,
+worker_spec(WorkerModule, Address, Port, Profile, ConfigFile,
ConfigList, AcceptTimeout) ->
- Name = {WorkerModule, Address, Port},
+ Name = {WorkerModule, Address, Port, Profile},
StartFunc = {WorkerModule, start_link,
[ConfigFile, ConfigList, AcceptTimeout]},
Restart = permanent,
@@ -138,9 +169,9 @@ worker_spec(WorkerModule, Address, Port, ConfigFile,
Type = worker,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
-worker_spec(WorkerModule, Address, Port, ListenInfo, ConfigFile,
+worker_spec(WorkerModule, Address, Port, Profile, ListenInfo, ConfigFile,
ConfigList, AcceptTimeout) ->
- Name = {WorkerModule, Address, Port},
+ Name = {WorkerModule, Address, Port, Profile},
StartFunc = {WorkerModule, start_link,
[ConfigFile, ConfigList, AcceptTimeout, ListenInfo]},
Restart = permanent,
@@ -149,8 +180,8 @@ worker_spec(WorkerModule, Address, Port, ListenInfo, ConfigFile,
Type = worker,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
-make_name(Address,Port) ->
- httpd_util:make_name("httpd_instance_sup", Address, Port).
+make_name(Address, Port, Profile) ->
+ httpd_util:make_name("httpd_instance_sup", Address, Port, Profile).
file_2_config(ConfigFile) ->
@@ -167,5 +198,3 @@ file_2_config(ConfigFile) ->
Error ->
Error
end.
-
-
diff --git a/lib/inets/src/http_server/httpd_internal.hrl b/lib/inets/src/http_server/httpd_internal.hrl
index 108469ea0a..09d720ee85 100644
--- a/lib/inets/src/http_server/httpd_internal.hrl
+++ b/lib/inets/src/http_server/httpd_internal.hrl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -31,6 +32,8 @@
-define(SOCKET_MAX_POLL,25).
-define(FILE_CHUNK_SIZE,64*1024).
-define(GATEWAY_INTERFACE,"CGI/1.1").
+-define(DEFAULT_PROFILE, default).
+
-define(NICE(Reason),lists:flatten(atom_to_list(?MODULE)++": "++Reason)).
-define(DEFAULT_CONTEXT,
[{errmsg,"[an error occurred while processing this directive]"},
diff --git a/lib/inets/src/http_server/httpd_log.erl b/lib/inets/src/http_server/httpd_log.erl
index a34435e0e8..56a08cd502 100644
--- a/lib/inets/src/http_server/httpd_log.erl
+++ b/lib/inets/src/http_server/httpd_log.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -39,14 +40,21 @@
Size :: 0 | pos_integer() | string()) ->
{Log :: atom() | pid(), Entry :: string()} | term() .
-access_entry(Log, NoLog, Info, RFC931, AuthUser, Date, StatusCode, SizeStr)
- when is_list(SizeStr) ->
+%% Somethime the size in the form of the content_length is put here, which
+%% is actually in the form of a string
+%% So it can either be the size as an integer, the size as a string
+%% or, worst case scenario, bytes.
+access_entry(Log, NoLog, Info, RFC931, AuthUser, Date, StatusCode,
+ SizeStrOrBytes)
+ when is_list(SizeStrOrBytes) ->
Size =
- case (catch list_to_integer(SizeStr)) of
+ case (catch list_to_integer(SizeStrOrBytes)) of
I when is_integer(I) ->
+ %% This is from using the content_length (which is a string)
I;
_ ->
- SizeStr % This is better then nothing
+ %% This is better than nothing
+ httpd_util:flatlength(SizeStrOrBytes)
end,
access_entry(Log, NoLog, Info, RFC931, AuthUser, Date, StatusCode, Size);
access_entry(Log, NoLog,
diff --git a/lib/inets/src/http_server/httpd_manager.erl b/lib/inets/src/http_server/httpd_manager.erl
index c83d06a158..7cb39937e8 100644
--- a/lib/inets/src/http_server/httpd_manager.erl
+++ b/lib/inets/src/http_server/httpd_manager.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -25,11 +26,11 @@
-behaviour(gen_server).
%% Application internal API
--export([start/2, start_link/2, start_link/3, start_link/4, stop/1, reload/2]).
--export([new_connection/1, done_connection/1]).
--export([config_lookup/2, config_lookup/3,
- config_multi_lookup/2, config_multi_lookup/3,
- config_match/2, config_match/3]).
+-export([start/2, start_link/2, start_link/3, start_link/4,
+ stop/1, reload/2]).
+-export([new_connection/1]).
+-export([config_match/3, config_match/4]).
+-export([block/2, block/3, unblock/1]).
%% gen_server exports
-export([init/1,
@@ -37,39 +38,25 @@
terminate/2,
code_change/3]).
-
-%% Management exports
--export([block/2, block/3, unblock/1]).
--export([get_admin_state/1, get_usage_state/1]).
--export([is_busy/1,is_busy/2,is_busy_or_blocked/1,is_blocked/1]). %% ???????
--export([get_status/1, get_status/2]).
-
--export([c/1]).
-
-record(state,{socket_type = ip_comm,
config_file,
config_db = null,
- connections, %% Current request handlers
+ connection_sup,
admin_state = unblocked,
blocker_ref = undefined,
- blocking_tmr = undefined,
+ blocking_from = undefined,
+ shutdown_poller = undefined,
status = []}).
+%%%--------------------------------------------------------------------
+%%% Application internal API
+%%%--------------------------------------------------------------------
-
-%%TODO: Clean up this module!
-
-c(Port) ->
- Ref = httpd_util:make_name("httpd",undefined,Port),
- call(Ref, fake_close).
-
-%%
-%% External API
-%%
%% Deprecated
start(ConfigFile, ConfigList) ->
Port = proplists:get_value(port,ConfigList,80),
Addr = proplists:get_value(bind_address, ConfigList),
- Name = make_name(Addr,Port),
+ Profile = proplists:get_value(profile, ConfigList, default),
+ Name = make_name(Addr, Port, Profile),
gen_server:start({local,Name},?MODULE,
[ConfigFile, ConfigList, 15000, Addr, Port],[]).
@@ -80,159 +67,49 @@ start_link(ConfigFile, ConfigList) ->
start_link(ConfigFile, ConfigList, AcceptTimeout) ->
Port = proplists:get_value(port, ConfigList, 80),
Addr = proplists:get_value(bind_address, ConfigList),
- Name = make_name(Addr, Port),
+ Profile = proplists:get_value(profile, ConfigList, default),
+ Name = make_name(Addr, Port, Profile),
gen_server:start_link({local, Name},?MODULE,
- [ConfigFile, ConfigList, AcceptTimeout, Addr, Port],[]).
+ [ConfigFile, ConfigList,
+ AcceptTimeout, Addr, Port],[]).
start_link(ConfigFile, ConfigList, AcceptTimeout, ListenSocket) ->
Port = proplists:get_value(port, ConfigList, 80),
Addr = proplists:get_value(bind_address, ConfigList),
- Name = make_name(Addr, Port),
+ Profile = proplists:get_value(profile, ConfigList, default),
+ Name = make_name(Addr, Port, Profile),
gen_server:start_link({local, Name},?MODULE,
[ConfigFile, ConfigList, AcceptTimeout, Addr,
Port, ListenSocket],[]).
-
stop(ServerRef) ->
call(ServerRef, stop).
reload(ServerRef, Conf) ->
call(ServerRef, {reload, Conf}).
-
-%%%----------------------------------------------------------------
-
-block(ServerRef, disturbing) ->
- call(ServerRef,block);
-
-block(ServerRef, non_disturbing) ->
- do_block(ServerRef, non_disturbing, infinity).
+block(ServerRef, Method) ->
+ block(ServerRef, Method, infinity).
block(ServerRef, Method, Timeout) ->
- do_block(ServerRef, Method, Timeout).
-
-
-%% The reason for not using call here, is that the manager cannot
-%% _wait_ for completion of the requests. It must be able to do
-%% do other things at the same time as the blocking goes on.
-do_block(ServerRef, Method, infinity) ->
- Ref = make_ref(),
- cast(ServerRef, {block, Method, infinity, self(), Ref}),
- receive
- {block_reply, Reply, Ref} ->
- Reply
- end;
-do_block(ServerRef,Method,Timeout) when Timeout > 0 ->
- Ref = make_ref(),
- cast(ServerRef,{block,Method,Timeout,self(),Ref}),
- receive
- {block_reply,Reply,Ref} ->
- Reply
- end.
-
-
-%%%----------------------------------------------------------------
-
-%% unblock
+ call(ServerRef, {block, self(), Method, Timeout}).
unblock(ServerRef) ->
- call(ServerRef,unblock).
-
-%% get admin/usage state
-
-get_admin_state(ServerRef) ->
- call(ServerRef,get_admin_state).
-
-get_usage_state(ServerRef) ->
- call(ServerRef,get_usage_state).
-
-
-%% get_status
-
-get_status(ServerRef) ->
- gen_server:call(ServerRef,get_status).
-
-get_status(ServerRef,Timeout) ->
- gen_server:call(ServerRef,get_status,Timeout).
-
-%%
-%% Internal API
-%%
-
-
-%% new_connection
+ call(ServerRef,{unblock, self()}).
new_connection(Manager) ->
- gen_server:call(Manager, {new_connection, self()}, infinity).
-
-%% done
-
-done_connection(Manager) ->
- gen_server:cast(Manager, {done_connection, self()}).
-
+ call(Manager, {new_connection, self()}).
-%% is_busy(ServerRef) -> true | false
-%%
-%% Tests if the server is (in usage state) busy,
-%% i.e. has rached the heavy load limit.
-%%
-
-is_busy(ServerRef) ->
- gen_server:call(ServerRef,is_busy).
-
-is_busy(ServerRef,Timeout) ->
- gen_server:call(ServerRef,is_busy,Timeout).
-
-
-%% is_busy_or_blocked(ServerRef) -> busy | blocked | false
-%%
-%% Tests if the server is busy (usage state), i.e. has rached,
-%% the heavy load limit, or blocked (admin state) .
-%%
-
-is_busy_or_blocked(ServerRef) ->
- gen_server:call(ServerRef,is_busy_or_blocked).
-
-
-%% is_blocked(ServerRef) -> true | false
-%%
-%% Tests if the server is blocked (admin state) .
-%%
-
-is_blocked(ServerRef) ->
- gen_server:call(ServerRef,is_blocked).
-
-
-%%
-%% Module API. Theese functions are intended for use from modules only.
-%%
-
-config_lookup(Port, Query) ->
- config_lookup(undefined, Port, Query).
-config_lookup(Addr, Port, Query) ->
- Name = httpd_util:make_name("httpd",Addr,Port),
- gen_server:call(whereis(Name), {config_lookup, Query}).
-
-config_multi_lookup(Port, Query) ->
- config_multi_lookup(undefined,Port,Query).
-config_multi_lookup(Addr,Port, Query) ->
- Name = httpd_util:make_name("httpd",Addr,Port),
- gen_server:call(whereis(Name), {config_multi_lookup, Query}).
-
-config_match(Port, Pattern) ->
- config_match(undefined,Port,Pattern).
-config_match(Addr, Port, Pattern) ->
- Name = httpd_util:make_name("httpd",Addr,Port),
- gen_server:call(whereis(Name), {config_match, Pattern}).
-
-
-%%
-%% Server call-back functions
-%%
-
-%% init
+config_match(Port, Profile, Pattern) ->
+ config_match(undefined,Port, Profile, Pattern).
+config_match(Addr, Port, Profile, Pattern) ->
+ Name = httpd_util:make_name("httpd",Addr,Port, Profile),
+ call(whereis(Name), {config_match, Pattern}).
+%%%--------------------------------------------------------------------
+%%% gen_server callbacks functions
+%%%--------------------------------------------------------------------
init([ConfigFile, ConfigList, AcceptTimeout, Addr, Port]) ->
process_flag(trap_exit, true),
case (catch do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port)) of
@@ -263,47 +140,35 @@ init([ConfigFile, ConfigList, AcceptTimeout, Addr, Port, ListenInfo]) ->
{ok, State}
end.
-do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port) ->
- IpFamily = proplists:get_value(ipfamily, ConfigList, inet6fb4),
+do_init(ConfigFile, ConfigList, _AcceptTimeout, Addr, Port) ->
+ Sup = httpd_util:make_name("httpd_connection_sup", Addr, Port),
NewConfigFile = proplists:get_value(file, ConfigList, ConfigFile),
ConfigDB = do_initial_store(ConfigList),
SocketType = httpd_conf:lookup_socket_type(ConfigDB),
- case httpd_acceptor_sup:start_acceptor(SocketType, Addr,
- Port, IpFamily, ConfigDB, AcceptTimeout) of
- {ok, _Pid} ->
- Status = [{max_conn, 0},
- {last_heavy_load, never},
- {last_connection, never}],
+ Status = [{max_conn, 0},
+ {last_heavy_load, never},
+ {last_connection, never}],
State = #state{socket_type = SocketType,
config_file = NewConfigFile,
config_db = ConfigDB,
- connections = [],
+ connection_sup = Sup,
status = Status},
- {ok, State};
- Else ->
- Else
- end.
+ {ok, State}.
-do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port, ListenInfo) ->
- IpFamily = proplists:get_value(ipfamily, ConfigList, inet6fb4),
+do_init(ConfigFile, ConfigList, _AcceptTimeout, Addr, Port, _ListenInfo) ->
+ Sup = httpd_util:make_name("httpd_connection_sup", Addr, Port),
NewConfigFile = proplists:get_value(file, ConfigList, ConfigFile),
ConfigDB = do_initial_store(ConfigList),
SocketType = httpd_conf:lookup_socket_type(ConfigDB),
- case httpd_acceptor_sup:start_acceptor(SocketType, Addr,
- Port, IpFamily, ConfigDB,
- AcceptTimeout, ListenInfo) of
- {ok, _Pid} ->
- Status = [{max_conn,0}, {last_heavy_load,never},
- {last_connection,never}],
+ Status = [{max_conn,0}, {last_heavy_load,never},
+ {last_connection,never}],
State = #state{socket_type = SocketType,
config_file = NewConfigFile,
config_db = ConfigDB,
- connections = [],
+ connection_sup = Sup,
status = Status},
- {ok, State};
- Else ->
- Else
- end.
+ {ok, State}.
+
do_initial_store(ConfigList) ->
case httpd_conf:store(ConfigList) of
@@ -313,75 +178,14 @@ do_initial_store(ConfigList) ->
throw({error, Reason})
end.
-
-
-%% handle_call
-
handle_call(stop, _From, State) ->
{stop, normal, ok, State};
-handle_call({config_lookup, Query}, _From, State) ->
- Res = httpd_util:lookup(State#state.config_db, Query),
- {reply, Res, State};
-
-handle_call({config_multi_lookup, Query}, _From, State) ->
- Res = httpd_util:multi_lookup(State#state.config_db, Query),
- {reply, Res, State};
-
handle_call({config_match, Query}, _From, State) ->
Res = ets:match_object(State#state.config_db, Query),
{reply, Res, State};
-handle_call(get_status, _From, State) ->
- ManagerStatus = manager_status(self()),
- S1 = [{current_conn,length(State#state.connections)}|State#state.status]++
- [ManagerStatus],
- {reply,S1,State};
-
-handle_call(is_busy, _From, State) ->
- Reply = case get_ustate(State) of
- busy ->
- true;
- _ ->
- false
- end,
- {reply,Reply,State};
-
-handle_call(is_busy_or_blocked, _From, State) ->
- Reply =
- case get_astate(State) of
- unblocked ->
- case get_ustate(State) of
- busy ->
- busy;
- _ ->
- false
- end;
- _ ->
- blocked
- end,
- {reply,Reply,State};
-
-handle_call(is_blocked, _From, State) ->
- Reply =
- case get_astate(State) of
- unblocked ->
- false;
- _ ->
- true
- end,
- {reply,Reply,State};
-
-handle_call(get_admin_state, _From, State) ->
- Reply = get_astate(State),
- {reply,Reply,State};
-
-handle_call(get_usage_state, _From, State) ->
- Reply = get_ustate(State),
- {reply,Reply,State};
-
-handle_call({reload, Conf}, _From, State)
- when State#state.admin_state =:= blocked ->
+handle_call({reload, Conf}, _From, #state{admin_state = blocked} = State) ->
case handle_reload(Conf, State) of
{stop, Reply,S1} ->
{stop, Reply, S1};
@@ -392,13 +196,33 @@ handle_call({reload, Conf}, _From, State)
handle_call({reload, _}, _From, State) ->
{reply,{error,{invalid_admin_state,State#state.admin_state}},State};
-handle_call(block, _From, State) ->
- {Reply,S1} = handle_block(State),
- {reply,Reply,S1};
+handle_call({block , Blocker, Mode, Timeout}, From,
+ #state{admin_state = unblocked,
+ connection_sup = CSup} = State) ->
+ Monitor = erlang:monitor(process, Blocker),
+ case count_children(CSup) of
+ 0 ->
+ %% Already in idle usage state => go directly to blocked
+ {reply, ok, State#state{admin_state = blocked,
+ blocker_ref = {Blocker, Monitor},
+ blocking_from = From}};
+ _ ->
+ handle_block(Mode, Timeout,
+ State#state{blocker_ref = {Blocker, Monitor},
+ blocking_from = From})
+ end;
+handle_call({block , _, _, _}, _, State) ->
+ {reply, {error, blocked}, State};
-handle_call(unblock, {From,_Tag}, State) ->
- {Reply,S1} = handle_unblock(State,From),
- {reply, Reply, S1};
+handle_call({unblock, Blocker}, _, #state{blocker_ref = {Blocker, Monitor},
+ admin_state = blocked} = State) ->
+
+ erlang:demonitor(Monitor),
+ {reply, ok,
+ State#state{admin_state = unblocked, blocker_ref = undefined}};
+
+handle_call({unblock, _}, _, State) ->
+ {reply, {error, only_blocker_may_unblock}, State};
handle_call({new_connection, Pid}, _From, State) ->
{Status, NewState} = handle_new_connection(State, Pid),
@@ -415,21 +239,6 @@ handle_call(Request, From, State) ->
report_error(State,String),
{reply, ok, State}.
-
-%% handle_cast
-
-handle_cast({done_connection, Pid}, State) ->
- S1 = handle_done_connection(State, Pid),
- {noreply, S1};
-
-handle_cast({block, disturbing, Timeout, From, Ref}, State) ->
- S1 = handle_block(State, Timeout, From, Ref),
- {noreply,S1};
-
-handle_cast({block, non_disturbing, Timeout, From, Ref}, State) ->
- S1 = handle_nd_block(State, Timeout, From, Ref),
- {noreply,S1};
-
handle_cast(Message, State) ->
String =
lists:flatten(
@@ -440,32 +249,50 @@ handle_cast(Message, State) ->
report_error(State, String),
{noreply, State}.
-%% handle_info
-
-handle_info({block_timeout, Method}, State) ->
- S1 = handle_block_timeout(State,Method),
- {noreply, S1};
+handle_info(connections_terminated, #state{admin_state = shutting_down,
+ blocking_from = From} = State) ->
+ gen_server:reply(From, ok),
+ {noreply, State#state{admin_state = blocked, blocking_from = undefined}};
+handle_info(connections_terminated, State) ->
+ {noreply, State};
-handle_info({'DOWN', Ref, process, _Object, _Info}, State) ->
- S1 =
- case State#state.blocker_ref of
- Ref ->
- handle_blocker_exit(State);
- _ ->
- %% Not our blocker, so ignore
- State
- end,
- {noreply, S1};
+handle_info({block_timeout, non_disturbing, Blocker},
+ #state{admin_state = shutting_down,
+ blocking_from = From,
+ blocker_ref = {_, Monitor} = Blocker} = State) ->
+ erlang:demonitor(Monitor),
+ gen_server:reply(From, {error, timeout}),
+ {noreply, State#state{admin_state = unblocked, blocking_from = undefined,
+ blocker_ref = undefined}};
+handle_info({block_timeout, disturbing, Blocker},
+ #state{admin_state = shutting_down,
+ blocking_from = From,
+ blocker_ref = Blocker,
+ connection_sup = Sup} = State) ->
+ SupPid = whereis(Sup),
+ shutdown_connections(SupPid),
+ gen_server:reply(From, ok),
+ {noreply, State#state{admin_state = blocked,
+ blocking_from = undefined}};
+handle_info({block_timeout, _, _}, State) ->
+ {noreply, State};
+
+handle_info({'DOWN', _, process, Pid, _Info},
+ #state{admin_state = Admin,
+ blocker_ref = {Pid, Monitor}} = State) when
+ Admin =/= unblocked ->
+ erlang:demonitor(Monitor),
+ {noreply, State#state{admin_state = unblocked,
+ blocking_from = undefined,
+ blocker_ref = undefined}};
+handle_info({'DOWN', _, process, _, _}, State) ->
+ {noreply, State};
handle_info({'EXIT', _, normal}, State) ->
{noreply, State};
-handle_info({'EXIT', _, blocked}, S) ->
- {noreply, S};
-
-handle_info({'EXIT', Pid, Reason}, State) ->
- S1 = check_connections(State, Pid, Reason),
- {noreply, S1};
+handle_info({'EXIT', _, shutdown}, State) ->
+ {stop, shutdown, State};
handle_info(Info, State) ->
String =
@@ -477,246 +304,64 @@ handle_info(Info, State) ->
report_error(State, String),
{noreply, State}.
-
-%% terminate
-
terminate(_, #state{config_db = Db}) ->
httpd_conf:remove_all(Db),
ok.
-
-%% code_change({down,ToVsn}, State, Extra)
-%%
-
code_change({down,_ToVsn}, State, _Extra) ->
{ok,State};
-%% code_change(FromVsn, State, Extra)
-%%
code_change(_FromVsn, State, _Extra) ->
{ok,State}.
-
-
-%% -------------------------------------------------------------------------
-%% check_connection
-%%
-%%
-%%
-%%
-
-check_connections(#state{connections = []} = State, _Pid, _Reason) ->
- State;
-check_connections(#state{admin_state = shutting_down,
- connections = Connections} = State, Pid, Reason) ->
- %% Could be a crashing request handler
- case lists:delete(Pid, Connections) of
- [] -> % Crashing request handler => block complete
- String =
- lists:flatten(
- io_lib:format("request handler (~p) crashed:"
- "~n ~p", [Pid, Reason])),
- report_error(State, String),
- demonitor_blocker(State#state.blocker_ref),
- {Tmr,From,Ref} = State#state.blocking_tmr,
- stop_block_tmr(Tmr),
- From ! {block_reply,ok,Ref},
- State#state{admin_state = blocked, connections = [],
- blocker_ref = undefined};
- Connections1 ->
- State#state{connections = Connections1}
- end;
-check_connections(#state{connections = Connections} = State, Pid, Reason) ->
- case lists:delete(Pid, Connections) of
- Connections -> % Not a request handler, so ignore
- State;
- NewConnections ->
- String =
- lists:flatten(
- io_lib:format("request handler (~p) crashed:"
- "~n ~p", [Pid, Reason])),
- report_error(State, String),
- State#state{connections = NewConnections}
- end.
-
-
-%% -------------------------------------------------------------------------
-%% handle_[new | done]_connection
-%%
-%%
-%%
-%%
-
-handle_new_connection(State, Handler) ->
+%%%--------------------------------------------------------------------
+%%% Internal functions
+%%%--------------------------------------------------------------------
+handle_new_connection(#state{admin_state = AdminState} = State, Handler) ->
UsageState = get_ustate(State),
- AdminState = get_astate(State),
handle_new_connection(UsageState, AdminState, State, Handler).
-handle_new_connection(busy, unblocked, State, _Handler) ->
- Status = update_heavy_load_status(State#state.status),
- {{reject, busy},
- State#state{status = Status}};
-
-handle_new_connection(_UsageState, unblocked, State, Handler) ->
- Connections = State#state.connections,
- Status = update_connection_status(State#state.status,
- length(Connections)+1),
- link(Handler),
- {{ok, accept},
- State#state{connections = [Handler|Connections], status = Status}};
-
-handle_new_connection(_UsageState, _AdminState, State, _Handler) ->
- {{reject, blocked},
- State}.
-
-handle_done_connection(#state{admin_state = shutting_down,
- connections = Connections} = State, Handler) ->
- unlink(Handler),
- case lists:delete(Handler, Connections) of
- [] -> % Ok, block complete
- demonitor_blocker(State#state.blocker_ref),
- {Tmr,From,Ref} = State#state.blocking_tmr,
- stop_block_tmr(Tmr),
- From ! {block_reply,ok,Ref},
- State#state{admin_state = blocked, connections = [],
- blocker_ref = undefined};
- Connections1 ->
- State#state{connections = Connections1}
- end;
-
-handle_done_connection(#state{connections = Connections} = State, Handler) ->
- State#state{connections = lists:delete(Handler, Connections)}.
-
-
-%% -------------------------------------------------------------------------
-%% handle_block
-%%
-%%
-%%
-%%
-handle_block(#state{admin_state = AdminState} = S) ->
- handle_block(S, AdminState).
-
-handle_block(S,unblocked) ->
- %% Kill all connections
- [kill_handler(Pid) || Pid <- S#state.connections],
- {ok,S#state{connections = [], admin_state = blocked}};
-handle_block(S,blocked) ->
- {ok,S};
-handle_block(S,shutting_down) ->
- {{error,shutting_down},S}.
-
-
-kill_handler(Pid) ->
- exit(Pid, blocked).
-
-handle_block(S,Timeout,From,Ref) when Timeout >= 0 ->
- do_block(S,Timeout,From,Ref);
-
-handle_block(S,Timeout,From,Ref) ->
- Reply = {error,{invalid_block_request,Timeout}},
- From ! {block_reply,Reply,Ref},
- S.
-
-do_block(S,Timeout,From,Ref) ->
- case S#state.connections of
- [] ->
- %% Already in idle usage state => go directly to blocked
- From ! {block_reply,ok,Ref},
- S#state{admin_state = blocked};
- _ ->
- %% Active or Busy usage state => go to shutting_down
- %% Make sure we get to know if blocker dies...
- MonitorRef = monitor_blocker(From),
- Tmr = {start_block_tmr(Timeout,disturbing),From,Ref},
- S#state{admin_state = shutting_down,
- blocker_ref = MonitorRef, blocking_tmr = Tmr}
- end.
-
-handle_nd_block(S,infinity,From,Ref) ->
- do_nd_block(S,infinity,From,Ref);
-
-handle_nd_block(S,Timeout,From,Ref) when Timeout >= 0 ->
- do_nd_block(S,Timeout,From,Ref);
-
-handle_nd_block(S,Timeout,From,Ref) ->
- Reply = {error,{invalid_block_request,Timeout}},
- From ! {block_reply,Reply,Ref},
- S.
-
-do_nd_block(S,Timeout,From,Ref) ->
- case S#state.connections of
- [] ->
- %% Already in idle usage state => go directly to blocked
- From ! {block_reply,ok,Ref},
- S#state{admin_state = blocked};
+handle_new_connection(_UsageState, unblocked,
+ #state{config_db = Db, connection_sup = CSup} =
+ State, _) ->
+ Max = httpd_util:lookup(Db, max_clients),
+ case count_children(CSup) of
+ Count when Count =< Max ->
+ {{ok, accept}, State};
_ ->
- %% Active or Busy usage state => go to shutting_down
- %% Make sure we get to know if blocker dies...
- MonitorRef = monitor_blocker(From),
- Tmr = {start_block_tmr(Timeout,non_disturbing),From,Ref},
- S#state{admin_state = shutting_down,
- blocker_ref = MonitorRef, blocking_tmr = Tmr}
- end.
+ {{reject, busy}, State}
+ end;
-handle_block_timeout(S,Method) ->
- %% Time to take this to the road...
- demonitor_blocker(S#state.blocker_ref),
- handle_block_timeout1(S,Method,S#state.blocking_tmr).
-
-handle_block_timeout1(S,non_disturbing,{_,From,Ref}) ->
- From ! {block_reply,{error,timeout},Ref},
- S#state{admin_state = unblocked,
- blocker_ref = undefined, blocking_tmr = undefined};
-
-handle_block_timeout1(S,disturbing,{_,From,Ref}) ->
- [exit(Pid,blocked) || Pid <- S#state.connections],
-
- From ! {block_reply,ok,Ref},
- S#state{admin_state = blocked, connections = [],
- blocker_ref = undefined, blocking_tmr = undefined};
-
-handle_block_timeout1(S,Method,{_,From,Ref}) ->
- From ! {block_reply,{error,{unknown_block_method,Method}},Ref},
- S#state{admin_state = blocked, connections = [],
- blocker_ref = undefined, blocking_tmr = undefined};
-
-handle_block_timeout1(S, _Method, _TmrInfo) ->
- S#state{admin_state = unblocked,
- blocker_ref = undefined, blocking_tmr = undefined}.
-
-handle_unblock(S, FromA) ->
- handle_unblock(S, FromA, S#state.admin_state).
-
-handle_unblock(S, _FromA, unblocked) ->
- {ok,S};
-handle_unblock(S, FromA, _AdminState) ->
- case S#state.blocking_tmr of
- {Tmr,FromB,Ref} ->
- %% Another process is trying to unblock
- %% Inform the blocker
- stop_block_tmr(Tmr),
- FromB ! {block_reply, {error,{unblocked,FromA}},Ref};
- _ ->
- ok
- end,
- {ok,S#state{admin_state = unblocked, blocking_tmr = undefined}}.
-
-%% The blocker died so we give up on the block.
-handle_blocker_exit(S) ->
- {Tmr,_From,_Ref} = S#state.blocking_tmr,
- stop_block_tmr(Tmr),
- S#state{admin_state = unblocked,
- blocker_ref = undefined, blocking_tmr = undefined}.
+handle_new_connection(_UsageState, _AdminState, State, _Handler) ->
+ {{reject, blocked}, State}.
+
+handle_block(disturbing, infinity,
+ #state{connection_sup = CSup,
+ blocking_from = From} = State) ->
+ SupPid = whereis(CSup),
+ shutdown_connections(SupPid),
+ gen_server:reply(From, ok),
+ {noreply, State#state{admin_state = blocked,
+ blocking_from = undefined}};
+handle_block(disturbing, Timeout, #state{connection_sup = CSup, blocker_ref = Blocker} = State) ->
+ Manager = self(),
+ spawn_link(fun() -> wait_for_shutdown(CSup, Manager) end),
+ erlang:send_after(Timeout, self(), {block_timeout, disturbing, Blocker}),
+ {noreply, State#state{admin_state = shutting_down}};
+
+handle_block(non_disturbing, infinity,
+ #state{connection_sup = CSup} = State) ->
+ Manager = self(),
+ spawn_link(fun() -> wait_for_shutdown(CSup, Manager) end),
+ {noreply, State#state{admin_state = shutting_down}};
+
+handle_block(non_disturbing, Timeout,
+ #state{connection_sup = CSup, blocker_ref = Blocker} = State) ->
+ Manager = self(),
+ spawn_link(fun() -> wait_for_shutdown(CSup, Manager) end),
+ erlang:send_after(Timeout, self(), {block_timeout, non_disturbing, Blocker}),
+ {noreply, State#state{admin_state = shutting_down}}.
-
-
-%% -------------------------------------------------------------------------
-%% handle_reload
-%%
-%%
-%%
-%%
handle_reload(undefined, #state{config_file = undefined} = State) ->
{continue, {error, undefined_config_file}, State};
handle_reload(undefined, #state{config_file = ConfigFile} = State) ->
@@ -792,7 +437,7 @@ check_constant_values(Db, Config) ->
%% Otherwise -> active
%%
get_ustate(State) ->
- get_ustate(length(State#state.connections),State).
+ get_ustate(count_children(State#state.connection_sup),State).
get_ustate(0,_State) ->
idle;
@@ -805,78 +450,8 @@ get_ustate(ConnectionCnt,State) ->
active
end.
-
-get_astate(S) -> S#state.admin_state.
-
-
-%% Timer handling functions
-start_block_tmr(infinity,_) ->
- undefined;
-start_block_tmr(T,M) ->
- erlang:send_after(T,self(),{block_timeout,M}).
-
-stop_block_tmr(undefined) ->
- ok;
-stop_block_tmr(Ref) ->
- erlang:cancel_timer(Ref).
-
-
-%% Monitor blocker functions
-monitor_blocker(Pid) when is_pid(Pid) ->
- case (catch erlang:monitor(process,Pid)) of
- {'EXIT', _Reason} ->
- undefined;
- MonitorRef ->
- MonitorRef
- end;
-monitor_blocker(_) ->
- undefined.
-
-demonitor_blocker(undefined) ->
- ok;
-demonitor_blocker(Ref) ->
- (catch erlang:demonitor(Ref)).
-
-
-%% Some status utility functions
-
-update_heavy_load_status(Status) ->
- update_status_with_time(Status,last_heavy_load).
-
-update_connection_status(Status,ConnCount) ->
- S1 = case lists:keysearch(max_conn,1,Status) of
- {value, {max_conn, C1}} when ConnCount > C1 ->
- lists:keyreplace(max_conn,1,Status,{max_conn,ConnCount});
- {value, {max_conn, _C2}} ->
- Status;
- false ->
- [{max_conn, ConnCount} | Status]
- end,
- update_status_with_time(S1,last_connection).
-
-update_status_with_time(Status,Key) ->
- lists:keyreplace(Key,1,Status,{Key,universal_time()}).
-
-universal_time() -> calendar:universal_time().
-
-manager_status(P) ->
- Items = [status, message_queue_len, reductions,
- heap_size, stack_size],
- {manager_status, process_status(P,Items,[])}.
-
-
-process_status(P,[],L) ->
- [{pid,P}|lists:reverse(L)];
-process_status(P,[H|T],L) ->
- case (catch process_info(P,H)) of
- {H, Value} ->
- process_status(P,T,[{H,Value}|L]);
- _ ->
- process_status(P,T,[{H,undefined}|L])
- end.
-
-make_name(Addr,Port) ->
- httpd_util:make_name("httpd",Addr,Port).
+make_name(Addr, Port, Profile) ->
+ httpd_util:make_name("httpd", Addr, Port, Profile).
report_error(State,String) ->
@@ -885,10 +460,31 @@ report_error(State,String) ->
mod_log:report_error(Cdb,String),
mod_disk_log:report_error(Cdb,String).
-%%
-call(ServerRef,Request) ->
- gen_server:call(ServerRef,Request).
+call(ServerRef, Request) ->
+ try gen_server:call(ServerRef, Request, infinity)
+ catch
+ exit:_ ->
+ {error, closed}
+ end.
+
+count_children(Sup) ->
+ Children = supervisor:count_children(whereis(Sup)),
+ proplists:get_value(workers, Children).
-cast(ServerRef,Message) ->
- gen_server:cast(ServerRef,Message).
+shutdown_connections(Sup) ->
+ Children = [Child || {_,Child,_,_} <- supervisor:which_children(Sup)],
+ lists:foreach(fun(Pid) -> exit(Pid, kill) end,
+ Children).
+
+wait_for_shutdown(CSup, Manager) ->
+ case count_children(CSup) of
+ 0 ->
+ Manager ! connections_terminated;
+ _ ->
+ receive
+ after 500 ->
+ ok
+ end,
+ wait_for_shutdown(CSup, Manager)
+ end.
diff --git a/lib/inets/src/http_server/httpd_misc_sup.erl b/lib/inets/src/http_server/httpd_misc_sup.erl
index fd7c28bd7d..cac9315c27 100644
--- a/lib/inets/src/http_server/httpd_misc_sup.erl
+++ b/lib/inets/src/http_server/httpd_misc_sup.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -27,8 +28,8 @@
-behaviour(supervisor).
%% API
--export([start_link/2, start_auth_server/2, stop_auth_server/2,
- start_sec_server/2, stop_sec_server/2]).
+-export([start_link/3, start_auth_server/3, stop_auth_server/3,
+ start_sec_server/3, stop_sec_server/3]).
%% Supervisor callback
-export([init/1]).
@@ -37,26 +38,26 @@
%%% API
%%%=========================================================================
-start_link(Addr, Port) ->
- SupName = make_name(Addr, Port),
+start_link(Addr, Port, Profile) ->
+ SupName = make_name(Addr, Port, Profile),
supervisor:start_link({local, SupName}, ?MODULE, []).
%%----------------------------------------------------------------------
%% Function: [start|stop]_[auth|sec]_server/3
%% Description: Starts a [auth | security] worker (child) process
%%----------------------------------------------------------------------
-start_auth_server(Addr, Port) ->
- start_permanent_worker(mod_auth_server, Addr, Port, [gen_server]).
+start_auth_server(Addr, Port, Profile) ->
+ start_permanent_worker(mod_auth_server, Addr, Port, Profile, [gen_server]).
-stop_auth_server(Addr, Port) ->
- stop_permanent_worker(mod_auth_server, Addr, Port).
+stop_auth_server(Addr, Port, Profile) ->
+ stop_permanent_worker(mod_auth_server, Addr, Port, Profile).
-start_sec_server(Addr, Port) ->
- start_permanent_worker(mod_security_server, Addr, Port, [gen_server]).
+start_sec_server(Addr, Port, Profile) ->
+ start_permanent_worker(mod_security_server, Addr, Port, Profile, [gen_server]).
-stop_sec_server(Addr, Port) ->
- stop_permanent_worker(mod_security_server, Addr, Port).
+stop_sec_server(Addr, Port, Profile) ->
+ stop_permanent_worker(mod_security_server, Addr, Port, Profile).
%%%=========================================================================
@@ -70,15 +71,15 @@ init(_) ->
%%%=========================================================================
%%% Internal functions
%%%=========================================================================
-start_permanent_worker(Mod, Addr, Port, Modules) ->
- SupName = make_name(Addr, Port),
+start_permanent_worker(Mod, Addr, Port, Profile, Modules) ->
+ SupName = make_name(Addr, Port, Profile),
Spec = {{Mod, Addr, Port},
- {Mod, start_link, [Addr, Port]},
+ {Mod, start_link, [Addr, Port, Profile]},
permanent, timer:seconds(1), worker, [Mod] ++ Modules},
supervisor:start_child(SupName, Spec).
-stop_permanent_worker(Mod, Addr, Port) ->
- SupName = make_name(Addr, Port),
+stop_permanent_worker(Mod, Addr, Port, Profile) ->
+ SupName = make_name(Addr, Port, Profile),
Name = {Mod, Addr, Port},
case supervisor:terminate_child(SupName, Name) of
ok ->
@@ -87,5 +88,5 @@ stop_permanent_worker(Mod, Addr, Port) ->
Error
end.
-make_name(Addr,Port) ->
- httpd_util:make_name("httpd_misc_sup",Addr,Port).
+make_name(Addr,Port, Profile) ->
+ httpd_util:make_name("httpd_misc_sup",Addr,Port, Profile).
diff --git a/lib/inets/src/http_server/httpd_request.erl b/lib/inets/src/http_server/httpd_request.erl
index 5ba79b2706..749f58c197 100644
--- a/lib/inets/src/http_server/httpd_request.erl
+++ b/lib/inets/src/http_server/httpd_request.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -42,28 +43,28 @@
%%%=========================================================================
%%% Internal application API
%%%=========================================================================
-parse([Bin, MaxSizes]) ->
- ?hdrt("parse", [{bin, Bin}, {max_sizes, MaxSizes}]),
- parse_method(Bin, [], MaxSizes, []);
+parse([Bin, Options]) ->
+ ?hdrt("parse", [{bin, Bin}, {max_sizes, Options}]),
+ parse_method(Bin, [], 0, proplists:get_value(max_method, Options), Options, []);
parse(Unknown) ->
?hdrt("parse", [{unknown, Unknown}]),
exit({bad_args, Unknown}).
%% Functions that may be returned during the decoding process
%% if the input data is incompleate.
-parse_method([Bin, Method, MaxSizes, Result]) ->
- parse_method(Bin, Method, MaxSizes, Result).
+parse_method([Bin, Method, Current, Max, Options, Result]) ->
+ parse_method(Bin, Method, Current, Max, Options, Result).
-parse_uri([Bin, URI, CurrSize, MaxSizes, Result]) ->
- parse_uri(Bin, URI, CurrSize, MaxSizes, Result).
+parse_uri([Bin, URI, Current, Max, Options, Result]) ->
+ parse_uri(Bin, URI, Current, Max, Options, Result).
-parse_version([Bin, Rest, Version, MaxSizes, Result]) ->
- parse_version(<<Rest/binary, Bin/binary>>, Version, MaxSizes,
+parse_version([Bin, Rest, Version, Current, Max, Options, Result]) ->
+ parse_version(<<Rest/binary, Bin/binary>>, Version, Current, Max, Options,
Result).
-parse_headers([Bin, Rest, Header, Headers, CurrSize, MaxSizes, Result]) ->
+parse_headers([Bin, Rest, Header, Headers, Current, Max, Options, Result]) ->
parse_headers(<<Rest/binary, Bin/binary>>,
- Header, Headers, CurrSize, MaxSizes, Result).
+ Header, Headers, Current, Max, Options, Result).
whole_body([Bin, Body, Length]) ->
whole_body(<<Body/binary, Bin/binary>>, Length).
@@ -85,7 +86,8 @@ body_data(Headers, Body) ->
%%-------------------------------------------------------------------------
%% validate(Method, Uri, Version) -> ok | {error, {bad_request, Reason} |
%% {error, {not_supported, {Method, Uri, Version}}
-%% Method = "HEAD" | "GET" | "POST" | "TRACE" | "PUT" | "DELETE"
+%% Method = "HEAD" | "GET" | "POST" | "PATCH" | "TRACE" | "PUT"
+%% | "DELETE"
%% Uri = uri()
%% Version = "HTTP/N.M"
%% Description: Checks that HTTP-request-line is valid.
@@ -104,187 +106,191 @@ validate("DELETE", Uri, "HTTP/1." ++ _N) ->
validate_uri(Uri);
validate("POST", Uri, "HTTP/1." ++ _N) ->
validate_uri(Uri);
+validate("PATCH", Uri, "HTTP/1." ++ _N) ->
+ validate_uri(Uri);
validate("TRACE", Uri, "HTTP/1." ++ N) when hd(N) >= $1 ->
validate_uri(Uri);
validate(Method, Uri, Version) ->
- {error, {not_supported, {Method, Uri, Version}}}.
-
+ case validate_version(Version) of
+ true ->
+ {error, {not_supported, {Method, Uri, Version}}};
+ false ->
+ {error, {bad_version, Version}}
+ end.
%%----------------------------------------------------------------------
%% The request is passed through the server as a record of type mod
%% create it.
%% ----------------------------------------------------------------------
update_mod_data(ModData, Method, RequestURI, HTTPVersion, Headers)->
- ParsedHeaders = tagup_header(Headers),
- PersistentConn = get_persistens(HTTPVersion, ParsedHeaders,
+ PersistentConn = get_persistens(HTTPVersion, Headers,
ModData#mod.config_db),
{ok, ModData#mod{data = [],
method = Method,
absolute_uri = format_absolute_uri(RequestURI,
- ParsedHeaders),
+ Headers),
request_uri = format_request_uri(RequestURI),
http_version = HTTPVersion,
request_line = Method ++ " " ++ RequestURI ++
" " ++ HTTPVersion,
- parsed_header = ParsedHeaders,
+ parsed_header = Headers,
connection = PersistentConn}}.
%%%========================================================================
%%% Internal functions
%%%========================================================================
-parse_method(<<>>, Method, MaxSizes, Result) ->
- ?hdrt("parse_method - empty bin",
- [{method, Method}, {max_sizes, MaxSizes}, {result, Result}]),
- {?MODULE, parse_method, [Method, MaxSizes, Result]};
-parse_method(<<?SP, Rest/binary>>, Method, MaxSizes, Result) ->
- ?hdrt("parse_method - SP begin",
- [{rest, Rest},
- {method, Method},
- {max_sizes, MaxSizes},
- {result, Result}]),
- parse_uri(Rest, [], 0, MaxSizes,
+parse_method(<<>>, Method, Current, Max, Options, Result) ->
+ {?MODULE, parse_method, [Method, Current, Max, Options, Result]};
+parse_method(<<?SP, Rest/binary>>, Method, _Current, _Max, Options, Result) ->
+ parse_uri(Rest, [], 0, proplists:get_value(max_uri, Options), Options,
[string:strip(lists:reverse(Method)) | Result]);
-parse_method(<<Octet, Rest/binary>>, Method, MaxSizes, Result) ->
- ?hdrt("parse_method",
- [{octet, Octet},
- {rest, Rest},
- {method, Method},
- {max_sizes, MaxSizes},
- {result, Result}]),
- parse_method(Rest, [Octet | Method], MaxSizes, Result).
-
-parse_uri(_, _, CurrSize, {MaxURI, _}, _)
- when (CurrSize > MaxURI) andalso (MaxURI =/= nolimit) ->
- ?hdrt("parse_uri",
- [{current_size, CurrSize},
- {max_uri, MaxURI}]),
+parse_method(<<Octet, Rest/binary>>, Method, Current, Max, Options, Result) when Current =< Max ->
+ parse_method(Rest, [Octet | Method], Current + 1, Max, Options, Result);
+parse_method(_, _, _, Max, _, _) ->
+ %% We do not know the version of the client as it comes after the
+ %% method send the lowest version in the response so that the client
+ %% will be able to handle it.
+ {error, {size_error, Max, 413, "Method unreasonably long"}, lowest_version()}.
+
+parse_uri(_, _, Current, MaxURI, _, _)
+ when (Current > MaxURI) andalso (MaxURI =/= nolimit) ->
%% We do not know the version of the client as it comes after the
%% uri send the lowest version in the response so that the client
%% will be able to handle it.
- HttpVersion = "HTTP/0.9",
- {error, {uri_too_long, MaxURI}, HttpVersion};
-parse_uri(<<>>, URI, CurrSize, MaxSizes, Result) ->
- ?hdrt("parse_uri - empty bin",
- [{uri, URI},
- {current_size, CurrSize},
- {max_sz, MaxSizes},
- {result, Result}]),
- {?MODULE, parse_uri, [URI, CurrSize, MaxSizes, Result]};
-parse_uri(<<?SP, Rest/binary>>, URI, _, MaxSizes, Result) ->
- ?hdrt("parse_uri - SP begin",
- [{uri, URI},
- {max_sz, MaxSizes},
- {result, Result}]),
- parse_version(Rest, [], MaxSizes,
+ {error, {size_error, MaxURI, 414, "URI unreasonably long"},lowest_version()};
+parse_uri(<<>>, URI, Current, Max, Options, Result) ->
+ {?MODULE, parse_uri, [URI, Current, Max, Options, Result]};
+parse_uri(<<?SP, Rest/binary>>, URI, _, _, Options, Result) ->
+ parse_version(Rest, [], 0, proplists:get_value(max_version, Options), Options,
[string:strip(lists:reverse(URI)) | Result]);
%% Can happen if it is a simple HTTP/0.9 request e.i "GET /\r\n\r\n"
-parse_uri(<<?CR, _Rest/binary>> = Data, URI, _, MaxSizes, Result) ->
- ?hdrt("parse_uri - CR begin",
- [{uri, URI},
- {max_sz, MaxSizes},
- {result, Result}]),
- parse_version(Data, [], MaxSizes,
+parse_uri(<<?CR, _Rest/binary>> = Data, URI, _, _, Options, Result) ->
+ parse_version(Data, [], 0, proplists:get_value(max_version, Options), Options,
[string:strip(lists:reverse(URI)) | Result]);
-parse_uri(<<Octet, Rest/binary>>, URI, CurrSize, MaxSizes, Result) ->
- ?hdrt("parse_uri",
- [{octet, Octet},
- {uri, URI},
- {curr_sz, CurrSize},
- {max_sz, MaxSizes},
- {result, Result}]),
- parse_uri(Rest, [Octet | URI], CurrSize + 1, MaxSizes, Result).
-
-parse_version(<<>>, Version, MaxSizes, Result) ->
- {?MODULE, parse_version, [<<>>, Version, MaxSizes, Result]};
-parse_version(<<?LF, Rest/binary>>, Version, MaxSizes, Result) ->
+parse_uri(<<Octet, Rest/binary>>, URI, Current, Max, Options, Result) ->
+ parse_uri(Rest, [Octet | URI], Current + 1, Max, Options, Result).
+
+parse_version(<<>>, Version, Current, Max, Options, Result) ->
+ {?MODULE, parse_version, [<<>>, Version, Current, Max, Options, Result]};
+parse_version(<<?LF, Rest/binary>>, Version, Current, Max, Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_version(<<?CR, ?LF, Rest/binary>>, Version, MaxSizes, Result);
-parse_version(<<?CR, ?LF, Rest/binary>>, Version, MaxSizes, Result) ->
- parse_headers(Rest, [], [], 0, MaxSizes,
+ parse_version(<<?CR, ?LF, Rest/binary>>, Version, Current, Max, Options, Result);
+parse_version(<<?CR, ?LF, Rest/binary>>, Version, _, _, Options, Result) ->
+ parse_headers(Rest, [], [], 0, proplists:get_value(max_header, Options), Options,
[string:strip(lists:reverse(Version)) | Result]);
-parse_version(<<?CR>> = Data, Version, MaxSizes, Result) ->
- {?MODULE, parse_version, [Data, Version, MaxSizes, Result]};
-parse_version(<<Octet, Rest/binary>>, Version, MaxSizes, Result) ->
- parse_version(Rest, [Octet | Version], MaxSizes, Result).
-
-parse_headers(_, _, _, CurrSize, {_, MaxHeaderSize}, Result)
- when CurrSize > MaxHeaderSize, MaxHeaderSize =/= nolimit ->
+parse_version(<<?CR>> = Data, Version, Current, Max, Options, Result) ->
+ {?MODULE, parse_version, [Data, Version, Current, Max, Options, Result]};
+parse_version(<<Octet, Rest/binary>>, Version, Current, Max, Options, Result) when Current =< Max ->
+ parse_version(Rest, [Octet | Version], Current + 1, Max, Options, Result);
+parse_version(_, _, _, Max,_,_) ->
+ {error, {size_error, Max, 413, "Version string unreasonably long"}, lowest_version()}.
+
+parse_headers(_, _, _, Current, Max, _, Result)
+ when Max =/= nolimit andalso Current > Max ->
HttpVersion = lists:nth(3, lists:reverse(Result)),
- {error, {header_too_long, MaxHeaderSize}, HttpVersion};
+ {error, {size_error, Max, 413, "Headers unreasonably long"}, HttpVersion};
-parse_headers(<<>>, Header, Headers, CurrSize, MaxSizes, Result) ->
- {?MODULE, parse_headers, [<<>>, Header, Headers, CurrSize,
- MaxSizes, Result]};
-parse_headers(<<?CR,?LF,?LF,Body/binary>>, [], [], CurrSize, MaxSizes, Result) ->
+parse_headers(<<>>, Header, Headers, Current, Max, Options, Result) ->
+ {?MODULE, parse_headers, [<<>>, Header, Headers, Current, Max,
+ Options, Result]};
+parse_headers(<<?CR,?LF,?LF,Body/binary>>, [], [], Current, Max, Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], CurrSize,
- MaxSizes, Result);
+ parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], Current, Max,
+ Options, Result);
-parse_headers(<<?LF,?LF,Body/binary>>, [], [], CurrSize, MaxSizes, Result) ->
+parse_headers(<<?LF,?LF,Body/binary>>, [], [], Current, Max, Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], CurrSize,
- MaxSizes, Result);
+ parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], Current, Max,
+ Options, Result);
-parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], _, _, Result) ->
+parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], _, _, _, Result) ->
NewResult = list_to_tuple(lists:reverse([Body, {#http_request_h{}, []} |
Result])),
{ok, NewResult};
-parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, _,
- _, Result) ->
- HTTPHeaders = [lists:reverse(Header) | Headers],
- RequestHeaderRcord =
- http_request:headers(HTTPHeaders, #http_request_h{}),
- NewResult =
- list_to_tuple(lists:reverse([Body, {RequestHeaderRcord,
- HTTPHeaders} | Result])),
- {ok, NewResult};
+parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, _, _,
+ Options, Result) ->
+ Customize = proplists:get_value(customize, Options),
+ case http_request:key_value(lists:reverse(Header)) of
+ undefined -> %% Skip headers with missing :
+ FinalHeaders = lists:filtermap(fun(H) ->
+ httpd_custom:customize_headers(Customize, request_header, H)
+ end,
+ Headers),
+ {ok, list_to_tuple(lists:reverse([Body, {http_request:headers(FinalHeaders, #http_request_h{}), FinalHeaders} | Result]))};
+ NewHeader ->
+ case check_header(NewHeader, Options) of
+ ok ->
+ FinalHeaders = lists:filtermap(fun(H) ->
+ httpd_custom:customize_headers(Customize, request_header, H)
+ end, [NewHeader | Headers]),
+ {ok, list_to_tuple(lists:reverse([Body, {http_request:headers(FinalHeaders,
+ #http_request_h{}),
+ FinalHeaders} | Result]))};
+
+ {error, Reason} ->
+ HttpVersion = lists:nth(3, lists:reverse(Result)),
+ {error, Reason, HttpVersion}
+ end
+ end;
-parse_headers(<<?CR,?LF,?CR>> = Data, Header, Headers, CurrSize,
- MaxSizes, Result) ->
- {?MODULE, parse_headers, [Data, Header, Headers, CurrSize,
- MaxSizes, Result]};
-parse_headers(<<?LF>>, [], [], CurrSize, MaxSizes, Result) ->
+parse_headers(<<?CR,?LF,?CR>> = Data, Header, Headers, Current, Max,
+ Options, Result) ->
+ {?MODULE, parse_headers, [Data, Header, Headers, Current, Max,
+ Options, Result]};
+parse_headers(<<?LF>>, [], [], Current, Max, Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR,?LF>>, [], [], CurrSize, MaxSizes, Result);
+ parse_headers(<<?CR,?LF>>, [], [], Current, Max, Options, Result);
%% There where no headers, which is unlikely to happen.
-parse_headers(<<?CR,?LF>>, [], [], _, _, Result) ->
+parse_headers(<<?CR,?LF>>, [], [], _, _, _, Result) ->
NewResult = list_to_tuple(lists:reverse([<<>>, {#http_request_h{}, []} |
Result])),
{ok, NewResult};
-parse_headers(<<?LF>>, Header, Headers, CurrSize,
- MaxSizes, Result) ->
+parse_headers(<<?LF>>, Header, Headers, Current, Max,
+ Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR,?LF>>, Header, Headers, CurrSize, MaxSizes, Result);
-
-parse_headers(<<?CR,?LF>> = Data, Header, Headers, CurrSize,
- MaxSizes, Result) ->
- {?MODULE, parse_headers, [Data, Header, Headers, CurrSize,
- MaxSizes, Result]};
-parse_headers(<<?LF, Octet, Rest/binary>>, Header, Headers, CurrSize,
- MaxSizes, Result) ->
+ parse_headers(<<?CR,?LF>>, Header, Headers, Current, Max, Options, Result);
+
+parse_headers(<<?CR,?LF>> = Data, Header, Headers, Current, Max,
+ Options, Result) ->
+ {?MODULE, parse_headers, [Data, Header, Headers, Current, Max,
+ Options, Result]};
+parse_headers(<<?LF, Octet, Rest/binary>>, Header, Headers, Current, Max,
+ Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, CurrSize,
- MaxSizes, Result);
-parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, CurrSize,
- MaxSizes, Result) ->
- parse_headers(Rest, [Octet], [lists:reverse(Header) | Headers],
- CurrSize + 1, MaxSizes, Result);
-
-parse_headers(<<?CR>> = Data, Header, Headers, CurrSize,
- MaxSizes, Result) ->
- {?MODULE, parse_headers, [Data, Header, Headers, CurrSize,
- MaxSizes, Result]};
-parse_headers(<<?LF>>, Header, Headers, CurrSize,
- MaxSizes, Result) ->
+ parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, Current, Max,
+ Options, Result);
+parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, _, Max,
+ Options, Result) ->
+ case http_request:key_value(lists:reverse(Header)) of
+ undefined -> %% Skip headers with missing :
+ parse_headers(Rest, [Octet], Headers,
+ 0, Max, Options, Result);
+ NewHeader ->
+ case check_header(NewHeader, Options) of
+ ok ->
+ parse_headers(Rest, [Octet], [NewHeader | Headers],
+ 0, Max, Options, Result);
+ {error, Reason} ->
+ HttpVersion = lists:nth(3, lists:reverse(Result)),
+ {error, Reason, HttpVersion}
+ end
+ end;
+
+parse_headers(<<?CR>> = Data, Header, Headers, Current, Max,
+ Options, Result) ->
+ {?MODULE, parse_headers, [Data, Header, Headers, Current, Max,
+ Options, Result]};
+parse_headers(<<?LF>>, Header, Headers, Current, Max,
+ Options, Result) ->
%% If ?CR is is missing RFC2616 section-19.3
- parse_headers(<<?CR, ?LF>>, Header, Headers, CurrSize,
- MaxSizes, Result);
+ parse_headers(<<?CR, ?LF>>, Header, Headers, Current, Max,
+ Options, Result);
-parse_headers(<<Octet, Rest/binary>>, Header, Headers,
- CurrSize, MaxSizes, Result) ->
- parse_headers(Rest, [Octet | Header], Headers, CurrSize + 1,
- MaxSizes, Result).
+parse_headers(<<Octet, Rest/binary>>, Header, Headers, Current,
+ Max, Options, Result) ->
+ parse_headers(Rest, [Octet | Header], Headers, Current + 1, Max,
+ Options, Result).
whole_body(Body, Length) ->
case size(Body) of
@@ -326,6 +332,14 @@ validate_path([".." | Rest], N, RequestURI) ->
validate_path([_ | Rest], N, RequestURI) ->
validate_path(Rest, N + 1, RequestURI).
+validate_version("HTTP/1.1") ->
+ true;
+validate_version("HTTP/1.0") ->
+ true;
+validate_version("HTTP/0.9") ->
+ true;
+validate_version(_) ->
+ false.
%%----------------------------------------------------------------------
%% There are 3 possible forms of the reuqest URI
%%
@@ -406,27 +420,29 @@ get_persistens(HTTPVersion,ParsedHeader,ConfigDB)->
false
end.
-
-%%----------------------------------------------------------------------
-%% tagup_header
-%%
-%% Parses the header of a HTTP request and returns a key,value tuple
-%% list containing Name and Value of each header directive as of:
-%%
-%% Content-Type: multipart/mixed -> {"Content-Type", "multipart/mixed"}
-%%
-%% But in http/1.1 the field-names are case insencitive so now it must be
-%% Content-Type: multipart/mixed -> {"content-type", "multipart/mixed"}
-%% The standard furthermore says that leading and traling white space
-%% is not a part of the fieldvalue and shall therefore be removed.
-%%----------------------------------------------------------------------
-tagup_header([]) -> [];
-tagup_header([Line|Rest]) -> [tag(Line, [])|tagup_header(Rest)].
-
-tag([], Tag) ->
- {http_util:to_lower(lists:reverse(Tag)), ""};
-tag([$:|Rest], Tag) ->
- {http_util:to_lower(lists:reverse(Tag)), string:strip(Rest)};
-tag([Chr|Rest], Tag) ->
- tag(Rest, [Chr|Tag]).
-
+lowest_version()->
+ "HTTP/0.9".
+
+check_header({"content-length", Value}, Maxsizes) ->
+ Max = proplists:get_value(max_content_length, Maxsizes),
+ MaxLen = length(integer_to_list(Max)),
+ case length(Value) =< MaxLen of
+ true ->
+ try
+ list_to_integer(Value)
+ of
+ I when I>= 0 ->
+ ok;
+ _ ->
+ {error, {size_error, Max, 411, "negative content-length"}}
+ catch _:_ ->
+ {error, {size_error, Max, 411, "content-length not an integer"}}
+ end;
+ false ->
+ {error, {size_error, Max, 413, "content-length unreasonably long"}}
+ end;
+check_header(_, _) ->
+ ok.
+
+
+
diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl
index 0f47d785ef..071fa94ef6 100644
--- a/lib/inets/src/http_server/httpd_request_handler.erl
+++ b/lib/inets/src/http_server/httpd_request_handler.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -25,16 +26,17 @@
-behaviour(gen_server).
%% Application internal API
--export([start/2, start/3, socket_ownership_transfered/3]).
+-export([start_link/2, start_link/3, socket_ownership_transfered/3]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
+ terminate/2, code_change/3, format_status/2]).
-include("httpd.hrl").
-include("http_internal.hrl").
-include("httpd_internal.hrl").
+-define(HANDSHAKE_TIMEOUT, 5000).
-record(state, {mod, %% #mod{}
manager, %% pid()
status, %% accept | busy | blocked
@@ -57,10 +59,10 @@
%% Description: Starts a httpd-request handler process. Intended to be
%% called by the httpd acceptor process.
%%--------------------------------------------------------------------
-start(Manager, ConfigDB) ->
- start(Manager, ConfigDB, 15000).
-start(Manager, ConfigDB, AcceptTimeout) ->
- proc_lib:start(?MODULE, init, [[Manager, ConfigDB,AcceptTimeout]]).
+start_link(Manager, ConfigDB) ->
+ start_link(Manager, ConfigDB, 15000).
+start_link(Manager, ConfigDB, AcceptTimeout) ->
+ proc_lib:start_link(?MODULE, init, [[Manager, ConfigDB,AcceptTimeout]]).
%%--------------------------------------------------------------------
@@ -87,34 +89,26 @@ socket_ownership_transfered(Pid, SocketType, Socket) ->
%% gen_server provides is needed.
%%--------------------------------------------------------------------
init([Manager, ConfigDB, AcceptTimeout]) ->
- ?hdrd("initiate",
- [{manager, Manager}, {cdb, ConfigDB}, {timeout, AcceptTimeout}]),
+ process_flag(trap_exit, true),
%% Make sure this process terminates if the httpd manager process
%% should die!
- link(Manager),
+ %%link(Manager),
%% At this point the function httpd_request_handler:start/2 will return.
proc_lib:init_ack({ok, self()}),
{SocketType, Socket} = await_socket_ownership_transfer(AcceptTimeout),
- ?hdrd("socket ownership transfered",
- [{socket_type, SocketType}, {socket, Socket}]),
-
- TimeOut = httpd_util:lookup(ConfigDB, keep_alive_timeout, 150000),
- Then = erlang:now(),
- ?hdrd("negotiate", []),
- case http_transport:negotiate(SocketType, Socket, TimeOut) of
+ %%Timeout value is in seconds we want it in milliseconds
+ KeepAliveTimeOut = 1000 * httpd_util:lookup(ConfigDB, keep_alive_timeout, 150),
+
+ case http_transport:negotiate(SocketType, Socket, ?HANDSHAKE_TIMEOUT) of
{error, Error} ->
- ?hdrd("negotiation failed", [{error, Error}]),
- exit(Error); %% Can be 'normal'.
+ exit({shutdown, Error}); %% Can be 'normal'.
ok ->
- ?hdrt("negotiation successfull", []),
- NewTimeout = TimeOut - timer:now_diff(now(),Then) div 1000,
- continue_init(Manager, ConfigDB, SocketType, Socket, NewTimeout)
+ continue_init(Manager, ConfigDB, SocketType, Socket, KeepAliveTimeOut)
end.
continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) ->
- ?hdrt("continue init", [{timeout, TimeOut}]),
Resolve = http_transport:resolve(),
Peername = httpd_socket:peername(SocketType, Socket),
@@ -127,10 +121,17 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) ->
MaxHeaderSize = max_header_size(ConfigDB),
MaxURISize = max_uri_size(ConfigDB),
NrOfRequest = max_keep_alive_request(ConfigDB),
-
+ MaxContentLen = max_content_length(ConfigDB),
+ Customize = customize(ConfigDB),
+
{_, Status} = httpd_manager:new_connection(Manager),
- MFA = {httpd_request, parse, [{MaxURISize, MaxHeaderSize}]},
+ MFA = {httpd_request, parse, [[{max_uri, MaxURISize}, {max_header, MaxHeaderSize},
+ {max_version, ?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, MaxContentLen},
+ {customize, Customize}
+ ]]},
State = #state{mod = Mod,
manager = Manager,
@@ -139,14 +140,10 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) ->
max_keep_alive_request = NrOfRequest,
mfa = MFA},
- ?hdrt("activate request timeout", []),
-
- ?hdrt("set socket options (binary, packet & active)", []),
http_transport:setopts(SocketType, Socket,
[binary, {packet, 0}, {active, once}]),
NewState = data_receive_counter(activate_request_timeout(State), httpd_util:lookup(ConfigDB, minimum_bytes_per_second, false)),
- ?hdrt("init done", []),
- gen_server:enter_loop(?MODULE, [], NewState).
+ gen_server:enter_loop(?MODULE, [], NewState).
%%====================================================================
@@ -195,18 +192,13 @@ handle_cast(Msg, #state{mod = ModData} = State) ->
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({Proto, Socket, Data},
- #state{mfa = {Module, Function, Args} = MFA,
+ #state{mfa = {Module, Function, Args},
mod = #mod{socket_type = SockType,
socket = Socket} = ModData} = State)
when (((Proto =:= tcp) orelse
(Proto =:= ssl) orelse
(Proto =:= dummy)) andalso is_binary(Data)) ->
- ?hdrd("received data",
- [{data, Data}, {proto, Proto},
- {socket, Socket}, {socket_type, SockType}, {mfa, MFA}]),
-
-%% case (catch Module:Function([Data | Args])) of
PROCESSED = (catch Module:Function([Data | Args])),
NewDataSize = case State#state.byte_limit of
undefined ->
@@ -214,10 +206,8 @@ handle_info({Proto, Socket, Data},
_ ->
State#state.data + byte_size(Data)
end,
- ?hdrt("data processed", [{processing_result, PROCESSED}]),
case PROCESSED of
{ok, Result} ->
- ?hdrd("data processed", [{result, Result}]),
NewState = case NewDataSize of
undefined ->
cancel_request_timeout(State);
@@ -225,28 +215,16 @@ handle_info({Proto, Socket, Data},
set_new_data_size(cancel_request_timeout(State), NewDataSize)
end,
handle_http_msg(Result, NewState);
-
- {error, {uri_too_long, MaxSize}, Version} ->
- ?hdrv("uri too long", [{max_size, MaxSize}, {version, Version}]),
+ {error, {size_error, MaxSize, ErrCode, ErrStr}, Version} ->
NewModData = ModData#mod{http_version = Version},
- httpd_response:send_status(NewModData, 414, "URI too long"),
- Reason = io_lib:format("Uri too long, max size is ~p~n",
- [MaxSize]),
- error_log(Reason, NewModData),
- {stop, normal, State#state{response_sent = true,
- mod = NewModData}};
- {error, {header_too_long, MaxSize}, Version} ->
- ?hdrv("header too long",
- [{max_size, MaxSize}, {version, Version}]),
- NewModData = ModData#mod{http_version = Version},
- httpd_response:send_status(NewModData, 413, "Header too long"),
- Reason = io_lib:format("Header too long, max size is ~p~n",
- [MaxSize]),
+ httpd_response:send_status(NewModData, ErrCode, ErrStr),
+ Reason = io_lib:format("~p: ~p max size is ~p~n",
+ [ErrCode, ErrStr, MaxSize]),
error_log(Reason, NewModData),
{stop, normal, State#state{response_sent = true,
mod = NewModData}};
+
NewMFA ->
- ?hdrd("data processed - reactivate socket", [{new_mfa, NewMFA}]),
http_transport:setopts(SockType, Socket, [{active, once}]),
case NewDataSize of
undefined ->
@@ -267,9 +245,9 @@ handle_info({ssl_error, _, _} = Reason, State) ->
{stop, Reason, State};
%% Timeouts
-handle_info(timeout, #state{mod = ModData, mfa = {_, parse, _}} = State) ->
- error_log("No request received on keep-alive connection "
- "before server side timeout", ModData),
+handle_info(timeout, #state{mfa = {_, parse, _}} = State) ->
+ %% error_log("No request received on keep-alive connection "
+ %% "before server side timeout", ModData),
%% No response should be sent!
{stop, normal, State#state{response_sent = true}};
handle_info(timeout, #state{mod = ModData} = State) ->
@@ -293,6 +271,10 @@ handle_info(check_data, #state{data = Data, byte_limit = Byte_Limit} = State) ->
_ ->
{stop, normal, State#state{response_sent = true}}
end;
+
+handle_info({'EXIT', _, Reason}, State) ->
+ {stop, Reason, State};
+
%% Default case
handle_info(Info, #state{mod = ModData} = State) ->
Error = lists:flatten(
@@ -312,21 +294,37 @@ handle_info(Info, #state{mod = ModData} = State) ->
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
-terminate(normal, State) ->
+terminate(Reason, State) when Reason == normal;
+ Reason == shutdown ->
+ do_terminate(State);
+terminate({shutdown,_}, State) ->
do_terminate(State);
terminate(Reason, #state{response_sent = false, mod = ModData} = State) ->
httpd_response:send_status(ModData, 500, none),
- error_log(httpd_util:reason_phrase(500), ModData),
+ ReasonStr =
+ lists:flatten(io_lib:format("~s - ~p",
+ [httpd_util:reason_phrase(500), Reason])),
+ error_log(ReasonStr, ModData),
terminate(Reason, State#state{response_sent = true, mod = ModData});
terminate(_Reason, State) ->
do_terminate(State).
-do_terminate(#state{mod = ModData, manager = Manager} = State) ->
- catch httpd_manager:done_connection(Manager),
+do_terminate(#state{mod = ModData} = State) ->
cancel_request_timeout(State),
- %% receive after 5000 -> ok end,
httpd_socket:close(ModData#mod.socket_type, ModData#mod.socket).
+format_status(normal, [_, State]) ->
+ [{data, [{"StateData", State}]}];
+format_status(terminate, [_, State]) ->
+ Mod = (State#state.mod),
+ case Mod#mod.socket_type of
+ ip_comm ->
+ [{data, [{"StateData", State}]}];
+ {essl, _} ->
+ %% Do not print ssl options in superviosr reports
+ [{data, [{"StateData",
+ State#state{mod = Mod#mod{socket_type = 'TLS'}}}]}]
+ end.
%%--------------------------------------------------------------------
%% code_change(OldVsn, State, Extra) -> {ok, NewState}
@@ -352,30 +350,24 @@ await_socket_ownership_transfer(AcceptTimeout) ->
handle_http_msg({_, _, Version, {_, _}, _},
#state{status = busy, mod = ModData} = State) ->
- ?hdrt("handle http msg when manager busy", [{mod, ModData}]),
handle_manager_busy(State#state{mod =
ModData#mod{http_version = Version}}),
{stop, normal, State};
handle_http_msg({_, _, Version, {_, _}, _},
#state{status = blocked, mod = ModData} = State) ->
- ?hdrt("handle http msg when manager blocket", [{mod, ModData}]),
handle_manager_blocked(State#state{mod =
ModData#mod{http_version = Version}}),
{stop, normal, State};
handle_http_msg({Method, Uri, Version, {RecordHeaders, Headers}, Body},
#state{status = accept, mod = ModData} = State) ->
- ?hdrt("handle http msg when manager accepting",
- [{method, Method}, {mod, ModData}]),
case httpd_request:validate(Method, Uri, Version) of
ok ->
- ?hdrt("request validated", []),
{ok, NewModData} =
httpd_request:update_mod_data(ModData, Method, Uri,
Version, Headers),
- ?hdrt("new mod data", [{mod, NewModData}]),
case is_host_specified_if_required(NewModData#mod.absolute_uri,
RecordHeaders, Version) of
true ->
@@ -389,37 +381,34 @@ handle_http_msg({Method, Uri, Version, {RecordHeaders, Headers}, Body},
{stop, normal, State#state{response_sent = true}}
end;
{error, {not_supported, What}} ->
- ?hdrd("validation failed: not supported", [{what, What}]),
httpd_response:send_status(ModData#mod{http_version = Version},
501, {Method, Uri, Version}),
Reason = io_lib:format("Not supported: ~p~n", [What]),
error_log(Reason, ModData),
{stop, normal, State#state{response_sent = true}};
{error, {bad_request, {forbidden, URI}}} ->
- ?hdrd("validation failed: bad request - forbidden",
- [{uri, URI}]),
httpd_response:send_status(ModData#mod{http_version = Version},
403, URI),
Reason = io_lib:format("Forbidden URI: ~p~n", [URI]),
error_log(Reason, ModData),
{stop, normal, State#state{response_sent = true}};
{error, {bad_request, {malformed_syntax, URI}}} ->
- ?hdrd("validation failed: bad request - malformed syntax",
- [{uri, URI}]),
httpd_response:send_status(ModData#mod{http_version = Version},
400, URI),
Reason = io_lib:format("Malformed syntax in URI: ~p~n", [URI]),
error_log(Reason, ModData),
+ {stop, normal, State#state{response_sent = true}};
+ {error, {bad_version, Ver}} ->
+ httpd_response:send_status(ModData#mod{http_version = "HTTP/0.9"}, 400, Ver),
+ Reason = io_lib:format("Malformed syntax version: ~p~n", [Ver]),
+ error_log(Reason, ModData),
{stop, normal, State#state{response_sent = true}}
end;
handle_http_msg({ChunkedHeaders, Body},
State = #state{headers = Headers}) ->
- ?hdrt("handle http msg",
- [{chunked_headers, ChunkedHeaders}, {body, Body}]),
NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders),
handle_response(State#state{headers = NewHeaders, body = Body});
handle_http_msg(Body, State) ->
- ?hdrt("handle http msg", [{body, Body}]),
handle_response(State#state{body = Body}).
handle_manager_busy(#state{mod = #mod{config_db = ConfigDB}} = State) ->
@@ -442,7 +431,6 @@ is_host_specified_if_required(_, _, _) ->
true.
handle_body(#state{mod = #mod{config_db = ConfigDB}} = State) ->
- ?hdrt("handle body", []),
MaxHeaderSize = max_header_size(ConfigDB),
MaxBodySize = max_body_size(ConfigDB),
@@ -456,34 +444,30 @@ handle_body(#state{mod = #mod{config_db = ConfigDB}} = State) ->
handle_body(#state{headers = Headers, body = Body, mod = ModData} = State,
MaxHeaderSize, MaxBodySize) ->
- ?hdrt("handle body", [{headers, Headers}, {body, Body}]),
case Headers#http_request_h.'transfer-encoding' of
"chunked" ->
- ?hdrt("chunked - attempt decode", []),
- case http_chunk:decode(Body, MaxBodySize, MaxHeaderSize) of
+ try http_chunk:decode(Body, MaxBodySize, MaxHeaderSize) of
{Module, Function, Args} ->
- ?hdrt("chunk decoded",
- [{module, Module},
- {function, Function},
- {args, Args}]),
http_transport:setopts(ModData#mod.socket_type,
ModData#mod.socket,
[{active, once}]),
{noreply, State#state{mfa =
{Module, Function, Args}}};
{ok, {ChunkedHeaders, NewBody}} ->
- ?hdrt("chunk decoded",
- [{chunked_headers, ChunkedHeaders},
- {new_body, NewBody}]),
NewHeaders =
http_chunk:handle_headers(Headers, ChunkedHeaders),
- ?hdrt("chunked - headers handled",
- [{new_headers, NewHeaders}]),
handle_response(State#state{headers = NewHeaders,
body = NewBody})
+ catch
+ throw:Error ->
+ httpd_response:send_status(ModData, 400,
+ "Bad input"),
+ Reason = io_lib:format("Chunk decoding failed: ~p~n",
+ [Error]),
+ error_log(Reason, ModData),
+ {stop, normal, State#state{response_sent = true}}
end;
Encoding when is_list(Encoding) ->
- ?hdrt("not chunked - encoding", [{encoding, Encoding}]),
httpd_response:send_status(ModData, 501,
"Unknown Transfer-Encoding"),
Reason = io_lib:format("Unknown Transfer-Encoding: ~p~n",
@@ -491,33 +475,23 @@ handle_body(#state{headers = Headers, body = Body, mod = ModData} = State,
error_log(Reason, ModData),
{stop, normal, State#state{response_sent = true}};
_ ->
- ?hdrt("not chunked", []),
- Length =
- list_to_integer(Headers#http_request_h.'content-length'),
+ Length = list_to_integer(Headers#http_request_h.'content-length'),
case ((Length =< MaxBodySize) or (MaxBodySize == nolimit)) of
true ->
case httpd_request:whole_body(Body, Length) of
{Module, Function, Args} ->
- ?hdrt("whole body",
- [{module, Module},
- {function, Function},
- {args, Args}]),
http_transport:setopts(ModData#mod.socket_type,
ModData#mod.socket,
[{active, once}]),
{noreply, State#state{mfa =
- {Module, Function, Args}}};
+ {Module, Function, Args}}};
{ok, NewBody} ->
- ?hdrt("whole body",
- [{new_body, NewBody}]),
handle_response(
State#state{headers = Headers,
body = NewBody})
end;
false ->
- ?hdrd("body too long",
- [{length, Length}, {max_body_size, MaxBodySize}]),
httpd_response:send_status(ModData, 413, "Body too long"),
error_log("Body too long", ModData),
{stop, normal, State#state{response_sent = true}}
@@ -527,7 +501,7 @@ handle_body(#state{headers = Headers, body = Body, mod = ModData} = State,
handle_expect(#state{headers = Headers, mod =
#mod{config_db = ConfigDB} = ModData} = State,
MaxBodySize) ->
- Length = Headers#http_request_h.'content-length',
+ Length = list_to_integer(Headers#http_request_h.'content-length'),
case expect(Headers, ModData#mod.http_version, ConfigDB) of
continue when (MaxBodySize > Length) orelse (MaxBodySize =:= nolimit) ->
httpd_response:send_status(ModData, 100, ""),
@@ -579,8 +553,6 @@ handle_response(#state{body = Body,
mod = ModData,
headers = Headers,
max_keep_alive_request = Max} = State) when Max > 0 ->
- ?hdrt("handle response",
- [{body, Body}, {mod, ModData}, {headers, Headers}, {max, Max}]),
{NewBody, Data} = httpd_request:body_data(Headers, Body),
ok = httpd_response:generate_and_send_response(
ModData#mod{entity_body = NewBody}),
@@ -589,8 +561,6 @@ handle_response(#state{body = Body,
handle_response(#state{body = Body,
headers = Headers,
mod = ModData} = State) ->
- ?hdrt("handle response",
- [{body, Body}, {mod, ModData}, {headers, Headers}]),
{NewBody, _} = httpd_request:body_data(Headers, Body),
ok = httpd_response:generate_and_send_response(
ModData#mod{entity_body = NewBody}),
@@ -598,7 +568,6 @@ handle_response(#state{body = Body,
handle_next_request(#state{mod = #mod{connection = true} = ModData,
max_keep_alive_request = Max} = State, Data) ->
- ?hdrt("handle next request", [{max, Max}]),
NewModData = #mod{socket_type = ModData#mod.socket_type,
socket = ModData#mod.socket,
@@ -606,8 +575,15 @@ handle_next_request(#state{mod = #mod{connection = true} = ModData,
init_data = ModData#mod.init_data},
MaxHeaderSize = max_header_size(ModData#mod.config_db),
MaxURISize = max_uri_size(ModData#mod.config_db),
-
- MFA = {httpd_request, parse, [{MaxURISize, MaxHeaderSize}]},
+ MaxContentLen = max_content_length(ModData#mod.config_db),
+ Customize = customize(ModData#mod.config_db),
+
+ MFA = {httpd_request, parse, [[{max_uri, MaxURISize}, {max_header, MaxHeaderSize},
+ {max_version, ?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, MaxContentLen},
+ {customize, Customize}
+ ]]},
TmpState = State#state{mod = NewModData,
mfa = MFA,
max_keep_alive_request = decrease(Max),
@@ -627,11 +603,9 @@ handle_next_request(#state{mod = #mod{connection = true} = ModData,
end;
handle_next_request(State, _) ->
- ?hdrt("handle next request - stop", []),
{stop, normal, State}.
activate_request_timeout(#state{timeout = Time} = State) ->
- ?hdrt("activate request timeout", [{time, Time}]),
Ref = erlang:send_after(Time, self(), timeout),
State#state{timer = Ref}.
data_receive_counter(State, Byte_limit) ->
@@ -659,21 +633,10 @@ decrease(N) when is_integer(N) ->
decrease(N) ->
N.
-error_log(ReasonString, Info) ->
+error_log(ReasonString, #mod{config_db = ConfigDB}) ->
Error = lists:flatten(
io_lib:format("Error reading request: ~s", [ReasonString])),
- error_log(mod_log, Info, Error),
- error_log(mod_disk_log, Info, Error).
-
-error_log(Mod, #mod{config_db = ConfigDB} = Info, String) ->
- Modules = httpd_util:lookup(ConfigDB, modules,
- [mod_get, mod_head, mod_log]),
- case lists:member(Mod, Modules) of
- true ->
- Mod:error_log(Info, String);
- _ ->
- ok
- end.
+ httpd_util:error_log(ConfigDB, Error).
%%--------------------------------------------------------------------
@@ -692,3 +655,8 @@ max_body_size(ConfigDB) ->
max_keep_alive_request(ConfigDB) ->
httpd_util:lookup(ConfigDB, max_keep_alive_request, infinity).
+max_content_length(ConfigDB) ->
+ httpd_util:lookup(ConfigDB, max_content_length, ?HTTP_MAX_CONTENT_LENGTH).
+
+customize(ConfigDB) ->
+ httpd_util:lookup(ConfigDB, customize, httpd_custom).
diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl
index 6b6532266b..1374b7e85e 100644
--- a/lib/inets/src/http_server/httpd_response.erl
+++ b/lib/inets/src/http_server/httpd_response.erl
@@ -1,31 +1,33 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(httpd_response).
-export([generate_and_send_response/1, send_status/3, send_header/3,
- send_body/3, send_chunk/3, send_final_chunk/2, split_header/2,
- is_disable_chunked_send/1, cache_headers/1]).
+ send_body/3, send_chunk/3, send_final_chunk/2, send_final_chunk/3,
+ split_header/2, is_disable_chunked_send/1, cache_headers/2]).
-export([map_status_code/2]).
--include("httpd.hrl").
--include("http_internal.hrl").
--include("httpd_internal.hrl").
+-include_lib("inets/src/inets_app/inets_internal.hrl").
+-include_lib("inets/include/httpd.hrl").
+-include_lib("inets/src/http_lib/http_internal.hrl").
+-include_lib("inets/src/http_server/httpd_internal.hrl").
-define(VMODULE,"RESPONSE").
@@ -35,7 +37,7 @@ generate_and_send_response(#mod{init_data =
#init_data{peername = {_,"unknown"}}}) ->
ok;
generate_and_send_response(#mod{config_db = ConfigDB} = ModData) ->
- Modules = httpd_util:lookup(ConfigDB,modules, ?DEFAULT_MODS),
+ Modules = httpd_util:lookup(ConfigDB, modules, ?DEFAULT_MODS),
case traverse_modules(ModData, Modules) of
done ->
ok;
@@ -68,16 +70,7 @@ traverse_modules(ModData,[]) ->
{proceed,ModData#mod.data};
traverse_modules(ModData,[Module|Rest]) ->
?hdrd("traverse modules", [{callback_module, Module}]),
- case (catch apply(Module, do, [ModData])) of
- {'EXIT', Reason} ->
- String =
- lists:flatten(
- io_lib:format("traverse exit from apply: ~p:do => ~n~p",
- [Module, Reason])),
- report_error(mod_log, ModData#mod.config_db, String),
- report_error(mod_disk_log, ModData#mod.config_db, String),
- send_status(ModData, 500, none),
- done;
+ try apply(Module, do, [ModData]) of
done ->
?hdrt("traverse modules - done", []),
done;
@@ -87,6 +80,18 @@ traverse_modules(ModData,[Module|Rest]) ->
{proceed, NewData} ->
?hdrt("traverse modules - proceed", [{new_data, NewData}]),
traverse_modules(ModData#mod{data = NewData}, Rest)
+ catch
+ T:E ->
+ String =
+ lists:flatten(
+ io_lib:format("module traverse failed: ~p:do => "
+ "~n Error Type: ~p"
+ "~n Error: ~p"
+ "~n Stack trace: ~p",
+ [Module, T, E, ?STACK()])),
+ httpd_util:error_log(ModData#mod.config_db, String),
+ send_status(ModData, 500, none),
+ done
end.
%% send_status %%
@@ -171,7 +176,7 @@ send_header(#mod{socket_type = Type,
StatusLine = [NewVer, " ", io_lib:write(NewStatusCode), " ",
httpd_util:reason_phrase(NewStatusCode), ?CRLF],
ConnectionHeader = get_connection(Conn, NewVer),
- Head = list_to_binary([StatusLine, Headers, ConnectionHeader , ?CRLF]),
+ Head = [StatusLine, Headers, ConnectionHeader , ?CRLF],
httpd_socket:deliver(Type, Sock, Head).
map_status_code("HTTP/1.0", Code)
@@ -239,7 +244,6 @@ send_chunk(_, <<>>, _) ->
ok;
send_chunk(_, [], _) ->
ok;
-
send_chunk(#mod{http_version = "HTTP/1.1",
socket_type = Type, socket = Sock}, Response0, false) ->
Response = http_chunk:encode(Response0),
@@ -248,10 +252,13 @@ send_chunk(#mod{http_version = "HTTP/1.1",
send_chunk(#mod{socket_type = Type, socket = Sock} = _ModData, Response, _) ->
httpd_socket:deliver(Type, Sock, Response).
+send_final_chunk(Mod, IsDisableChunkedSend) ->
+ send_final_chunk(Mod, [], IsDisableChunkedSend).
+
send_final_chunk(#mod{http_version = "HTTP/1.1",
- socket_type = Type, socket = Sock}, false) ->
- httpd_socket:deliver(Type, Sock, http_chunk:encode_last());
-send_final_chunk(#mod{socket_type = Type, socket = Sock}, _) ->
+ socket_type = Type, socket = Sock}, Trailers, false) ->
+ httpd_socket:deliver(Type, Sock, http_chunk:encode_last(Trailers));
+send_final_chunk(#mod{socket_type = Type, socket = Sock}, _, _) ->
httpd_socket:close(Type, Sock).
is_disable_chunked_send(Db) ->
@@ -266,8 +273,8 @@ get_connection(false,"HTTP/1.1") ->
get_connection(_,_) ->
"".
-cache_headers(#mod{config_db = Db}) ->
- case httpd_util:lookup(Db, script_nocache, false) of
+cache_headers(#mod{config_db = Db}, NoCacheType) ->
+ case httpd_util:lookup(Db, NoCacheType, false) of
true ->
Date = httpd_util:rfc1123_date(),
[{"cache-control", "no-cache"},
@@ -281,42 +288,28 @@ create_header(ConfigDb, KeyValueTupleHeaders) ->
Date = httpd_util:rfc1123_date(),
ContentType = "text/html",
Server = server(ConfigDb),
- NewHeaders = add_default_headers([{"date", Date},
- {"content-type", ContentType},
- {"server", Server}],
- KeyValueTupleHeaders),
- lists:map(fun fix_header/1, NewHeaders).
-
-
+ CustomizeCB = httpd_util:lookup(ConfigDb, customize, httpd_custom),
+
+ CustomDefaults = httpd_custom:response_default_headers(CustomizeCB),
+ SystemDefaultes = ([{"date", Date},
+ {"content-type", ContentType}
+ | if Server=="" -> [];
+ true -> [{"server", Server}]
+ end
+ ]),
+
+ %% System defaults not present in custom defaults will be added
+ %% to defaults
+ Defaults = add_default_headers(SystemDefaultes, CustomDefaults),
+
+ Headers0 = add_default_headers(Defaults, KeyValueTupleHeaders),
+ lists:filtermap(fun(H) ->
+ httpd_custom:customize_headers(CustomizeCB, response_header, H)
+ end,
+ [Header || Header <- Headers0]).
server(ConfigDb) ->
httpd_util:lookup(ConfigDb, server, ?SERVER_SOFTWARE).
-fix_header({Key0, Value}) ->
- %% make sure first letter is capital
- Words1 = string:tokens(Key0, "-"),
- Words2 = upify(Words1, []),
- Key = new_key(Words2),
- Key ++ ": " ++ Value ++ ?CRLF .
-
-new_key([]) ->
- "";
-new_key([W]) ->
- W;
-new_key([W1,W2]) ->
- W1 ++ "-" ++ W2;
-new_key([W|R]) ->
- W ++ "-" ++ new_key(R).
-
-upify([], Acc) ->
- lists:reverse(Acc);
-upify([Key|Rest], Acc) ->
- upify(Rest, [upify2(Key)|Acc]).
-
-upify2([C|Rest]) when (C >= $a) andalso (C =< $z) ->
- [C-($a-$A)|Rest];
-upify2(Str) ->
- Str.
-
add_default_headers([], Headers) ->
Headers;
@@ -397,24 +390,13 @@ send_response_old(#mod{socket_type = Type,
send_header(ModData, StatusCode, [{content_length,
content_length(NewResponse)}]),
httpd_socket:deliver(Type, Sock, NewResponse);
-
- {error, _Reason} ->
+ _ ->
send_status(ModData, 500, "Internal Server Error")
end.
content_length(Body)->
integer_to_list(httpd_util:flatlength(Body)).
-report_error(Mod, ConfigDB, Error) ->
- Modules = httpd_util:lookup(ConfigDB, modules,
- [mod_get, mod_head, mod_log]),
- case lists:member(Mod, Modules) of
- true ->
- Mod:report_error(ConfigDB, Error);
- _ ->
- ok
- end.
-
handle_headers([], NewHeaders) ->
{ok, NewHeaders};
diff --git a/lib/inets/src/http_server/httpd_script_env.erl b/lib/inets/src/http_server/httpd_script_env.erl
index ac79a8cc63..e15613273e 100644
--- a/lib/inets/src/http_server/httpd_script_env.erl
+++ b/lib/inets/src/http_server/httpd_script_env.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -60,6 +61,19 @@ which_port(#mod{config_db = ConfigDb}) ->
which_peername(#mod{init_data = #init_data{peername = {_, RemoteAddr}}}) ->
RemoteAddr.
+which_peercert(#mod{socket_type = {Type, _}, socket = Socket}) when Type == essl;
+ Type == ssl ->
+ case ssl:peercert(Socket) of
+ {ok, Cert} ->
+ Cert;
+ {error, no_peercert} ->
+ no_peercert;
+ _ ->
+ undefined
+ end;
+which_peercert(_) -> %% Not an ssl connection
+ undefined.
+
which_resolve(#mod{init_data = #init_data{resolve = Resolve}}) ->
Resolve.
@@ -77,6 +91,7 @@ create_basic_elements(esi, ModData) ->
{server_port, which_port(ModData)},
{request_method, which_method(ModData)},
{remote_addr, which_peername(ModData)},
+ {peer_cert, which_peercert(ModData)},
{script_name, which_request_uri(ModData)}];
create_basic_elements(cgi, ModData) ->
@@ -103,7 +118,7 @@ create_http_header_elements(ScriptType, [{Name, [Value | _] = Values } |
create_http_header_elements(ScriptType, [{Name, Value} | Headers], Acc)
when is_list(Value) ->
- {ok, NewName, _} = inets_regexp:gsub(Name,"-","_"),
+ NewName = re:replace(Name,"-","_", [{return,list}, global]),
Element = http_env_element(ScriptType, NewName, Value),
create_http_header_elements(ScriptType, Headers, [Element | Acc]).
diff --git a/lib/inets/src/http_server/httpd_socket.erl b/lib/inets/src/http_server/httpd_socket.erl
index e9d3e06b38..aa67c13621 100644
--- a/lib/inets/src/http_server/httpd_socket.erl
+++ b/lib/inets/src/http_server/httpd_socket.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl
index 3b1e16cf78..d1216e01b0 100644
--- a/lib/inets/src/http_server/httpd_sup.erl
+++ b/lib/inets/src/http_server/httpd_sup.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -28,7 +29,7 @@
%% Internal application API
-export([start_link/1, start_link/2]).
--export([start_child/1, restart_child/2, stop_child/2]).
+-export([start_child/1, restart_child/3, stop_child/3]).
%% Supervisor callback
-export([init/1]).
@@ -37,7 +38,6 @@
-define(TIMEOUT, 15000).
-include("httpd_internal.hrl").
--include("inets_internal.hrl").
%%%=========================================================================
%%% API
@@ -64,33 +64,32 @@ start_child(Config) ->
end.
-restart_child(Address, Port) ->
- Name = id(Address, Port),
+restart_child(Address, Port, Profile) ->
+ Name = id(Address, Port, Profile),
case supervisor:terminate_child(?MODULE, Name) of
- ok ->
- supervisor:restart_child(?MODULE, Name);
- Error ->
- Error
- end.
-
-stop_child(Address, Port) ->
- Name = id(Address, Port),
+ ok ->
+ supervisor:restart_child(?MODULE, Name);
+ Error ->
+ Error
+ end.
+
+stop_child(Address, Port, Profile) ->
+ Name = id(Address, Port, Profile),
case supervisor:terminate_child(?MODULE, Name) of
- ok ->
- supervisor:delete_child(?MODULE, Name);
- Error ->
+ ok ->
+ supervisor:delete_child(?MODULE, Name);
+ Error ->
Error
end.
-
-id(Address, Port) ->
- {httpd_instance_sup, Address, Port}.
+
+id(Address, Port, Profile) ->
+ {httpd_instance_sup, Address, Port, Profile}.
%%%=========================================================================
%%% Supervisor callback
%%%=========================================================================
init([HttpdServices]) ->
- ?hdrd("starting", [{httpd_service, HttpdServices}]),
RestartStrategy = one_for_one,
MaxR = 10,
MaxT = 3600,
@@ -118,23 +117,18 @@ init([HttpdServices]) ->
child_specs([], Acc) ->
Acc;
child_specs([{httpd, HttpdService} | Rest], Acc) ->
- ?hdrd("child specs", [{httpd, HttpdService}]),
NewHttpdService = (catch mk_tuple_list(HttpdService)),
- ?hdrd("child specs", [{new_httpd, NewHttpdService}]),
case catch child_spec(NewHttpdService) of
{error, Reason} ->
- ?hdri("failed generating child spec", [{reason, Reason}]),
error_msg("Failed to start service: ~n~p ~n due to: ~p~n",
[HttpdService, Reason]),
child_specs(Rest, Acc);
Spec ->
- ?hdrt("child spec", [{child_spec, Spec}]),
child_specs(Rest, [Spec | Acc])
end.
child_spec(HttpdService) ->
{ok, Config} = httpd_config(HttpdService),
- ?hdrt("child spec", [{config, Config}]),
Debug = proplists:get_value(debug, Config, []),
AcceptTimeout = proplists:get_value(accept_timeout, Config, 15000),
httpd_util:valid_options(Debug, AcceptTimeout, Config),
@@ -162,32 +156,27 @@ httpd_config([Value| _] = Config) when is_tuple(Value) ->
httpd_child_spec([Value| _] = Config, AcceptTimeout, Debug)
when is_tuple(Value) ->
- ?hdrt("httpd_child_spec - entry", [{accept_timeout, AcceptTimeout},
- {debug, Debug}]),
Address = proplists:get_value(bind_address, Config, any),
Port = proplists:get_value(port, Config, 80),
- httpd_child_spec(Config, AcceptTimeout, Debug, Address, Port);
+ Profile = proplists:get_value(profile, Config, ?DEFAULT_PROFILE),
+ httpd_child_spec(Config, AcceptTimeout, Debug, Address, Port, Profile);
%% In this case the AcceptTimeout and Debug will only have default values...
httpd_child_spec(ConfigFile, AcceptTimeoutDef, DebugDef) ->
- ?hdrt("httpd_child_spec - entry", [{config_file, ConfigFile},
- {accept_timeout_def, AcceptTimeoutDef},
- {debug_def, DebugDef}]),
case httpd_conf:load(ConfigFile) of
{ok, ConfigList} ->
- ?hdrt("httpd_child_spec - loaded", [{config_list, ConfigList}]),
case (catch httpd_conf:validate_properties(ConfigList)) of
{ok, Config} ->
- ?hdrt("httpd_child_spec - validated", [{config, Config}]),
Address = proplists:get_value(bind_address, Config, any),
Port = proplists:get_value(port, Config, 80),
+ Profile = proplists:get_value(profile, Config, ?DEFAULT_PROFILE),
AcceptTimeout =
proplists:get_value(accept_timeout, Config,
AcceptTimeoutDef),
Debug =
proplists:get_value(debug, Config, DebugDef),
httpd_child_spec([{file, ConfigFile} | Config],
- AcceptTimeout, Debug, Address, Port);
+ AcceptTimeout, Debug, Address, Port, Profile);
Error ->
Error
end;
@@ -195,19 +184,23 @@ httpd_child_spec(ConfigFile, AcceptTimeoutDef, DebugDef) ->
Error
end.
-httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port) ->
- Fd = proplists:get_value(fd, Config, undefined),
- case Port == 0 orelse Fd =/= undefined of
- true ->
- httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port);
- false ->
- httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port)
+httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port, Profile) ->
+ case get_fd(Port) of
+ {ok, Fd} ->
+ case Port == 0 orelse Fd =/= undefined of
+ true ->
+ httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port, Profile);
+ false ->
+ httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port, Profile)
+ end;
+ Error ->
+ Error
end.
-httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port) ->
+httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port, Profile) ->
case start_listen(Addr, Port, Config) of
{Pid, {NewPort, NewConfig, ListenSocket}} ->
- Name = {httpd_instance_sup, Addr, NewPort},
+ Name = {httpd_instance_sup, Addr, NewPort, Profile},
StartFunc = {httpd_instance_sup, start_link,
[NewConfig, AcceptTimeout,
{Pid, ListenSocket}, Debug]},
@@ -221,8 +214,8 @@ httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port) ->
{error, Reason}
end.
-httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port) ->
- Name = {httpd_instance_sup, Addr, Port},
+httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port, Profile) ->
+ Name = {httpd_instance_sup, Addr, Port, Profile},
StartFunc = {httpd_instance_sup, start_link,
[Config, AcceptTimeout, Debug]},
Restart = permanent,
@@ -247,8 +240,8 @@ listen(Address, Port, Config) ->
SocketType ->
case http_transport:start(SocketType) of
ok ->
- Fd = proplists:get_value(fd, Config),
- IpFamily = proplists:get_value(ipfamily, Config, inet6fb4),
+ {ok, Fd} = get_fd(Port),
+ IpFamily = proplists:get_value(ipfamily, Config, inet),
case http_transport:listen(SocketType, Address, Port, Fd, IpFamily) of
{ok, ListenSocket} ->
NewConfig = proplists:delete(port, Config),
@@ -293,6 +286,8 @@ socket_type(Config) ->
socket_type(ip_comm = SocketType, _) ->
SocketType;
+socket_type({ip_comm, _} = SocketType, _) ->
+ SocketType;
socket_type({essl, _} = SocketType, _) ->
SocketType;
socket_type(_, Config) ->
@@ -366,3 +361,19 @@ ssl_ca_certificate_file(Config) ->
File ->
[{cacertfile, File}]
end.
+
+get_fd(0) ->
+ {ok, undefined};
+get_fd(Port) ->
+ FdKey = list_to_atom("httpd_" ++ integer_to_list(Port)),
+ case init:get_argument(FdKey) of
+ {ok, [[Value]]} ->
+ case (catch list_to_integer(Value)) of
+ N when is_integer(N) ->
+ {ok, N};
+ _ ->
+ {error, {bad_descriptor, Value}}
+ end;
+ _ ->
+ {ok, undefined}
+ end.
diff --git a/lib/inets/src/http_server/httpd_util.erl b/lib/inets/src/http_server/httpd_util.erl
index b0b18b9c3d..a647f04ddc 100644
--- a/lib/inets/src/http_server/httpd_util.erl
+++ b/lib/inets/src/http_server/httpd_util.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -30,7 +31,7 @@
convert_netscapecookie_date/1, enable_debug/1, valid_options/3,
modules_validate/1, module_validate/1,
dir_validate/2, file_validate/2, mime_type_validate/1,
- mime_types_validate/1, custom_date/0]).
+ mime_types_validate/1, custom_date/0, error_log/2]).
-export([encode_hex/1, decode_hex/1]).
-include_lib("kernel/include/file.hrl").
@@ -41,17 +42,7 @@ ip_address({_,_,_,_,_,_,_,_} = Address, _IpFamily) ->
{ok, Address};
ip_address(Host, IpFamily)
when ((IpFamily =:= inet) orelse (IpFamily =:= inet6)) ->
- inet:getaddr(Host, IpFamily);
-ip_address(Host, inet6fb4 = _IpFamily) ->
- Inet = case gen_tcp:listen(0, [inet6]) of
- {ok, Dummyport} ->
- gen_tcp:close(Dummyport),
- inet6;
- _ ->
- inet
- end,
- inet:getaddr(Host, Inet).
-
+ inet:getaddr(Host, IpFamily).
%% lookup
@@ -206,9 +197,6 @@ message(413, Reason,_) ->
"Entity: " ++ html_encode(Reason);
message(414,ReasonPhrase,_) ->
"Message " ++ html_encode(ReasonPhrase) ++ ".";
-message(416,ReasonPhrase,_) ->
- html_encode(ReasonPhrase);
-
message(500,_,ConfigDB) ->
ServerAdmin=lookup(ConfigDB,server_admin,"unknown@unknown"),
"The server encountered an internal error or "
@@ -233,7 +221,9 @@ message(501,{Method, RequestURI, HTTPVersion}, _ConfigDB) ->
end;
message(503, String, _ConfigDB) ->
- "This service in unavailable due to: " ++ html_encode(String).
+ "This service in unavailable due to: " ++ html_encode(String);
+message(_, ReasonPhrase, _) ->
+ html_encode(ReasonPhrase).
maybe_encode(URI) ->
Decoded = try http_uri:decode(URI) of
@@ -430,11 +420,11 @@ flatlength([],L) ->
%% split_path
split_path(Path) ->
- case inets_regexp:match(Path,"[\?].*\$") of
+ case re:run(Path,"[\?].*\$", [{capture, first}]) of
%% A QUERY_STRING exists!
- {match,Start,Length} ->
- {http_uri:decode(string:substr(Path,1,Start-1)),
- string:substr(Path,Start,Length)};
+ {match,[{Start,Length}]} ->
+ {http_uri:decode(string:substr(Path,1,Start)),
+ string:substr(Path,Start+1,Length)};
%% A possible PATH_INFO exists!
nomatch ->
split_path(Path,[])
@@ -532,25 +522,8 @@ remove_ws(Rest) ->
%% split
-split(String,RegExp,Limit) ->
- case inets_regexp:parse(RegExp) of
- {error,Reason} ->
- {error,Reason};
- {ok,_} ->
- {ok,do_split(String,RegExp,Limit)}
- end.
-
-do_split(String, _RegExp, 1) ->
- [String];
-
-do_split(String,RegExp,Limit) ->
- case inets_regexp:first_match(String,RegExp) of
- {match,Start,Length} ->
- [string:substr(String,1,Start-1)|
- do_split(lists:nthtail(Start+Length-1,String),RegExp,Limit-1)];
- nomatch ->
- [String]
- end.
+split(String,RegExp,N) ->
+ {ok, re:split(String, RegExp, [{parts, N}, {return, list}])}.
%% make_name/2, make_name/3
%% Prefix -> string()
@@ -573,7 +546,10 @@ make_name(Prefix,Port) ->
make_name(Prefix,Addr,Port) ->
make_name(Prefix,Addr,Port,"").
-
+
+make_name(Prefix, Addr,Port,Postfix) when is_atom(Postfix)->
+ make_name(Prefix, Addr,Port, atom_to_list(Postfix));
+
make_name(Prefix,"*",Port,Postfix) ->
make_name(Prefix,undefined,Port,Postfix);
@@ -596,15 +572,7 @@ make_name2({A,B,C,D}) ->
io_lib:format("~w_~w_~w_~w", [A,B,C,D]);
make_name2({A, B, C, D, E, F, G, H}) ->
- io_lib:format("~s_~s_~s_~s_~s_~s_~s_~s", [integer_to_hexlist(A),
- integer_to_hexlist(B),
- integer_to_hexlist(C),
- integer_to_hexlist(D),
- integer_to_hexlist(E),
- integer_to_hexlist(F),
- integer_to_hexlist(G),
- integer_to_hexlist(H)
- ]);
+ io_lib:format("~w_~w_~w_~w_~w_~w_~w_~w", [A,B,C,D,E,F,G,H]);
make_name2(Addr) ->
search_and_replace(Addr,$.,$_).
@@ -791,3 +759,17 @@ do_enable_debug([{Level,Modules}|Rest])
ok
end,
do_enable_debug(Rest).
+
+error_log(ConfigDb, Error) ->
+ error_log(mod_log, ConfigDb, Error),
+ error_log(mod_disk_log, ConfigDb, Error).
+
+error_log(Mod, ConfigDB, Error) ->
+ Modules = httpd_util:lookup(ConfigDB, modules,
+ [mod_get, mod_head, mod_log]),
+ case lists:member(Mod, Modules) of
+ true ->
+ Mod:report_error(ConfigDB, Error);
+ _ ->
+ ok
+ end.
diff --git a/lib/inets/src/http_server/mod_actions.erl b/lib/inets/src/http_server/mod_actions.erl
index c3946ff9b4..b5449f20ee 100644
--- a/lib/inets/src/http_server/mod_actions.erl
+++ b/lib/inets/src/http_server/mod_actions.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -80,18 +81,18 @@ script(RequestURI, Method, [_ | Rest]) ->
%% load
load("Action "++ Action, []) ->
- case inets_regexp:split(Action, " ") of
- {ok,[MimeType, CGIScript]} ->
- {ok,[],{action, {MimeType, CGIScript}}};
- {ok,_} ->
- {error,?NICE(httpd_conf:clean(Action)++" is an invalid Action")}
+ case re:split(Action, " ", [{return, list}]) of
+ [MimeType, CGIScript] ->
+ {ok,[],{action, {MimeType, CGIScript}}};
+ _ ->
+ {error,?NICE(string:strip(Action)++" is an invalid Action")}
end;
load("Script " ++ Script,[]) ->
- case inets_regexp:split(Script, " ") of
- {ok,[Method, CGIScript]} ->
- {ok,[],{script, {Method, CGIScript}}};
- {ok,_} ->
- {error,?NICE(httpd_conf:clean(Script)++" is an invalid Script")}
+ case re:split(Script, " ", [{return, list}]) of
+ [Method, CGIScript] ->
+ {ok,[],{script, {Method, CGIScript}}};
+ _ ->
+ {error,?NICE(string:strip(Script)++" is an invalid Script")}
end.
store({action, {MimeType, CGIScript}} = Conf, _) when is_list(MimeType),
diff --git a/lib/inets/src/http_server/mod_alias.erl b/lib/inets/src/http_server/mod_alias.erl
index 0b9fe4cfe0..0333076546 100644
--- a/lib/inets/src/http_server/mod_alias.erl
+++ b/lib/inets/src/http_server/mod_alias.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -55,6 +56,7 @@ do(#mod{data = Data} = Info) ->
do_alias(#mod{config_db = ConfigDB,
request_uri = ReqURI,
+ socket_type = SocketType,
data = Data}) ->
{ShortPath, Path, AfterPath} =
real_name(ConfigDB, ReqURI, which_alias(ConfigDB)),
@@ -70,8 +72,9 @@ do_alias(#mod{config_db = ConfigDB,
(LastChar =/= $/)) ->
?hdrt("directory and last-char is a /", []),
ServerName = which_server_name(ConfigDB),
- Port = port_string( which_port(ConfigDB) ),
- URL = "http://" ++ ServerName ++ Port ++ ReqURI ++ "/",
+ Port = port_string(which_port(ConfigDB)),
+ Protocol = get_protocol(SocketType),
+ URL = Protocol ++ ServerName ++ Port ++ ReqURI ++ "/",
ReasonPhrase = httpd_util:reason_phrase(301),
Message = httpd_util:message(301, URL, ConfigDB),
{proceed,
@@ -94,6 +97,12 @@ port_string(80) ->
port_string(Port) ->
":" ++ integer_to_list(Port).
+get_protocol(ip_comm) ->
+ "http://";
+get_protocol(_) ->
+ %% Should clean up to have only one ssl type essl vs ssl is not relevant any more
+ "https://".
+
%% real_name
real_name(ConfigDB, RequestURI, []) ->
@@ -104,32 +113,52 @@ real_name(ConfigDB, RequestURI, []) ->
httpd_util:split_path(default_index(ConfigDB, RealName)),
{ShortPath, Path, AfterPath};
-real_name(ConfigDB, RequestURI, [{MP,Replacement}|Rest])
+real_name(ConfigDB, RequestURI, [{MP,Replacement}| _] = Aliases)
when element(1, MP) =:= re_pattern ->
- case re:run(RequestURI, MP, [{capture,[]}]) of
- match ->
+ case longest_match(Aliases, RequestURI) of
+ {match, {MP, Replacement}} ->
NewURI = re:replace(RequestURI, MP, Replacement, [{return,list}]),
{ShortPath,_} = httpd_util:split_path(NewURI),
{Path,AfterPath} =
httpd_util:split_path(default_index(ConfigDB, NewURI)),
{ShortPath, Path, AfterPath};
nomatch ->
- real_name(ConfigDB, RequestURI, Rest)
+ real_name(ConfigDB, RequestURI, [])
end;
-real_name(ConfigDB, RequestURI, [{FakeName,RealName}|Rest]) ->
- case inets_regexp:match(RequestURI, "^" ++ FakeName) of
- {match, _, _} ->
- {ok, ActualName, _} = inets_regexp:sub(RequestURI,
- "^" ++ FakeName, RealName),
+real_name(ConfigDB, RequestURI, [{_,_}|_] = Aliases) ->
+ case longest_match(Aliases, RequestURI) of
+ {match, {FakeName, RealName}} ->
+ ActualName = re:replace(RequestURI,
+ "^" ++ FakeName, RealName, [{return,list}]),
{ShortPath, _AfterPath} = httpd_util:split_path(ActualName),
{Path, AfterPath} =
- httpd_util:split_path(default_index(ConfigDB, ActualName)),
+ httpd_util:split_path(default_index(ConfigDB, ActualName)),
{ShortPath, Path, AfterPath};
- nomatch ->
- real_name(ConfigDB, RequestURI, Rest)
+ nomatch ->
+ real_name(ConfigDB, RequestURI, [])
end.
+longest_match(Aliases, RequestURI) ->
+ longest_match(Aliases, RequestURI, _LongestNo = 0, _LongestAlias = undefined).
+
+longest_match([{FakeName, RealName} | Rest], RequestURI, LongestNo, LongestAlias) ->
+ case re:run(RequestURI, "^" ++ FakeName, [{capture, first}]) of
+ {match, [{_, Length}]} ->
+ if
+ Length > LongestNo ->
+ longest_match(Rest, RequestURI, Length, {FakeName, RealName});
+ true ->
+ longest_match(Rest, RequestURI, LongestNo, LongestAlias)
+ end;
+ nomatch ->
+ longest_match(Rest, RequestURI, LongestNo, LongestAlias)
+ end;
+longest_match([], _RequestURI, 0, _LongestAlias) ->
+ nomatch;
+longest_match([], _RequestURI, _LongestNo, LongestAlias) ->
+ {match, LongestAlias}.
+
%% real_script_name
real_script_name(_ConfigDB, _RequestURI, []) ->
@@ -137,7 +166,7 @@ real_script_name(_ConfigDB, _RequestURI, []) ->
real_script_name(ConfigDB, RequestURI, [{MP,Replacement} | Rest])
when element(1, MP) =:= re_pattern ->
- case re:run(RequestURI, MP, [{capture,[]}]) of
+ case re:run(RequestURI, MP, [{capture, none}]) of
match ->
ActualName =
re:replace(RequestURI, MP, Replacement, [{return,list}]),
@@ -147,10 +176,10 @@ real_script_name(ConfigDB, RequestURI, [{MP,Replacement} | Rest])
end;
real_script_name(ConfigDB, RequestURI, [{FakeName,RealName} | Rest]) ->
- case inets_regexp:match(RequestURI, "^" ++ FakeName) of
- {match,_,_} ->
- {ok, ActualName, _} =
- inets_regexp:sub(RequestURI, "^" ++ FakeName, RealName),
+ case re:run(RequestURI, "^" ++ FakeName, [{capture, none}]) of
+ match ->
+ ActualName =
+ re:replace(RequestURI, "^" ++ FakeName, RealName, [{return,list}]),
httpd_util:split_script_path(default_index(ConfigDB, ActualName));
nomatch ->
real_script_name(ConfigDB, RequestURI, Rest)
@@ -197,26 +226,26 @@ path(Data, ConfigDB, RequestURI) ->
%% load
load("DirectoryIndex " ++ DirectoryIndex, []) ->
- {ok, DirectoryIndexes} = inets_regexp:split(DirectoryIndex," "),
+ DirectoryIndexes = re:split(DirectoryIndex," ", [{return, list}]),
{ok,[], {directory_index, DirectoryIndexes}};
load("Alias " ++ Alias, []) ->
- case inets_regexp:split(Alias," ") of
- {ok, [FakeName, RealName]} ->
+ case re:split(Alias," ", [{return, list}]) of
+ [FakeName, RealName] ->
{ok,[],{alias,{FakeName,RealName}}};
- {ok, _} ->
- {error,?NICE(httpd_conf:clean(Alias)++" is an invalid Alias")}
+ _ ->
+ {error,?NICE(string:strip(Alias)++" is an invalid Alias")}
end;
load("ReWrite " ++ Rule, Acc) ->
load_re_write(Rule, Acc, "ReWrite", re_write);
load("ScriptAlias " ++ ScriptAlias, []) ->
- case inets_regexp:split(ScriptAlias, " ") of
- {ok, [FakeName, RealName]} ->
+ case re:split(ScriptAlias, " ", [{return, list}]) of
+ [FakeName, RealName] ->
%% Make sure the path always has a trailing slash..
RealName1 = filename:join(filename:split(RealName)),
{ok, [], {script_alias, {FakeName, RealName1++"/"}}};
- {ok, _} ->
- {error, ?NICE(httpd_conf:clean(ScriptAlias)++
- " is an invalid ScriptAlias")}
+ _ ->
+ {error, ?NICE(string:strip(ScriptAlias)++
+ " is an invalid ScriptAlias")}
end;
load("ScriptReWrite " ++ Rule, Acc) ->
load_re_write(Rule, Acc, "ScriptReWrite", script_re_write).
@@ -226,7 +255,7 @@ load_re_write(Rule0, Acc, Type, Tag) ->
fun ($\s) -> true; ($\t) -> true; (_) -> false end,
Rule0) of
"" ->
- {error, ?NICE(httpd_conf:clean(Rule0)++" is an invalid "++Type)};
+ {error, ?NICE(string:strip(Rule0)++" is an invalid "++Type)};
Rule ->
case string:chr(Rule, $\s) of
0 ->
diff --git a/lib/inets/src/http_server/mod_auth.erl b/lib/inets/src/http_server/mod_auth.erl
index 85a87ab884..fba94df176 100644
--- a/lib/inets/src/http_server/mod_auth.erl
+++ b/lib/inets/src/http_server/mod_auth.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -38,15 +39,16 @@
-include("httpd.hrl").
-include("mod_auth.hrl").
-include("httpd_internal.hrl").
--include("inets_internal.hrl").
-define(VMODULE,"AUTH").
-define(NOPASSWORD,"NoPassword").
-%% do
+%%====================================================================
+%% Internal application API
+%%====================================================================
+
do(Info) ->
- ?hdrt("do", [{info, Info}]),
case proplists:get_value(status,Info#mod.data) of
%% A status code has been generated!
{_StatusCode, _PhraseArgs, _Reason} ->
@@ -61,22 +63,15 @@ do(Info) ->
%% Is it a secret area?
case secretp(Path,Info#mod.config_db) of
{yes, {Directory, DirectoryData}} ->
- ?hdrt("secret area",
- [{directory, Directory},
- {directory_data, DirectoryData}]),
-
- %% Authenticate (allow)
case allow((Info#mod.init_data)#init_data.peername,
Info#mod.socket_type,Info#mod.socket,
DirectoryData) of
allowed ->
- ?hdrt("allowed", []),
case deny((Info#mod.init_data)#init_data.peername,
Info#mod.socket_type,
Info#mod.socket,
DirectoryData) of
not_denied ->
- ?hdrt("not denied", []),
case proplists:get_value(auth_type,
DirectoryData) of
undefined ->
@@ -90,15 +85,13 @@ do(Info) ->
AuthType)
end;
{denied, Reason} ->
- ?hdrt("denied", [{reason, Reason}]),
{proceed,
[{status, {403,
- Info#mod.request_uri,
- Reason}}|
+ Info#mod.request_uri,
+ Reason}}|
Info#mod.data]}
end;
{not_allowed, Reason} ->
- ?hdrt("not allowed", [{reason, Reason}]),
{proceed,[{status,{403,
Info#mod.request_uri,
Reason}} |
@@ -114,18 +107,299 @@ do(Info) ->
end.
-do_auth(Info, Directory, DirectoryData, AuthType) ->
+%% mod_auth recognizes the following Configuration Directives:
+%% <Directory /path/to/directory>
+%% AuthDBType
+%% AuthName
+%% AuthUserFile
+%% AuthGroupFile
+%% AuthAccessPassword
+%% require
+%% allow
+%% </Directory>
+
+%% When a <Directory> directive is found, a new context is set to
+%% [{directory, Directory, DirData}|OtherContext]
+%% DirData in this case is a key-value list of data belonging to the
+%% directory in question.
+%%
+%% When the </Directory> statement is found, the Context created earlier
+%% will be returned as a ConfigList and the context will return to the
+%% state it was previously.
+
+load("<Directory " ++ Directory,[]) ->
+ Dir = string:strip(string:strip(Directory),right, $>),
+ {ok,[{directory, {Dir, [{path, Dir}]}}]};
+load(eof,[{directory, {Directory, _DirData}}|_]) ->
+ {error, ?NICE("Premature end-of-file in "++ Directory)};
+
+load("AuthName " ++ AuthName, [{directory, {Directory, DirData}}|Rest]) ->
+ {ok, [{directory, {Directory,
+ [{auth_name, string:strip(AuthName)} | DirData]}}
+ | Rest ]};
+load("AuthUserFile " ++ AuthUserFile0,
+ [{directory, {Directory, DirData}}|Rest]) ->
+ AuthUserFile = string:strip(AuthUserFile0),
+ {ok, [{directory, {Directory,
+ [{auth_user_file, AuthUserFile}|DirData]}} | Rest ]};
+load("AuthGroupFile " ++ AuthGroupFile0,
+ [{directory, {Directory, DirData}}|Rest]) ->
+ AuthGroupFile = string:strip(AuthGroupFile0),
+ {ok,[{directory, {Directory,
+ [{auth_group_file, AuthGroupFile}|DirData]}} | Rest]};
+
+load("AuthAccessPassword " ++ AuthAccessPassword0,
+ [{directory, {Directory, DirData}}|Rest]) ->
+ AuthAccessPassword = string:strip(AuthAccessPassword0),
+ {ok,[{directory, {Directory,
+ [{auth_access_password, AuthAccessPassword}|DirData]}} | Rest]};
+
+load("AuthDBType " ++ Type,
+ [{directory, {Dir, DirData}}|Rest]) ->
+ case string:strip(Type) of
+ "plain" ->
+ {ok, [{directory, {Dir, [{auth_type, plain}|DirData]}} | Rest ]};
+ "mnesia" ->
+ {ok, [{directory, {Dir, [{auth_type, mnesia}|DirData]}} | Rest ]};
+ "dets" ->
+ {ok, [{directory, {Dir, [{auth_type, dets}|DirData]}} | Rest ]};
+ _ ->
+ {error, ?NICE(string:strip(Type)++" is an invalid AuthDBType")}
+ end;
+
+load("require " ++ Require,[{directory, {Directory, DirData}}|Rest]) ->
+ case re:split(Require," ", [{return, list}]) of
+ ["user" | Users] ->
+ {ok,[{directory, {Directory,
+ [{require_user,Users}|DirData]}} | Rest]};
+ ["group"|Groups] ->
+ {ok,[{directory, {Directory,
+ [{require_group,Groups}|DirData]}} | Rest]};
+ _ ->
+ {error,?NICE(string:strip(Require) ++" is an invalid require")}
+ end;
+
+load("allow " ++ Allow,[{directory, {Directory, DirData}}|Rest]) ->
+ case re:split(Allow," ", [{return, list}]) of
+ ["from","all"] ->
+ {ok,[{directory, {Directory,
+ [{allow_from,all}|DirData]}} | Rest]};
+ ["from"|Hosts] ->
+ {ok,[{directory, {Directory,
+ [{allow_from,Hosts}|DirData]}} | Rest]};
+ _ ->
+ {error,?NICE(string:strip(Allow) ++" is an invalid allow")}
+ end;
+
+load("deny " ++ Deny,[{directory, {Directory, DirData}}|Rest]) ->
+ case re:split(Deny," ", [{return, list}]) of
+ ["from", "all"] ->
+ {ok,[{{directory, Directory,
+ [{deny_from, all}|DirData]}} | Rest]};
+ ["from"|Hosts] ->
+ {ok,[{{directory, Directory,
+ [{deny_from, Hosts}|DirData]}} | Rest]};
+ _ ->
+ {error,?NICE(string:strip(Deny) ++" is an invalid deny")}
+ end;
+
+load("</Directory>",[{directory, {Directory, DirData}}|Rest]) ->
+ {ok, Rest, {directory, {Directory, DirData}}};
+
+load("AuthMnesiaDB " ++ AuthMnesiaDB,
+ [{directory, {Dir, DirData}}|Rest]) ->
+ case string:strip(AuthMnesiaDB) of
+ "On" ->
+ {ok,[{directory, {Dir,[{auth_type,mnesia}|DirData]}}|Rest]};
+ "Off" ->
+ {ok,[{directory, {Dir,[{auth_type,plain}|DirData]}}|Rest]};
+ _ ->
+ {error, ?NICE(string:strip(AuthMnesiaDB) ++
+ " is an invalid AuthMnesiaDB")}
+ end.
+
+store({directory, {Directory, DirData}}, ConfigList)
+ when is_list(Directory) andalso is_list(DirData) ->
+ try directory_config_check(Directory, DirData) of
+ ok ->
+ store_directory(Directory, DirData, ConfigList)
+ catch
+ throw:Error ->
+ {error, Error, {directory, Directory, DirData}}
+ end;
+store({directory, {Directory, DirData}}, _) ->
+ {error, {wrong_type, {directory, {Directory, DirData}}}}.
+
+remove(ConfigDB) ->
+ lists:foreach(fun({directory, {_Dir, DirData}}) ->
+ AuthMod = auth_mod_name(DirData),
+ (catch apply(AuthMod, remove, [DirData]))
+ end,
+ ets:match_object(ConfigDB,{directory,{'_','_'}})),
+
+ Addr = httpd_util:lookup(ConfigDB, bind_address, undefined),
+ Port = httpd_util:lookup(ConfigDB, port),
+ Profile = httpd_util:lookup(ConfigDB, profile, ?DEFAULT_PROFILE),
+ mod_auth_server:stop(Addr, Port, Profile),
+ ok.
+
+add_user(UserName, Opt) ->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd}->
+ case get_options(Opt, userData) of
+ {error, Reason}->
+ {error, Reason};
+ {UserData, Password}->
+ User = [#httpd_user{username = UserName,
+ password = Password,
+ user_data = UserData}],
+ mod_auth_server:add_user(Addr, Port, Dir, User, AuthPwd)
+ end
+ end.
+
+
+add_user(UserName, Password, UserData, Port, Dir) ->
+ add_user(UserName, Password, UserData, undefined, Port, Dir).
+add_user(UserName, Password, UserData, Addr, Port, Dir) ->
+ User = [#httpd_user{username = UserName,
+ password = Password,
+ user_data = UserData}],
+ mod_auth_server:add_user(Addr, Port, Dir, User, ?NOPASSWORD).
+
+get_user(UserName, Opt) ->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd} ->
+ mod_auth_server:get_user(Addr, Port, Dir, UserName, AuthPwd);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+get_user(UserName, Port, Dir) ->
+ get_user(UserName, undefined, Port, Dir).
+get_user(UserName, Addr, Port, Dir) ->
+ mod_auth_server:get_user(Addr, Port, Dir, UserName, ?NOPASSWORD).
+
+add_group_member(GroupName, UserName, Opt)->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd}->
+ mod_auth_server:add_group_member(Addr, Port, Dir,
+ GroupName, UserName, AuthPwd);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+add_group_member(GroupName, UserName, Port, Dir) ->
+ add_group_member(GroupName, UserName, undefined, Port, Dir).
+
+add_group_member(GroupName, UserName, Addr, Port, Dir) ->
+ mod_auth_server:add_group_member(Addr, Port, Dir,
+ GroupName, UserName, ?NOPASSWORD).
+
+delete_group_member(GroupName, UserName, Opt) ->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd} ->
+ mod_auth_server:delete_group_member(Addr, Port, Dir,
+ GroupName, UserName, AuthPwd);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+delete_group_member(GroupName, UserName, Port, Dir) ->
+ delete_group_member(GroupName, UserName, undefined, Port, Dir).
+delete_group_member(GroupName, UserName, Addr, Port, Dir) ->
+ mod_auth_server:delete_group_member(Addr, Port, Dir,
+ GroupName, UserName, ?NOPASSWORD).
+
+list_users(Opt) ->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd} ->
+ mod_auth_server:list_users(Addr, Port, Dir, AuthPwd);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+list_users(Port, Dir) ->
+ list_users(undefined, Port, Dir).
+list_users(Addr, Port, Dir) ->
+ mod_auth_server:list_users(Addr, Port, Dir, ?NOPASSWORD).
+
+delete_user(UserName, Opt) ->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd} ->
+ mod_auth_server:delete_user(Addr, Port, Dir, UserName, AuthPwd);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+delete_user(UserName, Port, Dir) ->
+ delete_user(UserName, undefined, Port, Dir).
+delete_user(UserName, Addr, Port, Dir) ->
+ mod_auth_server:delete_user(Addr, Port, Dir, UserName, ?NOPASSWORD).
+
+delete_group(GroupName, Opt) ->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd} ->
+ mod_auth_server:delete_group(Addr, Port, Dir, GroupName, AuthPwd);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+delete_group(GroupName, Port, Dir) ->
+ delete_group(GroupName, undefined, Port, Dir).
+delete_group(GroupName, Addr, Port, Dir) ->
+ mod_auth_server:delete_group(Addr, Port, Dir, GroupName, ?NOPASSWORD).
+
+list_groups(Opt) ->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd} ->
+ mod_auth_server:list_groups(Addr, Port, Dir, AuthPwd);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+list_groups(Port, Dir) ->
+ list_groups(undefined, Port, Dir).
+list_groups(Addr, Port, Dir) ->
+ mod_auth_server:list_groups(Addr, Port, Dir, ?NOPASSWORD).
+
+list_group_members(GroupName, Opt) ->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd} ->
+ mod_auth_server:list_group_members(Addr, Port, Dir, GroupName,
+ AuthPwd);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+list_group_members(GroupName, Port, Dir) ->
+ list_group_members(GroupName, undefined, Port, Dir).
+list_group_members(GroupName, Addr, Port, Dir) ->
+ mod_auth_server:list_group_members(Addr, Port, Dir,
+ GroupName, ?NOPASSWORD).
+
+update_password(Port, Dir, Old, New, New)->
+ update_password(undefined, Port, Dir, Old, New, New).
+
+update_password(Addr, Port, Dir, Old, New, New) when is_list(New) ->
+ mod_auth_server:update_password(Addr, Port, Dir, Old, New);
+
+update_password(_Addr, _Port, _Dir, _Old, _New, _New) ->
+ {error, badtype};
+update_password(_Addr, _Port, _Dir, _Old, _New, _New1) ->
+ {error, notqeual}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+do_auth(Info, Directory, DirectoryData, _AuthType) ->
%% Authenticate (require)
- ?hdrt("authenticate", [{auth_type, AuthType}]),
case require(Info, Directory, DirectoryData) of
authorized ->
- ?hdrt("authorized", []),
{proceed,Info#mod.data};
{authorized, User} ->
- ?hdrt("authorized", [{user, User}]),
{proceed, [{remote_user,User}|Info#mod.data]};
{authorization_required, Realm} ->
- ?hdrt("authorization required", [{realm, Realm}]),
ReasonPhrase = httpd_util:reason_phrase(401),
Message = httpd_util:message(401,none,Info#mod.config_db),
{proceed,
@@ -142,8 +416,6 @@ do_auth(Info, Directory, DirectoryData, AuthType) ->
Info#mod.data]}
end.
-%% require
-
require(Info, Directory, DirectoryData) ->
ParsedHeader = Info#mod.parsed_header,
ValidUsers = proplists:get_value(require_user, DirectoryData),
@@ -270,13 +542,6 @@ auth_mod_name(DirData) ->
dets -> mod_auth_dets
end.
-
-%%
-%% Is it a secret area?
-%%
-
-%% secretp
-
secretp(Path,ConfigDB) ->
Directories = ets:match(ConfigDB,{directory, {'$1','_'}}),
case secret_path(Path, Directories) of
@@ -296,23 +561,17 @@ secret_path(_Path, [], to_be_found) ->
secret_path(_Path, [], Directory) ->
{yes, Directory};
secret_path(Path, [[NewDirectory] | Rest], Directory) ->
- case inets_regexp:match(Path, NewDirectory) of
- {match, _, _} when Directory =:= to_be_found ->
+ case re:run(Path, NewDirectory, [{capture, first}]) of
+ {match, _} when Directory =:= to_be_found ->
secret_path(Path, Rest, NewDirectory);
- {match, _, Length} when Length > length(Directory)->
+ {match, [{_, Length}]} when Length > length(Directory)->
secret_path(Path, Rest,NewDirectory);
- {match, _, _Length} ->
+ {match, _} ->
secret_path(Path, Rest, Directory);
nomatch ->
secret_path(Path, Rest, Directory)
end.
-%%
-%% Authenticate
-%%
-
-%% allow
-
allow({_,RemoteAddr}, _SocketType, _Socket, DirectoryData) ->
Hosts = proplists:get_value(allow_from, DirectoryData, all),
case validate_addr(RemoteAddr, Hosts) of
@@ -329,15 +588,13 @@ validate_addr(_RemoteAddr, none) -> % When called from 'deny'
validate_addr(_RemoteAddr, []) ->
false;
validate_addr(RemoteAddr, [HostRegExp | Rest]) ->
- case inets_regexp:match(RemoteAddr, HostRegExp) of
- {match,_,_} ->
+ case re:run(RemoteAddr, HostRegExp, [{capture, none}]) of
+ match ->
true;
nomatch ->
validate_addr(RemoteAddr,Rest)
end.
-%% deny
-
deny({_,RemoteAddr}, _SocketType, _Socket,DirectoryData) ->
Hosts = proplists:get_value(deny_from, DirectoryData, none),
case validate_addr(RemoteAddr,Hosts) of
@@ -347,124 +604,6 @@ deny({_,RemoteAddr}, _SocketType, _Socket,DirectoryData) ->
not_denied
end.
-%%
-%% Configuration
-%%
-
-%% load/2
-%%
-
-%% mod_auth recognizes the following Configuration Directives:
-%% <Directory /path/to/directory>
-%% AuthDBType
-%% AuthName
-%% AuthUserFile
-%% AuthGroupFile
-%% AuthAccessPassword
-%% require
-%% allow
-%% </Directory>
-
-%% When a <Directory> directive is found, a new context is set to
-%% [{directory, Directory, DirData}|OtherContext]
-%% DirData in this case is a key-value list of data belonging to the
-%% directory in question.
-%%
-%% When the </Directory> statement is found, the Context created earlier
-%% will be returned as a ConfigList and the context will return to the
-%% state it was previously.
-
-load("<Directory " ++ Directory,[]) ->
- Dir = httpd_conf:custom_clean(Directory,"",">"),
- {ok,[{directory, {Dir, [{path, Dir}]}}]};
-load(eof,[{directory, {Directory, _DirData}}|_]) ->
- {error, ?NICE("Premature end-of-file in "++ Directory)};
-
-load("AuthName " ++ AuthName, [{directory, {Directory, DirData}}|Rest]) ->
- {ok, [{directory, {Directory,
- [{auth_name, httpd_conf:clean(AuthName)} | DirData]}}
- | Rest ]};
-load("AuthUserFile " ++ AuthUserFile0,
- [{directory, {Directory, DirData}}|Rest]) ->
- AuthUserFile = httpd_conf:clean(AuthUserFile0),
- {ok, [{directory, {Directory,
- [{auth_user_file, AuthUserFile}|DirData]}} | Rest ]};
-load("AuthGroupFile " ++ AuthGroupFile0,
- [{directory, {Directory, DirData}}|Rest]) ->
- AuthGroupFile = httpd_conf:clean(AuthGroupFile0),
- {ok,[{directory, {Directory,
- [{auth_group_file, AuthGroupFile}|DirData]}} | Rest]};
-
-%AuthAccessPassword
-load("AuthAccessPassword " ++ AuthAccessPassword0,
- [{directory, {Directory, DirData}}|Rest]) ->
- AuthAccessPassword = httpd_conf:clean(AuthAccessPassword0),
- {ok,[{directory, {Directory,
- [{auth_access_password, AuthAccessPassword}|DirData]}} | Rest]};
-
-load("AuthDBType " ++ Type,
- [{directory, {Dir, DirData}}|Rest]) ->
- case httpd_conf:clean(Type) of
- "plain" ->
- {ok, [{directory, {Dir, [{auth_type, plain}|DirData]}} | Rest ]};
- "mnesia" ->
- {ok, [{directory, {Dir, [{auth_type, mnesia}|DirData]}} | Rest ]};
- "dets" ->
- {ok, [{directory, {Dir, [{auth_type, dets}|DirData]}} | Rest ]};
- _ ->
- {error, ?NICE(httpd_conf:clean(Type)++" is an invalid AuthDBType")}
- end;
-
-load("require " ++ Require,[{directory, {Directory, DirData}}|Rest]) ->
- case inets_regexp:split(Require," ") of
- {ok,["user"|Users]} ->
- {ok,[{directory, {Directory,
- [{require_user,Users}|DirData]}} | Rest]};
- {ok,["group"|Groups]} ->
- {ok,[{directory, {Directory,
- [{require_group,Groups}|DirData]}} | Rest]};
- {ok,_} ->
- {error,?NICE(httpd_conf:clean(Require) ++" is an invalid require")}
- end;
-
-load("allow " ++ Allow,[{directory, {Directory, DirData}}|Rest]) ->
- case inets_regexp:split(Allow," ") of
- {ok,["from","all"]} ->
- {ok,[{directory, {Directory,
- [{allow_from,all}|DirData]}} | Rest]};
- {ok,["from"|Hosts]} ->
- {ok,[{directory, {Directory,
- [{allow_from,Hosts}|DirData]}} | Rest]};
- {ok,_} ->
- {error,?NICE(httpd_conf:clean(Allow) ++" is an invalid allow")}
- end;
-
-load("deny " ++ Deny,[{directory, {Directory, DirData}}|Rest]) ->
- case inets_regexp:split(Deny," ") of
- {ok, ["from", "all"]} ->
- {ok,[{{directory, Directory,
- [{deny_from, all}|DirData]}} | Rest]};
- {ok, ["from"|Hosts]} ->
- {ok,[{{directory, Directory,
- [{deny_from, Hosts}|DirData]}} | Rest]};
- {ok, _} ->
- {error,?NICE(httpd_conf:clean(Deny) ++" is an invalid deny")}
- end;
-
-load("</Directory>",[{directory, {Directory, DirData}}|Rest]) ->
- {ok, Rest, {directory, {Directory, DirData}}};
-
-load("AuthMnesiaDB " ++ AuthMnesiaDB,
- [{directory, {Dir, DirData}}|Rest]) ->
- case httpd_conf:clean(AuthMnesiaDB) of
- "On" ->
- {ok,[{directory, {Dir,[{auth_type,mnesia}|DirData]}}|Rest]};
- "Off" ->
- {ok,[{directory, {Dir,[{auth_type,plain}|DirData]}}|Rest]};
- _ ->
- {error, ?NICE(httpd_conf:clean(AuthMnesiaDB) ++
- " is an invalid AuthMnesiaDB")}
- end.
directory_config_check(Directory, DirData) ->
case proplists:get_value(auth_type, DirData) of
@@ -482,25 +621,7 @@ check_filename_present(Dir,AuthFile,DirData) ->
throw({missing_auth_file, AuthFile, {directory, {Dir, DirData}}})
end.
-%% store
-
-store({directory, {Directory, DirData}}, ConfigList)
- when is_list(Directory) andalso is_list(DirData) ->
- ?hdrt("store",
- [{directory, Directory}, {dir_data, DirData}]),
- try directory_config_check(Directory, DirData) of
- ok ->
- store_directory(Directory, DirData, ConfigList)
- catch
- throw:Error ->
- {error, Error, {directory, Directory, DirData}}
- end;
-store({directory, {Directory, DirData}}, _) ->
- {error, {wrong_type, {directory, {Directory, DirData}}}}.
-
store_directory(Directory0, DirData0, ConfigList) ->
- ?hdrt("store directory - entry",
- [{directory, Directory0}, {dir_data, DirData0}]),
Port = proplists:get_value(port, ConfigList),
DirData = case proplists:get_value(bind_address, ConfigList) of
undefined ->
@@ -522,9 +643,7 @@ store_directory(Directory0, DirData0, ConfigList) ->
dets -> mod_auth_dets;
plain -> mod_auth_plain;
_ -> no_module_at_all
- end,
- ?hdrt("store directory",
- [{directory, Directory}, {dir_data, DirData}, {auth_mod, AuthMod}]),
+ end,
case AuthMod of
no_module_at_all ->
{ok, {directory, {Directory, DirData}}};
@@ -560,204 +679,10 @@ store_directory(Directory0, DirData0, ConfigList) ->
add_auth_password(Dir, Pwd0, ConfigList) ->
Addr = proplists:get_value(bind_address, ConfigList),
Port = proplists:get_value(port, ConfigList),
- mod_auth_server:start(Addr, Port),
+ Profile = proplists:get_value(profile, ConfigList, ?DEFAULT_PROFILE),
+ mod_auth_server:start(Addr, Port, Profile),
mod_auth_server:add_password(Addr, Port, Dir, Pwd0).
-%% remove
-
-
-remove(ConfigDB) ->
- lists:foreach(fun({directory, {_Dir, DirData}}) ->
- AuthMod = auth_mod_name(DirData),
- (catch apply(AuthMod, remove, [DirData]))
- end,
- ets:match_object(ConfigDB,{directory,{'_','_'}})),
- Addr = case lookup(ConfigDB, bind_address) of
- [] ->
- undefined;
- [{bind_address, Address}] ->
- Address
- end,
- [{port, Port}] = lookup(ConfigDB, port),
- mod_auth_server:stop(Addr, Port),
- ok.
-
-%% --------------------------------------------------------------------
-
-%% update_password
-
-update_password(Port, Dir, Old, New, New)->
- update_password(undefined, Port, Dir, Old, New, New).
-
-update_password(Addr, Port, Dir, Old, New, New) when is_list(New) ->
- mod_auth_server:update_password(Addr, Port, Dir, Old, New);
-
-update_password(_Addr, _Port, _Dir, _Old, _New, _New) ->
- {error, badtype};
-update_password(_Addr, _Port, _Dir, _Old, _New, _New1) ->
- {error, notqeual}.
-
-
-%% add_user
-
-add_user(UserName, Opt) ->
- case get_options(Opt, mandatory) of
- {Addr, Port, Dir, AuthPwd}->
- case get_options(Opt, userData) of
- {error, Reason}->
- {error, Reason};
- {UserData, Password}->
- User = [#httpd_user{username = UserName,
- password = Password,
- user_data = UserData}],
- mod_auth_server:add_user(Addr, Port, Dir, User, AuthPwd)
- end
- end.
-
-
-add_user(UserName, Password, UserData, Port, Dir) ->
- add_user(UserName, Password, UserData, undefined, Port, Dir).
-add_user(UserName, Password, UserData, Addr, Port, Dir) ->
- User = [#httpd_user{username = UserName,
- password = Password,
- user_data = UserData}],
- mod_auth_server:add_user(Addr, Port, Dir, User, ?NOPASSWORD).
-
-
-%% get_user
-
-get_user(UserName, Opt) ->
- case get_options(Opt, mandatory) of
- {Addr, Port, Dir, AuthPwd} ->
- mod_auth_server:get_user(Addr, Port, Dir, UserName, AuthPwd);
- {error, Reason} ->
- {error, Reason}
- end.
-
-get_user(UserName, Port, Dir) ->
- get_user(UserName, undefined, Port, Dir).
-get_user(UserName, Addr, Port, Dir) ->
- mod_auth_server:get_user(Addr, Port, Dir, UserName, ?NOPASSWORD).
-
-
-%% add_group_member
-
-add_group_member(GroupName, UserName, Opt)->
- case get_options(Opt, mandatory) of
- {Addr, Port, Dir, AuthPwd}->
- mod_auth_server:add_group_member(Addr, Port, Dir,
- GroupName, UserName, AuthPwd);
- {error, Reason} ->
- {error, Reason}
- end.
-
-add_group_member(GroupName, UserName, Port, Dir) ->
- add_group_member(GroupName, UserName, undefined, Port, Dir).
-
-add_group_member(GroupName, UserName, Addr, Port, Dir) ->
- mod_auth_server:add_group_member(Addr, Port, Dir,
- GroupName, UserName, ?NOPASSWORD).
-
-
-%% delete_group_member
-
-delete_group_member(GroupName, UserName, Opt) ->
- case get_options(Opt, mandatory) of
- {Addr, Port, Dir, AuthPwd} ->
- mod_auth_server:delete_group_member(Addr, Port, Dir,
- GroupName, UserName, AuthPwd);
- {error, Reason} ->
- {error, Reason}
- end.
-
-delete_group_member(GroupName, UserName, Port, Dir) ->
- delete_group_member(GroupName, UserName, undefined, Port, Dir).
-delete_group_member(GroupName, UserName, Addr, Port, Dir) ->
- mod_auth_server:delete_group_member(Addr, Port, Dir,
- GroupName, UserName, ?NOPASSWORD).
-
-
-%% list_users
-
-list_users(Opt) ->
- case get_options(Opt, mandatory) of
- {Addr, Port, Dir, AuthPwd} ->
- mod_auth_server:list_users(Addr, Port, Dir, AuthPwd);
- {error, Reason} ->
- {error, Reason}
- end.
-
-list_users(Port, Dir) ->
- list_users(undefined, Port, Dir).
-list_users(Addr, Port, Dir) ->
- mod_auth_server:list_users(Addr, Port, Dir, ?NOPASSWORD).
-
-
-%% delete_user
-
-delete_user(UserName, Opt) ->
- case get_options(Opt, mandatory) of
- {Addr, Port, Dir, AuthPwd} ->
- mod_auth_server:delete_user(Addr, Port, Dir, UserName, AuthPwd);
- {error, Reason} ->
- {error, Reason}
- end.
-
-delete_user(UserName, Port, Dir) ->
- delete_user(UserName, undefined, Port, Dir).
-delete_user(UserName, Addr, Port, Dir) ->
- mod_auth_server:delete_user(Addr, Port, Dir, UserName, ?NOPASSWORD).
-
-
-%% delete_group
-
-delete_group(GroupName, Opt) ->
- case get_options(Opt, mandatory) of
- {Addr, Port, Dir, AuthPwd} ->
- mod_auth_server:delete_group(Addr, Port, Dir, GroupName, AuthPwd);
- {error, Reason} ->
- {error, Reason}
- end.
-
-delete_group(GroupName, Port, Dir) ->
- delete_group(GroupName, undefined, Port, Dir).
-delete_group(GroupName, Addr, Port, Dir) ->
- mod_auth_server:delete_group(Addr, Port, Dir, GroupName, ?NOPASSWORD).
-
-
-%% list_groups
-
-list_groups(Opt) ->
- case get_options(Opt, mandatory) of
- {Addr, Port, Dir, AuthPwd} ->
- mod_auth_server:list_groups(Addr, Port, Dir, AuthPwd);
- {error, Reason} ->
- {error, Reason}
- end.
-
-list_groups(Port, Dir) ->
- list_groups(undefined, Port, Dir).
-list_groups(Addr, Port, Dir) ->
- mod_auth_server:list_groups(Addr, Port, Dir, ?NOPASSWORD).
-
-
-%% list_group_members
-
-list_group_members(GroupName, Opt) ->
- case get_options(Opt, mandatory) of
- {Addr, Port, Dir, AuthPwd} ->
- mod_auth_server:list_group_members(Addr, Port, Dir, GroupName,
- AuthPwd);
- {error, Reason} ->
- {error, Reason}
- end.
-
-list_group_members(GroupName, Port, Dir) ->
- list_group_members(GroupName, undefined, Port, Dir).
-list_group_members(GroupName, Addr, Port, Dir) ->
- mod_auth_server:list_group_members(Addr, Port, Dir,
- GroupName, ?NOPASSWORD).
-
%% Opt = [{port, Port},
%% {addr, Addr},
%% {dir, Dir},
@@ -792,7 +717,3 @@ get_options(Opt, userData)->
{UserData, Pwd}
end
end.
-
-
-lookup(Db, Key) ->
- ets:lookup(Db, Key).
diff --git a/lib/inets/src/http_server/mod_auth.hrl b/lib/inets/src/http_server/mod_auth.hrl
index 674e6d1652..dd7794c78c 100644
--- a/lib/inets/src/http_server/mod_auth.hrl
+++ b/lib/inets/src/http_server/mod_auth.hrl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_server/mod_auth_dets.erl b/lib/inets/src/http_server/mod_auth_dets.erl
index a48725d5d9..9f95b2ca2d 100644
--- a/lib/inets/src/http_server/mod_auth_dets.erl
+++ b/lib/inets/src/http_server/mod_auth_dets.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -38,23 +39,23 @@
-include("httpd_internal.hrl").
-include("mod_auth.hrl").
-store_directory_data(_Directory, DirData, Server_root) ->
- ?CDEBUG("store_directory_data -> ~n"
- " Directory: ~p~n"
- " DirData: ~p",
- [_Directory, DirData]),
+%%====================================================================
+%% Internal application API
+%%====================================================================
+store_directory_data(_Directory, DirData, Server_root) ->
{PWFile, Absolute_pwdfile} = absolute_file_name(auth_user_file, DirData,
Server_root),
{GroupFile, Absolute_groupfile} = absolute_file_name(auth_group_file,
DirData, Server_root),
Addr = proplists:get_value(bind_address, DirData),
Port = proplists:get_value(port, DirData),
+ Profile = proplists:get_value(profile, DirData, ?DEFAULT_PROFILE),
- PWName = httpd_util:make_name("httpd_dets_pwdb",Addr,Port),
+ PWName = httpd_util:make_name("httpd_dets_pwdb", Addr, Port, Profile),
case dets:open_file(PWName,[{type,set},{file,Absolute_pwdfile},{repair,true}]) of
{ok, PWDB} ->
- GDBName = httpd_util:make_name("httpd_dets_groupdb",Addr,Port),
+ GDBName = httpd_util:make_name("httpd_dets_groupdb", Addr, Port, Profile),
case dets:open_file(GDBName,[{type,set},{file,Absolute_groupfile},{repair,true}]) of
{ok, GDB} ->
NDD1 = lists:keyreplace(auth_user_file, 1, DirData,
@@ -69,11 +70,8 @@ store_directory_data(_Directory, DirData, Server_root) ->
{error, {{file, PWFile},Err2}}
end.
-%%
%% Storage format of users in the dets table:
%% {{UserName, Addr, Port, Dir}, Password, UserData}
-%%
-
add_user(DirData, UStruct) ->
{Addr, Port, Dir} = lookup_common(DirData),
PWDB = proplists:get_value(auth_user_file, DirData),
@@ -99,21 +97,15 @@ get_user(DirData, UserName) ->
end.
list_users(DirData) ->
- ?DEBUG("list_users -> ~n"
- " DirData: ~p", [DirData]),
{Addr, Port, Dir} = lookup_common(DirData),
PWDB = proplists:get_value(auth_user_file, DirData),
- case dets:traverse(PWDB, fun(X) -> {continue, X} end) of %% SOOOO Ugly !
+ case dets:traverse(PWDB, fun(X) -> {continue, X} end) of
Records when is_list(Records) ->
- ?DEBUG("list_users -> ~n"
- " Records: ~p", [Records]),
{ok, [UserName || {{UserName, AnyAddr, AnyPort, AnyDir},
_Password, _Data} <- Records,
AnyAddr == Addr, AnyPort == Port,
AnyDir == Dir]};
_O ->
- ?DEBUG("list_users -> ~n"
- " O: ~p", [_O]),
{ok, []}
end.
@@ -134,10 +126,8 @@ delete_user(DirData, UserName) ->
{error, no_such_user}
end.
-%%
%% Storage of groups in the dets table:
%% {Group, UserList} where UserList is a list of strings.
-%%
add_group_member(DirData, GroupName, UserName) ->
{Addr, Port, Dir} = lookup_common(DirData),
GDB = proplists:get_value(auth_group_file, DirData),
@@ -215,16 +205,7 @@ delete_group(DirData, GroupName) ->
{error, no_such_group}
end.
-lookup_common(DirData) ->
- Dir = proplists:get_value(path, DirData),
- Port = proplists:get_value(port, DirData),
- Addr = proplists:get_value(bind_address, DirData),
- {Addr, Port, Dir}.
-
-%% remove/1
-%%
%% Closes dets tables used by this auth mod.
-%%
remove(DirData) ->
PWDB = proplists:get_value(auth_user_file, DirData),
GDB = proplists:get_value(auth_group_file, DirData),
@@ -232,8 +213,9 @@ remove(DirData) ->
dets:close(PWDB),
ok.
-%% absolute_file_name/2
-%%
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
%% Return the absolute path name of File_type.
absolute_file_name(File_type, DirData, Server_root) ->
Path = proplists:get_value(File_type, DirData),
@@ -253,3 +235,8 @@ absolute_file_name(File_type, DirData, Server_root) ->
end,
{Path, Absolute_path}.
+lookup_common(DirData) ->
+ Dir = proplists:get_value(path, DirData),
+ Port = proplists:get_value(port, DirData),
+ Addr = proplists:get_value(bind_address, DirData),
+ {Addr, Port, Dir}.
diff --git a/lib/inets/src/http_server/mod_auth_mnesia.erl b/lib/inets/src/http_server/mod_auth_mnesia.erl
index 91beb0e062..62acc554d4 100644
--- a/lib/inets/src/http_server/mod_auth_mnesia.erl
+++ b/lib/inets/src/http_server/mod_auth_mnesia.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_server/mod_auth_plain.erl b/lib/inets/src/http_server/mod_auth_plain.erl
index c0a83711ba..f2b0dfd71f 100644
--- a/lib/inets/src/http_server/mod_auth_plain.erl
+++ b/lib/inets/src/http_server/mod_auth_plain.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -22,15 +23,11 @@
-include("httpd.hrl").
-include("mod_auth.hrl").
-include("httpd_internal.hrl").
--include("inets_internal.hrl").
-
-define(VMODULE,"AUTH_PLAIN").
%% Internal API
-export([store_directory_data/3]).
-
-
-export([get_user/2,
list_group_members/2,
add_user/2,
@@ -42,17 +39,13 @@
delete_group/2,
remove/1]).
-%%
-%% API
-%%
+%%====================================================================
+%% Internal application API
+%%====================================================================
-%%
%% Storage format of users in the ets table:
%% {UserName, Password, UserData}
-%%
-
add_user(DirData, #httpd_user{username = User} = UStruct) ->
- ?hdrt("add user", [{user, UStruct}]),
PWDB = proplists:get_value(auth_user_file, DirData),
Record = {User,
UStruct#httpd_user.password,
@@ -66,7 +59,6 @@ add_user(DirData, #httpd_user{username = User} = UStruct) ->
end.
get_user(DirData, User) ->
- ?hdrt("get user", [{dir_data, DirData}, {user, User}]),
PWDB = proplists:get_value(auth_user_file, DirData),
case ets:lookup(PWDB, User) of
[{User, PassWd, Data}] ->
@@ -84,7 +76,6 @@ list_users(DirData) ->
[], lists:flatten(Records))}.
delete_user(DirData, UserName) ->
- ?hdrt("delete user", [{dir_data, DirData}, {user, UserName}]),
PWDB = proplists:get_value(auth_user_file, DirData),
case ets:lookup(PWDB, UserName) of
[{UserName, _SomePassword, _SomeData}] ->
@@ -98,11 +89,8 @@ delete_user(DirData, UserName) ->
{error, no_such_user}
end.
-%%
%% Storage of groups in the ets table:
%% {Group, UserList} where UserList is a list of strings.
-%%
-
add_group_member(DirData, Group, UserName) ->
GDB = proplists:get_value(auth_group_file, DirData),
case ets:lookup(GDB, Group) of
@@ -163,17 +151,12 @@ delete_group(DirData, Group) ->
end.
store_directory_data(_Directory, DirData, Server_root) ->
- ?hdrt("store directory data",
- [{dir_data, DirData}, {server_root, Server_root}]),
PWFile = absolute_file_name(auth_user_file, DirData, Server_root),
GroupFile = absolute_file_name(auth_group_file, DirData, Server_root),
case load_passwd(PWFile) of
{ok, PWDB} ->
- ?hdrt("password file loaded", [{file, PWFile}, {pwdb, PWDB}]),
case load_group(GroupFile) of
{ok, GRDB} ->
- ?hdrt("group file loaded",
- [{file, GroupFile}, {grdb, GRDB}]),
%% Address and port is included in the file names...
Addr = proplists:get_value(bind_address, DirData),
Port = proplists:get_value(port, DirData),
@@ -191,9 +174,83 @@ store_directory_data(_Directory, DirData, Server_root) ->
{error, Err2}
end.
+%% Deletes ets tables used by this auth mod.
+remove(DirData) ->
+ PWDB = proplists:get_value(auth_user_file, DirData),
+ GDB = proplists:get_value(auth_group_file, DirData),
+ ets:delete(PWDB),
+ ets:delete(GDB).
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+%% Return the absolute path name of File_type.
+absolute_file_name(File_type, DirData, Server_root) ->
+ Path = proplists:get_value(File_type, DirData),
+ case filename:pathtype(Path) of
+ relative ->
+ case Server_root of
+ undefined ->
+ {error,
+ ?NICE(Path++
+ " is an invalid file name because "
+ "ServerRoot is not defined")};
+ _ ->
+ filename:join(Server_root,Path)
+ end;
+ _ ->
+ Path
+ end.
+
+store_group(Addr,Port,GroupList) ->
+ %% Not a named table so not importante to add Profile to name
+ Name = httpd_util:make_name("httpd_group",Addr,Port),
+ GroupDB = ets:new(Name, [set, public]),
+ store_group(GroupDB, GroupList).
+
+store_group(GroupDB,[]) ->
+ {ok, GroupDB};
+store_group(GroupDB, [User|Rest]) ->
+ ets:insert(GroupDB, User),
+ store_group(GroupDB, Rest).
+
+store_passwd(Addr,Port,PasswdList) ->
+ %% Not a named table so not importante to add Profile to name
+ Name = httpd_util:make_name("httpd_passwd",Addr,Port),
+ PasswdDB = ets:new(Name, [set, public]),
+ store_passwd(PasswdDB, PasswdList).
+
+store_passwd(PasswdDB, []) ->
+ {ok, PasswdDB};
+store_passwd(PasswdDB, [User|Rest]) ->
+ ets:insert(PasswdDB, User),
+ store_passwd(PasswdDB, Rest).
+parse_group(Stream, GroupList) ->
+ Line =
+ case io:get_line(Stream,'') of
+ eof ->
+ eof;
+ String ->
+ httpd_conf:white_space_clean(String)
+ end,
+ parse_group(Stream, GroupList, Line).
-%% load_passwd
+parse_group(Stream, GroupList, eof) ->
+ file:close(Stream),
+ {ok, GroupList};
+parse_group(Stream, GroupList, "") ->
+ parse_group(Stream, GroupList);
+parse_group(Stream, GroupList, [$#|_]) ->
+ parse_group(Stream, GroupList);
+parse_group(Stream, GroupList, Line) ->
+ case re:split(Line, ":", [{return, list}]) of
+ [Group,Users] ->
+ UserList = re:split(Users," ", [{return, list}]),
+ parse_group(Stream, [{Group,UserList}|GroupList]);
+ _ ->
+ {error, ?NICE(Line)}
+ end.
load_passwd(AuthUserFile) ->
case file:open(AuthUserFile, [read]) of
@@ -209,7 +266,7 @@ parse_passwd(Stream, PasswdList) ->
eof ->
eof;
String ->
- httpd_conf:clean(String)
+ httpd_conf:white_space_clean(String)
end,
parse_passwd(Stream, PasswdList, Line).
@@ -221,15 +278,13 @@ parse_passwd(Stream, PasswdList, "") ->
parse_passwd(Stream, PasswdList, [$#|_]) ->
parse_passwd(Stream, PasswdList);
parse_passwd(Stream, PasswdList, Line) ->
- case inets_regexp:split(Line,":") of
- {ok, [User,Password]} ->
+ case re:split(Line,":", [{return, list}]) of
+ [User,Password] ->
parse_passwd(Stream, [{User,Password, []}|PasswdList]);
- {ok,_} ->
+ _ ->
{error, ?NICE(Line)}
end.
-%% load_group
-
load_group(AuthGroupFile) ->
case file:open(AuthGroupFile, [read]) of
{ok, Stream} ->
@@ -237,91 +292,3 @@ load_group(AuthGroupFile) ->
{error, _} ->
{error, ?NICE("Can't open " ++ AuthGroupFile)}
end.
-
-parse_group(Stream, GroupList) ->
- Line =
- case io:get_line(Stream,'') of
- eof ->
- eof;
- String ->
- httpd_conf:clean(String)
- end,
- parse_group(Stream, GroupList, Line).
-
-parse_group(Stream, GroupList, eof) ->
- file:close(Stream),
- {ok, GroupList};
-parse_group(Stream, GroupList, "") ->
- parse_group(Stream, GroupList);
-parse_group(Stream, GroupList, [$#|_]) ->
- parse_group(Stream, GroupList);
-parse_group(Stream, GroupList, Line) ->
- case inets_regexp:split(Line, ":") of
- {ok, [Group,Users]} ->
- {ok, UserList} = inets_regexp:split(Users," "),
- parse_group(Stream, [{Group,UserList}|GroupList]);
- {ok, _} ->
- {error, ?NICE(Line)}
- end.
-
-
-%% store_passwd
-
-store_passwd(Addr,Port,PasswdList) ->
- Name = httpd_util:make_name("httpd_passwd",Addr,Port),
- PasswdDB = ets:new(Name, [set, public]),
- store_passwd(PasswdDB, PasswdList).
-
-store_passwd(PasswdDB, []) ->
- {ok, PasswdDB};
-store_passwd(PasswdDB, [User|Rest]) ->
- ets:insert(PasswdDB, User),
- store_passwd(PasswdDB, Rest).
-
-%% store_group
-
-store_group(Addr,Port,GroupList) ->
- Name = httpd_util:make_name("httpd_group",Addr,Port),
- GroupDB = ets:new(Name, [set, public]),
- store_group(GroupDB, GroupList).
-
-
-store_group(GroupDB,[]) ->
- {ok, GroupDB};
-store_group(GroupDB, [User|Rest]) ->
- ets:insert(GroupDB, User),
- store_group(GroupDB, Rest).
-
-
-%% remove/1
-%%
-%% Deletes ets tables used by this auth mod.
-%%
-remove(DirData) ->
- PWDB = proplists:get_value(auth_user_file, DirData),
- GDB = proplists:get_value(auth_group_file, DirData),
- ets:delete(PWDB),
- ets:delete(GDB).
-
-
-
-%% absolute_file_name/2
-%%
-%% Return the absolute path name of File_type.
-absolute_file_name(File_type, DirData, Server_root) ->
- Path = proplists:get_value(File_type, DirData),
- case filename:pathtype(Path) of
- relative ->
- case Server_root of
- undefined ->
- {error,
- ?NICE(Path++
- " is an invalid file name because "
- "ServerRoot is not defined")};
- _ ->
- filename:join(Server_root,Path)
- end;
- _ ->
- Path
- end.
-
diff --git a/lib/inets/src/http_server/mod_auth_server.erl b/lib/inets/src/http_server/mod_auth_server.erl
index 947273bd9e..93d8145821 100644
--- a/lib/inets/src/http_server/mod_auth_server.erl
+++ b/lib/inets/src/http_server/mod_auth_server.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -22,246 +23,184 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
--include("inets_internal.hrl").
-behaviour(gen_server).
-
%% mod_auth exports
--export([start/2, stop/2,
+-export([start/3, stop/3,
add_password/4, update_password/5,
add_user/5, delete_user/5, get_user/5, list_users/4,
add_group_member/6, delete_group_member/6, list_group_members/5,
delete_group/5, list_groups/4]).
%% gen_server exports
--export([start_link/2, init/1,
+-export([start_link/3, init/1,
handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-record(state, {tab}).
+%%====================================================================
+%% Internal application API
+%%====================================================================
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% External API %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% start_link/3
-%%
%% NOTE: This is called by httpd_misc_sup when the process is started
%%
-start_link(Addr, Port) ->
- ?hdrt("start_link", [{address, Addr}, {port, Port}]),
- Name = make_name(Addr, Port),
+start_link(Addr, Port, Profile) ->
+ Name = make_name(Addr, Port, Profile),
gen_server:start_link({local, Name}, ?MODULE, [], [{timeout, infinity}]).
-
-%% start/2
-
-start(Addr, Port) ->
- ?hdrd("start", [{address, Addr}, {port, Port}]),
- Name = make_name(Addr, Port),
+start(Addr, Port, Profile) ->
+ Name = make_name(Addr, Port, Profile),
case whereis(Name) of
undefined ->
- httpd_misc_sup:start_auth_server(Addr, Port);
+ httpd_misc_sup:start_auth_server(Addr, Port, Profile);
_ -> %% Already started...
ok
end.
-
-%% stop/2
-
-stop(Addr, Port) ->
- ?hdrd("stop", [{address, Addr}, {port, Port}]),
- Name = make_name(Addr, Port),
+stop(Addr, Port, Profile) ->
+ Name = make_name(Addr, Port, Profile),
case whereis(Name) of
undefined -> %% Already stopped
ok;
_ ->
- (catch httpd_misc_sup:stop_auth_server(Addr, Port))
+ (catch httpd_misc_sup:stop_auth_server(Addr, Port, Profile))
end.
-%% add_password/4
-
add_password(Addr, Port, Dir, Password) ->
- ?hdrt("add password", [{address, Addr}, {port, Port}]),
- Name = make_name(Addr, Port),
+ add_password(Addr, Port, ?DEFAULT_PROFILE, Dir, Password).
+add_password(Addr, Port, Profile, Dir, Password) ->
+ Name = make_name(Addr, Port, Profile),
Req = {add_password, Dir, Password},
call(Name, Req).
-
-%% update_password/6
-
-update_password(Addr, Port, Dir, Old, New) when is_list(New) ->
- ?hdrt("update password",
- [{address, Addr}, {port, Port}, {dir, Dir}, {old, Old}, {new, New}]),
- Name = make_name(Addr, Port),
+update_password(Addr, Port, Dir, Old, New) ->
+ update_password(Addr, Port, ?DEFAULT_PROFILE, Dir, Old, New).
+update_password(Addr, Port, Profile, Dir, Old, New) when is_list(New) ->
+ Name = make_name(Addr, Port, Profile),
Req = {update_password, Dir, Old, New},
call(Name, Req).
-
-
-%% add_user/5
add_user(Addr, Port, Dir, User, Password) ->
- ?hdrt("add user",
- [{address, Addr}, {port, Port},
- {dir, Dir}, {user, User}, {passwd, Password}]),
- Name = make_name(Addr, Port),
- Req = {add_user, Addr, Port, Dir, User, Password},
+ add_user(Addr, Port, ?DEFAULT_PROFILE, Dir, User, Password).
+add_user(Addr, Port, Profile, Dir, User, Password) ->
+ Name = make_name(Addr, Port, Profile),
+ Req = {add_user, Addr, Port, Profile, Dir, User, Password},
call(Name, Req).
-
-%% delete_user/5
-
delete_user(Addr, Port, Dir, UserName, Password) ->
- ?hdrt("delete user",
- [{address, Addr}, {port, Port},
- {dir, Dir}, {user, UserName}, {passwd, Password}]),
- Name = make_name(Addr, Port),
- Req = {delete_user, Addr, Port, Dir, UserName, Password},
+ delete_user(Addr, Port, ?DEFAULT_PROFILE, Dir, UserName, Password).
+delete_user(Addr, Port, Profile, Dir, UserName, Password) ->
+ Name = make_name(Addr, Port, Profile),
+ Req = {delete_user, Addr, Port, Profile, Dir, UserName, Password},
call(Name, Req).
-
-%% get_user/5
-
get_user(Addr, Port, Dir, UserName, Password) ->
- ?hdrt("get user",
- [{address, Addr}, {port, Port},
- {dir, Dir}, {user, UserName}, {passwd, Password}]),
- Name = make_name(Addr, Port),
- Req = {get_user, Addr, Port, Dir, UserName, Password},
+ get_user(Addr, Port, ?DEFAULT_PROFILE, Dir, UserName, Password).
+get_user(Addr, Port, Profile,Dir, UserName, Password) ->
+ Name = make_name(Addr, Port, Profile),
+ Req = {get_user, Addr, Port, Profile, Dir, UserName, Password},
call(Name, Req).
-
-%% list_users/4
-
list_users(Addr, Port, Dir, Password) ->
- ?hdrt("list users",
- [{address, Addr}, {port, Port}, {dir, Dir}, {passwd, Password}]),
- Name = make_name(Addr,Port),
- Req = {list_users, Addr, Port, Dir, Password},
+ list_users(Addr, Port, ?DEFAULT_PROFILE, Dir, Password).
+list_users(Addr, Port, Profile, Dir, Password) ->
+ Name = make_name(Addr,Port, Profile),
+ Req = {list_users, Addr, Port, Profile, Dir, Password},
call(Name, Req).
-
-%% add_group_member/6
-
add_group_member(Addr, Port, Dir, GroupName, UserName, Password) ->
- ?hdrt("add group member",
- [{address, Addr}, {port, Port}, {dir, Dir},
- {group, GroupName}, {user, UserName}, {passwd, Password}]),
- Name = make_name(Addr,Port),
- Req = {add_group_member, Addr, Port, Dir, GroupName, UserName, Password},
+ add_group_member(Addr, Port, ?DEFAULT_PROFILE, Dir, GroupName, UserName, Password).
+add_group_member(Addr, Port, Profile, Dir, GroupName, UserName, Password) ->
+ Name = make_name(Addr,Port, Profile),
+ Req = {add_group_member, Addr, Port, Profile, Dir, GroupName, UserName, Password},
call(Name, Req).
-
-%% delete_group_member/6
-
delete_group_member(Addr, Port, Dir, GroupName, UserName, Password) ->
- ?hdrt("delete group member",
- [{address, Addr}, {port, Port}, {dir, Dir},
- {group, GroupName}, {user, UserName}, {passwd, Password}]),
- Name = make_name(Addr,Port),
- Req = {delete_group_member, Addr, Port, Dir, GroupName, UserName, Password},
+ delete_group_member(Addr, Port, ?DEFAULT_PROFILE, Dir, GroupName, UserName, Password).
+delete_group_member(Addr, Port, Profile, Dir, GroupName, UserName, Password) ->
+ Name = make_name(Addr,Port,Profile),
+ Req = {delete_group_member, Addr, Port, Profile, Dir, GroupName, UserName, Password},
call(Name, Req).
-
-%% list_group_members/4
-
list_group_members(Addr, Port, Dir, Group, Password) ->
- ?hdrt("list group members",
- [{address, Addr}, {port, Port}, {dir, Dir},
- {group, Group}, {passwd, Password}]),
- Name = make_name(Addr, Port),
+ list_group_members(Addr, Port, ?DEFAULT_PROFILE, Dir, Group, Password).
+list_group_members(Addr, Port, Profile, Dir, Group, Password) ->
+ Name = make_name(Addr, Port, Profile),
Req = {list_group_members, Addr, Port, Dir, Group, Password},
call(Name, Req).
-
-%% delete_group/5
-
delete_group(Addr, Port, Dir, GroupName, Password) ->
- ?hdrt("delete group",
- [{address, Addr}, {port, Port}, {dir, Dir},
- {group, GroupName}, {passwd, Password}]),
- Name = make_name(Addr, Port),
- Req = {delete_group, Addr, Port, Dir, GroupName, Password},
+ delete_group(Addr, Port, ?DEFAULT_PROFILE, Dir, GroupName, Password).
+delete_group(Addr, Port, Profile, Dir, GroupName, Password) ->
+ Name = make_name(Addr, Port, Profile),
+ Req = {delete_group, Addr, Port, Profile, Dir, GroupName, Password},
call(Name, Req).
-
-%% list_groups/4
-
list_groups(Addr, Port, Dir, Password) ->
- ?hdrt("list groups",
- [{address, Addr}, {port, Port}, {dir, Dir}, {passwd, Password}]),
- Name = make_name(Addr, Port),
- Req = {list_groups, Addr, Port, Dir, Password},
+ list_groups(Addr, Port, ?DEFAULT_PROFILE, Dir, Password).
+list_groups(Addr, Port, Profile, Dir, Password) ->
+ Name = make_name(Addr, Port, Profile),
+ Req = {list_groups, Addr, Port,Profile, Dir, Password},
call(Name, Req).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% Server call-back functions %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% init
-
+%%====================================================================
+%% Behavior call backs
+%%====================================================================
init(_) ->
- ?hdrv("initiating", []),
{ok,#state{tab = ets:new(auth_pwd,[set,protected])}}.
%% handle_call
%% Add a user
-handle_call({add_user, Addr, Port, Dir, User, AuthPwd}, _From, State) ->
- Reply = api_call(Addr, Port, Dir, add_user, User, AuthPwd, State),
- ?hdrt("add user", [{reply, Reply}]),
+handle_call({add_user, Addr, Port, Profile, Dir, User, AuthPwd}, _From, State) ->
+ Reply = api_call(Addr, Port, Profile, Dir, add_user, User, AuthPwd, State),
{reply, Reply, State};
%% Get data about a user
-handle_call({get_user, Addr, Port, Dir, User, AuthPwd}, _From, State) ->
- Reply = api_call(Addr, Port, Dir, get_user, [User], AuthPwd, State),
+handle_call({get_user, Addr, Port, Profile, Dir, User, AuthPwd}, _From, State) ->
+ Reply = api_call(Addr, Port, Profile, Dir, get_user, [User], AuthPwd, State),
{reply, Reply, State};
%% Add a group member
-handle_call({add_group_member, Addr, Port, Dir, Group, User, AuthPwd},
+handle_call({add_group_member, Addr, Port, Profile, Dir, Group, User, AuthPwd},
_From, State) ->
- Reply = api_call(Addr, Port, Dir, add_group_member, [Group, User],
+ Reply = api_call(Addr, Port, Profile, Dir, add_group_member, [Group, User],
AuthPwd, State),
{reply, Reply, State};
%% delete a group
-handle_call({delete_group_member, Addr, Port, Dir, Group, User, AuthPwd},
+handle_call({delete_group_member, Addr, Port, Profile, Dir, Group, User, AuthPwd},
_From, State) ->
- Reply = api_call(Addr, Port, Dir, delete_group_member, [Group, User],
+ Reply = api_call(Addr, Port, Profile, Dir, delete_group_member, [Group, User],
AuthPwd, State),
{reply, Reply, State};
%% List all users thats standalone users
-handle_call({list_users, Addr, Port, Dir, AuthPwd}, _From, State) ->
- Reply = api_call(Addr, Port, Dir, list_users, [], AuthPwd, State),
+handle_call({list_users, Addr, Port, Profile, Dir, AuthPwd}, _From, State) ->
+ Reply = api_call(Addr, Port, Profile, Dir, list_users, [], AuthPwd, State),
{reply, Reply, State};
%% Delete a user
-handle_call({delete_user, Addr, Port, Dir, User, AuthPwd}, _From, State) ->
- Reply = api_call(Addr, Port, Dir, delete_user, [User], AuthPwd, State),
+handle_call({delete_user, Addr, Port, Profile, Dir, User, AuthPwd}, _From, State) ->
+ Reply = api_call(Addr, Port, Profile, Dir, delete_user, [User], AuthPwd, State),
{reply, Reply, State};
%% Delete a group
-handle_call({delete_group, Addr, Port, Dir, Group, AuthPwd}, _From, State) ->
- Reply = api_call(Addr, Port, Dir, delete_group, [Group], AuthPwd, State),
+handle_call({delete_group, Addr, Port, Profile, Dir, Group, AuthPwd}, _From, State) ->
+ Reply = api_call(Addr, Port, Profile, Dir, delete_group, [Group], AuthPwd, State),
{reply, Reply, State};
%% List the current groups
-handle_call({list_groups, Addr, Port, Dir, AuthPwd}, _From, State) ->
- Reply = api_call(Addr, Port, Dir, list_groups, [], AuthPwd, State),
+handle_call({list_groups, Addr, Port, Profile, Dir, AuthPwd}, _From, State) ->
+ Reply = api_call(Addr, Port, Profile, Dir, list_groups, [], AuthPwd, State),
{reply, Reply, State};
%% List the members of the given group
-handle_call({list_group_members, Addr, Port, Dir, Group, AuthPwd},
+handle_call({list_group_members, Addr, Port, Profile, Dir, Group, AuthPwd},
_From, State) ->
- Reply = api_call(Addr, Port, Dir, list_group_members, [Group],
+ Reply = api_call(Addr, Port, Profile, Dir, list_group_members, [Group],
AuthPwd, State),
{reply, Reply, State};
@@ -306,26 +245,16 @@ terminate(_Reason,State) ->
ets:delete(State#state.tab),
ok.
-
-%% code_change(Vsn, State, Extra)
-%%
code_change(_Vsn, State, _Extra) ->
{ok, State}.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% The functions that really changes the data in the database %%
-%% of users to different directories %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% API gateway
-
-api_call(Addr, Port, Dir, Func, Args,Password,State) ->
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+api_call(Addr, Port, Profile, Dir, Func, Args,Password,State) ->
case controlPassword(Password, State, Dir) of
ok->
- ConfigName = httpd_util:make_name("httpd_conf", Addr, Port),
+ ConfigName = httpd_util:make_name("httpd_conf", Addr, Port, Profile),
case ets:match_object(ConfigName, {directory, {Dir, '$1'}}) of
[{directory, {Dir, DirData}}] ->
AuthMod = auth_mod_name(DirData),
@@ -386,8 +315,8 @@ lookup(Db, Key) ->
ets:lookup(Db, Key).
-make_name(Addr,Port) ->
- httpd_util:make_name("httpd_auth",Addr,Port).
+make_name(Addr, Port, Profile) ->
+ httpd_util:make_name(?MODULE_STRING, Addr, Port, Profile).
call(Name, Req) ->
@@ -397,5 +326,3 @@ call(Name, Req) ->
Reply ->
Reply
end.
-
-
diff --git a/lib/inets/src/http_server/mod_browser.erl b/lib/inets/src/http_server/mod_browser.erl
index 1c9b33dffa..1e8f860746 100644
--- a/lib/inets/src/http_server/mod_browser.erl
+++ b/lib/inets/src/http_server/mod_browser.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -97,9 +98,9 @@ getBrowser1(Info) ->
getBrowser(AgentString) ->
LAgentString = http_util:to_lower(AgentString),
- case inets_regexp:first_match(LAgentString,"^[^ ]*") of
- {match,Start,Length} ->
- Browser = lists:sublist(LAgentString,Start,Length),
+ case re:run(LAgentString,"^[^ ]*", [{capture, first}]) of
+ {match,[{Start,Length}]} ->
+ Browser = lists:sublist(LAgentString,Start+1,Length),
case browserType(Browser) of
{mozilla,Vsn} ->
{getMozilla(LAgentString,
@@ -163,8 +164,8 @@ operativeSystem(OpString,[{RetVal,RegExps}|Rest]) ->
controlOperativeSystem(_OpString,[]) ->
false;
controlOperativeSystem(OpString,[Regexp|Regexps]) ->
- case inets_regexp:match(OpString,Regexp) of
- {match,_,_} ->
+ case re:run(OpString,Regexp, [{capture, none}]) of
+ match ->
true;
nomatch ->
controlOperativeSystem(OpString,Regexps)
@@ -181,18 +182,19 @@ controlOperativeSystem(OpString,[Regexp|Regexps]) ->
getMozilla(_AgentString,[],Default) ->
Default;
getMozilla(AgentString,[{Agent,AgentRegExp}|Rest],Default) ->
- case inets_regexp:match(AgentString,AgentRegExp) of
- {match,_,_} ->
+ case re:run(AgentString,AgentRegExp, [{capture, none}]) of
+ match ->
{Agent,getMozVersion(AgentString,AgentRegExp)};
nomatch ->
getMozilla(AgentString,Rest,Default)
end.
getMozVersion(AgentString, AgentRegExp) ->
- case inets_regexp:match(AgentString,AgentRegExp++"[0-9\.\ \/]*") of
- {match,Start,Length} when length(AgentRegExp) < Length ->
+ case re:run(AgentString,AgentRegExp++"[0-9\.\ \/]*",
+ [{capture, first}]) of
+ {match, [{Start,Length}]} when length(AgentRegExp) < Length ->
%% Ok we got the number split it out
- RealStart = Start+length(AgentRegExp),
+ RealStart = Start+1+length(AgentRegExp),
RealLength = Length-length(AgentRegExp),
VsnString = string:substr(AgentString,RealStart,RealLength),
%% case string:strip(VsnString,both,$\ ) of
diff --git a/lib/inets/src/http_server/mod_cgi.erl b/lib/inets/src/http_server/mod_cgi.erl
index c854166c29..1454332895 100644
--- a/lib/inets/src/http_server/mod_cgi.erl
+++ b/lib/inets/src/http_server/mod_cgi.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -95,24 +96,24 @@ do(ModData) ->
%% or cache
%%
load("ScriptNoCache " ++ CacheArg, [])->
- case catch list_to_atom(httpd_conf:clean(CacheArg)) of
+ case catch list_to_atom(string:strip(CacheArg)) of
true ->
{ok, [], {script_nocache, true}};
false ->
{ok, [], {script_nocache, false}};
_ ->
- {error, ?NICE(httpd_conf:clean(CacheArg)++
+ {error, ?NICE(string:strip(CacheArg)++
" is an invalid ScriptNoCache directive")}
end;
%% ScriptTimeout Seconds, The number of seconds that the server
%% maximum will wait for the script to
%% generate a part of the document
load("ScriptTimeout " ++ Timeout, [])->
- case catch list_to_integer(httpd_conf:clean(Timeout)) of
+ case catch list_to_integer(string:strip(Timeout)) of
TimeoutSec when is_integer(TimeoutSec) ->
{ok, [], {script_timeout,TimeoutSec*1000}};
_ ->
- {error, ?NICE(httpd_conf:clean(Timeout)++
+ {error, ?NICE(string:strip(Timeout)++
" is an invalid ScriptTimeout")}
end.
@@ -131,9 +132,9 @@ store({script_nocache, Value} = Conf, _)
{ok, Conf};
store({script_nocache, Value}, _) ->
{error, {wrong_type, {script_nocache, Value}}};
-store({script_timeout, Value} = Conf, _)
+store({script_timeout, Value}, _)
when is_integer(Value), Value >= 0 ->
- {ok, Conf};
+ {ok, {script_timeout, Value * 1000}};
store({script_timeout, Value}, _) ->
{error, {wrong_type, {script_timeout, Value}}}.
@@ -238,7 +239,7 @@ send_request_body_to_script(ModData, Port) ->
end.
deliver_webpage(#mod{config_db = Db} = ModData, Port) ->
- Timeout = cgi_timeout(Db),
+ Timeout = script_timeout(Db),
case receive_headers(Port, httpd_cgi, parse_headers,
[<<>>, [], []], Timeout) of
{Headers, Body} ->
@@ -295,7 +296,7 @@ receive_headers(Port, Module, Function, Args, Timeout) ->
end.
send_headers(ModData, {StatusCode, _}, HTTPHeaders) ->
- ExtraHeaders = httpd_response:cache_headers(ModData),
+ ExtraHeaders = httpd_response:cache_headers(ModData, script_nocache),
httpd_response:send_header(ModData, StatusCode,
ExtraHeaders ++ HTTPHeaders).
@@ -336,13 +337,15 @@ script_elements(#mod{method = "GET"}, {PathInfo, QueryString}) ->
[{query_string, QueryString}, {path_info, PathInfo}];
script_elements(#mod{method = "POST", entity_body = Body}, _) ->
[{entity_body, Body}];
+script_elements(#mod{method = "PATCH", entity_body = Body}, _) ->
+ [{entity_body, Body}];
script_elements(#mod{method = "PUT", entity_body = Body}, _) ->
[{entity_body, Body}];
script_elements(_, _) ->
[].
-cgi_timeout(Db) ->
- httpd_util:lookup(Db, cgi_timeout, ?DEFAULT_CGI_TIMEOUT).
+script_timeout(Db) ->
+ httpd_util:lookup(Db, script_timeout, ?DEFAULT_CGI_TIMEOUT).
%% Convert error to printable string
%%
diff --git a/lib/inets/src/http_server/mod_dir.erl b/lib/inets/src/http_server/mod_dir.erl
index d791ee28e9..ba93d0b271 100644
--- a/lib/inets/src/http_server/mod_dir.erl
+++ b/lib/inets/src/http_server/mod_dir.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -124,12 +125,13 @@ header(Path,RequestURI) ->
RequestURI ++ "</H1>\n<PRE><IMG SRC=\"" ++ icon(blank) ++
"\" ALT=" "> Name Last modified "
"Size Description <HR>\n",
- case inets_regexp:sub(RequestURI,"[^/]*\$","") of
- {ok,"/",_} ->
+ case re:replace(RequestURI,"[^/]*\$","", [{return,list}]) of
+ "/" ->
Header;
- {ok,ParentRequestURI,_} ->
- {ok,ParentPath,_} =
- inets_regexp:sub(string:strip(Path,right,$/),"[^/]*\$",""),
+ ParentRequestURI ->
+ ParentPath =
+ re:replace(string:strip(Path,right,$/),"[^/]*\$","",
+ [{return,list}]),
Header++format(ParentPath,ParentRequestURI)
end.
diff --git a/lib/inets/src/http_server/mod_disk_log.erl b/lib/inets/src/http_server/mod_disk_log.erl
index 5a3766de66..3be5f2dd74 100644
--- a/lib/inets/src/http_server/mod_disk_log.erl
+++ b/lib/inets/src/http_server/mod_disk_log.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -137,72 +138,81 @@ do(Info) ->
%% Description: See httpd(3) ESWAPI CALLBACK FUNCTIONS
%%-------------------------------------------------------------------------
load("TransferDiskLogSize " ++ TransferDiskLogSize, []) ->
- case inets_regexp:split(TransferDiskLogSize," ") of
- {ok,[MaxBytes,MaxFiles]} ->
- case httpd_conf:make_integer(MaxBytes) of
+ try re:split(TransferDiskLogSize, " ", [{return, list}]) of
+ [MaxBytes, MaxFiles] ->
+ case make_integer(MaxBytes) of
{ok,MaxBytesInteger} ->
- case httpd_conf:make_integer(MaxFiles) of
+ case make_integer(MaxFiles) of
{ok,MaxFilesInteger} ->
{ok,[],{transfer_disk_log_size,
{MaxBytesInteger,MaxFilesInteger}}};
{error,_} ->
{error,
- ?NICE(httpd_conf:clean(TransferDiskLogSize)++
+ ?NICE(string:strip(TransferDiskLogSize)++
" is an invalid TransferDiskLogSize")}
end;
- {error,_} ->
- {error,?NICE(httpd_conf:clean(TransferDiskLogSize)++
- " is an invalid TransferDiskLogSize")}
+ _ ->
+ {error,?NICE(string:strip(TransferDiskLogSize)++
+ " is an invalid TransferDiskLogSize")}
end
+ catch _:_ ->
+ {error,?NICE(string:strip(TransferDiskLogSize) ++
+ " is an invalid TransferDiskLogSize")}
end;
load("TransferDiskLog " ++ TransferDiskLog,[]) ->
- {ok,[],{transfer_disk_log,httpd_conf:clean(TransferDiskLog)}};
+ {ok,[],{transfer_disk_log,string:strip(TransferDiskLog)}};
load("ErrorDiskLogSize " ++ ErrorDiskLogSize, []) ->
- case inets_regexp:split(ErrorDiskLogSize," ") of
- {ok,[MaxBytes,MaxFiles]} ->
- case httpd_conf:make_integer(MaxBytes) of
+ try re:split(ErrorDiskLogSize," ", [{return, list}]) of
+ [MaxBytes,MaxFiles] ->
+ case make_integer(MaxBytes) of
{ok,MaxBytesInteger} ->
- case httpd_conf:make_integer(MaxFiles) of
+ case make_integer(MaxFiles) of
{ok,MaxFilesInteger} ->
{ok,[],{error_disk_log_size,
{MaxBytesInteger,MaxFilesInteger}}};
{error,_} ->
- {error,?NICE(httpd_conf:clean(ErrorDiskLogSize)++
+ {error,?NICE(string:strip(ErrorDiskLogSize)++
" is an invalid ErrorDiskLogSize")}
end;
{error,_} ->
- {error,?NICE(httpd_conf:clean(ErrorDiskLogSize)++
+ {error,?NICE(string:strip(ErrorDiskLogSize)++
" is an invalid ErrorDiskLogSize")}
end
+ catch _:_ ->
+ {error,?NICE(string:strip(ErrorDiskLogSize) ++
+ " is an invalid TransferDiskLogSize")}
end;
load("ErrorDiskLog " ++ ErrorDiskLog, []) ->
- {ok, [], {error_disk_log, httpd_conf:clean(ErrorDiskLog)}};
+ {ok, [], {error_disk_log, string:strip(ErrorDiskLog)}};
load("SecurityDiskLogSize " ++ SecurityDiskLogSize, []) ->
- case inets_regexp:split(SecurityDiskLogSize, " ") of
- {ok, [MaxBytes, MaxFiles]} ->
- case httpd_conf:make_integer(MaxBytes) of
+ try re:split(SecurityDiskLogSize, " ", [{return, list}]) of
+ [MaxBytes, MaxFiles] ->
+ case make_integer(MaxBytes) of
{ok, MaxBytesInteger} ->
- case httpd_conf:make_integer(MaxFiles) of
+ case make_integer(MaxFiles) of
{ok, MaxFilesInteger} ->
{ok, [], {security_disk_log_size,
{MaxBytesInteger, MaxFilesInteger}}};
{error,_} ->
{error,
- ?NICE(httpd_conf:clean(SecurityDiskLogSize) ++
+ ?NICE(string:strip(SecurityDiskLogSize) ++
" is an invalid SecurityDiskLogSize")}
end;
{error, _} ->
- {error, ?NICE(httpd_conf:clean(SecurityDiskLogSize) ++
+ {error, ?NICE(string:strip(SecurityDiskLogSize) ++
" is an invalid SecurityDiskLogSize")}
end
+ catch _:_ ->
+ {error,?NICE(string:strip(SecurityDiskLogSize) ++
+ " is an invalid SecurityDiskLogSize")}
end;
load("SecurityDiskLog " ++ SecurityDiskLog, []) ->
- {ok, [], {security_disk_log, httpd_conf:clean(SecurityDiskLog)}};
+ {ok, [], {security_disk_log, string:strip(SecurityDiskLog)}};
load("DiskLogFormat " ++ Format, []) ->
- case httpd_conf:clean(Format) of
+ case string:strip(Format) of
"internal" ->
{ok, [], {disk_log_format,internal}};
"external" ->
@@ -314,7 +324,7 @@ log_size(ConfigList, Tag) ->
proplists:get_value(Tag, ConfigList, {500*1024,8}).
create_disk_log(LogFile, SizeTag, ConfigList) ->
- Filename = httpd_conf:clean(LogFile),
+ Filename = string:strip(LogFile),
{MaxBytes, MaxFiles} = log_size(ConfigList, SizeTag),
case filename:pathtype(Filename) of
absolute ->
@@ -413,3 +423,11 @@ log_internal_info(Info,Date,[{internal_info,Reason}|Rest]) ->
log_internal_info(Info,Date,[_|Rest]) ->
log_internal_info(Info,Date,Rest).
+make_integer(List) ->
+ try list_to_integer(List) of
+ N ->
+ {ok, N}
+ catch
+ _:_ ->
+ {error, {badarg, list_to_integer, List}}
+ end.
diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl
index e36c33b282..2800250727 100644
--- a/lib/inets/src/http_server/mod_esi.erl
+++ b/lib/inets/src/http_server/mod_esi.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -95,43 +96,44 @@ do(ModData) ->
%% Description: See httpd(3) ESWAPI CALLBACK FUNCTIONS
%%-------------------------------------------------------------------------
load("ErlScriptAlias " ++ ErlScriptAlias, []) ->
- case inets_regexp:split(ErlScriptAlias," ") of
- {ok, [ErlName | StrModules]} ->
+ try re:split(ErlScriptAlias," ", [{return, list}]) of
+ [ErlName | StrModules] ->
Modules = lists:map(fun(Str) ->
- list_to_atom(httpd_conf:clean(Str))
+ list_to_atom(string:strip(Str))
end, StrModules),
- {ok, [], {erl_script_alias, {ErlName, Modules}}};
- {ok, _} ->
- {error, ?NICE(httpd_conf:clean(ErlScriptAlias) ++
- " is an invalid ErlScriptAlias")}
+ {ok, [], {erl_script_alias, {ErlName, Modules}}}
+ catch _:_ ->
+ {error, ?NICE(string:strip(ErlScriptAlias) ++
+ " is an invalid ErlScriptAlias")}
end;
load("EvalScriptAlias " ++ EvalScriptAlias, []) ->
- case inets_regexp:split(EvalScriptAlias, " ") of
- {ok, [EvalName | StrModules]} ->
+ try re:split(EvalScriptAlias, " ", [{return, list}]) of
+ [EvalName | StrModules] ->
Modules = lists:map(fun(Str) ->
- list_to_atom(httpd_conf:clean(Str))
+ list_to_atom(string:strip(Str))
end, StrModules),
- {ok, [], {eval_script_alias, {EvalName, Modules}}};
- {ok, _} ->
- {error, ?NICE(httpd_conf:clean(EvalScriptAlias) ++
- " is an invalid EvalScriptAlias")}
+ {ok, [], {eval_script_alias, {EvalName, Modules}}}
+ catch
+ _:_ ->
+ {error, ?NICE(string:strip(EvalScriptAlias) ++
+ " is an invalid EvalScriptAlias")}
end;
load("ErlScriptTimeout " ++ Timeout, [])->
- case catch list_to_integer(httpd_conf:clean(Timeout)) of
+ case catch list_to_integer(string:strip(Timeout)) of
TimeoutSec when is_integer(TimeoutSec) ->
{ok, [], {erl_script_timeout, TimeoutSec * 1000}};
_ ->
- {error, ?NICE(httpd_conf:clean(Timeout) ++
+ {error, ?NICE(string:strip(Timeout) ++
" is an invalid ErlScriptTimeout")}
end;
load("ErlScriptNoCache " ++ CacheArg, [])->
- case catch list_to_atom(httpd_conf:clean(CacheArg)) of
+ case catch list_to_atom(string:strip(CacheArg)) of
true ->
{ok, [], {erl_script_nocache, true}};
false ->
{ok, [], {erl_script_nocache, false}};
_ ->
- {error, ?NICE(httpd_conf:clean(CacheArg)++
+ {error, ?NICE(string:strip(CacheArg)++
" is an invalid ErlScriptNoCache directive")}
end.
@@ -223,8 +225,8 @@ match_esi_script(_, [], _) ->
no_match;
match_esi_script(RequestURI, [{Alias,Modules} | Rest], AliasType) ->
AliasMatchStr = alias_match_str(Alias, AliasType),
- case inets_regexp:first_match(RequestURI, AliasMatchStr) of
- {match, 1, Length} ->
+ case re:run(RequestURI, AliasMatchStr, [{capture, first}]) of
+ {match, [{0, Length}]} ->
{string:substr(RequestURI, Length + 1), Modules};
nomatch ->
match_esi_script(RequestURI, Rest, AliasType)
@@ -280,6 +282,15 @@ erl(#mod{request_uri = ReqUri,
?NICE("Erl mechanism doesn't support method DELETE")}}|
Data]};
+erl(#mod{request_uri = ReqUri,
+ method = "PATCH",
+ http_version = Version,
+ data = Data}, _ESIBody, _Modules) ->
+ ?hdrt("erl", [{method, patch}]),
+ {proceed, [{status,{501,{"PATCH", ReqUri, Version},
+ ?NICE("Erl mechanism doesn't support method PATCH")}}|
+ Data]};
+
erl(#mod{method = "POST",
entity_body = Body} = ModData, ESIBody, Modules) ->
?hdrt("erl", [{method, post}]),
@@ -375,7 +386,6 @@ erl_scheme_webpage_chunk(Mod, Func, Env, Input, ModData) ->
end),
Response = deliver_webpage_chunk(ModData, Pid),
-
process_flag(trap_exit,false),
Response.
@@ -417,7 +427,6 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) ->
?hdrv("deliver_webpage_chunk - timeout", []),
send_headers(ModData, 504, [{"connection", "close"}]),
httpd_socket:close(ModData#mod.socket_type, ModData#mod.socket),
- process_flag(trap_exit,false),
{proceed,[{response, {already_sent, 200, 0}} | ModData#mod.data]}
end.
@@ -440,12 +449,11 @@ receive_headers(Timeout) ->
end.
send_headers(ModData, StatusCode, HTTPHeaders) ->
- ExtraHeaders = httpd_response:cache_headers(ModData),
+ ExtraHeaders = httpd_response:cache_headers(ModData, erl_script_nocache),
httpd_response:send_header(ModData, StatusCode,
ExtraHeaders ++ HTTPHeaders).
handle_body(_, #mod{method = "HEAD"} = ModData, _, _, Size, _) ->
- process_flag(trap_exit,false),
{proceed, [{response, {already_sent, 200, Size}} | ModData#mod.data]};
handle_body(Pid, ModData, Body, Timeout, Size, IsDisableChunkedSend) ->
@@ -453,34 +461,54 @@ handle_body(Pid, ModData, Body, Timeout, Size, IsDisableChunkedSend) ->
httpd_response:send_chunk(ModData, Body, IsDisableChunkedSend),
receive
{esi_data, Data} when is_binary(Data) ->
- ?hdrt("handle_body - received binary data (esi)", []),
handle_body(Pid, ModData, Data, Timeout, Size + byte_size(Data),
IsDisableChunkedSend);
{esi_data, Data} ->
- ?hdrt("handle_body - received data (esi)", []),
handle_body(Pid, ModData, Data, Timeout, Size + length(Data),
IsDisableChunkedSend);
{ok, Data} ->
- ?hdrt("handle_body - received data (ok)", []),
handle_body(Pid, ModData, Data, Timeout, Size + length(Data),
IsDisableChunkedSend);
{'EXIT', Pid, normal} when is_pid(Pid) ->
- ?hdrt("handle_body - exit:normal", []),
httpd_response:send_final_chunk(ModData, IsDisableChunkedSend),
{proceed, [{response, {already_sent, 200, Size}} |
ModData#mod.data]};
{'EXIT', Pid, Reason} when is_pid(Pid) ->
- ?hdrv("handle_body - exit", [{reason, Reason}]),
- httpd_response:send_final_chunk(ModData, IsDisableChunkedSend),
- exit({mod_esi_linked_process_died, Pid, Reason})
-
+ Error = lists:flatten(io_lib:format("mod_esi process failed with reason ~p", [Reason])),
+ httpd_util:error_log(ModData#mod.config_db, Error),
+ httpd_response:send_final_chunk(ModData,
+ [{"Warning", "199 inets server - body maybe incomplete, "
+ "internal server error"}],
+ IsDisableChunkedSend),
+ done
after Timeout ->
- ?hdrv("handle_body - timeout", []),
- process_flag(trap_exit,false),
- httpd_response:send_final_chunk(ModData, IsDisableChunkedSend),
- exit({mod_esi_linked_process_timeout, Pid})
+ kill_esi_delivery_process(Pid),
+ httpd_response:send_final_chunk(ModData, [{"Warning", "199 inets server - "
+ "body maybe incomplete, timed out"}],
+ IsDisableChunkedSend),
+ done
end.
+kill_esi_delivery_process(Pid) ->
+ exit(Pid, kill),
+ receive
+ {'EXIT', Pid, killed} ->
+ %% Clean message queue
+ receive
+ {esi_data, _} ->
+ ok
+ after 0 ->
+ ok
+ end,
+ receive
+ {ok, _} ->
+ ok
+ after 0 ->
+ ok
+ end
+ end.
+
+
erl_script_timeout(Db) ->
httpd_util:lookup(Db, erl_script_timeout, ?DEFAULT_ERL_TIMEOUT).
@@ -566,9 +594,9 @@ generate_webpage(ESIBody) ->
is_authorized(_ESIBody, [all]) ->
true;
is_authorized(ESIBody, Modules) ->
- case inets_regexp:match(ESIBody, "^[^\:(%3A)]*") of
- {match, Start, Length} ->
- lists:member(list_to_atom(string:substr(ESIBody, Start, Length)),
+ case re:run(ESIBody, "^[^\:(%3A)]*", [{capture, first}]) of
+ {match, [{Start, Length}]} ->
+ lists:member(list_to_atom(string:substr(ESIBody, Start+1, Length)),
Modules);
nomatch ->
false
diff --git a/lib/inets/src/http_server/mod_get.erl b/lib/inets/src/http_server/mod_get.erl
index 758985f330..58600f5e3e 100644
--- a/lib/inets/src/http_server/mod_get.erl
+++ b/lib/inets/src/http_server/mod_get.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_server/mod_head.erl b/lib/inets/src/http_server/mod_head.erl
index c346fd4d23..25c11360eb 100644
--- a/lib/inets/src/http_server/mod_head.erl
+++ b/lib/inets/src/http_server/mod_head.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -42,6 +43,10 @@ do(Info) ->
%% A response has been sent! Nothing to do about it!
{already_sent, _StatusCode, _Size} ->
{proceed,Info#mod.data};
+ {response, Header, _Body} -> %% New way
+ {proceed,
+ lists:keyreplace(response, 1, Info#mod.data,
+ {response, Header, nobody})};
%% A response has been generated!
{_StatusCode, _Response} ->
{proceed,Info#mod.data}
diff --git a/lib/inets/src/http_server/mod_htaccess.erl b/lib/inets/src/http_server/mod_htaccess.erl
index e1f66d01c8..7b742bba24 100644
--- a/lib/inets/src/http_server/mod_htaccess.erl
+++ b/lib/inets/src/http_server/mod_htaccess.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -34,7 +35,7 @@
% Names on accessfiles
%----------------------------------------------------------------------
load("AccessFileName" ++ FileNames, _Context)->
- CleanFileNames=httpd_conf:clean(FileNames),
+ CleanFileNames=string:strip(FileNames),
{ok,[],{access_files,string:tokens(CleanFileNames," ")}}.
store({access_files, Files} = Conf, _) when is_list(Files)->
@@ -326,9 +327,9 @@ memberNetwork(Networks,UserNetwork,IfTrue,IfFalse)->
%ipadresses or subnet addresses.
memberNetwork(Networks,UserNetwork)->
case lists:filter(fun(Net)->
- case inets_regexp:match(UserNetwork,
- formatRegexp(Net)) of
- {match,1,_}->
+ case re:run(UserNetwork,
+ formatRegexp(Net), [{capture, first}]) of
+ {match,[{0,_}]}->
true;
_NotSubNet ->
false
@@ -411,8 +412,8 @@ getAuthenticatingDataFromHeader(Info)->
case httpd_util:split(UnCodedString,":",2) of
{ok,[User,PassWord]}->
{user,User,PassWord};
- {error,Error}->
- {error,Error}
+ Other ->
+ {error, Other}
end
end;
BadCredentials ->
@@ -637,13 +638,8 @@ getHtAccessFileNames(Info)->
%HtAccessFileNames=["accessfileName1",..."AccessFileName2"]
%----------------------------------------------------------------------
getData(Path,Info,HtAccessFileNames)->
- case inets_regexp:split(Path,"/") of
- {error,Error}->
- {error,Error};
- {ok,SplittedPath}->
- getData2(HtAccessFileNames,SplittedPath,Info)
- end.
-
+ SplittedPath = re:split(Path, "/", [{return, list}]),
+ getData2(HtAccessFileNames,SplittedPath,Info).
%----------------------------------------------------------------------
%Add to together the data in the Splittedpath up to the path
@@ -941,20 +937,16 @@ getAuthorizationType(AuthType)->
%Returns a list of the specified methods to limit or the atom all
%----------------------------------------------------------------------
getLimits(Limits)->
- case inets_regexp:split(Limits,">")of
- {ok,[_NoEndOnLimit]}->
+ case re:split(Limits,">", [{return, list}])of
+ [_NoEndOnLimit]->
error;
- {ok, [Methods | _Crap]}->
- case inets_regexp:split(Methods," ") of
- {ok,[]}->
+ [Methods | _Crap]->
+ case re:split(Methods," ", [{return, list}]) of
+ [[]]->
all;
- {ok,SplittedMethods}->
- SplittedMethods;
- {error, _Error}->
- error
- end;
- {error,_Error}->
- error
+ SplittedMethods ->
+ SplittedMethods
+ end
end.
diff --git a/lib/inets/src/http_server/mod_include.erl b/lib/inets/src/http_server/mod_include.erl
deleted file mode 100644
index 35f45bdd33..0000000000
--- a/lib/inets/src/http_server/mod_include.erl
+++ /dev/null
@@ -1,598 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
--module(mod_include).
--export([do/1,parse/2,config/6,include/6,echo/6,fsize/6,flastmod/6,exec/6]).
-
--include("httpd.hrl").
--include("httpd_internal.hrl").
-
--define(VMODULE,"INCLUDE").
-
-%% do
-
-do(Info) ->
- case Info#mod.method of
- "GET" ->
- case proplists:get_value(status, Info#mod.data) of
- %% A status code has been generated!
- {_StatusCode, _PhraseArgs, _Reason} ->
- {proceed,Info#mod.data};
- %% No status code has been generated!
- undefined ->
- case proplists:get_value(response, Info#mod.data) of
- %% No response has been generated!
- undefined ->
- do_include(Info);
- %% A response has been generated or sent!
- _Response ->
- {proceed,Info#mod.data}
- end
- end;
- %% Not a GET method!
- _ ->
- {proceed,Info#mod.data}
- end.
-
-do_include(Info) ->
- Path = mod_alias:path(Info#mod.data,Info#mod.config_db,
- Info#mod.request_uri),
- Suffix = httpd_util:suffix(Path),
- case httpd_util:lookup_mime_default(Info#mod.config_db,Suffix) of
- "text/x-server-parsed-html" ->
- HeaderStart = [{content_type, "text/html"}],
- case send_in(Info, Path, HeaderStart, file:read_file_info(Path)) of
- {ok, ErrorLog, Size} ->
- {proceed, [{response, {already_sent, 200, Size}},
- {mime_type, "text/html"} |
- lists:append(ErrorLog, Info#mod.data)]};
- {error, Reason} ->
- {proceed,
- [{status,send_error(Reason,Info,Path)}|Info#mod.data]}
- end;
- _ -> %% Unknown mime type, ignore
- {proceed,Info#mod.data}
- end.
-
-
-%%
-%% config directive
-%%
-
-config(_Info, Context, ErrorLog, TagList, ValueList, R) ->
- case verify_tags("config",[errmsg,timefmt,sizefmt],
- TagList,ValueList) of
- ok ->
- {ok,update_context(TagList,ValueList,Context),ErrorLog,"",R};
- {error,Reason} ->
- {ok,Context,[{internal_info,Reason}|ErrorLog],
- proplists:get_value(errmsg,Context,""),R}
- end.
-
-update_context([],[],Context) ->
- Context;
-update_context([Tag|R1],[Value|R2],Context) ->
- update_context(R1,R2,[{Tag,Value}|Context]).
-
-verify_tags(Command,ValidTags,TagList,ValueList)
- when length(TagList) =:= length(ValueList) ->
- verify_tags(Command, ValidTags, TagList);
-verify_tags(Command, _ValidTags, _TagList, _ValueList) ->
- {error, ?NICE(Command ++ " directive has spurious tags")}.
-
-verify_tags(_Command, _ValidTags, []) ->
- ok;
-verify_tags(Command, ValidTags, [Tag|Rest]) ->
- case lists:member(Tag, ValidTags) of
- true ->
- verify_tags(Command, ValidTags, Rest);
- false ->
- {error, ?NICE(Command++" directive has a spurious tag ("++
- atom_to_list(Tag)++")")}
- end.
-
-%%
-%% include directive
-%%
-
-include(Info,Context,ErrorLog,[virtual],[VirtualPath],R) ->
- Aliases = httpd_util:multi_lookup(Info#mod.config_db,alias),
- {_, Path, _AfterPath} =
- mod_alias:real_name(Info#mod.config_db, VirtualPath, Aliases),
- include(Info,Context,ErrorLog,R,Path);
-include(Info, Context, ErrorLog, [file], [FileName], R) ->
- Path = file(Info#mod.config_db, Info#mod.request_uri, FileName),
- include(Info, Context, ErrorLog, R, Path);
-include(_Info, Context, ErrorLog, _TagList, _ValueList, R) ->
- {ok, Context,
- [{internal_info,?NICE("include directive has a spurious tag")}|
- ErrorLog], proplists:get_value(errmsg, Context, ""), R}.
-
-include(Info, Context, ErrorLog, R, Path) ->
- case file:read_file(Path) of
- {ok, Body} ->
- {ok, NewContext, NewErrorLog, Result} =
- parse(Info, binary_to_list(Body), Context, ErrorLog, []),
- {ok, NewContext, NewErrorLog, Result, R};
- {error, _Reason} ->
- {ok, Context,
- [{internal_info, ?NICE("Can't open "++Path)}|ErrorLog],
- proplists:get_value(errmsg, Context, ""), R}
- end.
-
-file(ConfigDB, RequestURI, FileName) ->
- Aliases = httpd_util:multi_lookup(ConfigDB, alias),
- {_, Path, _AfterPath}
- = mod_alias:real_name(ConfigDB, RequestURI, Aliases),
- Pwd = filename:dirname(Path),
- filename:join(Pwd, FileName).
-
-%%
-%% echo directive
-%%
-
-echo(Info,Context,ErrorLog,[var],["DOCUMENT_NAME"],R) ->
- {ok,Context,ErrorLog,document_name(Info#mod.data,Info#mod.config_db,
- Info#mod.request_uri),R};
-echo(Info,Context,ErrorLog,[var],["DOCUMENT_URI"],R) ->
- {ok,Context,ErrorLog,document_uri(Info#mod.config_db,
- Info#mod.request_uri),R};
-echo(Info,Context,ErrorLog,[var],["QUERY_STRING_UNESCAPED"],R) ->
- {ok,Context,ErrorLog,query_string_unescaped(Info#mod.request_uri),R};
-echo(_Info,Context,ErrorLog,[var],["DATE_LOCAL"],R) ->
- {ok,Context,ErrorLog,date_local(),R};
-echo(_Info,Context,ErrorLog,[var],["DATE_GMT"],R) ->
- {ok,Context,ErrorLog,date_gmt(),R};
-echo(Info,Context,ErrorLog,[var],["LAST_MODIFIED"],R) ->
- {ok,Context,ErrorLog,last_modified(Info#mod.data,Info#mod.config_db,
- Info#mod.request_uri),R};
-echo(_Info, Context, ErrorLog, _TagList, _ValueList, R) ->
- {ok,Context,
- [{internal_info,?NICE("echo directive has a spurious tag")}|
- ErrorLog],"(none)",R}.
-
-document_name(Data,ConfigDB,RequestURI) ->
- Path = mod_alias:path(Data,ConfigDB,RequestURI),
- case inets_regexp:match(Path,"[^/]*\$") of
- {match,Start,Length} ->
- string:substr(Path,Start,Length);
- nomatch ->
- "(none)"
- end.
-
-document_uri(ConfigDB, RequestURI) ->
- Aliases = httpd_util:multi_lookup(ConfigDB, alias),
-
- {_, Path, AfterPath} = mod_alias:real_name(ConfigDB, RequestURI, Aliases),
-
- VirtualPath = string:substr(RequestURI, 1,
- length(RequestURI)-length(AfterPath)),
- {match, Start, Length} = inets_regexp:match(Path,"[^/]*\$"),
- FileName = string:substr(Path,Start,Length),
- case inets_regexp:match(VirtualPath, FileName++"\$") of
- {match, _, _} ->
- http_uri:decode(VirtualPath)++AfterPath;
- nomatch ->
- string:strip(http_uri:decode(VirtualPath),right,$/)++
- "/"++FileName++AfterPath
- end.
-
-query_string_unescaped(RequestURI) ->
- case inets_regexp:match(RequestURI,"[\?].*\$") of
- {match,Start,Length} ->
- %% Escape all shell-special variables with \
- escape(string:substr(RequestURI,Start+1,Length-1));
- nomatch ->
- "(none)"
- end.
-
-escape([]) -> [];
-escape([$;|R]) -> [$\\,$;|escape(R)];
-escape([$&|R]) -> [$\\,$&|escape(R)];
-escape([$(|R]) -> [$\\,$(|escape(R)];
-escape([$)|R]) -> [$\\,$)|escape(R)];
-escape([$||R]) -> [$\\,$||escape(R)];
-escape([$^|R]) -> [$\\,$^|escape(R)];
-escape([$<|R]) -> [$\\,$<|escape(R)];
-escape([$>|R]) -> [$\\,$>|escape(R)];
-escape([$\n|R]) -> [$\\,$\n|escape(R)];
-escape([$ |R]) -> [$\\,$ |escape(R)];
-escape([$\t|R]) -> [$\\,$\t|escape(R)];
-escape([C|R]) -> [C|escape(R)].
-
-date_local() ->
- {{Year,Month,Day},{Hour,Minute,Second}}=calendar:local_time(),
- %% Time format hard-wired to: "%a %b %e %T %Y" according to strftime(3)
- io_lib:format("~s ~s ~2w ~2.2.0w:~2.2.0w:~2.2.0w ~w",
- [httpd_util:day(calendar:day_of_the_week(Year,Month,Day)),
- httpd_util:month(Month),Day,Hour,Minute,Second,Year]).
-
-date_gmt() ->
- {{Year,Month,Day},{Hour,Minute,Second}}=calendar:universal_time(),
- %% Time format hard-wired to: "%a %b %e %T %Z %Y" according to strftime(3)
- io_lib:format("~s ~s ~2w ~2.2.0w:~2.2.0w:~2.2.0w GMT ~w",
- [httpd_util:day(calendar:day_of_the_week(Year,Month,Day)),
- httpd_util:month(Month),Day,Hour,Minute,Second,Year]).
-
-last_modified(Data,ConfigDB,RequestURI) ->
- {ok,FileInfo}=file:read_file_info(mod_alias:path(Data,ConfigDB,RequestURI)),
- {{Year,Month,Day},{Hour,Minute,Second}}=FileInfo#file_info.mtime,
- io_lib:format("~s ~s ~2w ~2.2.0w:~2.2.0w:~2.2.0w ~w",
- [httpd_util:day(calendar:day_of_the_week(Year,Month,Day)),
- httpd_util:month(Month),Day,Hour,Minute,Second,Year]).
-
-%%
-%% fsize directive
-%%
-
-fsize(Info,Context,ErrorLog,[virtual],[VirtualPath],R) ->
- Aliases = httpd_util:multi_lookup(Info#mod.config_db,alias),
- {_,Path, _AfterPath}=
- mod_alias:real_name(Info#mod.config_db,VirtualPath,Aliases),
- fsize(Info, Context, ErrorLog, R, Path);
-fsize(Info,Context,ErrorLog,[file],[FileName],R) ->
- Path = file(Info#mod.config_db,Info#mod.request_uri,FileName),
- fsize(Info,Context,ErrorLog,R,Path);
-fsize(_Info, Context, ErrorLog, _TagList, _ValueList, R) ->
- {ok,Context,[{internal_info,?NICE("fsize directive has a spurious tag")}|
- ErrorLog],proplists:get_value(errmsg,Context,""),R}.
-
-fsize(_Info, Context, ErrorLog, R, Path) ->
- case file:read_file_info(Path) of
- {ok,FileInfo} ->
- case proplists:get_value(sizefmt,Context) of
- "bytes" ->
- {ok,Context,ErrorLog,
- integer_to_list(FileInfo#file_info.size),R};
- "abbrev" ->
- Size = integer_to_list(trunc(FileInfo#file_info.size/1024+1))++"k",
- {ok,Context,ErrorLog,Size,R};
- Value->
- {ok,Context,
- [{internal_info,
- ?NICE("fsize directive has a spurious tag value ("++
- Value++")")}|
- ErrorLog],
- proplists:get_value(errmsg, Context, ""), R}
- end;
- {error, _Reason} ->
- {ok,Context,[{internal_info,?NICE("Can't open "++Path)}|ErrorLog],
- proplists:get_value(errmsg,Context,""),R}
- end.
-
-%%
-%% flastmod directive
-%%
-
-flastmod(#mod{config_db = Db} = Info,
- Context, ErrorLog, [virtual], [VirtualPath],R) ->
- Aliases = httpd_util:multi_lookup(Db,alias),
- {_,Path, _AfterPath} = mod_alias:real_name(Db, VirtualPath, Aliases),
- flastmod(Info,Context,ErrorLog,R,Path);
-flastmod(#mod{config_db = Db, request_uri = RequestUri} = Info,
- Context, ErrorLog, [file], [FileName], R) ->
- Path = file(Db, RequestUri, FileName),
- flastmod(Info, Context, ErrorLog, R, Path);
-flastmod(_Info, Context, ErrorLog, _TagList, _ValueList, R) ->
- {ok,Context,
- [{internal_info,?NICE("flastmod directive has a spurious tag")}|
- ErrorLog],proplists:get_value(errmsg,Context,""),R}.
-
-flastmod(_Info, Context, ErrorLog, R, File) ->
- case file:read_file_info(File) of
- {ok, FileInfo} ->
- {{Yr,Mon,Day},{Hour,Minute,Second}}=FileInfo#file_info.mtime,
- Result =
- io_lib:format("~s ~s ~2w ~w:~w:~w ~w",
- [httpd_util:day(
- calendar:day_of_the_week(Yr,Mon, Day)),
- httpd_util:month(Mon),Day,Hour,Minute,Second, Yr]),
- {ok, Context, ErrorLog, Result, R};
- {error, _Reason} ->
- {ok,Context,[{internal_info,?NICE("Can't open "++File)}|ErrorLog],
- proplists:get_value(errmsg,Context,""),R}
- end.
-
-%%
-%% exec directive
-%%
-
-exec(Info,Context,ErrorLog,[cmd],[Command],R) ->
- cmd(Info,Context,ErrorLog,R,Command);
-exec(Info,Context,ErrorLog,[cgi],[RequestURI],R) ->
- cgi(Info,Context,ErrorLog,R,RequestURI);
-exec(_Info, Context, ErrorLog, _TagList, _ValueList, R) ->
- {ok, Context,
- [{internal_info,?NICE("exec directive has a spurious tag")}|
- ErrorLog], proplists:get_value(errmsg,Context,""),R}.
-
-%% cmd
-
-cmd(Info, Context, ErrorLog, R, Command) ->
- process_flag(trap_exit,true),
- Env = env(Info),
- Dir = filename:dirname(Command),
- Port = (catch open_port({spawn,Command},[stream,{cd,Dir},{env,Env}])),
- case Port of
- P when is_port(P) ->
- {NewErrorLog, Result} = proxy(Port, ErrorLog),
- {ok, Context, NewErrorLog, Result, R};
- {'EXIT', Reason} ->
- exit({open_port_failed,Reason,
- [{uri,Info#mod.request_uri},{script,Command},
- {env,Env},{dir,Dir}]});
- O ->
- exit({open_port_failed,O,
- [{uri,Info#mod.request_uri},{script,Command},
- {env,Env},{dir,Dir}]})
- end.
-
-env(Info) ->
- [{"DOCUMENT_NAME",document_name(Info#mod.data,Info#mod.config_db,
- Info#mod.request_uri)},
- {"DOCUMENT_URI", document_uri(Info#mod.config_db, Info#mod.request_uri)},
- {"QUERY_STRING_UNESCAPED", query_string_unescaped(Info#mod.request_uri)},
- {"DATE_LOCAL", date_local()},
- {"DATE_GMT", date_gmt()},
- {"LAST_MODIFIED", last_modified(Info#mod.data, Info#mod.config_db,
- Info#mod.request_uri)}
- ].
-
-%% cgi
-
-cgi(Info, Context, ErrorLog, R, RequestURI) ->
- ScriptAliases = httpd_util:multi_lookup(Info#mod.config_db, script_alias),
- case mod_alias:real_script_name(Info#mod.config_db, RequestURI,
- ScriptAliases) of
- {Script, AfterScript} ->
- exec_script(Info,Script,AfterScript,ErrorLog,Context,R);
- not_a_script ->
- {ok, Context,
- [{internal_info, ?NICE(RequestURI++" is not a script")}|
- ErrorLog], proplists:get_value(errmsg, Context, ""),R}
- end.
-
-remove_header([]) ->
- [];
-remove_header([$\n,$\n|Rest]) ->
- Rest;
-remove_header([_C|Rest]) ->
- remove_header(Rest).
-
-
-exec_script(#mod{config_db = Db, request_uri = RequestUri} = Info,
- Script, _AfterScript, ErrorLog, Context, R) ->
- process_flag(trap_exit,true),
- Aliases = httpd_util:multi_lookup(Db, alias),
- {_, Path, AfterPath} = mod_alias:real_name(Db, RequestUri, Aliases),
- Env = env(Info) ++ mod_cgi:env(Info, Path, AfterPath),
- Dir = filename:dirname(Path),
- Port = (catch open_port({spawn,Script},[stream,{env, Env},{cd, Dir}])),
- case Port of
- P when is_port(P) ->
- %% Send entity body to port.
- Res = case Info#mod.entity_body of
- [] ->
- true;
- EntityBody ->
- (catch port_command(Port, EntityBody))
- end,
- case Res of
- {'EXIT', Reason} ->
- exit({open_cmd_failed,Reason,
- [{mod,?MODULE},{port,Port},
- {uri,RequestUri},
- {script,Script},{env,Env},{dir,Dir},
- {ebody_size,sz(Info#mod.entity_body)}]});
- true ->
- {NewErrorLog, Result} = proxy(Port, ErrorLog),
- {ok, Context, NewErrorLog, remove_header(Result), R}
- end;
- {'EXIT', Reason} ->
- exit({open_port_failed,Reason,
- [{mod,?MODULE},{uri,RequestUri},{script,Script},
- {env,Env},{dir,Dir}]});
- O ->
- exit({open_port_failed,O,
- [{mod,?MODULE},{uri,RequestUri},{script,Script},
- {env,Env},{dir,Dir}]})
- end.
-
-
-%%
-%% Port communication
-%%
-
-proxy(Port, ErrorLog) ->
- process_flag(trap_exit, true),
- proxy(Port, ErrorLog, []).
-
-proxy(Port, ErrorLog, Result) ->
- receive
- {Port, {data, Response}} ->
- proxy(Port, ErrorLog, lists:append(Result,Response));
- {'EXIT', Port, normal} when is_port(Port) ->
- process_flag(trap_exit, false),
- {ErrorLog, Result};
- {'EXIT', Port, _Reason} when is_port(Port) ->
- process_flag(trap_exit, false),
- {[{internal_info,
- ?NICE("Scrambled output from CGI-script")}|ErrorLog],
- Result};
- {'EXIT', Pid, Reason} when is_pid(Pid) ->
- process_flag(trap_exit, false),
- {'EXIT', Pid, Reason};
- %% This should not happen!
- _WhatEver ->
- process_flag(trap_exit, false),
- {ErrorLog, Result}
- end.
-
-
-%% ------
-%% Temporary until I figure out a way to fix send_in_chunks
-%% (comments and directives that start in one chunk but end
-%% in another is not handled).
-%%
-
-send_in(Info, Path, Head, {ok,FileInfo}) ->
- case file:read_file(Path) of
- {ok, Bin} ->
- send_in1(Info, binary_to_list(Bin), Head, FileInfo);
- {error, Reason} ->
- {error, {read,Reason}}
- end;
-send_in(_Info , _Path, _Head,{error,Reason}) ->
- {error, {open,Reason}}.
-
-send_in1(Info, Data, Head, FileInfo) ->
- {ok, _Context, Err, ParsedBody} = parse(Info,Data,?DEFAULT_CONTEXT,[],[]),
- Size = length(ParsedBody),
- LastModified = case catch httpd_util:rfc1123_date(FileInfo#file_info.mtime) of
- Date when is_list(Date) -> [{last_modified,Date}];
- _ -> []
- end,
- Head1 = case Info#mod.http_version of
- "HTTP/1.1"->
- Head ++ [{content_length, integer_to_list(Size)},
- {etag, httpd_util:create_etag(FileInfo,Size)}|
- LastModified];
- _->
- %% i.e http/1.0 and http/0.9
- Head ++ [{content_length, integer_to_list(Size)}|
- LastModified]
- end,
- httpd_response:send_header(Info, 200, Head1),
- httpd_socket:deliver(Info#mod.socket_type,Info#mod.socket, ParsedBody),
- {ok, Err, Size}.
-
-
-parse(Info,Body) ->
- parse(Info, Body, ?DEFAULT_CONTEXT, [], []).
-
-parse(_Info, [], Context, ErrorLog, Result) ->
- {ok, Context, lists:reverse(ErrorLog), lists:reverse(Result)};
-parse(Info,[$<,$!,$-,$-,$#|R1],Context,ErrorLog,Result) ->
- case catch parse0(R1,Context) of
- {parse_error,Reason} ->
- parse(Info,R1,Context,[{internal_info,?NICE(Reason)}|ErrorLog],
- [$#,$-,$-,$!,$<|Result]);
- {ok,Context,Command,TagList,ValueList,R2} ->
- {ok,NewContext,NewErrorLog,MoreResult,R3}=
- handle(Info,Context,ErrorLog,Command,TagList,ValueList,R2),
- parse(Info,R3,NewContext,NewErrorLog,
- lists:reverse(MoreResult)++Result)
- end;
-parse(Info,[$<,$!,$-,$-|R1],Context,ErrorLog,Result) ->
- case catch parse5(R1,[],0) of
- {parse_error,Reason} ->
- parse(Info,R1,Context,
- [{internal_info,?NICE(Reason)}|ErrorLog],Result);
- {Comment,R2} ->
- parse(Info,R2,Context,ErrorLog,Comment++Result)
- end;
-parse(Info,[C|R],Context,ErrorLog,Result) ->
- parse(Info,R,Context,ErrorLog,[C|Result]).
-
-handle(Info,Context,ErrorLog,Command,TagList,ValueList,R) ->
- case catch apply(?MODULE,Command,[Info,Context,ErrorLog,TagList,ValueList,
- R]) of
- {'EXIT',{undef,_}} ->
- throw({parse_error,"Unknown command "++atom_to_list(Command)++
- " in parsed doc"});
- Result ->
- Result
- end.
-
-parse0([], _Context) ->
- throw({parse_error,"Premature EOF in parsed file"});
-parse0([$-,$-,$>|_R], _Context) ->
- throw({parse_error,"Premature EOF in parsed file"});
-parse0([$ |R], Context) ->
- parse0(R,Context);
-parse0(String, Context) ->
- parse1(String, Context,"").
-
-parse1([], _Context, _Command) ->
- throw({parse_error,"Premature EOF in parsed file"});
-parse1([$-,$-,$>|_R], _Context, _Command) ->
- throw({parse_error,"Premature EOF in parsed file"});
-parse1([$ |R], Context, Command) ->
- parse2(R,Context,list_to_atom(lists:reverse(Command)),[],[],"");
-parse1([C|R], Context, Command) ->
- parse1(R,Context,[C|Command]).
-
-parse2([], _Context, _Command, _TagList, _ValueList, _Tag) ->
- throw({parse_error,"Premature EOF in parsed file"});
-parse2([$-,$-,$>|R], Context, Command, TagList, ValueList, _Tag) ->
- {ok,Context,Command,TagList,ValueList,R};
-parse2([$ |R],Context,Command,TagList,ValueList,Tag) ->
- parse2(R,Context,Command,TagList,ValueList,Tag);
-parse2([$=|R],Context,Command,TagList,ValueList,Tag) ->
- parse3(R,Context,Command,[list_to_atom(lists:reverse(Tag))|TagList],
- ValueList);
-parse2([C|R],Context,Command,TagList,ValueList,Tag) ->
- parse2(R,Context,Command,TagList,ValueList,[C|Tag]).
-
-parse3([], _Context, _Command, _TagList, _ValueList) ->
- throw({parse_error,"Premature EOF in parsed file"});
-parse3([$-,$-,$>|_R], _Context, _Command, _TagList, _ValueList) ->
- throw({parse_error,"Premature EOF in parsed file"});
-parse3([$ |R], Context, Command, TagList, ValueList) ->
- parse3(R, Context, Command, TagList, ValueList);
-parse3([$"|R], Context, Command, TagList, ValueList) ->
- parse4(R,Context,Command,TagList,ValueList,"");
-parse3(_String, _Context, _Command, _TagList, _ValueList) ->
- throw({parse_error,"Premature EOF in parsed file"}).
-
-parse4([], _Context, _Command, _TagList, _ValueList, _Value) ->
- throw({parse_error,"Premature EOF in parsed file"});
-parse4([$-,$-,$>|_R], _Context, _Command, _TagList, _ValueList, _Value) ->
- throw({parse_error,"Premature EOF in parsed file"});
-parse4([$"|R],Context,Command,TagList,ValueList,Value) ->
- parse2(R,Context,Command,TagList,[lists:reverse(Value)|ValueList],"");
-parse4([C|R],Context,Command,TagList,ValueList,Value) ->
- parse4(R,Context,Command,TagList,ValueList,[C|Value]).
-
-parse5([], _Comment, _Depth) ->
- throw({parse_error,"Premature EOF in parsed file"});
-parse5([$<,$!,$-,$-|R],Comment,Depth) ->
- parse5(R,[$-,$-,$!,$<|Comment],Depth+1);
-parse5([$-,$-,$>|R],Comment,0) ->
- {">--"++Comment++"--!<",R};
-parse5([$-,$-,$>|R],Comment,Depth) ->
- parse5(R,[$>,$-,$-|Comment],Depth-1);
-parse5([C|R],Comment,Depth) ->
- parse5(R,[C|Comment],Depth).
-
-
-sz(B) when is_binary(B) -> {binary,size(B)};
-sz(L) when is_list(L) -> {list,length(L)};
-sz(_) -> undefined.
-
-%% send_error - Handle failure to send the file
-%%
-send_error({open,Reason},Info,Path) ->
- httpd_file:handle_error(Reason, "open", Info, Path);
-send_error({read,Reason},Info,Path) ->
- httpd_file:handle_error(Reason, "read", Info, Path).
-
-
-
-
diff --git a/lib/inets/src/http_server/mod_log.erl b/lib/inets/src/http_server/mod_log.erl
index a912f5616c..ad7e9713d9 100644
--- a/lib/inets/src/http_server/mod_log.erl
+++ b/lib/inets/src/http_server/mod_log.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -127,11 +128,11 @@ do(Info) ->
%% Description: See httpd(3) ESWAPI CALLBACK FUNCTIONS
%%-------------------------------------------------------------------------
load("TransferLog " ++ TransferLog, []) ->
- {ok,[],{transfer_log,httpd_conf:clean(TransferLog)}};
+ {ok,[],{transfer_log,string:strip(TransferLog)}};
load("ErrorLog " ++ ErrorLog, []) ->
- {ok,[],{error_log,httpd_conf:clean(ErrorLog)}};
+ {ok,[],{error_log,string:strip(ErrorLog)}};
load("SecurityLog " ++ SecurityLog, []) ->
- {ok, [], {security_log, httpd_conf:clean(SecurityLog)}}.
+ {ok, [], {security_log, string:strip(SecurityLog)}}.
%%--------------------------------------------------------------------------
%% store(Directive, DirectiveList) -> {ok, NewDirective} |
@@ -200,7 +201,7 @@ transfer_log(Info,RFC931,AuthUser,Date,StatusCode,Bytes) ->
end.
create_log(LogFile, ConfigList) ->
- Filename = httpd_conf:clean(LogFile),
+ Filename = string:strip(LogFile),
case filename:pathtype(Filename) of
absolute ->
case file:open(Filename, [read, write]) of
diff --git a/lib/inets/src/http_server/mod_range.erl b/lib/inets/src/http_server/mod_range.erl
index a0408cba79..1c6c6d927d 100644
--- a/lib/inets/src/http_server/mod_range.erl
+++ b/lib/inets/src/http_server/mod_range.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_server/mod_responsecontrol.erl b/lib/inets/src/http_server/mod_responsecontrol.erl
index 6af5f6211e..07129940a5 100644
--- a/lib/inets/src/http_server/mod_responsecontrol.erl
+++ b/lib/inets/src/http_server/mod_responsecontrol.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_server/mod_security.erl b/lib/inets/src/http_server/mod_security.erl
index 41988732ad..e7fc043217 100644
--- a/lib/inets/src/http_server/mod_security.erl
+++ b/lib/inets/src/http_server/mod_security.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -32,14 +33,13 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
--include("inets_internal.hrl").
-define(VMODULE,"SEC").
-
-%% do/1
+%%====================================================================
+%% Internal application API
+%%====================================================================
do(Info) ->
- ?hdrt("do", [{info, Info}]),
%% Check and see if any user has been authorized.
case proplists:get_value(remote_user, Info#mod.data,not_defined_user) of
not_defined_user ->
@@ -84,151 +84,66 @@ do(Info) ->
{_Dir, SDirData} = secretp(Path, Info#mod.config_db),
Addr = httpd_util:lookup(Info#mod.config_db, bind_address),
Port = httpd_util:lookup(Info#mod.config_db, port),
+ Profile = httpd_util:lookup(Info#mod.config_db, profile, ?DEFAULT_PROFILE),
case mod_security_server:check_blocked_user(Info, User,
SDirData,
- Addr, Port) of
+ Addr, Port, Profile) of
true ->
report_failed(Info, User ,"User Blocked"),
{proceed, [{status, {403, Info#mod.request_uri, ""}} |
Info#mod.data]};
false ->
report_failed(Info, User,"Authentication Succedded"),
- mod_security_server:store_successful_auth(Addr, Port,
+ mod_security_server:store_successful_auth(Addr, Port, Profile,
User,
SDirData),
{proceed, Info#mod.data}
end
end.
-report_failed(Info, Auth, Event) ->
- Request = Info#mod.request_line,
- {_PortNumber,RemoteHost}=(Info#mod.init_data)#init_data.peername,
- String = RemoteHost ++ " : " ++ Event ++ " : " ++ Request ++
- " : " ++ Auth,
- mod_disk_log:security_log(Info,String),
- mod_log:security_log(Info, String).
-
-take_failed_action(Info, Auth) ->
- ?hdrd("take failed action", [{auth, Auth}]),
- Path = mod_alias:path(Info#mod.data, Info#mod.config_db,
- Info#mod.request_uri),
- {_Dir, SDirData} = secretp(Path, Info#mod.config_db),
- Addr = httpd_util:lookup(Info#mod.config_db, bind_address),
- Port = httpd_util:lookup(Info#mod.config_db, port),
- mod_security_server:store_failed_auth(Info, Addr, Port,
- Auth, SDirData).
-
-secretp(Path, ConfigDB) ->
- Directories = ets:match(ConfigDB,{directory,{'$1','_'}}),
- case secret_path(Path, Directories) of
- {yes, Directory} ->
- ?hdrd("secretp - yes", [{dir, Directory}]),
- SDirs0 = httpd_util:multi_lookup(ConfigDB, security_directory),
- [SDir] = lists:filter(fun({Directory0, _})
- when Directory0 == Directory ->
- true;
- (_) ->
- false
- end, SDirs0),
- SDir;
- no ->
- {[], []}
- end.
-
-secret_path(Path,Directories) ->
- secret_path(Path, httpd_util:uniq(lists:sort(Directories)), to_be_found).
-
-secret_path(_Path, [], to_be_found) ->
- no;
-secret_path(_Path, [], Dir) ->
- {yes, Dir};
-secret_path(Path, [[NewDir]|Rest], Dir) ->
- case inets_regexp:match(Path, NewDir) of
- {match, _, _} when Dir =:= to_be_found ->
- secret_path(Path, Rest, NewDir);
- {match, _, Length} when Length > length(Dir) ->
- secret_path(Path, Rest, NewDir);
- {match, _, _} ->
- secret_path(Path, Rest, Dir);
- nomatch ->
- secret_path(Path, Rest, Dir)
- end.
-
-
load("<Directory " ++ Directory, []) ->
- ?hdrt("load security directory - begin", [{directory, Directory}]),
- Dir = httpd_conf:custom_clean(Directory,"",">"),
+ Dir = string:strip(string:strip(Directory),right, $>),
{ok, [{security_directory, {Dir, [{path, Dir}]}}]};
load(eof,[{security_directory, {Directory, _DirData}}|_]) ->
{error, ?NICE("Premature end-of-file in "++Directory)};
load("SecurityDataFile " ++ FileName,
[{security_directory, {Dir, DirData}}]) ->
- ?hdrt("load security directory",
- [{file, FileName}, {dir, Dir}, {dir_data, DirData}]),
- File = httpd_conf:clean(FileName),
+ File = string:strip(FileName),
{ok, [{security_directory, {Dir, [{data_file, File}|DirData]}}]};
load("SecurityCallbackModule " ++ ModuleName,
[{security_directory, {Dir, DirData}}]) ->
- ?hdrt("load security directory",
- [{module, ModuleName}, {dir, Dir}, {dir_data, DirData}]),
- Mod = list_to_atom(httpd_conf:clean(ModuleName)),
+ Mod = list_to_atom(string:strip(ModuleName)),
{ok, [{security_directory, {Dir, [{callback_module, Mod}|DirData]}}]};
load("SecurityMaxRetries " ++ Retries,
[{security_directory, {Dir, DirData}}]) ->
- ?hdrt("load security directory",
- [{max_retries, Retries}, {dir, Dir}, {dir_data, DirData}]),
load_return_int_tag("SecurityMaxRetries", max_retries,
- httpd_conf:clean(Retries), Dir, DirData);
+ string:strip(Retries), Dir, DirData);
load("SecurityBlockTime " ++ Time,
[{security_directory, {Dir, DirData}}]) ->
- ?hdrt("load security directory",
- [{block_time, Time}, {dir, Dir}, {dir_data, DirData}]),
load_return_int_tag("SecurityBlockTime", block_time,
- httpd_conf:clean(Time), Dir, DirData);
+ string:strip(Time), Dir, DirData);
load("SecurityFailExpireTime " ++ Time,
[{security_directory, {Dir, DirData}}]) ->
- ?hdrt("load security directory",
- [{expire_time, Time}, {dir, Dir}, {dir_data, DirData}]),
load_return_int_tag("SecurityFailExpireTime", fail_expire_time,
- httpd_conf:clean(Time), Dir, DirData);
+ string:strip(Time), Dir, DirData);
load("SecurityAuthTimeout " ++ Time0,
[{security_directory, {Dir, DirData}}]) ->
- ?hdrt("load security directory",
- [{auth_timeout, Time0}, {dir, Dir}, {dir_data, DirData}]),
- Time = httpd_conf:clean(Time0),
+ Time = string:strip(Time0),
load_return_int_tag("SecurityAuthTimeout", auth_timeout,
- httpd_conf:clean(Time), Dir, DirData);
+ string:strip(Time), Dir, DirData);
load("AuthName " ++ Name0,
[{security_directory, {Dir, DirData}}]) ->
- ?hdrt("load security directory",
- [{name, Name0}, {dir, Dir}, {dir_data, DirData}]),
- Name = httpd_conf:clean(Name0),
+ Name = string:strip(Name0),
{ok, [{security_directory, {Dir, [{auth_name, Name}|DirData]}}]};
load("</Directory>",[{security_directory, {Dir, DirData}}]) ->
- ?hdrt("load security directory - end",
- [{dir, Dir}, {dir_data, DirData}]),
{ok, [], {security_directory, {Dir, DirData}}}.
-load_return_int_tag(Name, Atom, Time, Dir, DirData) ->
- case Time of
- "infinity" ->
- {ok, [{security_directory, {Dir,
- [{Atom, 99999999999999999999999999999} | DirData]}}]};
- _Int ->
- case catch list_to_integer(Time) of
- {'EXIT', _} ->
- {error, Time++" is an invalid "++Name};
- Val ->
- {ok, [{security_directory, {Dir, [{Atom, Val}|DirData]}}]}
- end
- end.
-
store({security_directory, {Dir, DirData}}, ConfigList)
when is_list(Dir) andalso is_list(DirData) ->
- ?hdrt("store security directory", [{dir, Dir}, {dir_data, DirData}]),
Addr = proplists:get_value(bind_address, ConfigList),
Port = proplists:get_value(port, ConfigList),
- mod_security_server:start(Addr, Port),
+ Profile = proplists:get_value(profile, ConfigList, ?DEFAULT_PROFILE),
+ mod_security_server:start(Addr, Port, Profile),
SR = proplists:get_value(server_root, ConfigList),
case proplists:get_value(data_file, DirData, no_data_file) of
no_data_file ->
@@ -241,7 +156,7 @@ store({security_directory, {Dir, DirData}}, ConfigList)
_ ->
DataFile0
end,
- case mod_security_server:new_table(Addr, Port, DataFile) of
+ case mod_security_server:new_table(Addr, Port, Profile, DataFile) of
{ok, TwoTables} ->
NewDirData0 = lists:keyreplace(data_file, 1, DirData,
{data_file, TwoTables}),
@@ -261,45 +176,35 @@ store({directory, {Directory, DirData}}, _) ->
{error, {wrong_type, {security_directory, {Directory, DirData}}}}.
remove(ConfigDB) ->
- Addr = case ets:lookup(ConfigDB, bind_address) of
- [] ->
- undefined;
- [{bind_address, Address}] ->
- Address
- end,
- [{port, Port}] = ets:lookup(ConfigDB, port),
- mod_security_server:delete_tables(Addr, Port),
- mod_security_server:stop(Addr, Port).
+ Addr = httpd_util:lookup(ConfigDB, bind_address, undefined),
+ Port = httpd_util:lookup(ConfigDB, port),
+ Profile = httpd_util:lookup(ConfigDB, profile, ?DEFAULT_PROFILE),
+ mod_security_server:delete_tables(Addr, Port, Profile),
+ mod_security_server:stop(Addr, Port, Profile).
-%%
-%% User API
-%%
-
-%% list_blocked_users
-
list_blocked_users(Port) ->
list_blocked_users(undefined, Port).
list_blocked_users(Port, Dir) when is_integer(Port) ->
list_blocked_users(undefined,Port,Dir);
list_blocked_users(Addr, Port) when is_integer(Port) ->
- mod_security_server:list_blocked_users(Addr, Port).
+ lists:map(fun({User, Addr0, Port0, ?DEFAULT_PROFILE, Dir0, Time}) ->
+ {User, Addr0, Port0, Dir0,Time}
+ end,
+ mod_security_server:list_blocked_users(Addr, Port)).
list_blocked_users(Addr, Port, Dir) ->
- mod_security_server:list_blocked_users(Addr, Port, Dir).
-
-
-%% block_user
+ lists:map(fun({User, Addr0, Port0, ?DEFAULT_PROFILE, Dir0, Time}) ->
+ {User, Addr0, Port0, Dir0,Time}
+ end,
+ mod_security_server:list_blocked_users(Addr, Port, Dir)).
block_user(User, Port, Dir, Time) ->
block_user(User, undefined, Port, Dir, Time).
block_user(User, Addr, Port, Dir, Time) ->
mod_security_server:block_user(User, Addr, Port, Dir, Time).
-
-%% unblock_user
-
unblock_user(User, Port) ->
unblock_user(User, undefined, Port).
@@ -311,9 +216,6 @@ unblock_user(User, Addr, Port) when is_integer(Port) ->
unblock_user(User, Addr, Port, Dir) ->
mod_security_server:unblock_user(User, Addr, Port, Dir).
-
-%% list_auth_users
-
list_auth_users(Port) ->
list_auth_users(undefined,Port).
@@ -324,3 +226,76 @@ list_auth_users(Addr, Port) when is_integer(Port) ->
list_auth_users(Addr, Port, Dir) ->
mod_security_server:list_auth_users(Addr, Port, Dir).
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+report_failed(Info, Auth, Event) ->
+ Request = Info#mod.request_line,
+ {_PortNumber,RemoteHost}=(Info#mod.init_data)#init_data.peername,
+ String = RemoteHost ++ " : " ++ Event ++ " : " ++ Request ++
+ " : " ++ Auth,
+ mod_disk_log:security_log(Info,String),
+ mod_log:security_log(Info, String).
+
+take_failed_action(Info, Auth) ->
+ Path = mod_alias:path(Info#mod.data, Info#mod.config_db,
+ Info#mod.request_uri),
+ {_Dir, SDirData} = secretp(Path, Info#mod.config_db),
+ Addr = httpd_util:lookup(Info#mod.config_db, bind_address),
+ Port = httpd_util:lookup(Info#mod.config_db, port),
+ Profile = httpd_util:lookup(Info#mod.config_db, profile, ?DEFAULT_PROFILE),
+ mod_security_server:store_failed_auth(Info, Addr, Port, Profile,
+ Auth, SDirData).
+
+secretp(Path, ConfigDB) ->
+ Directories = ets:match(ConfigDB,{directory,{'$1','_'}}),
+ case secret_path(Path, Directories) of
+ {yes, Directory} ->
+ SDirs0 = httpd_util:multi_lookup(ConfigDB, security_directory),
+ [SDir] = lists:filter(fun({Directory0, _})
+ when Directory0 == Directory ->
+ true;
+ (_) ->
+ false
+ end, SDirs0),
+ SDir;
+ no ->
+ {[], []}
+ end.
+
+secret_path(Path,Directories) ->
+ secret_path(Path, httpd_util:uniq(lists:sort(Directories)), to_be_found).
+
+secret_path(_Path, [], to_be_found) ->
+ no;
+secret_path(_Path, [], Dir) ->
+ {yes, Dir};
+secret_path(Path, [[NewDir]|Rest], Dir) ->
+ case re:run(Path, NewDir, [{capture, first}]) of
+ {match, _} when Dir =:= to_be_found ->
+ secret_path(Path, Rest, NewDir);
+ {match, [{_, Length}]} when Length > length(Dir) ->
+ secret_path(Path, Rest, NewDir);
+ {match, _} ->
+ secret_path(Path, Rest, Dir);
+ nomatch ->
+ secret_path(Path, Rest, Dir)
+ end.
+
+
+
+load_return_int_tag(Name, Atom, Time, Dir, DirData) ->
+ case Time of
+ "infinity" ->
+ {ok, [{security_directory, {Dir,
+ [{Atom, 99999999999999999999999999999} | DirData]}}]};
+ _Int ->
+ case catch list_to_integer(Time) of
+ {'EXIT', _} ->
+ {error, Time++" is an invalid "++Name};
+ Val ->
+ {ok, [{security_directory, {Dir, [{Atom, Val}|DirData]}}]}
+ end
+ end.
diff --git a/lib/inets/src/http_server/mod_security_server.erl b/lib/inets/src/http_server/mod_security_server.erl
index 784b3eba70..4f52357af3 100644
--- a/lib/inets/src/http_server/mod_security_server.erl
+++ b/lib/inets/src/http_server/mod_security_server.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -45,7 +46,6 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
--include("inets_internal.hrl").
-behaviour(gen_server).
@@ -57,129 +57,105 @@
list_auth_users/2, list_auth_users/3]).
%% Internal exports (for mod_security only)
--export([start/2, stop/1, stop/2,
- new_table/3, delete_tables/2,
- store_failed_auth/5, store_successful_auth/4,
- check_blocked_user/5]).
+-export([start/3, stop/2, stop/3,
+ new_table/4, delete_tables/3,
+ store_failed_auth/6, store_successful_auth/5,
+ check_blocked_user/6]).
%% gen_server exports
--export([start_link/2, init/1,
+-export([start_link/3, init/1,
handle_info/2, handle_call/3, handle_cast/2,
terminate/2,
code_change/3]).
+%%====================================================================
+%% Internal application API
+%%====================================================================
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% External API %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% start_link/3
-%%
%% NOTE: This is called by httpd_misc_sup when the process is started
-%%
-
-start_link(Addr, Port) ->
- ?hdrt("start_link", [{address, Addr}, {port, Port}]),
- Name = make_name(Addr, Port),
+start_link(Addr, Port, Profile) ->
+ Name = make_name(Addr, Port, Profile),
gen_server:start_link({local, Name}, ?MODULE, [], [{timeout, infinity}]).
-
-%% start/2
%% Called by the mod_security module.
-
-start(Addr, Port) ->
- ?hdrt("start", [{address, Addr}, {port, Port}]),
- Name = make_name(Addr, Port),
+start(Addr, Port, Profile) ->
+ Name = make_name(Addr, Port, Profile),
case whereis(Name) of
undefined ->
- httpd_misc_sup:start_sec_server(Addr, Port);
+ httpd_misc_sup:start_sec_server(Addr, Port, Profile);
_ -> %% Already started...
ok
end.
-
-%% stop
-
-stop(Port) ->
- stop(undefined, Port).
-stop(Addr, Port) ->
- ?hdrt("stop", [{address, Addr}, {port, Port}]),
- Name = make_name(Addr, Port),
+stop(Port, Profile) ->
+ stop(undefined, Port, Profile).
+stop(Addr, Port, Profile) ->
+ Name = make_name(Addr, Port, Profile),
case whereis(Name) of
undefined ->
ok;
_ ->
- httpd_misc_sup:stop_sec_server(Addr, Port)
+ httpd_misc_sup:stop_sec_server(Addr, Port, Profile)
end.
-
addr(undefined) ->
any;
addr(Addr) ->
Addr.
-
-%% list_blocked_users
-
list_blocked_users(Addr, Port) ->
- Name = make_name(Addr, Port),
- Req = {list_blocked_users, addr(Addr), Port, '_'},
- call(Name, Req).
-
+ list_blocked_users(Addr, Port, ?DEFAULT_PROFILE).
+list_blocked_users(Addr, Port, Profile) when is_atom(Profile)->
+ Name = make_name(Addr, Port, Profile),
+ Req = {list_blocked_users, addr(Addr), Port, Profile,'_'},
+ call(Name, Req);
list_blocked_users(Addr, Port, Dir) ->
- Name = make_name(Addr, Port),
- Req = {list_blocked_users, addr(Addr), Port, Dir},
+ list_blocked_users(Addr, Port, ?DEFAULT_PROFILE, Dir).
+list_blocked_users(Addr, Port, Profile, Dir) ->
+ Name = make_name(Addr, Port, Profile),
+ Req = {list_blocked_users, addr(Addr), Port, Profile, Dir},
call(Name, Req).
-
-%% block_user
-
block_user(User, Addr, Port, Dir, Time) ->
- Name = make_name(Addr, Port),
- Req = {block_user, User, addr(Addr), Port, Dir, Time},
+ block_user(User, Addr, Port, ?DEFAULT_PROFILE, Dir, Time).
+block_user(User, Addr, Port, Profile, Dir, Time) ->
+ Name = make_name(Addr, Port, Profile),
+ Req = {block_user, User, addr(Addr), Port, Profile, Dir, Time},
call(Name, Req).
-
-%% unblock_user
-
unblock_user(User, Addr, Port) ->
- Name = make_name(Addr, Port),
- Req = {unblock_user, User, addr(Addr), Port, '_'},
- call(Name, Req).
-
+ unblock_user(User, Addr, Port, ?DEFAULT_PROFILE).
+unblock_user(User, Addr, Port, Profile) when is_atom(Profile)->
+ Name = make_name(Addr, Port, Profile),
+ Req = {unblock_user, User, addr(Addr), Port, Profile, '_'},
+ call(Name, Req);
unblock_user(User, Addr, Port, Dir) ->
- Name = make_name(Addr, Port),
- Req = {unblock_user, User, addr(Addr), Port, Dir},
+ unblock_user(User, Addr, Port, ?DEFAULT_PROFILE, Dir).
+unblock_user(User, Addr, Port, Profile, Dir) ->
+ Name = make_name(Addr, Port, Profile),
+ Req = {unblock_user, User, addr(Addr), Port, Profile, Dir},
call(Name, Req).
-
-%% list_auth_users
-
list_auth_users(Addr, Port) ->
- Name = make_name(Addr, Port),
- Req = {list_auth_users, addr(Addr), Port, '_'},
- call(Name, Req).
-
+ list_auth_users(Addr, Port, ?DEFAULT_PROFILE).
+list_auth_users(Addr, Port, Profile) when is_atom(Profile) ->
+ Name = make_name(Addr, Port, Profile),
+ Req = {list_auth_users, addr(Addr), Port, Profile, '_'},
+ call(Name, Req);
list_auth_users(Addr, Port, Dir) ->
- Name = make_name(Addr,Port),
- Req = {list_auth_users, addr(Addr), Port, Dir},
+ list_auth_users(Addr, Port, ?DEFAULT_PROFILE, Dir).
+list_auth_users(Addr, Port, Profile, Dir) ->
+ Name = make_name(Addr,Port, Profile),
+ Req = {list_auth_users, addr(Addr), Port, Profile, Dir},
call(Name, Req).
-
-%% new_table
-
-new_table(Addr, Port, TabName) ->
- Name = make_name(Addr,Port),
- Req = {new_table, addr(Addr), Port, TabName},
+new_table(Addr, Port, Profile, TabName) ->
+ Name = make_name(Addr,Port, Profile),
+ Req = {new_table, addr(Addr), Port, Profile, TabName},
call(Name, Req).
-
-%% delete_tables
-
-delete_tables(Addr, Port) ->
- Name = make_name(Addr, Port),
+delete_tables(Addr, Port, Profile) ->
+ Name = make_name(Addr, Port, Profile),
case whereis(Name) of
undefined ->
ok;
@@ -187,79 +163,53 @@ delete_tables(Addr, Port) ->
call(Name, delete_tables)
end.
-
-%% store_failed_auth
-
-store_failed_auth(Info, Addr, Port, DecodedString, SDirData) ->
- ?hdrv("store failed auth",
- [{addr, Addr}, {port, Port},
- {decoded_string, DecodedString}, {sdir_data, SDirData}]),
- Name = make_name(Addr,Port),
- Msg = {store_failed_auth,[Info,DecodedString,SDirData]},
+store_failed_auth(Info, Addr, Port, Profile, DecodedString, SDirData) ->
+ Name = make_name(Addr, Port, Profile),
+ Msg = {store_failed_auth, Profile, [Info,DecodedString,SDirData]},
cast(Name, Msg).
-
-%% store_successful_auth
-
-store_successful_auth(Addr, Port, User, SDirData) ->
- Name = make_name(Addr,Port),
- Msg = {store_successful_auth, [User,Addr,Port,SDirData]},
+store_successful_auth(Addr, Port, Profile, User, SDirData) ->
+ Name = make_name(Addr,Port, Profile),
+ Msg = {store_successful_auth, [User,Addr,Port, Profile, SDirData]},
cast(Name, Msg).
-
-
-%% check_blocked_user
-
-check_blocked_user(Info, User, SDirData, Addr, Port) ->
- Name = make_name(Addr, Port),
- Req = {check_blocked_user, [Info, User, SDirData]},
+
+check_blocked_user(Info, User, SDirData, Addr, Port, Profile) ->
+ Name = make_name(Addr, Port, Profile),
+ Req = {check_blocked_user, Profile, [Info, User, SDirData]},
call(Name, Req).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% Server call-back functions %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
+%%====================================================================
+%% Behavior call backs
+%%====================================================================
init(_) ->
- ?hdrv("initiating", []),
process_flag(trap_exit, true),
{ok, []}.
handle_call(stop, _From, _Tables) ->
{stop, normal, ok, []};
-handle_call({block_user, User, Addr, Port, Dir, Time}, _From, Tables) ->
- ?hdrv("block user",
- [{user, User}, {addr, Addr}, {port, Port}, {dir, Dir},
- {time, Time}]),
- Ret = block_user_int(User, Addr, Port, Dir, Time),
+handle_call({block_user, User, Addr, Port, Profile, Dir, Time}, _From, Tables) ->
+ Ret = block_user_int(User, Addr, Port, Profile, Dir, Time),
{reply, Ret, Tables};
-handle_call({list_blocked_users, Addr, Port, Dir}, _From, Tables) ->
- ?hdrv("list blocked users",
- [{addr, Addr}, {port, Port}, {dir, Dir}]),
- Blocked = list_blocked(Tables, Addr, Port, Dir, []),
+handle_call({list_blocked_users, Addr, Port, Profile, Dir}, _From, Tables) ->
+ Blocked = list_blocked(Tables, Addr, Port, Profile, Dir, []),
{reply, Blocked, Tables};
-handle_call({unblock_user, User, Addr, Port, Dir}, _From, Tables) ->
- ?hdrv("block user",
- [{user, User}, {addr, Addr}, {port, Port}, {dir, Dir}]),
- Ret = unblock_user_int(User, Addr, Port, Dir),
+handle_call({unblock_user, User, Addr, Port, Profile, Dir}, _From, Tables) ->
+ Ret = unblock_user_int(User, Addr, Port, Profile,Dir),
{reply, Ret, Tables};
-handle_call({list_auth_users, Addr, Port, Dir}, _From, Tables) ->
- ?hdrv("list auth users",
- [{addr, Addr}, {port, Port}, {dir, Dir}]),
- Auth = list_auth(Tables, Addr, Port, Dir, []),
+handle_call({list_auth_users, Addr, Port, Profile, Dir}, _From, Tables) ->
+ Auth = list_auth(Tables, Addr, Port, Profile, Dir, []),
{reply, Auth, Tables};
-handle_call({new_table, Addr, Port, Name}, _From, Tables) ->
+handle_call({new_table, Addr, Port, Profile, Name}, _From, Tables) ->
case lists:keysearch(Name, 1, Tables) of
{value, {Name, {Ets, Dets}}} ->
{reply, {ok, {Ets, Dets}}, Tables};
false ->
- TName = make_name(Addr,Port,length(Tables)),
+ TName = make_name(Addr,Port, Profile, length(Tables)),
case dets:open_file(TName, [{type, bag}, {file, Name},
{repair, true},
{access, read_write}]) of
@@ -280,7 +230,7 @@ handle_call(delete_tables, _From, Tables) ->
end, Tables),
{reply, ok, []};
-handle_call({check_blocked_user, [Info, User, SDirData]}, _From, Tables) ->
+handle_call({check_blocked_user, Profile, [Info, User, SDirData]}, _From, Tables) ->
{ETS, DETS} = proplists:get_value(data_file, SDirData),
Dir = proplists:get_value(path, SDirData),
Addr = proplists:get_value(bind_address, SDirData),
@@ -288,27 +238,24 @@ handle_call({check_blocked_user, [Info, User, SDirData]}, _From, Tables) ->
CBModule =
proplists:get_value(callback_module, SDirData, no_module_at_all),
Ret =
- check_blocked_user(Info, User, Dir, Addr, Port, ETS, DETS, CBModule),
+ check_blocked_user(Info, User, Dir, Addr, Port, Profile, ETS, DETS, CBModule),
{reply, Ret, Tables};
handle_call(_Request,_From,Tables) ->
{reply,ok,Tables}.
-
-%% handle_cast
-
-handle_cast({store_failed_auth, [_, _, []]}, Tables) ->
+handle_cast({store_failed_auth, _,[_, _, []]}, Tables) ->
%% Some other authentication scheme than mod_auth (example mod_htacess)
%% was the source for the authentication failure so we should ignor it!
{noreply, Tables};
-handle_cast({store_failed_auth, [Info, DecodedString, SDirData]}, Tables) ->
+handle_cast({store_failed_auth, Profile, [Info, DecodedString, SDirData]}, Tables) ->
{ETS, DETS} = proplists:get_value(data_file, SDirData),
Dir = proplists:get_value(path, SDirData),
Addr = proplists:get_value(bind_address, SDirData),
Port = proplists:get_value(port, SDirData),
{ok, [User,Password]} = httpd_util:split(DecodedString,":",2),
Seconds = universal_time(),
- Key = {User, Dir, Addr, Port},
+ Key = {User, Dir, Addr, Port, Profile},
%% Event
CBModule = proplists:get_value(callback_module,
SDirData, no_module_at_all),
@@ -363,7 +310,7 @@ handle_cast({store_failed_auth, [Info, DecodedString, SDirData]}, Tables) ->
dets:match_delete(DETS, {blocked_user,
{User, Addr, Port, Dir, '$1'}}),
BlockRecord = {blocked_user,
- {User, Addr, Port, Dir, Future}},
+ {User, Addr, Port, Profile, Dir, Future}},
ets:insert(ETS, BlockRecord),
dets:insert(DETS, BlockRecord),
%% Remove previous failed requests.
@@ -374,11 +321,11 @@ handle_cast({store_failed_auth, [Info, DecodedString, SDirData]}, Tables) ->
end,
{noreply, Tables};
-handle_cast({store_successful_auth, [User, Addr, Port, SDirData]}, Tables) ->
+handle_cast({store_successful_auth, [User, Addr, Port, Profile, SDirData]}, Tables) ->
{ETS, DETS} = proplists:get_value(data_file, SDirData),
AuthTimeOut = proplists:get_value(auth_timeout, SDirData, 30),
Dir = proplists:get_value(path, SDirData),
- Key = {User, Dir, Addr, Port},
+ Key = {User, Dir, Addr, Port, Profile},
%% Remove failed entries for this Key
dets:match_delete(DETS, {failed, {Key, '_', '_'}}),
@@ -396,33 +343,22 @@ handle_cast(Req, Tables) ->
error_msg("security server got unknown cast: ~p",[Req]),
{noreply, Tables}.
-
-%% handle_info
-
handle_info(_Info, State) ->
{noreply, State}.
-
-%% terminate
-
terminate(_Reason, _Tables) ->
ok.
-
-%% code_change({down, ToVsn}, State, Extra)
-%%
-code_change({down, _}, State, _Extra) ->
- {ok, State};
-
-
-%% code_change(FromVsn, State, Extra)
-%%
code_change(_, State, _Extra) ->
{ok, State}.
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
%% block_user_int/5
-block_user_int(User, Addr, Port, Dir, Time) ->
- Dirs = httpd_manager:config_match(Addr, Port,
+block_user_int(User, Addr, Port, Profile, Dir, Time) ->
+ Dirs = httpd_manager:config_match(Addr, Port, Profile,
{security_directory, {'_', '_'}}),
case find_dirdata(Dirs, Dir) of
{ok, DirData, {ETS, DETS}} ->
@@ -434,11 +370,11 @@ block_user_int(User, Addr, Port, Dir, Time) ->
Time
end,
Future = universal_time()+Time1,
- ets:match_delete(ETS, {blocked_user, {User,Addr,Port,Dir,'_'}}),
+ ets:match_delete(ETS, {blocked_user, {User,Addr,Port,Profile, Dir,'_'}}),
dets:match_delete(DETS, {blocked_user,
- {User,Addr,Port,Dir,'_'}}),
- ets:insert(ETS, {blocked_user, {User,Addr,Port,Dir,Future}}),
- dets:insert(DETS, {blocked_user, {User,Addr,Port,Dir,Future}}),
+ {User,Addr,Port,Profile, Dir,'_'}}),
+ ets:insert(ETS, {blocked_user, {User,Addr,Port, Profile, Dir,Future}}),
+ dets:insert(DETS, {blocked_user, {User,Addr,Port,Profile, Dir,Future}}),
CBModule = proplists:get_value(callback_module, DirData,
no_module_at_all),
user_block_event(CBModule,Addr,Port,Dir,User),
@@ -447,7 +383,6 @@ block_user_int(User, Addr, Port, Dir, Time) ->
{error, no_such_directory}
end.
-
find_dirdata([], _Dir) ->
false;
find_dirdata([{security_directory, {_, DirData}}|SDirs], Dir) ->
@@ -460,21 +395,20 @@ find_dirdata([{security_directory, {_, DirData}}|SDirs], Dir) ->
find_dirdata(SDirs, Dir)
end.
-%% unblock_user_int/4
-unblock_user_int(User, Addr, Port, Dir) ->
- Dirs = httpd_manager:config_match(Addr, Port,
+unblock_user_int(User, Addr, Port, Profile, Dir) ->
+ Dirs = httpd_manager:config_match(Addr, Port, Profile,
{security_directory, {'_', '_'}}),
case find_dirdata(Dirs, Dir) of
{ok, DirData, {ETS, DETS}} ->
case ets:match_object(ETS,
- {blocked_user,{User,Addr,Port,Dir,'_'}}) of
+ {blocked_user,{User,Addr,Port,Profile,Dir,'_'}}) of
[] ->
{error, not_blocked};
_Objects ->
ets:match_delete(ETS, {blocked_user,
- {User, Addr, Port, Dir, '_'}}),
+ {User, Addr, Port, Profile, Dir, '_'}}),
dets:match_delete(DETS, {blocked_user,
- {User, Addr, Port, Dir, '_'}}),
+ {User, Addr, Port, Profile, Dir, '_'}}),
CBModule = proplists:get_value(callback_module,
DirData,
no_module_at_all),
@@ -485,63 +419,51 @@ unblock_user_int(User, Addr, Port, Dir) ->
{error, no_such_directory}
end.
-
-
-%% list_auth/2
-
-list_auth([], _Addr, _Port, _Dir, Acc) ->
+list_auth([], _, _, _, _, Acc) ->
Acc;
-list_auth([{_Name, {ETS, DETS}}|Tables], Addr, Port, Dir, Acc) ->
- case ets:match_object(ETS, {success, {{'_', Dir, Addr, Port}, '_'}}) of
+list_auth([{_Name, {ETS, DETS}}|Tables], Addr, Port, Profile, Dir, Acc) ->
+ case ets:match_object(ETS, {success, {{'_', Dir, Addr, Port, Profile}, '_'}}) of
[] ->
- list_auth(Tables, Addr, Port, Dir, Acc);
+ list_auth(Tables, Addr, Port, Profile, Dir, Acc);
List ->
TN = universal_time(),
- NewAcc = lists:foldr(fun({success,{{U,Ad,P,D},T}},Ac) ->
+ NewAcc = lists:foldr(fun({success,{{U,Ad,P, Pr,D},T}},Ac) ->
if
T-TN > 0 ->
[U|Ac];
true ->
Rec = {success,
- {{U,Ad,P,D},T}},
+ {{U,Ad,P,Pr,D},T}},
ets:match_delete(ETS,Rec),
dets:match_delete(DETS,Rec),
Ac
end
end,
Acc, List),
- list_auth(Tables, Addr, Port, Dir, NewAcc)
+ list_auth(Tables, Addr, Port, Profile, Dir, NewAcc)
end.
-
-%% list_blocked/2
-
-list_blocked([], _Addr, _Port, _Dir, Acc) ->
- ?hdrv("list blocked", [{acc, Acc}]),
+list_blocked([], _, _, _, _, Acc) ->
TN = universal_time(),
- lists:foldl(fun({U,Ad,P,D,T}, Ac) ->
+ lists:foldl(fun({U,Ad,P,Pr,D,T}, Ac) ->
if
T-TN > 0 ->
- [{U,Ad,P,D,local_time(T)}|Ac];
+ [{U,Ad,P, Pr,D,local_time(T)}|Ac];
true ->
Ac
end
end,
[], Acc);
-list_blocked([{_Name, {ETS, _DETS}}|Tables], Addr, Port, Dir, Acc) ->
- ?hdrv("list blocked", [{ets, ETS}, {tab2list, ets:tab2list(ETS)}]),
+list_blocked([{_Name, {ETS, _DETS}}|Tables], Addr, Port, Profile, Dir, Acc) ->
List = ets:match_object(ETS, {blocked_user,
- {'_',Addr,Port,Dir,'_'}}),
+ {'_',Addr,Port,Profile, Dir,'_'}}),
NewBlocked = lists:foldl(fun({blocked_user, X}, A) ->
[X|A] end, Acc, List),
- list_blocked(Tables, Addr, Port, Dir, NewBlocked).
+ list_blocked(Tables, Addr, Port, Profile, Dir, NewBlocked).
-%%
-%% sync_dets_to_ets/2
-%%
%% Reads dets-table DETS and syncronizes it with the ets-table ETS.
%%
sync_dets_to_ets(DETS, ETS) ->
@@ -550,68 +472,62 @@ sync_dets_to_ets(DETS, ETS) ->
continue
end).
-%%
-%% check_blocked_user/7 -> true | false
-%%
%% Check if a specific user is blocked from access.
%%
%% The sideeffect of this routine is that it unblocks also other users
%% whos blocking time has expired. This to keep the tables as small
%% as possible.
%%
-check_blocked_user(Info, User, Dir, Addr, Port, ETS, DETS, CBModule) ->
+check_blocked_user(Info, User, Dir, Addr, Port, Profile, ETS, DETS, CBModule) ->
TN = universal_time(),
- BlockList = ets:match_object(ETS, {blocked_user, {User, '_', '_', '_', '_'}}),
+ BlockList = ets:match_object(ETS, {blocked_user, {User, '_', '_', '_', '_', '_'}}),
Blocked = lists:foldl(fun({blocked_user, X}, A) ->
[X|A] end, [], BlockList),
check_blocked_user(Info,User,Dir,
- Addr,Port,ETS,DETS,TN,Blocked,CBModule).
+ Addr,Port, Profile, ETS,DETS,TN,Blocked,CBModule).
-check_blocked_user(_Info, _User, _Dir, _Addr, _Port, _ETS, _DETS, _TN,
- [], _CBModule) ->
+check_blocked_user(_Info, _, _, _, _, _, _, _, _,[], _CBModule) ->
false;
-check_blocked_user(Info, User, Dir, Addr, Port, ETS, DETS, TN,
- [{User,Addr,Port,Dir,T}| _], CBModule) ->
+check_blocked_user(Info, User, Dir, Addr, Port, Profile, ETS, DETS, TN,
+ [{User,Addr,Port,Profile, Dir,T}| _], CBModule) ->
TD = T-TN,
if
TD =< 0 ->
%% Blocking has expired, remove and grant access.
- unblock_user(Info, User, Dir, Addr, Port, ETS, DETS, CBModule),
+ unblock_user(Info, User, Dir, Addr, Port, Profile, ETS, DETS, CBModule),
false;
true ->
true
end;
-check_blocked_user(Info, User, Dir, Addr, Port, ETS, DETS, TN,
- [{OUser,ODir,OAddr,OPort,T}|Ls], CBModule) ->
+check_blocked_user(Info, User, Dir, Addr, Port, Profile, ETS, DETS, TN,
+ [{OUser,ODir,OAddr,OPort, OProfile, T}|Ls], CBModule) ->
TD = T-TN,
if
TD =< 0 ->
%% Blocking has expired, remove.
- unblock_user(Info, OUser, ODir, OAddr, OPort,
+ unblock_user(Info, OUser, ODir, OAddr, OPort, OProfile,
ETS, DETS, CBModule);
true ->
true
end,
- check_blocked_user(Info, User, Dir, Addr, Port, ETS, DETS,
+ check_blocked_user(Info, User, Dir, Addr, Port, Profile, ETS, DETS,
TN, Ls, CBModule).
-unblock_user(Info, User, Dir, Addr, Port, ETS, DETS, CBModule) ->
+unblock_user(Info, User, Dir, Addr, Port, Profile, ETS, DETS, CBModule) ->
Reason =
io_lib:format("User ~s was removed from the block list for dir ~s",
[User, Dir]),
mod_log:security_log(Info, lists:flatten(Reason)),
user_unblock_event(CBModule,Addr,Port,Dir,User),
- dets:match_delete(DETS, {blocked_user, {User, Addr, Port, Dir, '_'}}),
- ets:match_delete(ETS, {blocked_user, {User, Addr, Port, Dir, '_'}}).
+ dets:match_delete(DETS, {blocked_user, {User, Addr, Port, Profile, Dir, '_'}}),
+ ets:match_delete(ETS, {blocked_user, {User, Addr, Port, Profile, Dir, '_'}}).
+make_name(Addr,Port, Profile) ->
+ httpd_util:make_name(?MODULE_STRING, Addr, Port, Profile).
-make_name(Addr,Port) ->
- httpd_util:make_name("httpd_security",Addr,Port).
-
-make_name(Addr,Port,Num) ->
- httpd_util:make_name("httpd_security",Addr,Port,
- "__" ++ integer_to_list(Num)).
-
+make_name(Addr,Port, Profile, Num) ->
+ httpd_util:make_name(?MODULE_STRING, Addr,Port,
+ atom_to_list(Profile) ++ "__" ++ integer_to_list(Num)).
auth_fail_event(Mod,Addr,Port,Dir,User,Passwd) ->
event(auth_fail,Mod,Addr,Port,Dir,[{user,User},{password,Passwd}]).
@@ -623,17 +539,10 @@ user_unblock_event(Mod,Addr,Port,Dir,User) ->
event(user_unblock,Mod,Addr,Port,Dir,[{user,User}]).
event(Event, Mod, undefined, Port, Dir, Info) ->
- ?hdrt("event",
- [{event, Event}, {mod, Mod}, {port, Port}, {dir, Dir}]),
(catch Mod:event(Event,Port,Dir,Info));
event(Event, Mod, any, Port, Dir, Info) ->
- ?hdrt("event",
- [{event, Event}, {mod, Mod}, {port, Port}, {dir, Dir}]),
(catch Mod:event(Event,Port,Dir,Info));
event(Event, Mod, Addr, Port, Dir, Info) ->
- ?hdrt("event",
- [{event, Event}, {mod, Mod},
- {addr, Addr}, {port, Port}, {dir, Dir}]),
(catch Mod:event(Event,Addr,Port,Dir,Info)).
universal_time() ->
@@ -643,11 +552,9 @@ local_time(T) ->
calendar:universal_time_to_local_time(
calendar:gregorian_seconds_to_datetime(T)).
-
error_msg(F, A) ->
error_logger:error_msg(F, A).
-
call(Name, Req) ->
case (catch gen_server:call(Name, Req)) of
{'EXIT', Reason} ->
@@ -656,7 +563,6 @@ call(Name, Req) ->
Reply
end.
-
cast(Name, Msg) ->
case (catch gen_server:cast(Name, Msg)) of
{'EXIT', Reason} ->
diff --git a/lib/inets/src/http_server/mod_trace.erl b/lib/inets/src/http_server/mod_trace.erl
index 7233925783..f007426ae3 100644
--- a/lib/inets/src/http_server/mod_trace.erl
+++ b/lib/inets/src/http_server/mod_trace.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/inets_app/Makefile b/lib/inets/src/inets_app/Makefile
index 22426eee79..eb0098dbee 100644
--- a/lib/inets/src/inets_app/Makefile
+++ b/lib/inets/src/inets_app/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2012. All Rights Reserved.
+# Copyright Ericsson AB 2005-2016. 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/.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# 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.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
@@ -46,8 +47,8 @@ MODULES = \
inets_service \
inets_app \
inets_sup \
- inets_regexp \
- inets_trace
+ inets_trace \
+ inets_lib
INTERNAL_HRL_FILES = inets_internal.hrl
EXTERNAL_HRL_FILES = ../../include/httpd.hrl \
diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src
index 4aea2ef3d7..eb4be932ac 100644
--- a/lib/inets/src/inets_app/inets.app.src
+++ b/lib/inets/src/inets_app/inets.app.src
@@ -1,18 +1,19 @@
%% This is an -*- erlang -*- file.
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -25,8 +26,8 @@
inets_sup,
inets_app,
inets_service,
- inets_regexp,
- inets_trace,
+ inets_trace,
+ inets_lib,
%% FTP
ftp,
@@ -59,7 +60,10 @@
httpd_acceptor,
httpd_acceptor_sup,
httpd_cgi,
+ httpd_connection_sup,
httpd_conf,
+ httpd_custom,
+ httpd_custom_api,
httpd_esi,
httpd_example,
httpd_file,
@@ -89,7 +93,6 @@
mod_get,
mod_head,
mod_htaccess,
- mod_include,
mod_log,
mod_range,
mod_responsecontrol,
@@ -109,4 +112,6 @@
{registered,[inets_sup, httpc_manager]},
%% If the "new" ssl is used then 'crypto' must be started before inets.
{applications,[kernel,stdlib]},
- {mod,{inets_app,[]}}]}.
+ {mod,{inets_app,[]}},
+ {runtime_dependencies, ["stdlib-2.0","ssl-5.3.4","runtime_tools-1.8.14",
+ "mnesia-4.12","kernel-3.0","erts-6.0"]}]}.
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index c63dcafa6c..3a31daeb20 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -1,25 +1,28 @@
-%% This is an -*- erlang -*- file.
+%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
-
{"%VSN%",
[
- {<<"5\\.*">>, [{restart_application, inets}]}
- ],
+ {<<"6\\..*">>,[{restart_application, inets}]},
+ {<<"5\\..*">>,[{restart_application, inets}]}
+ ],
[
- {<<"5\\.*">>, [{restart_application, inets}]}
-]}.
+ {<<"6\\..*">>,[{restart_application, inets}]},
+ {<<"5\\..*">>,[{restart_application, inets}]}
+ ]
+}.
diff --git a/lib/inets/src/inets_app/inets.erl b/lib/inets/src/inets_app/inets.erl
index ed8082534f..2d380012d7 100644
--- a/lib/inets/src/inets_app/inets.erl
+++ b/lib/inets/src/inets_app/inets.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/inets_app/inets.mk b/lib/inets/src/inets_app/inets.mk
index adef32dc19..1228514661 100644
--- a/lib/inets/src/inets_app/inets.mk
+++ b/lib/inets/src/inets_app/inets.mk
@@ -2,18 +2,19 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2012. All Rights Reserved.
+# Copyright Ericsson AB 2010-2016. 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/.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# 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.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
diff --git a/lib/inets/src/inets_app/inets_app.erl b/lib/inets/src/inets_app/inets_app.erl
index ee3359c4c3..da4e9ead25 100644
--- a/lib/inets/src/inets_app/inets_app.erl
+++ b/lib/inets/src/inets_app/inets_app.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/inets_app/inets_internal.hrl b/lib/inets/src/inets_app/inets_internal.hrl
index e56af3b59d..079b415b56 100644
--- a/lib/inets/src/inets_app/inets_internal.hrl
+++ b/lib/inets/src/inets_app/inets_internal.hrl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -21,6 +22,8 @@
-ifndef(inets_internal_hrl).
-define(inets_internal_hrl, true).
+-define(STACK(), erlang:get_stacktrace()).
+
%% Various trace macros
-define(report(Severity, Label, Service, Content),
diff --git a/lib/inets/src/inets_app/inets_lib.erl b/lib/inets/src/inets_app/inets_lib.erl
new file mode 100644
index 0000000000..8993be29e4
--- /dev/null
+++ b/lib/inets/src/inets_app/inets_lib.erl
@@ -0,0 +1,50 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015-2015. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(inets_lib).
+
+-export([millisec_passed/1, formated_timestamp/0, format_timestamp/1]).
+
+
+
+%% Help function, elapsed milliseconds since T0
+millisec_passed({_,_,_} = T0 ) ->
+ %% OTP 17 and earlier
+ timer:now_diff(erlang:timestamp(), T0) div 1000;
+
+millisec_passed(T0) ->
+ %% OTP 18
+ erlang:convert_time_unit(erlang:monotonic_time() - T0,
+ native,
+ micro_seconds) div 1000.
+
+%% Return formated time stamp (e.g. 2015:03:16 10:05:23 1234)
+formated_timestamp() ->
+ format_timestamp( os:timestamp() ).
+
+%% Return formated time stamp (e.g. 2015:03:16 10:05:23 1234)
+format_timestamp({_N1, _N2, N3} = Tme) ->
+ {Date, Time} = calendar:now_to_datetime(Tme),
+ {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/src/inets_app/inets_regexp.erl b/lib/inets/src/inets_app/inets_regexp.erl
deleted file mode 100644
index a065533236..0000000000
--- a/lib/inets/src/inets_app/inets_regexp.erl
+++ /dev/null
@@ -1,413 +0,0 @@
-%%
-%% %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(inets_regexp).
-
--export([parse/1, match/2, first_match/2, split/2, sub/3, gsub/3]).
-
-
-%%%=========================================================================
-%%% API
-%%%=========================================================================
-
-%% parse(RegExp) -> {ok, RE} | {error, E}.
-%% Parse the regexp described in the string RegExp.
-
-parse(S) ->
- case (catch reg(S)) of
- {R, []} ->
- {ok, R};
- {_R, [C|_]} ->
- {error, {illegal, [C]}};
- {error, E} ->
- {error, E}
- end.
-
-
-%% Find the longest match of RegExp in String.
-
-match(S, RegExp) when is_list(RegExp) ->
- case parse(RegExp) of
- {ok,RE} -> match(S, RE);
- {error,E} -> {error,E}
- end;
-match(S, RE) ->
- case match(RE, S, 1, 0, -1) of
- {Start,Len} when Len >= 0 ->
- {match, Start, Len};
- {_Start,_Len} ->
- nomatch
- end.
-
-%% Find the first match of RegExp in String.
-
-first_match(S, RegExp) when is_list(RegExp) ->
- case parse(RegExp) of
- {ok, RE} ->
- first_match(S, RE);
- {error, E} ->
- {error, E}
- end;
-first_match(S, RE) ->
- case first_match(RE, S, 1) of
- {Start,Len} when Len >= 0 ->
- {match, Start,Len};
- nomatch ->
- nomatch
- end.
-
-first_match(RE, S, St) when S =/= [] ->
- case re_apply(S, St, RE) of
- {match, P, _Rest} ->
- {St, P-St};
- nomatch ->
- first_match(RE, tl(S), St+1)
- end;
-first_match(_RE, [], _St) ->
- nomatch.
-
-
-match(RE, S, St, Pos, L) ->
- case first_match(RE, S, St) of
- {St1, L1} ->
- Nst = St1 + 1,
- if L1 > L ->
- match(RE, lists:nthtail(Nst-St, S), Nst, St1, L1);
- true ->
- match(RE, lists:nthtail(Nst-St, S), Nst, Pos, L)
- end;
- nomatch ->
- {Pos, L}
- end.
-
-
-%% Split a string into substrings where the RegExp describes the
-%% field seperator. The RegExp " " is specially treated.
-
-split(String, " ") -> %This is really special
- {ok, RE} = parse("[ \t]+"),
- case split_apply(String, RE, true) of
- [[]|Ss] ->
- {ok,Ss};
- Ss ->
- {ok,Ss}
- end;
-split(String, RegExp) when is_list(RegExp) ->
- case parse(RegExp) of
- {ok, RE} ->
- {ok, split_apply(String, RE, false)};
- {error, E} ->
- {error,E}
- end;
-split(String, RE) ->
- {ok, split_apply(String, RE, false)}.
-
-
-%% Substitute the first match of the regular expression RegExp
-%% with the string Replace in String. Accept pre-parsed regular
-%% expressions.
-
-sub(String, RegExp, Rep) when is_list(RegExp) ->
- case parse(RegExp) of
- {ok, RE} ->
- sub(String, RE, Rep);
- {error, E} ->
- {error, E}
- end;
-sub(String, RE, Rep) ->
- Ss = sub_match(String, RE, 1),
- {ok, sub_repl(Ss, Rep, String, 1), length(Ss)}.
-
-
-%% Substitute every match of the regular expression RegExp with
-%% the string New in String. Accept pre-parsed regular expressions.
-
-gsub(String, RegExp, Rep) when is_list(RegExp) ->
- case parse(RegExp) of
- {ok, RE} ->
- gsub(String, RE, Rep);
- {error, E} ->
- {error, E}
- end;
-gsub(String, RE, Rep) ->
- Ss = matches(String, RE, 1),
- {ok, sub_repl(Ss, Rep, String, 1), length(Ss)}.
-
-
-%%%========================================================================
-%%% Internal functions
-%%%========================================================================
-
-%% This is the regular expression grammar used. It is equivalent to the
-%% one used in AWK, except that we allow ^ $ to be used anywhere and fail
-%% in the matching.
-%%
-%% reg -> reg1 : '$1'.
-%% reg1 -> reg1 "|" reg2 : {'or','$1','$2'}.
-%% reg1 -> reg2 : '$1'.
-%% reg2 -> reg2 reg3 : {concat,'$1','$2'}.
-%% reg2 -> reg3 : '$1'.
-%% reg3 -> reg3 "*" : {kclosure,'$1'}.
-%% reg3 -> reg3 "+" : {pclosure,'$1'}.
-%% reg3 -> reg3 "?" : {optional,'$1'}.
-%% reg3 -> reg4 : '$1'.
-%% reg4 -> "(" reg ")" : '$2'.
-%% reg4 -> "\\" char : '$2'.
-%% reg4 -> "^" : bos.
-%% reg4 -> "$" : eos.
-%% reg4 -> "." : char.
-%% reg4 -> "[" class "]" : {char_class,char_class('$2')}
-%% reg4 -> "[" "^" class "]" : {comp_class,char_class('$3')}
-%% reg4 -> "\"" chars "\"" : char_string('$2')
-%% reg4 -> char : '$1'.
-%% reg4 -> empty : epsilon.
-%% The grammar of the current regular expressions. The actual parser
-%% is a recursive descent implementation of the grammar.
-
-reg(S) -> reg1(S).
-
-%% reg1 -> reg2 reg1'
-%% reg1' -> "|" reg2
-%% reg1' -> empty
-
-reg1(S0) ->
- {L,S1} = reg2(S0),
- reg1p(S1, L).
-
-reg1p([$||S0], L) ->
- {R,S1} = reg2(S0),
- reg1p(S1, {'or',L,R});
-reg1p(S, L) -> {L,S}.
-
-%% reg2 -> reg3 reg2'
-%% reg2' -> reg3
-%% reg2' -> empty
-
-reg2(S0) ->
- {L,S1} = reg3(S0),
- reg2p(S1, L).
-
-reg2p([C|S0], L) when (C =/= $|) andalso (C =/= $)) ->
- {R,S1} = reg3([C|S0]),
- reg2p(S1, {concat,L,R});
-reg2p(S, L) -> {L,S}.
-
-%% reg3 -> reg4 reg3'
-%% reg3' -> "*" reg3'
-%% reg3' -> "+" reg3'
-%% reg3' -> "?" reg3'
-%% reg3' -> empty
-
-reg3(S0) ->
- {L,S1} = reg4(S0),
- reg3p(S1, L).
-
-reg3p([$*|S], L) -> reg3p(S, {kclosure,L});
-reg3p([$+|S], L) -> reg3p(S, {pclosure,L});
-reg3p([$?|S], L) -> reg3p(S, {optional,L});
-reg3p(S, L) -> {L,S}.
-
-reg4([$(|S0]) ->
- case reg(S0) of
- {R,[$)|S1]} -> {R,S1};
- {_R,_S} -> throw({error,{unterminated,"("}})
- end;
-reg4([$\\,O1,O2,O3|S])
- when ((O1 >= $0) andalso
- (O1 =< $7) andalso
- (O2 >= $0) andalso
- (O2 =< $7) andalso
- (O3 >= $0) andalso
- (O3 =< $7)) ->
- {(O1*8 + O2)*8 + O3 - 73*$0,S};
-reg4([$\\,C|S]) ->
- {escape_char(C),S};
-reg4([$\\]) ->
- throw({error, {unterminated,"\\"}});
-reg4([$^|S]) ->
- {bos,S};
-reg4([$$|S]) ->
- {eos,S};
-reg4([$.|S]) ->
- {{comp_class,"\n"},S};
-reg4("[^" ++ S0) ->
- case char_class(S0) of
- {Cc,[$]|S1]} -> {{comp_class,Cc},S1};
- {_Cc,_S} -> throw({error,{unterminated,"["}})
- end;
-reg4([$[|S0]) ->
- case char_class(S0) of
- {Cc,[$]|S1]} -> {{char_class,Cc},S1};
- {_Cc,_S1} -> throw({error,{unterminated,"["}})
- end;
-reg4([C|S])
- when (C =/= $*) andalso (C =/= $+) andalso (C =/= $?) andalso (C =/= $]) ->
- {C, S};
-reg4([C|_S]) ->
- throw({error,{illegal,[C]}});
-reg4([]) ->
- {epsilon,[]}.
-
-escape_char($n) -> $\n; %\n = LF
-escape_char($r) -> $\r; %\r = CR
-escape_char($t) -> $\t; %\t = TAB
-escape_char($v) -> $\v; %\v = VT
-escape_char($b) -> $\b; %\b = BS
-escape_char($f) -> $\f; %\f = FF
-escape_char($e) -> $\e; %\e = ESC
-escape_char($s) -> $\s; %\s = SPACE
-escape_char($d) -> $\d; %\d = DEL
-escape_char(C) -> C.
-
-char_class([$]|S]) -> char_class(S, [$]]);
-char_class(S) -> char_class(S, []).
-
-char($\\, [O1,O2,O3|S]) when
- O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7, O3 >= $0, O3 =< $7 ->
- {(O1*8 + O2)*8 + O3 - 73*$0,S};
-char($\\, [C|S]) -> {escape_char(C),S};
-char(C, S) -> {C,S}.
-
-char_class([C1|S0], Cc) when C1 =/= $] ->
- case char(C1, S0) of
- {Cf,[$-,C2|S1]} when C2 =/= $] ->
- case char(C2, S1) of
- {Cl,S2} when Cf < Cl -> char_class(S2, [{Cf,Cl}|Cc]);
- {Cl,_S2} -> throw({error,{char_class,[Cf,$-,Cl]}})
- end;
- {C,S1} -> char_class(S1, [C|Cc])
- end;
-char_class(S, Cc) -> {Cc,S}.
-
-
-%% re_apply(String, StartPos, RegExp) -> re_app_res().
-%%
-%% Apply the (parse of the) regular expression RegExp to String. If
-%% there is a match return the position of the remaining string and
-%% the string if else return 'nomatch'. BestMatch specifies if we want
-%% the longest match, or just a match.
-%%
-%% StartPos should be the real start position as it is used to decide
-%% if we ae at the beginning of the string.
-%%
-%% Pass two functions to re_apply_or so it can decide, on the basis
-%% of BestMatch, whether to just any take any match or try both to
-%% find the longest. This is slower but saves duplicatng code.
-
-re_apply(S, St, RE) -> re_apply(RE, [], S, St).
-
-re_apply(epsilon, More, S, P) -> %This always matches
- re_apply_more(More, S, P);
-re_apply({'or',RE1,RE2}, More, S, P) ->
- re_apply_or(re_apply(RE1, More, S, P),
- re_apply(RE2, More, S, P));
-re_apply({concat,RE1,RE2}, More, S0, P) ->
- re_apply(RE1, [RE2|More], S0, P);
-re_apply({kclosure,CE}, More, S, P) ->
- %% Be careful with the recursion, explicitly do one call before
- %% looping.
- re_apply_or(re_apply_more(More, S, P),
- re_apply(CE, [{kclosure,CE}|More], S, P));
-re_apply({pclosure,CE}, More, S, P) ->
- re_apply(CE, [{kclosure,CE}|More], S, P);
-re_apply({optional,CE}, More, S, P) ->
- re_apply_or(re_apply_more(More, S, P),
- re_apply(CE, More, S, P));
-re_apply(bos, More, S, 1) -> re_apply_more(More, S, 1);
-re_apply(eos, More, [$\n|S], P) -> re_apply_more(More, S, P);
-re_apply(eos, More, [], P) -> re_apply_more(More, [], P);
-re_apply({char_class,Cc}, More, [C|S], P) ->
- case in_char_class(C, Cc) of
- true -> re_apply_more(More, S, P+1);
- false -> nomatch
- end;
-re_apply({comp_class,Cc}, More, [C|S], P) ->
- case in_char_class(C, Cc) of
- true -> nomatch;
- false -> re_apply_more(More, S, P+1)
- end;
-re_apply(C, More, [C|S], P) when is_integer(C) ->
- re_apply_more(More, S, P+1);
-re_apply(_RE, _More, _S, _P) -> nomatch.
-
-%% re_apply_more([RegExp], String, Length) -> re_app_res().
-
-re_apply_more([RE|More], S, P) -> re_apply(RE, More, S, P);
-re_apply_more([], S, P) -> {match,P,S}.
-
-%% in_char_class(Char, Class) -> bool().
-
-in_char_class(C, [{C1,C2}|_Cc]) when C >= C1, C =< C2 -> true;
-in_char_class(C, [C|_Cc]) -> true;
-in_char_class(C, [_|Cc]) -> in_char_class(C, Cc);
-in_char_class(_C, []) -> false.
-
-%% re_apply_or(Match1, Match2) -> re_app_res().
-%% If we want the best match then choose the longest match, else just
-%% choose one by trying sequentially.
-
-re_apply_or({match,P1,S1}, {match,P2,_S2}) when P1 >= P2 -> {match,P1,S1};
-re_apply_or({match,_P1,_S1}, {match,P2,S2}) -> {match,P2,S2};
-re_apply_or(nomatch, R2) -> R2;
-re_apply_or(R1, nomatch) -> R1.
-
-
-matches(S, RE, St) ->
- case first_match(RE, S, St) of
- {St1,0} ->
- [{St1,0}|matches(string:substr(S, St1+2-St), RE, St1+1)];
- {St1,L1} ->
- [{St1,L1}|matches(string:substr(S, St1+L1+1-St), RE, St1+L1)];
- nomatch ->
- []
- end.
-
-sub_match(S, RE, St) ->
- case first_match(RE, S, St) of
- {St1,L1} -> [{St1,L1}];
- nomatch -> []
- end.
-
-sub_repl([{St,L}|Ss], Rep, S, Pos) ->
- Rs = sub_repl(Ss, Rep, S, St+L),
- string:substr(S, Pos, St-Pos) ++
- sub_repl(Rep, string:substr(S, St, L), Rs);
-sub_repl([], _Rep, S, Pos) ->
- string:substr(S, Pos).
-
-sub_repl([$&|Rep], M, Rest) -> M ++ sub_repl(Rep, M, Rest);
-sub_repl("\\&" ++ Rep, M, Rest) -> [$&|sub_repl(Rep, M, Rest)];
-sub_repl([C|Rep], M, Rest) -> [C|sub_repl(Rep, M, Rest)];
-sub_repl([], _M, Rest) -> Rest.
-
-split_apply(S, RE, Trim) -> split_apply(S, 1, RE, Trim, []).
-
-split_apply([], _P, _RE, true, []) ->
- [];
-split_apply([], _P, _RE, _T, Sub) ->
- [lists:reverse(Sub)];
-split_apply(S, P, RE, T, Sub) ->
- case re_apply(S, P, RE) of
- {match,P,_Rest} ->
- split_apply(tl(S), P+1, RE, T, [hd(S)|Sub]);
- {match,P1,Rest} ->
- [lists:reverse(Sub)|split_apply(Rest, P1, RE, T, [])];
- nomatch ->
- split_apply(tl(S), P+1, RE, T, [hd(S)|Sub])
- end.
diff --git a/lib/inets/src/inets_app/inets_service.erl b/lib/inets/src/inets_app/inets_service.erl
index d17fdfe13e..3441d2a2e4 100644
--- a/lib/inets/src/inets_app/inets_service.erl
+++ b/lib/inets/src/inets_app/inets_service.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/inets_app/inets_sup.erl b/lib/inets/src/inets_app/inets_sup.erl
index 66a0cdf785..d8ae7eff26 100644
--- a/lib/inets/src/inets_app/inets_sup.erl
+++ b/lib/inets/src/inets_app/inets_sup.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/inets_app/inets_trace.erl b/lib/inets/src/inets_app/inets_trace.erl
index 8911f65897..64ab9bec35 100644
--- a/lib/inets/src/inets_app/inets_trace.erl
+++ b/lib/inets/src/inets_app/inets_trace.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -228,21 +229,24 @@ handle_trace({trace_ts, _Who, call,
[_Sev, "stop trace", stop_trace, [stop_trace]]},
Timestamp},
{_, standard_io} = Fd) ->
- (catch io:format(standard_io, "stop trace at ~s~n", [format_timestamp(Timestamp)])),
+ (catch io:format(standard_io, "stop trace at ~s~n",
+ [inets_lib:format_timestamp(Timestamp)])),
Fd;
handle_trace({trace_ts, _Who, call,
{?MODULE, report_event,
[_Sev, "stop trace", stop_trace, [stop_trace]]},
Timestamp},
standard_io = Fd) ->
- (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])),
+ (catch io:format(Fd, "stop trace at ~s~n",
+ [inets_lib:format_timestamp(Timestamp)])),
Fd;
handle_trace({trace_ts, _Who, call,
{?MODULE, report_event,
[_Sev, "stop trace", stop_trace, [stop_trace]]},
Timestamp},
{_Service, Fd}) ->
- (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])),
+ (catch io:format(Fd, "stop trace at ~s~n",
+ [inets_lib:format_timestamp(Timestamp)])),
(catch file:close(Fd)),
closed_file;
handle_trace({trace_ts, _Who, call,
@@ -250,7 +254,8 @@ handle_trace({trace_ts, _Who, call,
[_Sev, "stop trace", stop_trace, [stop_trace]]},
Timestamp},
Fd) ->
- (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])),
+ (catch io:format(Fd, "stop trace at ~s~n",
+ [inets_lib:format_timestamp(Timestamp)])),
(catch file:close(Fd)),
closed_file;
handle_trace({trace_ts, Who, call,
@@ -280,7 +285,7 @@ print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) ->
do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content).
do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) ->
- Ts = format_timestamp(Timestamp),
+ Ts = inets_lib:format_timestamp(Timestamp),
io:format(Fd, "[inets ~w trace ~w ~w ~s] ~s "
"~n Content: ~p"
"~n",
@@ -307,7 +312,7 @@ do_print_trace(Fd, {trace, Who, What, Where, Extra}) ->
"~n", [Who, What, Where, Extra]);
do_print_trace(Fd, {trace_ts, Who, What, Where, When}) ->
- Ts = format_timestamp(When),
+ Ts = inets_lib:format_timestamp(When),
io:format(Fd, "[trace ~s]"
"~n Who: ~p"
"~n What: ~p"
@@ -315,7 +320,7 @@ do_print_trace(Fd, {trace_ts, Who, What, Where, When}) ->
"~n", [Ts, Who, What, Where]);
do_print_trace(Fd, {trace_ts, Who, What, Where, Extra, When}) ->
- Ts = format_timestamp(When),
+ Ts = inets_lib:format_timestamp(When),
io:format(Fd, "[trace ~s]"
"~n Who: ~p"
"~n What: ~p"
@@ -330,7 +335,7 @@ do_print_trace(Fd, {seq_trace, What, Where}) ->
"~n", [What, Where]);
do_print_trace(Fd, {seq_trace, What, Where, When}) ->
- Ts = format_timestamp(When),
+ Ts = inets_lib:format_timestamp(When),
io:format(Fd, "[seq trace ~s]"
"~n What: ~p"
"~n Where: ~p"
@@ -345,13 +350,3 @@ do_print_trace(Fd, Trace) ->
"~n", [Trace]).
-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/src/tftp/Makefile b/lib/inets/src/tftp/Makefile
index b368b12462..4eaa959cce 100644
--- a/lib/inets/src/tftp/Makefile
+++ b/lib/inets/src/tftp/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2012. All Rights Reserved.
+# Copyright Ericsson AB 2005-2016. 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/.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# 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.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
diff --git a/lib/inets/src/tftp/tftp.erl b/lib/inets/src/tftp/tftp.erl
index 1621add246..c8804ea55f 100644
--- a/lib/inets/src/tftp/tftp.erl
+++ b/lib/inets/src/tftp/tftp.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/tftp/tftp.hrl b/lib/inets/src/tftp/tftp.hrl
index 6846b07690..25543e0b9e 100644
--- a/lib/inets/src/tftp/tftp.hrl
+++ b/lib/inets/src/tftp/tftp.hrl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/tftp/tftp_binary.erl b/lib/inets/src/tftp/tftp_binary.erl
index 9efa79105d..09adcfc41f 100644
--- a/lib/inets/src/tftp/tftp_binary.erl
+++ b/lib/inets/src/tftp/tftp_binary.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/tftp/tftp_engine.erl b/lib/inets/src/tftp/tftp_engine.erl
index 81c53bbe40..fb2c9749e5 100644
--- a/lib/inets/src/tftp/tftp_engine.erl
+++ b/lib/inets/src/tftp/tftp_engine.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -62,7 +63,8 @@
-record(file_info, {peer_req, pid}).
-record(sys_misc, {module, function, arguments}).
-record(error, {where, code, text, filename}).
--record(prepared, {status :: prep_status(), result, block_no, next_data, prev_data}).
+-record(prepared, {status :: prep_status() | 'undefined',
+ result, block_no, next_data, prev_data}).
-record(transfer_res, {status, decoded_msg, prepared}).
-define(ERROR(Where, Code, Text, Filename),
#error{where = Where, code = Code, text = Text, filename = Filename}).
@@ -127,8 +129,8 @@ daemon_start(Options) when is_list(Options) ->
daemon_init(Config) when is_record(Config, config),
is_pid(Config#config.parent_pid) ->
process_flag(trap_exit, true),
- UdpOptions = prepare_daemon_udp(Config),
- case catch gen_udp:open(Config#config.udp_port, UdpOptions) of
+ {Port, UdpOptions} = prepare_daemon_udp(Config),
+ case catch gen_udp:open(Port, UdpOptions) of
{ok, Socket} ->
{ok, ActualPort} = inet:port(Socket),
proc_lib:init_ack({ok, self()}),
@@ -156,7 +158,7 @@ prepare_daemon_udp(#config{udp_port = Port, udp_options = UdpOptions} = Config)
case lists:keymember(fd, 1, UdpOptions) of
true ->
%% Use explicit fd
- UdpOptions;
+ {Port, UdpOptions};
false ->
%% Use fd from setuid_socket_wrap, such as -tftpd_69
InitArg = list_to_atom("tftpd_" ++ integer_to_list(Port)),
@@ -164,7 +166,7 @@ prepare_daemon_udp(#config{udp_port = Port, udp_options = UdpOptions} = Config)
{ok, [[FdStr]] = Badarg} when is_list(FdStr) ->
case catch list_to_integer(FdStr) of
Fd when is_integer(Fd) ->
- [{fd, Fd} | UdpOptions];
+ {0, [{fd, Fd} | lists:keydelete(ip, 1, UdpOptions)]};
{'EXIT', _} ->
Text = lists:flatten(io_lib:format("Illegal prebound fd ~p: ~p", [InitArg, Badarg])),
print_debug_info(Config, daemon, open, ?ERROR(open, undef, Text, "")),
@@ -175,7 +177,7 @@ prepare_daemon_udp(#config{udp_port = Port, udp_options = UdpOptions} = Config)
print_debug_info(Config, daemon, open, ?ERROR(open, undef, Text, "")),
exit({badarg, {prebound_fd, InitArg, Badarg}});
error ->
- UdpOptions
+ {Port, UdpOptions}
end
end.
@@ -655,22 +657,11 @@ common_read(Config, Callback, Req, _LocalAccess, ExpectedBlockNo, ActualBlockNo,
do_common_read(Config, Callback, Req, LocalAccess, BlockNo, Data, Prepared)
when is_binary(Data), is_record(Prepared, prepared) ->
- NextBlockNo = BlockNo + 1,
- case NextBlockNo =< 65535 of
- true ->
- Reply = #tftp_msg_data{block_no = NextBlockNo, data = Data},
- {Config2, Callback2, TransferRes} =
- transfer(Config, Callback, Req, Reply, LocalAccess, NextBlockNo, Prepared),
- ?MODULE:common_loop(Config2, Callback2, Req, TransferRes, LocalAccess, NextBlockNo);
- false ->
- Code = badblk,
- Text = "Too big transfer ID = " ++
- integer_to_list(NextBlockNo) ++ " > 65535",
- {undefined, Error} =
- callback({abort, {Code, Text}}, Config, Callback, Req),
- send_msg(Config, Req, Error),
- terminate(Config, Req, ?ERROR(read, Code, Text, Req#tftp_msg_req.filename))
- end.
+ NextBlockNo = (BlockNo + 1) rem 65536,
+ Reply = #tftp_msg_data{block_no = NextBlockNo, data = Data},
+ {Config2, Callback2, TransferRes} =
+ transfer(Config, Callback, Req, Reply, LocalAccess, NextBlockNo, Prepared),
+ ?MODULE:common_loop(Config2, Callback2, Req, TransferRes, LocalAccess, NextBlockNo).
-spec common_write(#config{}, #callback{}, _, 'write', integer(), integer(), _, #prepared{}) -> no_return().
@@ -714,21 +705,10 @@ common_write(Config, Callback, Req, _, ExpectedBlockNo, ActualBlockNo, Data, Pre
common_ack(Config, Callback, Req, LocalAccess, BlockNo, Prepared)
when is_record(Prepared, prepared) ->
Reply = #tftp_msg_ack{block_no = BlockNo},
- NextBlockNo = BlockNo + 1,
+ NextBlockNo = (BlockNo + 1) rem 65536,
{Config2, Callback2, TransferRes} =
transfer(Config, Callback, Req, Reply, LocalAccess, NextBlockNo, Prepared),
- case NextBlockNo =< 65535 of
- true ->
- ?MODULE:common_loop(Config2, Callback2, Req, TransferRes, LocalAccess, NextBlockNo);
- false ->
- Code = badblk,
- Text = "Too big transfer ID = " ++
- integer_to_list(NextBlockNo) ++ " > 65535",
- {undefined, Error} =
- callback({abort, {Code, Text}}, Config, Callback2, Req),
- send_msg(Config, Req, Error),
- terminate(Config, Req, ?ERROR(read, Code, Text, Req#tftp_msg_req.filename))
- end.
+ ?MODULE:common_loop(Config2, Callback2, Req, TransferRes, LocalAccess, NextBlockNo).
pre_terminate(Config, Req, Result) ->
if
@@ -1152,8 +1132,8 @@ match_callback(Filename, Callbacks) ->
end.
do_match_callback(Filename, [C | Tail]) when is_record(C, callback) ->
- case catch inets_regexp:match(Filename, C#callback.internal) of
- {match, _, _} ->
+ case catch re:run(Filename, C#callback.internal, [{capture, none}]) of
+ match ->
{ok, C};
nomatch ->
do_match_callback(Filename, Tail);
diff --git a/lib/inets/src/tftp/tftp_file.erl b/lib/inets/src/tftp/tftp_file.erl
index e0cbb49330..7664324808 100644
--- a/lib/inets/src/tftp/tftp_file.erl
+++ b/lib/inets/src/tftp/tftp_file.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/tftp/tftp_lib.erl b/lib/inets/src/tftp/tftp_lib.erl
index ffb7b9a797..454754f0a3 100644
--- a/lib/inets/src/tftp/tftp_lib.erl
+++ b/lib/inets/src/tftp/tftp_lib.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -183,7 +184,7 @@ do_parse_config([{Key, Val} | Tail], Config) when is_record(Config, config) ->
callback ->
case Val of
{RegExp, Mod, State} when is_list(RegExp), is_atom(Mod) ->
- case inets_regexp:parse(RegExp) of
+ case re:compile(RegExp) of
{ok, Internal} ->
Callback = #callback{regexp = RegExp,
internal = Internal,
@@ -252,7 +253,7 @@ do_parse_config(Options, Config) when is_record(Config, config) ->
add_default_callbacks(Callbacks) ->
RegExp = "",
- {ok, Internal} = inets_regexp:parse(RegExp),
+ {ok, Internal} = re:compile(RegExp),
File = #callback{regexp = RegExp,
internal = Internal,
module = tftp_file,
diff --git a/lib/inets/src/tftp/tftp_logger.erl b/lib/inets/src/tftp/tftp_logger.erl
index 0c3620e665..a869958484 100644
--- a/lib/inets/src/tftp/tftp_logger.erl
+++ b/lib/inets/src/tftp/tftp_logger.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -84,8 +85,8 @@ info_msg(Format, Data) ->
%%-------------------------------------------------------------------
add_timestamp(Format, Data) ->
- Now = {_MegaSecs, _Secs, _MicroSecs} = erlang:now(),
- {{_Y, _Mo, _D}, {H, Mi, S}} = calendar:now_to_universal_time(Now),
+ Time = erlang:timestamp(),
+ {{_Y, _Mo, _D}, {H, Mi, S}} = calendar:now_to_universal_time(Time),
%% {"~p-~s-~sT~s:~s:~sZ,~6.6.0w tftp: " ++ Format ++ "\n",
%% [Y, t(Mo), t(D), t(H), t(Mi), t(S), MicroSecs | Data]}.
{"~s:~s:~s tftp: " ++ Format, [t(H), t(Mi), t(S) | Data]}.
diff --git a/lib/inets/src/tftp/tftp_sup.erl b/lib/inets/src/tftp/tftp_sup.erl
index 1cafcc1069..40b67c499c 100644
--- a/lib/inets/src/tftp/tftp_sup.erl
+++ b/lib/inets/src/tftp/tftp_sup.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -93,7 +94,7 @@ unique_name(Options) ->
{value, {_, Port}} when is_integer(Port), Port > 0 ->
{tftpd, Port};
_ ->
- {tftpd, erlang:now()}
+ {tftpd, erlang:unique_integer([positive])}
end.
default_kill_after() ->
diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile
index dfa86906fd..ffc512050a 100644
--- a/lib/inets/test/Makefile
+++ b/lib/inets/test/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2013. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. 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/.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# 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.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
@@ -41,7 +42,6 @@ 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 \
@@ -152,40 +152,26 @@ MODULES = \
erl_make_certs \
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 \
httpc_proxy_SUITE \
httpd_SUITE \
+ old_httpd_SUITE \
httpd_basic_SUITE \
httpd_mod \
httpd_block \
httpd_load \
httpd_time_test \
httpd_1_1 \
+ httpd_1_0 \
httpd_test_lib \
inets_sup_SUITE \
inets_SUITE \
- inets_app_test \
- inets_appup_test \
tftp_test_lib \
tftp_SUITE \
- uri_SUITE
+ uri_SUITE \
+ inets_socketwrap_SUITE
EBIN = .
@@ -214,8 +200,8 @@ INETS_FILES = inets.config $(INETS_SPECS)
# inets_ftp_suite \
# inets_tftp_suite
-INETS_DATADIRS = inets_SUITE_data inets_sup_SUITE_data
-HTTPD_DATADIRS = httpd_test_data httpd_SUITE_data
+INETS_DATADIRS = inets_SUITE_data inets_socketwrap_SUITE_data
+HTTPD_DATADIRS = httpd_test_data httpd_SUITE_data httpd_basic_SUITE_data old_httpd_SUITE_data
HTTPC_DATADIRS = httpc_SUITE_data httpc_proxy_SUITE_data
FTP_DATADIRS = ftp_SUITE_data
@@ -248,7 +234,6 @@ RELTESTSYSBINDIR = $(RELTESTSYSALLDATADIR)/bin
# running the target "targets".
# ----------------------------------------------------
ERL_COMPILE_FLAGS += \
- -pa ../../../internal_tools/test_server/ebin \
$(INCLUDES) \
$(FTP_FLAGS) \
$(INETS_FLAGS)
@@ -261,7 +246,7 @@ ERL_COMPILE_FLAGS += \
# 1) INETS_PRIV_DIR must be created
# ----------------------------------------------------
-tests debug opt: $(BUILDTARGET)
+tests debug opt: $(BUILDTARGET)
targets: $(TARGET_FILES)
diff --git a/lib/inets/test/erl_make_certs.erl b/lib/inets/test/erl_make_certs.erl
index 22dc951ac1..2db95825bc 100644
--- a/lib/inets/test/erl_make_certs.erl
+++ b/lib/inets/test/erl_make_certs.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -204,7 +205,7 @@ issuer_der(Issuer) ->
Subject.
subject(undefined, IsRootCA) ->
- User = if IsRootCA -> "RootCA"; true -> user() end,
+ User = if IsRootCA -> "RootCA"; true -> os:getenv("USER", "test_user") end,
Opts = [{email, User ++ "@erlang.org"},
{name, User},
{city, "Stockholm"},
@@ -215,14 +216,6 @@ subject(undefined, IsRootCA) ->
subject(Opts, _) ->
subject(Opts).
-user() ->
- case os:getenv("USER") of
- false ->
- "test_user";
- User ->
- User
- end.
-
subject(SubjectOpts) when is_list(SubjectOpts) ->
Encode = fun(Opt) ->
{Type,Value} = subject_enc(Opt),
diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl
index 17e5f6777e..e2dec0c42a 100644
--- a/lib/inets/test/ftp_SUITE.erl
+++ b/lib/inets/test/ftp_SUITE.erl
@@ -1,127 +1,1080 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
+%%
+%% ct:run("../inets_test", ftp_SUITE).
+%%
+
-module(ftp_SUITE).
+-include_lib("kernel/include/file.hrl").
-include_lib("common_test/include/ct.hrl").
--include("test_server_line.hrl").
+-include("inets_test_lib.hrl").
-%% Test server specific exports
--export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
-% -export([init_per_testcase/2, end_per_testcase/2]).
--export([init_per_suite/1, end_per_suite/1]).
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
-define(FTP_USER, "anonymous").
--define(FTP_PASS, passwd()).
--define(FTP_PORT, 21).
+-define(FTP_PASS(Cmnt), (fun({ok,__H}) -> "ftp_SUITE_"++Cmnt++"@" ++ __H;
+ (_) -> "ftp_SUITE_"++Cmnt++"@localhost"
+ end)(inet:gethostname())
+ ).
-define(BAD_HOST, "badhostname").
-define(BAD_USER, "baduser").
-define(BAD_DIR, "baddirectory").
--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.
-
+-record(progress, {
+ current = 0,
+ total
+ }).
%%--------------------------------------------------------------------
-%% all(Arg) -> [Doc] | [Case] | {skip, Comment}
-%% Arg - doc | suite
-%% Doc - string()
-%% Case - atom()
-%% Name of a test case function.
-%% Comment - string()
-%% Description: Returns documentation/test cases in this test suite
-%% or a skip tuple if the platform is not supported.
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-suite() -> [{ct_hooks, [ts_install_cth]}].
+suite() ->
+ [{timetrap,{seconds,20}}].
+
+all() ->
+ [
+ {group, ftp_passive},
+ {group, ftp_active},
+ {group, ftps_passive},
+ {group, ftps_active},
+ error_ehost,
+ clean_shutdown
+ ].
-all() ->
+groups() ->
[
- {group, solaris8_test},
- {group, solaris9_test},
- {group, solaris10_test},
- {group, linux_x86_test},
- {group, linux_ppc_test},
- {group, macosx_x86_test},
- {group, macosx_ppc_test},
- {group, openbsd_test},
- {group, freebsd_test},
- {group, netbsd_test},
- {group, windows_xp_test},
- {group, windows_2003_server_test},
- {group, ticket_tests}
+ {ftp_passive, [], ftp_tests()},
+ {ftp_active, [], ftp_tests()},
+ {ftps_passive, [], ftp_tests()},
+ {ftps_active, [], ftp_tests()}
].
-groups() ->
+ftp_tests()->
[
- {solaris8_test, [], [{ftp_solaris8_sparc_test, all}]},
- {solaris9_test, [], [{ftp_solaris9_sparc_test, all}]},
- {solaris10_test, [], [{ftp_solaris10_sparc_test, all},
- {ftp_solaris10_x86_test, all}]},
- {linux_x86_test, [], [{ftp_linux_x86_test, all}]},
- {linux_ppc_test, [], [{ftp_linux_ppc_test, all}]},
- {macosx_x86_test, [], [{ftp_macosx_x86_test, all}]},
- {macosx_ppc_test, [], [{ftp_macosx_ppc_test, all}]},
- {openbsd_test, [], [{ftp_openbsd_x86_test, all}]},
- {freebsd_test, [], [{ftp_freebsd_x86_test, all}]},
- {netbsd_test, [], [{ftp_netbsd_x86_test, all}]},
- {windows_xp_test, [], [{ftp_windows_xp_test, all}]},
- {windows_2003_server_test, [], [{ftp_windows_2003_server_test, all}]},
- {ticket_tests, [], [{ftp_ticket_test, all}]}
+ user,
+ bad_user,
+ pwd,
+ cd,
+ lcd,
+ ls,
+ nlist,
+ rename,
+ delete,
+ mkdir,
+ rmdir,
+ send,
+ send_3,
+ send_bin,
+ send_chunk,
+ append,
+ append_bin,
+ append_chunk,
+ recv,
+ recv_3,
+ recv_bin,
+ recv_chunk,
+ type,
+ quote,
+ error_elogin,
+ progress_report_send,
+ progress_report_recv,
+ not_owner,
+ unexpected_call,
+ unexpected_cast,
+ unexpected_bang
].
-init_per_group(_GroupName, Config) ->
- Config.
+%%--------------------------------------------------------------------
-end_per_group(_GroupName, Config) ->
- Config.
+%%% Config
+%%% key meaning
+%%% ................................................................
+%%% ftpservers list of servers to check if they are available
+%%% The element is:
+%%% {Name, % string(). The os command name
+%%% Path, % string(). The os PATH syntax, e.g "/bin:/usr/bin"
+%%% StartCommand, % fun()->{ok,start_result()} | {error,string()}.
+%%% % The command to start the daemon with.
+%%% ChkUp, % fun(start_result()) -> string(). Os command to check
+%%% % if the server is running. [] if not running.
+%%% % The string in string() is suitable for logging.
+%%% StopCommand, % fun(start_result()) -> void(). The command to stop the daemon with.
+%%% AugmentFun, % fun(config()) -> config() Adds two funs for transforming names of files
+%%% % and directories to the form they are returned from this server
+%%% ServerHost, % string(). Mostly "localhost"
+%%% ServerPort % pos_integer()
+%%% }
+%%%
+-define(default_ftp_servers,
+ [{"vsftpd",
+ "/sbin:/usr/sbin:/usr/local/sbin",
+ fun(__CONF__, AbsName) ->
+ DataDir = proplists:get_value(data_dir,__CONF__),
+ ConfFile = filename:join(DataDir, "vsftpd.conf"),
+ PrivDir = proplists:get_value(priv_dir,__CONF__),
+ AnonRoot = PrivDir,
+ Cmd = [AbsName ++" "++filename:join(DataDir,"vsftpd.conf"),
+ " -oftpd_banner=erlang_otp_testing",
+ " -oanon_root=\"",AnonRoot,"\"",
+ " -orsa_cert_file=\"",filename:join(DataDir,"server-cert.pem"),"\"",
+ " -orsa_private_key_file=\"",filename:join(DataDir,"server-key.pem"),"\""
+ ],
+ Result = os:cmd(Cmd),
+ ct:log("Config file:~n~s~n~nServer start command:~n ~s~nResult:~n ~p",
+ [case file:read_file(ConfFile) of
+ {ok,X} -> X;
+ _ -> ""
+ end,
+ Cmd, Result
+ ]),
+ case Result of
+ [] -> {ok,'dont care'};
+ [Msg] -> {error,Msg}
+ end
+ end,
+ fun(_StartResult) -> os:cmd("ps ax | grep erlang_otp_testing | grep -v grep")
+ end,
+ fun(_StartResult) -> os:cmd("kill `ps ax | grep erlang_otp_testing | awk '/vsftpd/{print $1}'`")
+ end,
+ fun(__CONF__) ->
+ AnonRoot = proplists:get_value(priv_dir,__CONF__),
+ [{id2ftp, fun(Id) -> filename:join(AnonRoot,Id) end},
+ {id2ftp_result,fun(Id) -> filename:join(AnonRoot,Id) end} | __CONF__]
+ end,
+ "localhost",
+ 9999
+ }
+ ]
+ ).
+init_per_suite(Config) ->
+ case find_executable(Config) of
+ false ->
+ {skip, "No ftp server found"};
+ {ok,Data} ->
+ TstDir = filename:join(proplists:get_value(priv_dir,Config), "test"),
+ file:make_dir(TstDir),
+ make_cert_files(dsa, rsa, "server-", proplists:get_value(data_dir,Config)),
+ start_ftpd([{test_dir,TstDir},
+ {ftpd_data,Data}
+ | Config])
+ end.
+
+end_per_suite(Config) ->
+ ps_ftpd(Config),
+ stop_ftpd(Config),
+ ps_ftpd(Config),
+ ok.
%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
+init_per_group(_Group, Config) -> Config.
+
+end_per_group(_Group, Config) -> Config.
+
%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- inets:start(),
- Config.
+init_per_testcase(Case, Config0) ->
+ Group = proplists:get_value(name, proplists:get_value(tc_group_properties,Config0)),
+ TLS = [{tls,[{reuse_sessions,true}]}],
+ ACTIVE = [{mode,active}],
+ PASSIVE = [{mode,passive}],
+ CaseOpts = case Case of
+ progress_report_send -> [{progress, {?MODULE,progress,#progress{}}}];
+ progress_report_recv -> [{progress, {?MODULE,progress,#progress{}}}];
+ _ -> []
+ end,
+ ExtraOpts = [verbose | CaseOpts],
+ Config =
+ case Group of
+ ftp_active -> ftp__open(Config0, ACTIVE ++ ExtraOpts);
+ ftps_active -> ftp__open(Config0, TLS++ ACTIVE ++ ExtraOpts);
+ ftp_passive -> ftp__open(Config0, PASSIVE ++ ExtraOpts);
+ ftps_passive -> ftp__open(Config0, TLS++PASSIVE ++ ExtraOpts);
+ undefined -> Config0
+ end,
+ case Case of
+ user -> Config;
+ bad_user -> Config;
+ error_elogin -> Config;
+ error_ehost -> Config;
+ clean_shutdown -> Config;
+ _ ->
+ Pid = proplists:get_value(ftp,Config),
+ ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS(atom_to_list(Group)++"-"++atom_to_list(Case)) ),
+ ok = ftp:cd(Pid, proplists:get_value(priv_dir,Config)),
+ Config
+ end.
+
+
+end_per_testcase(user, _Config) -> ok;
+end_per_testcase(bad_user, _Config) -> ok;
+end_per_testcase(error_elogin, _Config) -> ok;
+end_per_testcase(error_ehost, _Config) -> ok;
+end_per_testcase(clean_shutdown, _Config) -> ok;
+end_per_testcase(_Case, Config) ->
+ case proplists:get_value(tc_status,Config) of
+ ok -> ok;
+ _ ->
+ try ftp:latest_ctrl_response(proplists:get_value(ftp,Config))
+ of
+ {ok,S} -> ct:log("***~n*** Latest ctrl channel response:~n*** ~p~n***",[S])
+ catch
+ _:_ -> ok
+ end
+ end,
+ ftp__close(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
+%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-end_per_suite(_Config) ->
- inets:stop(),
+user() -> [
+ {doc, "Open an ftp connection to a host, and logon as anonymous ftp,"
+ " then logoff"}].
+user(Config) ->
+ Pid = proplists:get_value(ftp, Config),
+ ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS("")),% logon
+ ok = ftp:close(Pid), % logoff
+ {error,eclosed} = ftp:pwd(Pid), % check logoff result
+ ok.
+
+%%-------------------------------------------------------------------------
+bad_user() ->
+ [{doc, "Open an ftp connection to a host, and logon with bad user."}].
+bad_user(Config) ->
+ Pid = proplists:get_value(ftp, Config),
+ {error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS("")),
+ ok.
+
+%%-------------------------------------------------------------------------
+pwd() ->
+ [{doc, "Test ftp:pwd/1 & ftp:lpwd/1"}].
+pwd(Config0) ->
+ Config = set_state([reset], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ {ok, PWD} = ftp:pwd(Pid),
+ {ok, PathLpwd} = ftp:lpwd(Pid),
+ PWD = id2ftp_result("", Config),
+ PathLpwd = id2ftp_result("", Config).
+
+%%-------------------------------------------------------------------------
+cd() ->
+ ["Open an ftp connection, log on as anonymous ftp, and cd to a"
+ "directory and to a non-existent directory."].
+cd(Config0) ->
+ Dir = "test",
+ Config = set_state([reset,{mkdir,Dir}], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ ok = ftp:cd(Pid, id2ftp(Dir,Config)),
+ {ok, PWD} = ftp:pwd(Pid),
+ ExpectedPWD = id2ftp_result(Dir, Config),
+ PWD = ExpectedPWD,
+ {error, epath} = ftp:cd(Pid, ?BAD_DIR),
+ ok.
+
+%%-------------------------------------------------------------------------
+lcd() ->
+ [{doc, "Test api function ftp:lcd/2"}].
+lcd(Config0) ->
+ Dir = "test",
+ Config = set_state([reset,{mkdir,Dir}], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ ok = ftp:lcd(Pid, id2ftp(Dir,Config)),
+ {ok, PWD} = ftp:lpwd(Pid),
+ ExpectedPWD = id2ftp_result(Dir, Config),
+ PWD = ExpectedPWD,
+ {error, epath} = ftp:lcd(Pid, ?BAD_DIR).
+
+%%-------------------------------------------------------------------------
+ls() ->
+ [{doc, "Open an ftp connection; ls the current directory, and the "
+ "\"test\" directory. We assume that ls never fails, since "
+ "it's output is meant to be read by humans. "}].
+ls(Config0) ->
+ Config = set_state([reset,{mkdir,"test"}], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ {ok, _R1} = ftp:ls(Pid),
+ {ok, _R2} = ftp:ls(Pid, id2ftp("test",Config)),
+ %% neither nlist nor ls operates on a directory
+ %% they operate on a pathname, which *can* be a
+ %% directory, but can also be a filename or a group
+ %% of files (including wildcards).
+ case proplists:get_value(wildcard_support, Config) of
+ true ->
+ {ok, _R3} = ftp:ls(Pid, id2ftp("te*",Config));
+ _ ->
+ ok
+ end.
+
+%%-------------------------------------------------------------------------
+nlist() ->
+ [{doc,"Open an ftp connection; nlist the current directory, and the "
+ "\"test\" directory. Nlist does not behave consistenly over "
+ "operating systems. On some it is an error to have an empty "
+ "directory."}].
+nlist(Config0) ->
+ Config = set_state([reset,{mkdir,"test"}], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ {ok, _R1} = ftp:nlist(Pid),
+ {ok, _R2} = ftp:nlist(Pid, id2ftp("test",Config)),
+ %% neither nlist nor ls operates on a directory
+ %% they operate on a pathname, which *can* be a
+ %% directory, but can also be a filename or a group
+ %% of files (including wildcards).
+ case proplists:get_value(wildcard_support, Config) of
+ true ->
+ {ok, _R3} = ftp:nlist(Pid, id2ftp("te*",Config));
+ _ ->
+ ok
+ end.
+
+%%-------------------------------------------------------------------------
+rename() ->
+ [{doc, "Rename a file."}].
+rename(Config0) ->
+ Contents = <<"ftp_SUITE test ...">>,
+ OldFile = "old.txt",
+ NewFile = "new.txt",
+ Config = set_state([reset,{mkfile,OldFile,Contents}], Config0),
+ Pid = proplists:get_value(ftp, Config),
+
+ ok = ftp:rename(Pid,
+ id2ftp(OldFile,Config),
+ id2ftp(NewFile,Config)),
+
+ true = (chk_file(NewFile,Contents,Config)
+ and chk_no_file([OldFile],Config)),
+ {error,epath} = ftp:rename(Pid,
+ id2ftp("non_existing_file",Config),
+ id2ftp(NewFile,Config)),
+ ok.
+
+%%-------------------------------------------------------------------------
+send() ->
+ [{doc, "Transfer a file with ftp using send/2."}].
+send(Config0) ->
+ Contents = <<"ftp_SUITE test ...">>,
+ SrcDir = "data",
+ File = "file.txt",
+ Config = set_state([reset,{mkfile,[SrcDir,File],Contents}], Config0),
+ Pid = proplists:get_value(ftp, Config),
+
+ chk_no_file([File],Config),
+ chk_file([SrcDir,File],Contents,Config),
+
+ ok = ftp:lcd(Pid, id2ftp(SrcDir,Config)),
+ ok = ftp:cd(Pid, id2ftp("",Config)),
+ ok = ftp:send(Pid, File),
+ chk_file(File, Contents, Config),
+
+ {error,epath} = ftp:send(Pid, "non_existing_file"),
ok.
+
+%%-------------------------------------------------------------------------
+send_3() ->
+ [{doc, "Transfer a file with ftp using send/3."}].
+send_3(Config0) ->
+ Contents = <<"ftp_SUITE test ...">>,
+ Dir = "incoming",
+ File = "file.txt",
+ RemoteFile = "remfile.txt",
+ Config = set_state([reset,{mkfile,File,Contents},{mkdir,Dir}], Config0),
+ Pid = proplists:get_value(ftp, Config),
+
+ ok = ftp:cd(Pid, id2ftp(Dir,Config)),
+ ok = ftp:lcd(Pid, id2ftp("",Config)),
+ ok = ftp:send(Pid, File, RemoteFile),
+ chk_file([Dir,RemoteFile], Contents, Config),
+
+ {error,epath} = ftp:send(Pid, "non_existing_file", RemoteFile),
+ ok.
+
+%%-------------------------------------------------------------------------
+send_bin() ->
+ [{doc, "Send a binary."}].
+send_bin(Config0) ->
+ BinContents = <<"ftp_SUITE test ...">>,
+ File = "file.txt",
+ Config = set_state([reset], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ {error, enotbinary} = ftp:send_bin(Pid, "some string", id2ftp(File,Config)),
+ ok = ftp:send_bin(Pid, BinContents, id2ftp(File,Config)),
+ chk_file(File, BinContents, Config),
+ {error, efnamena} = ftp:send_bin(Pid, BinContents, "/nothere"),
+ ok.
+
+%%-------------------------------------------------------------------------
+send_chunk() ->
+ [{doc, "Send a binary using chunks."}].
+send_chunk(Config0) ->
+ Contents1 = <<"1: ftp_SUITE test ...">>,
+ Contents2 = <<"2: ftp_SUITE test ...">>,
+ File = "file.txt",
+ Config = set_state([reset,{mkdir,"incoming"}], Config0),
+ Pid = proplists:get_value(ftp, Config),
+
+ ok = ftp:send_chunk_start(Pid, id2ftp(File,Config)),
+ {error, echunk} = ftp:send_chunk_start(Pid, id2ftp(File,Config)),
+ {error, echunk} = ftp:cd(Pid, "incoming"),
+ {error, enotbinary} = ftp:send_chunk(Pid, "some string"),
+ ok = ftp:send_chunk(Pid, Contents1),
+ ok = ftp:send_chunk(Pid, Contents2),
+ ok = ftp:send_chunk_end(Pid),
+ chk_file(File, <<Contents1/binary,Contents2/binary>>, Config),
+
+ {error, echunk} = ftp:send_chunk(Pid, Contents1),
+ {error, echunk} = ftp:send_chunk_end(Pid),
+ {error, efnamena} = ftp:send_chunk_start(Pid, "/"),
+ ok.
+
+%%-------------------------------------------------------------------------
+delete() ->
+ [{doc, "Delete a file."}].
+delete(Config0) ->
+ Contents = <<"ftp_SUITE test ...">>,
+ File = "file.txt",
+ Config = set_state([reset,{mkfile,File,Contents}], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ ok = ftp:delete(Pid, id2ftp(File,Config)),
+ chk_no_file([File], Config),
+ {error,epath} = ftp:delete(Pid, id2ftp(File,Config)),
+ ok.
+
+%%-------------------------------------------------------------------------
+mkdir() ->
+ [{doc, "Make a remote directory."}].
+mkdir(Config0) ->
+ NewDir = "new_dir",
+ Config = set_state([reset], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ ok = ftp:mkdir(Pid, id2ftp(NewDir,Config)),
+ chk_dir([NewDir], Config),
+ {error,epath} = ftp:mkdir(Pid, id2ftp(NewDir,Config)),
+ ok.
+
+%%-------------------------------------------------------------------------
+rmdir() ->
+ [{doc, "Remove a directory."}].
+rmdir(Config0) ->
+ Dir = "dir",
+ Config = set_state([reset,{mkdir,Dir}], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ ok = ftp:rmdir(Pid, id2ftp(Dir,Config)),
+ chk_no_dir([Dir], Config),
+ {error,epath} = ftp:rmdir(Pid, id2ftp(Dir,Config)),
+ ok.
+
+%%-------------------------------------------------------------------------
+append() ->
+ [{doc, "Append a local file twice to a remote file"}].
+append(Config0) ->
+ SrcFile = "f_src.txt",
+ DstFile = "f_dst.txt",
+ Contents = <<"ftp_SUITE test ...">>,
+ Config = set_state([reset,{mkfile,SrcFile,Contents}], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ ok = ftp:append(Pid, id2ftp(SrcFile,Config), id2ftp(DstFile,Config)),
+ ok = ftp:append(Pid, id2ftp(SrcFile,Config), id2ftp(DstFile,Config)),
+ chk_file(DstFile, <<Contents/binary,Contents/binary>>, Config),
+ {error,epath} = ftp:append(Pid, id2ftp("non_existing_file",Config), id2ftp(DstFile,Config)),
+ ok.
+
+%%-------------------------------------------------------------------------
+append_bin() ->
+ [{doc, "Append a local file twice to a remote file using append_bin"}].
+append_bin(Config0) ->
+ DstFile = "f_dst.txt",
+ Contents = <<"ftp_SUITE test ...">>,
+ Config = set_state([reset], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ ok = ftp:append_bin(Pid, Contents, id2ftp(DstFile,Config)),
+ ok = ftp:append_bin(Pid, Contents, id2ftp(DstFile,Config)),
+ chk_file(DstFile, <<Contents/binary,Contents/binary>>, Config).
+
+%%-------------------------------------------------------------------------
+append_chunk() ->
+ [{doc, "Append chunks."}].
+append_chunk(Config0) ->
+ File = "f_dst.txt",
+ Contents = [<<"ER">>,<<"LE">>,<<"RL">>],
+ Config = set_state([reset], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ ok = ftp:append_chunk_start(Pid, id2ftp(File,Config)),
+ {error, enotbinary} = ftp:append_chunk(Pid, binary_to_list(lists:nth(1,Contents))),
+ ok = ftp:append_chunk(Pid,lists:nth(1,Contents)),
+ ok = ftp:append_chunk(Pid,lists:nth(2,Contents)),
+ ok = ftp:append_chunk(Pid,lists:nth(3,Contents)),
+ ok = ftp:append_chunk_end(Pid),
+ chk_file(File, <<"ERLERL">>, Config).
+
+%%-------------------------------------------------------------------------
+recv() ->
+ [{doc, "Receive a file using recv/2"}].
+recv(Config0) ->
+ File = "f_dst.txt",
+ SrcDir = "a_dir",
+ Contents = <<"ftp_SUITE test ...">>,
+ Config = set_state([reset, {mkfile,[SrcDir,File],Contents}], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ ok = ftp:cd(Pid, id2ftp(SrcDir,Config)),
+ ok = ftp:lcd(Pid, id2ftp("",Config)),
+ ok = ftp:recv(Pid, File),
+ chk_file(File, Contents, Config),
+ {error,epath} = ftp:recv(Pid, "non_existing_file"),
+ ok.
+
+%%-------------------------------------------------------------------------
+recv_3() ->
+ [{doc,"Receive a file using recv/3"}].
+recv_3(Config0) ->
+ DstFile = "f_src.txt",
+ SrcFile = "f_dst.txt",
+ Contents = <<"ftp_SUITE test ...">>,
+ Config = set_state([reset, {mkfile,SrcFile,Contents}], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ ok = ftp:cd(Pid, id2ftp("",Config)),
+ ok = ftp:recv(Pid, SrcFile, id2abs(DstFile,Config)),
+ chk_file(DstFile, Contents, Config).
+
+%%-------------------------------------------------------------------------
+recv_bin() ->
+ [{doc, "Receive a file as a binary."}].
+recv_bin(Config0) ->
+ File = "f_dst.txt",
+ Contents = <<"ftp_SUITE test ...">>,
+ Config = set_state([reset, {mkfile,File,Contents}], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ {ok,Received} = ftp:recv_bin(Pid, id2ftp(File,Config)),
+ find_diff(Received, Contents),
+ {error,epath} = ftp:recv_bin(Pid, id2ftp("non_existing_file",Config)),
+ ok.
+
+%%-------------------------------------------------------------------------
+recv_chunk() ->
+ [{doc, "Receive a file using chunk-wise."}].
+recv_chunk(Config0) ->
+ File = "big_file.txt",
+ Contents = list_to_binary( lists:duplicate(1000, lists:seq(0,255)) ),
+ Config = set_state([reset, {mkfile,File,Contents}], Config0),
+ Pid = proplists:get_value(ftp, Config),
+ {{error, "ftp:recv_chunk_start/2 not called"},_} = recv_chunk(Pid, <<>>),
+ ok = ftp:recv_chunk_start(Pid, id2ftp(File,Config)),
+ {ok, ReceivedContents, _Ncunks} = recv_chunk(Pid, <<>>),
+ find_diff(ReceivedContents, Contents).
+
+recv_chunk(Pid, Acc) ->
+ recv_chunk(Pid, Acc, 0).
+
+recv_chunk(Pid, Acc, N) ->
+ case ftp:recv_chunk(Pid) of
+ ok -> {ok, Acc, N};
+ {ok, Bin} -> recv_chunk(Pid, <<Acc/binary, Bin/binary>>, N+1);
+ Error -> {Error, N}
+ end.
+
+%%-------------------------------------------------------------------------
+type() ->
+ [{doc,"Test that we can change btween ASCCI and binary transfer mode"}].
+type(Config) ->
+ Pid = proplists:get_value(ftp, Config),
+ ok = ftp:type(Pid, ascii),
+ ok = ftp:type(Pid, binary),
+ ok = ftp:type(Pid, ascii),
+ {error, etype} = ftp:type(Pid, foobar).
+
+%%-------------------------------------------------------------------------
+quote(Config) ->
+ Pid = proplists:get_value(ftp, Config),
+ ["257 \""++_Rest] = ftp:quote(Pid, "pwd"), %% 257
+ [_| _] = ftp:quote(Pid, "help"),
+ %% This negativ test causes some ftp servers to hang. This test
+ %% is not important for the client, so we skip it for now.
+ %%["425 Can't build data connection: Connection refused."]
+ %% = ftp:quote(Pid, "list"),
+ ok.
+
+%%-------------------------------------------------------------------------
+progress_report_send() ->
+ [{doc, "Test the option progress for ftp:send/[2,3]"}].
+progress_report_send(Config) when is_list(Config) ->
+ ReportPid =
+ spawn_link(?MODULE, progress_report_receiver_init, [self(), 1]),
+ send(Config),
+ receive
+ {ReportPid, ok} ->
+ ok
+ end.
+
+%%-------------------------------------------------------------------------
+progress_report_recv() ->
+ [{doc, "Test the option progress for ftp:recv/[2,3]"}].
+progress_report_recv(Config) when is_list(Config) ->
+ ReportPid =
+ spawn_link(?MODULE, progress_report_receiver_init, [self(), 3]),
+ recv(Config),
+ receive
+ {ReportPid, ok} ->
+ ok
+ end.
+
+%%-------------------------------------------------------------------------
+
+not_owner() ->
+ [{doc, "Test what happens if a process that not owns the connection tries "
+ "to use it"}].
+not_owner(Config) when is_list(Config) ->
+ Pid = proplists:get_value(ftp, Config),
+
+ Parent = self(),
+ OtherPid = spawn_link(
+ fun() ->
+ {error, not_connection_owner} = ftp:pwd(Pid),
+ ftp:close(Pid),
+ Parent ! {self(), ok}
+ end),
+ receive
+ {OtherPid, ok} ->
+ {ok, _} = ftp:pwd(Pid)
+ end.
+
+
+%%-------------------------------------------------------------------------
+
+
+unexpected_call()->
+ [{doc, "Test that behaviour of the ftp process if the api is abused"}].
+unexpected_call(Config) when is_list(Config) ->
+ Flag = process_flag(trap_exit, true),
+ Pid = proplists:get_value(ftp, Config),
+
+ %% Serious programming fault, connetion will be shut down
+ case (catch gen_server:call(Pid, {self(), foobar, 10}, infinity)) of
+ {error, {connection_terminated, 'API_violation'}} ->
+ ok;
+ Unexpected1 ->
+ exit({unexpected_result, Unexpected1})
+ end,
+ ct:sleep(500),
+ undefined = process_info(Pid, status),
+ process_flag(trap_exit, Flag).
+%%-------------------------------------------------------------------------
+
+unexpected_cast()->
+ [{doc, "Test that behaviour of the ftp process if the api is abused"}].
+unexpected_cast(Config) when is_list(Config) ->
+ Flag = process_flag(trap_exit, true),
+ Pid = proplists:get_value(ftp, Config),
+ %% Serious programming fault, connetion will be shut down
+ gen_server:cast(Pid, {self(), foobar, 10}),
+ ct:sleep(500),
+ undefined = process_info(Pid, status),
+ process_flag(trap_exit, Flag).
+%%-------------------------------------------------------------------------
+
+unexpected_bang()->
+ [{doc, "Test that connection ignores unexpected bang"}].
+unexpected_bang(Config) when is_list(Config) ->
+ Flag = process_flag(trap_exit, true),
+ Pid = proplists:get_value(ftp, Config),
+ %% Could be an innocent misstake the connection lives.
+ Pid ! foobar,
+ ct:sleep(500),
+ {status, _} = process_info(Pid, status),
+ process_flag(trap_exit, Flag).
+
+%%-------------------------------------------------------------------------
+
+clean_shutdown() ->
+ [{doc, "Test that owning process that exits with reason "
+ "'shutdown' does not cause an error message. OTP 6035"}].
+
+clean_shutdown(Config) ->
+ Parent = self(),
+ HelperPid = spawn(
+ fun() ->
+ ftp__open(Config, [verbose]),
+ Parent ! ok,
+ receive
+ nothing -> ok
+ end
+ end),
+ receive
+ ok ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ LogFile = filename:join([PrivDir,"ticket_6035.log"]),
+ error_logger:logfile({open, LogFile}),
+ exit(HelperPid, shutdown),
+ timer:sleep(2000),
+ error_logger:logfile(close),
+ case is_error_report_6035(LogFile) of
+ true -> ok;
+ false -> {fail, "Bad logfile"}
+ end
+ end.
+
+%%%----------------------------------------------------------------
+%%% Error codes not tested elsewhere
+
+error_elogin(Config0) ->
+ Dir = "test",
+ OldFile = "old.txt",
+ NewFile = "new.txt",
+ SrcDir = "data",
+ File = "file.txt",
+ Config = set_state([reset,
+ {mkdir,Dir},
+ {mkfile,OldFile,<<"Contents..">>},
+ {mkfile,[SrcDir,File],<<"Contents..">>}], Config0),
+
+ Pid = proplists:get_value(ftp, Config),
+ ok = ftp:lcd(Pid, id2ftp(SrcDir,Config)),
+ {error,elogin} = ftp:send(Pid, File),
+ ok = ftp:lcd(Pid, id2ftp("",Config)),
+ {error,elogin} = ftp:pwd(Pid),
+ {error,elogin} = ftp:cd(Pid, id2ftp(Dir,Config)),
+ {error,elogin} = ftp:rename(Pid,
+ id2ftp(OldFile,Config),
+ id2ftp(NewFile,Config)),
+ ok.
+
+error_ehost(_Config) ->
+ {error, ehost} = ftp:open("nohost.nodomain"),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Internal functions -----------------------------------------------
+%%--------------------------------------------------------------------
+
+make_cert_files(Alg1, Alg2, Prefix, Dir) ->
+ CaInfo = {CaCert,_} = erl_make_certs:make_cert([{key,Alg1}]),
+ {Cert,CertKey} = erl_make_certs:make_cert([{key,Alg2},{issuer,CaInfo}]),
+ CaCertFile = filename:join(Dir, Prefix++"cacerts.pem"),
+ CertFile = filename:join(Dir, Prefix++"cert.pem"),
+ KeyFile = filename:join(Dir, Prefix++"key.pem"),
+ der_to_pem(CaCertFile, [{'Certificate', CaCert, not_encrypted}]),
+ der_to_pem(CertFile, [{'Certificate', Cert, not_encrypted}]),
+ der_to_pem(KeyFile, [CertKey]),
+ ok.
+
+der_to_pem(File, Entries) ->
+ PemBin = public_key:pem_encode(Entries),
+ file:write_file(File, PemBin).
+
+%%--------------------------------------------------------------------
+chk_file(Path=[C|_], ExpectedContents, Config) when 0<C,C=<255 ->
+ chk_file([Path], ExpectedContents, Config);
+
+chk_file(PathList, ExpectedContents, Config) ->
+ Path = filename:join(PathList),
+ AbsPath = id2abs(Path,Config),
+ case file:read_file(AbsPath) of
+ {ok,ExpectedContents} ->
+ true;
+ {ok,ReadContents} ->
+ {error,{diff,Pos,RC,LC}} = find_diff(ReadContents, ExpectedContents, 1),
+ ct:log("Bad contents of ~p.~nGot:~n~p~nExpected:~n~p~nDiff at pos ~p ~nRead: ~p~nExp : ~p",
+ [AbsPath,ReadContents,ExpectedContents,Pos,RC,LC]),
+ ct:fail("Bad contents of ~p", [Path]);
+ {error,Error} ->
+ try begin
+ {ok,CWD} = file:get_cwd(),
+ ct:log("file:get_cwd()=~p~nfiles:~n~p",[CWD,file:list_dir(CWD)])
+ end
+ of _ -> ok
+ catch _:_ ->ok
+ end,
+ ct:fail("Error reading ~p: ~p",[Path,Error])
+ end.
+
+
+chk_no_file(Path=[C|_], Config) when 0<C,C=<255 ->
+ chk_no_file([Path], Config);
+
+chk_no_file(PathList, Config) ->
+ Path = filename:join(PathList),
+ AbsPath = id2abs(Path,Config),
+ case file:read_file(AbsPath) of
+ {error,enoent} ->
+ true;
+ {ok,Contents} ->
+ ct:log("File ~p exists although it shouldn't. Contents:~n~p",
+ [AbsPath,Contents]),
+ ct:fail("File exists: ~p", [Path]);
+ {error,Error} ->
+ ct:fail("Unexpected error reading ~p: ~p",[Path,Error])
+ end.
+
+
+chk_dir(Path=[C|_], Config) when 0<C,C=<255 ->
+ chk_dir([Path], Config);
+
+chk_dir(PathList, Config) ->
+ Path = filename:join(PathList),
+ AbsPath = id2abs(Path,Config),
+ case file:read_file_info(AbsPath) of
+ {ok, #file_info{type=directory}} ->
+ true;
+ {ok, #file_info{type=Type}} ->
+ ct:fail("Expected dir ~p is a ~p",[Path,Type]);
+ {error,Error} ->
+ ct:fail("Expected dir ~p: ~p",[Path,Error])
+ end.
+
+chk_no_dir(PathList, Config) ->
+ Path = filename:join(PathList),
+ AbsPath = id2abs(Path,Config),
+ case file:read_file_info(AbsPath) of
+ {error,enoent} ->
+ true;
+ {ok, #file_info{type=directory}} ->
+ ct:fail("Dir ~p erroneously exists",[Path]);
+ {ok, #file_info{type=Type}} ->
+ ct:fail("~p ~p erroneously exists",[Type,Path]);
+ {error,Error} ->
+ ct:fail("Unexpected error for ~p: ~p",[Path,Error])
+ end.
+
+%%--------------------------------------------------------------------
+find_executable(Config) ->
+ search_executable(proplists:get_value(ftpservers, Config, ?default_ftp_servers)).
+
+
+search_executable([{Name,Paths,_StartCmd,_ChkUp,_StopCommand,_ConfigUpd,_Host,_Port}|Srvrs]) ->
+ case os_find(Name,Paths) of
+ false ->
+ ct:log("~p not found",[Name]),
+ search_executable(Srvrs);
+ AbsName ->
+ ct:comment("Found ~p",[AbsName]),
+ {ok, {AbsName,_StartCmd,_ChkUp,_StopCommand,_ConfigUpd,_Host,_Port}}
+ end;
+search_executable([]) ->
+ false.
+
+
+os_find(Name, Paths) ->
+ case os:find_executable(Name, Paths) of
+ false -> os:find_executable(Name);
+ AbsName -> AbsName
+ end.
+
+%%%----------------------------------------------------------------
+start_ftpd(Config0) ->
+ {AbsName,StartCmd,_ChkUp,_StopCommand,ConfigRewrite,Host,Port} =
+ proplists:get_value(ftpd_data, Config0),
+ case StartCmd(Config0, AbsName) of
+ {ok,StartResult} ->
+ Config = [{ftpd_host,Host},
+ {ftpd_port,Port},
+ {ftpd_start_result,StartResult} | ConfigRewrite(Config0)],
+ try
+ ftp__close(ftp__open(Config,[verbose]))
+ of
+ Config1 when is_list(Config1) ->
+ ct:log("Usuable ftp server ~p started on ~p:~p",[AbsName,Host,Port]),
+ Config
+ catch
+ Class:Exception ->
+ ct:log("Ftp server ~p started on ~p:~p but is unusable:~n~p:~p",
+ [AbsName,Host,Port,Class,Exception]),
+ {skip, [AbsName," started but unusuable"]}
+ end;
+ {error,Msg} ->
+ {skip, [AbsName," not started: ",Msg]}
+ end.
+
+stop_ftpd(Config) ->
+ {_Name,_StartCmd,_ChkUp,StopCommand,_ConfigUpd,_Host,_Port} = proplists:get_value(ftpd_data, Config),
+ StopCommand(proplists:get_value(ftpd_start_result,Config)).
+
+ps_ftpd(Config) ->
+ {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = proplists:get_value(ftpd_data, Config),
+ ct:log( ChkUp(proplists:get_value(ftpd_start_result,Config)) ).
+
+
+ftpd_running(Config) ->
+ {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = proplists:get_value(ftpd_data, Config),
+ ChkUp(proplists:get_value(ftpd_start_result,Config)).
+
+ftp__open(Config, Options) ->
+ Host = proplists:get_value(ftpd_host,Config),
+ Port = proplists:get_value(ftpd_port,Config),
+ ct:log("Host=~p, Port=~p",[Host,Port]),
+ {ok,Pid} = ftp:open(Host, [{port,Port} | Options]),
+ [{ftp,Pid}|Config].
+
+ftp__close(Config) ->
+ ok = ftp:close(proplists:get_value(ftp,Config)),
+ Config.
+
+split(Cs) -> string:tokens(Cs, "\r\n").
+
+find_diff(Bin1, Bin2) ->
+ case find_diff(Bin1, Bin2, 1) of
+ {error, {diff,Pos,RC,LC}} ->
+ ct:log("Contents differ at position ~p.~nOp1: ~p~nOp2: ~p",[Pos,RC,LC]),
+ ct:fail("Contents differ at pos ~p",[Pos]);
+ Other ->
+ Other
+ end.
+
+find_diff(A, A, _) -> true;
+find_diff(<<H,T1/binary>>, <<H,T2/binary>>, Pos) -> find_diff(T1, T2, Pos+1);
+find_diff(RC, LC, Pos) -> {error, {diff, Pos, RC, LC}}.
+
+set_state(Ops, Config) when is_list(Ops) -> lists:foldl(fun set_state/2, Config, Ops);
+
+set_state(reset, Config) ->
+ rm('*', id2abs("",Config)),
+ PrivDir = proplists:get_value(priv_dir,Config),
+ file:set_cwd(PrivDir),
+ ftp:lcd(proplists:get_value(ftp,Config),PrivDir),
+ set_state({mkdir,""},Config);
+set_state({mkdir,Id}, Config) ->
+ Abs = id2abs(Id, Config),
+ mk_path(Abs),
+ file:make_dir(Abs),
+ Config;
+set_state({mkfile,Id,Contents}, Config) ->
+ Abs = id2abs(Id, Config),
+ mk_path(Abs),
+ ok = file:write_file(Abs, Contents),
+ Config.
+
+mk_path(Abs) -> lists:foldl(fun mk_path/2, [], filename:split(filename:dirname(Abs))).
+
+mk_path(F, Pfx) ->
+ case file:read_file_info(AbsName=filename:join(Pfx,F)) of
+ {ok,#file_info{type=directory}} ->
+ AbsName;
+ {error,eexist} ->
+ AbsName;
+ {error,enoent} ->
+ ok = file:make_dir(AbsName),
+ AbsName
+ end.
+
+rm('*', Pfx) ->
+ {ok,Fs} = file:list_dir(Pfx),
+ lists:foreach(fun(F) -> rm(F, Pfx) end, Fs);
+rm(F, Pfx) ->
+ case file:read_file_info(AbsName=filename:join(Pfx,F)) of
+ {ok,#file_info{type=directory}} ->
+ {ok,Fs} = file:list_dir(AbsName),
+ lists:foreach(fun(F1) -> rm(F1,AbsName) end, Fs),
+ ok = file:del_dir(AbsName);
+
+ {ok,#file_info{type=regular}} ->
+ ok = file:delete(AbsName);
+
+ {error,enoent} ->
+ ok
+ end.
+
+id2abs(Id, Conf) -> filename:join(proplists:get_value(priv_dir,Conf),ids(Id)).
+id2ftp(Id, Conf) -> (proplists:get_value(id2ftp,Conf))(ids(Id)).
+id2ftp_result(Id, Conf) -> (proplists:get_value(id2ftp_result,Conf))(ids(Id)).
+
+ids([[_|_]|_]=Ids) -> filename:join(Ids);
+ids(Id) -> Id.
+
+
+is_expected_absName(Id, File, Conf) -> File = (proplists:get_value(id2abs,Conf))(Id).
+is_expected_ftpInName(Id, File, Conf) -> File = (proplists:get_value(id2ftp,Conf))(Id).
+is_expected_ftpOutName(Id, File, Conf) -> File = (proplists:get_value(id2ftp_result,Conf))(Id).
+
+
+%%%----------------------------------------------------------------
+%%% Help functions for the option '{progress,Progress}'
+%%%
+
+%%%----------------
+%%% Callback:
+
+progress(#progress{} = P, _File, {file_size, Total} = M) ->
+ ct:pal("Progress: ~p",[M]),
+ progress_report_receiver ! start,
+ P#progress{total = Total};
+
+progress(#progress{current = Current} = P, _File, {transfer_size, 0} = M) ->
+ ct:pal("Progress: ~p",[M]),
+ progress_report_receiver ! finish,
+ case P#progress.total of
+ unknown -> P;
+ Current -> P;
+ Total -> ct:fail({error, {progress, {total,Total}, {current,Current}}}),
+ P
+ end;
+
+progress(#progress{current = Current} = P, _File, {transfer_size, Size} = M) ->
+ ct:pal("Progress: ~p",[M]),
+ progress_report_receiver ! update,
+ P#progress{current = Current + Size};
+
+progress(P, _File, M) ->
+ ct:pal("Progress **** Strange: ~p",[M]),
+ P.
+
+
+%%%----------------
+%%% Help process that counts the files transferred:
+
+progress_report_receiver_init(Parent, N) ->
+ register(progress_report_receiver, self()),
+ progress_report_receiver_expect_N_files(Parent, N).
+
+progress_report_receiver_expect_N_files(_Parent, 0) ->
+ ct:pal("progress_report got all files!", []);
+progress_report_receiver_expect_N_files(Parent, N) ->
+ ct:pal("progress_report expects ~p more files",[N]),
+ receive
+ start -> ok
+ end,
+ progress_report_receiver_loop(Parent, N-1).
+
+
+progress_report_receiver_loop(Parent, N) ->
+ ct:pal("progress_report expect update | finish. N = ~p",[N]),
+ receive
+ update ->
+ ct:pal("progress_report got update",[]),
+ progress_report_receiver_loop(Parent, N);
+ finish ->
+ ct:pal("progress_report got finish, send ~p to ~p",[{self(),ok}, Parent]),
+ Parent ! {self(), ok},
+ progress_report_receiver_expect_N_files(Parent, N)
+ end.
+
+%%%----------------------------------------------------------------
+%%% Help functions for bug OTP-6035
+
+is_error_report_6035(LogFile) ->
+ case file:read_file(LogFile) of
+ {ok, Bin} ->
+ nomatch =/= binary:match(Bin, <<"=ERROR REPORT====">>);
+ _ ->
+ false
+ end.
+
diff --git a/lib/inets/test/ftp_SUITE_data/vsftpd.conf b/lib/inets/test/ftp_SUITE_data/vsftpd.conf
new file mode 100644
index 0000000000..a5584f5916
--- /dev/null
+++ b/lib/inets/test/ftp_SUITE_data/vsftpd.conf
@@ -0,0 +1,26 @@
+
+###
+### Some parameters are given in the vsftpd start command.
+###
+### Typical command-line paramters are such that has a file path
+### component like cert files.
+###
+
+
+listen=YES
+listen_port=9999
+run_as_launching_user=YES
+ssl_enable=YES
+allow_anon_ssl=YES
+
+background=YES
+
+write_enable=YES
+anonymous_enable=YES
+anon_upload_enable=YES
+anon_mkdir_write_enable=YES
+anon_other_write_enable=YES
+anon_world_readable_only=NO
+
+### Shouldn't be necessary....
+require_ssl_reuse=NO
diff --git a/lib/inets/test/ftp_format_SUITE.erl b/lib/inets/test/ftp_format_SUITE.erl
index 16e5cdb4bc..a33b31f46f 100644
--- a/lib/inets/test/ftp_format_SUITE.erl
+++ b/lib/inets/test/ftp_format_SUITE.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -21,21 +22,15 @@
-author('[email protected]').
-include_lib("common_test/include/ct.hrl").
--include("test_server_line.hrl").
-include("ftp_internal.hrl").
-%% Test server specific exports
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2, end_per_testcase/2]).
-
-%% Test cases must be exported.
--export([ 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]).
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds,5}}
+ ].
all() ->
[{group, ftp_response}, format_error].
@@ -60,23 +55,16 @@ end_per_group(_GroupName, Config) ->
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),
+ Config.
+end_per_testcase(_, _) ->
ok.
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
-ftp_150(doc) ->
- ["Especially check that respons can be devided in a random place."];
-ftp_150(suite) ->
- [];
+ftp_150() ->
+ [{doc, "Especially check that respons can be devided in a random place."}].
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"],
@@ -84,14 +72,11 @@ ftp_150(Config) when is_list(Config) ->
"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) ->
- [];
+ {pos_prel, _} = ftp_response:interpret(Msg).
+
+ftp_200() ->
+ [{doc, "Especially check that respons can be devided after the first status "
+ "code character and in the end delimiter."}].
ftp_200(Config) when is_list(Config) ->
FtpResponse = ["2", "00 PORT command successful.", [?CR], [?LF]],
@@ -100,11 +85,9 @@ ftp_200(Config) when is_list(Config) ->
{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() ->
+ [{doc, "Especially check that respons can be devided after the "
+ "first with space "}].
ftp_220(Config) when is_list(Config) ->
FtpResponse = ["220 ","fingon FTP server (SunOS 5.8) ready.\r\n"],
@@ -113,11 +96,9 @@ ftp_220(Config) when is_list(Config) ->
{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() ->
+ [{doc, "Especially check that respons can be devided after second status code"
+ " character and in the end delimiter."}].
ftp_226(Config) when is_list(Config) ->
FtpResponse = ["22" "6 Transfer complete.\r", [?LF]],
@@ -126,10 +107,8 @@ ftp_226(Config) when is_list(Config) ->
{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() ->
+ [{doc, "Especially check that quoted chars do not cause a problem."}].
ftp_257(Config) when is_list(Config) ->
FtpResponse = ["257 \"/\" is current directory.\r\n"],
@@ -138,11 +117,9 @@ ftp_257(Config) when is_list(Config) ->
{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() ->
+ [{doc, "Especially check that respons can be devided after the third status "
+ " status code character."}].
ftp_331(Config) when is_list(Config) ->
%% Brake before white space after code
FtpResponse =
@@ -153,10 +130,8 @@ ftp_331(Config) when is_list(Config) ->
{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() ->
+ [{doc, "Especially check a message that was received in only one part."}].
ftp_425(Config) when is_list(Config) ->
FtpResponse =
["425 Can't build data connection: Connection refused.\r\n"],
@@ -166,10 +141,8 @@ ftp_425(Config) when is_list(Config) ->
{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() ->
+ [{doc, "Especially check multiple lines devided in significant places"}].
ftp_multiple_lines(Config) when is_list(Config) ->
FtpResponse = ["21", "4","-The",
" following commands are recognized:\r\n"
@@ -247,13 +220,11 @@ ftp_multiple_lines(Config) when is_list(Config) ->
FtpResponse2),
ok.
-ftp_other_status_codes(doc) ->
- ["Check that other valid status codes, than the ones above, are handled"
+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) ->
- [];
+ "they are not included here."}].
ftp_other_status_codes(Config) when is_list(Config) ->
%% 1XX
@@ -282,18 +253,16 @@ ftp_other_status_codes(Config) when is_list(Config) ->
{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"),
+ {elogin, _ } = 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() ->
+ [{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(Config) when is_list(Config) ->
FtpResponse = ["200 PORT command successful.\r\n200 Foobar\r\n"],
@@ -306,10 +275,6 @@ ftp_multipel_ctrl_messages(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-format_error(doc) ->
- [""];
-format_error(suite) ->
- [];
format_error(Config) when is_list(Config) ->
"Synchronisation error during chunk sending." =
ftp:formaterror(echunk),
@@ -346,7 +311,7 @@ parse(Module, Function, [AccLines, StatusCode], [Data | Rest]) ->
{continue, {NewData, NewAccLines, NewStatusCode}} ->
case Rest of
[] ->
- test_server:fail({wrong_input, Data, Rest});
+ ct:fail({wrong_input, Data, Rest});
[_ | _] ->
parse(Module, Function, [NewAccLines, NewStatusCode],
[binary_to_list(NewData) ++ hd(Rest) | tl(Rest)])
diff --git a/lib/inets/test/ftp_freebsd_x86_test.erl b/lib/inets/test/ftp_freebsd_x86_test.erl
deleted file mode 100644
index 1d66779882..0000000000
--- a/lib/inets/test/ftp_freebsd_x86_test.erl
+++ /dev/null
@@ -1,160 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ftp_freebsd_x86_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Freebsd x86 ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(freebsd_x86, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_linux_ppc_test.erl b/lib/inets/test/ftp_linux_ppc_test.erl
deleted file mode 100644
index bba97237f1..0000000000
--- a/lib/inets/test/ftp_linux_ppc_test.erl
+++ /dev/null
@@ -1,158 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ftp_linux_ppc_test).
-
-%% Note: This directive should only be used in test suites.
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Linux ppc ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(linux_ppc, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
diff --git a/lib/inets/test/ftp_linux_x86_test.erl b/lib/inets/test/ftp_linux_x86_test.erl
deleted file mode 100644
index bbefd8231e..0000000000
--- a/lib/inets/test/ftp_linux_x86_test.erl
+++ /dev/null
@@ -1,160 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ftp_linux_x86_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Linux x86 ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(linux_x86, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_macosx_ppc_test.erl b/lib/inets/test/ftp_macosx_ppc_test.erl
deleted file mode 100644
index c9f33b8beb..0000000000
--- a/lib/inets/test/ftp_macosx_ppc_test.erl
+++ /dev/null
@@ -1,159 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ftp_macosx_ppc_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Macosx ppc ").
-
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(macosx_ppc, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
-[open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(_X) -> {skipped,"unknown error"}.%?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(_X) -> {skipped,"unknown error"}.%%?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_macosx_x86_test.erl b/lib/inets/test/ftp_macosx_x86_test.erl
deleted file mode 100644
index 17b7160b95..0000000000
--- a/lib/inets/test/ftp_macosx_x86_test.erl
+++ /dev/null
@@ -1,159 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ftp_macosx_x86_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Macosx x86 ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(macosx_x86, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
-[open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist([{wildcard_support, false} | X]).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist([{wildcard_support, false} | X]).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_netbsd_x86_test.erl b/lib/inets/test/ftp_netbsd_x86_test.erl
deleted file mode 100644
index bb474852c5..0000000000
--- a/lib/inets/test/ftp_netbsd_x86_test.erl
+++ /dev/null
@@ -1,159 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ftp_netbsd_x86_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Netbsd x86 ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(netbsd_x86, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_openbsd_x86_test.erl b/lib/inets/test/ftp_openbsd_x86_test.erl
deleted file mode 100644
index 54fce702a0..0000000000
--- a/lib/inets/test/ftp_openbsd_x86_test.erl
+++ /dev/null
@@ -1,158 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ftp_openbsd_x86_test).
-
-%% Note: This directive should only be used in test suites.
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Openbsd x86 ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(openbsd_x86, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
diff --git a/lib/inets/test/ftp_property_test_SUITE.erl b/lib/inets/test/ftp_property_test_SUITE.erl
new file mode 100644
index 0000000000..b314882296
--- /dev/null
+++ b/lib/inets/test/ftp_property_test_SUITE.erl
@@ -0,0 +1,53 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+%%% Run like this:
+%%% ct:run_test([{suite,"ftp_property_test_SUITE"}, {logdir,"/ldisk/OTP/LOG"}]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% %%%
+%%% WARNING %%%
+%%% %%%
+%%% This is experimental code which may be changed or removed %%%
+%%% anytime without any warning. %%%
+%%% %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-module(ftp_property_test_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+all() -> [prop_ftp_case].
+
+
+init_per_suite(Config) ->
+ inets:start(),
+ ct_property_test:init_per_suite(Config).
+
+
+%%%---- test case
+prop_ftp_case(Config) ->
+ ct_property_test:quickcheck(
+ ftp_simple_client_server:prop_ftp(Config),
+ Config
+ ).
diff --git a/lib/inets/test/ftp_solaris10_sparc_test.erl b/lib/inets/test/ftp_solaris10_sparc_test.erl
deleted file mode 100644
index 0da50dc91b..0000000000
--- a/lib/inets/test/ftp_solaris10_sparc_test.erl
+++ /dev/null
@@ -1,161 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ftp_solaris10_sparc_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Solaris 10 sparc ").
-
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(solaris10_sparc, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_solaris10_x86_test.erl b/lib/inets/test/ftp_solaris10_x86_test.erl
deleted file mode 100644
index 3e7045bb4d..0000000000
--- a/lib/inets/test/ftp_solaris10_x86_test.erl
+++ /dev/null
@@ -1,162 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ftp_solaris10_x86_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD, ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_), ?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM, "Solaris 10 x86 ").
-
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(solaris10_x86, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-%%--------------------------------------------------------------------
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_solaris8_sparc_test.erl b/lib/inets/test/ftp_solaris8_sparc_test.erl
deleted file mode 100644
index 23dbfc8fe3..0000000000
--- a/lib/inets/test/ftp_solaris8_sparc_test.erl
+++ /dev/null
@@ -1,159 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ftp_solaris8_sparc_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Solaris 8 sparc ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(solaris8_sparc, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_solaris9_sparc_test.erl b/lib/inets/test/ftp_solaris9_sparc_test.erl
deleted file mode 100644
index 896e2f497f..0000000000
--- a/lib/inets/test/ftp_solaris9_sparc_test.erl
+++ /dev/null
@@ -1,158 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ftp_solaris9_sparc_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Solaris 9 sparc ").
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(solaris9_sparc, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_suite_lib.erl b/lib/inets/test/ftp_suite_lib.erl
deleted file mode 100644
index 35f21cc74d..0000000000
--- a/lib/inets/test/ftp_suite_lib.erl
+++ /dev/null
@@ -1,1675 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ftp_suite_lib).
-
-
--include_lib("test_server/include/test_server.hrl").
--include_lib("test_server/include/test_server_line.hrl").
--include("inets_test_lib.hrl").
-
-%% Test server specific exports
-% -export([init_per_testcase/2, end_per_testcase/2]).
-
--compile(export_all).
-
-
--record(progress, {
- current = 0,
- total
- }).
-
-
-
--define(FTP_USER, "anonymous").
--define(FTP_PASS, passwd()).
--define(FTP_PORT, 21).
-
--define(BAD_HOST, "badhostname").
--define(BAD_USER, "baduser").
--define(BAD_DIR, "baddirectory").
-
--ifdef(ftp_debug_client).
--define(ftp_open(Host, Flags),
- do_ftp_open(Host, [{debug, debug},
- {timeout, timer:seconds(15)} | Flags])).
--else.
--ifdef(ftp_trace_client).
--define(ftp_open(Host, Flags),
- do_ftp_open(Host, [{debug, trace},
- {timeout, timer:seconds(15)} | Flags])).
--else.
--define(ftp_open(Host, Flags),
- do_ftp_open(Host, [{verbose, true},
- {timeout, timer:seconds(15)} | Flags])).
--endif.
--endif.
-
-%% -- Tickets --
-
-tickets(doc) ->
- "Test cases for reported bugs";
-tickets(suite) ->
- [ticket_6035].
-
-%% --
-
-ftpd_init(FtpdTag, Config) ->
- %% Get the host name(s) of FTP server
- Hosts =
- case ct:get_config(ftpd_hosts) of
- undefined ->
- ftpd_hosts(data_dir(Config));
- H ->
- H
- end,
- p("ftpd_init -> "
- "~n Hosts: ~p"
- "~n Config: ~p"
- "~n FtpdTag: ~p", [Hosts, Config, FtpdTag]),
- %% Get the first host that actually have a running FTP server
- case lists:keysearch(FtpdTag, 1, Hosts) of
- {value, {_, TagHosts}} when is_list(TagHosts) ->
- inets:start(),
- case (catch get_ftpd_host(TagHosts)) of
- {ok, Host} ->
- inets:stop(),
- [{ftp_remote_host, Host}|Config];
- _ ->
- inets:stop(),
- Reason = lists:flatten(
- io_lib:format("Could not find a valid "
- "FTP server for ~p (~p)",
- [FtpdTag, TagHosts])),
- {skip, Reason}
- end;
- _ ->
- Reason = lists:flatten(
- io_lib:format("No host(s) running FTPD server "
- "for ~p", [FtpdTag])),
- {skip, Reason}
- end.
-
-ftpd_fin(Config) ->
- lists:keydelete(ftp_remote_host, 1, Config).
-
-get_ftpd_host([]) ->
- {error, no_host};
-get_ftpd_host([Host|Hosts]) ->
- p("get_ftpd_host -> entry with"
- "~n Host: ~p"
- "~n", [Host]),
- case (catch ftp:open(Host, [{port, ?FTP_PORT}, {timeout, 20000}])) of
- {ok, Pid} ->
- (catch ftp:close(Pid)),
- {ok, Host};
- _ ->
- get_ftpd_host(Hosts)
- end.
-
-
-%%--------------------------------------------------------------------
-
-dirty_select_ftpd_host(Config) ->
- Hosts =
- case ct:get_config(ftpd_hosts) of
- undefined ->
- ftpd_hosts(data_dir(Config));
- H ->
- H
- end,
- dirty_select_ftpd_host2(Hosts).
-
-dirty_select_ftpd_host2([]) ->
- throw({error, not_found});
-dirty_select_ftpd_host2([{PlatformTag, Hosts} | PlatformHosts]) ->
- case dirty_select_ftpd_host3(Hosts) of
- none ->
- dirty_select_ftpd_host2(PlatformHosts);
- {ok, Host} ->
- {PlatformTag, Host}
- end.
-
-dirty_select_ftpd_host3([]) ->
- none;
-dirty_select_ftpd_host3([Host|Hosts]) when is_list(Host) ->
- case dirty_select_ftpd_host4(Host) of
- true ->
- {ok, Host};
- false ->
- dirty_select_ftpd_host3(Hosts)
- end;
-dirty_select_ftpd_host3([_|Hosts]) ->
- dirty_select_ftpd_host3(Hosts).
-
-%% This is a very simple and dirty test that there is a
-%% (FTP) deamon on the other end.
-dirty_select_ftpd_host4(Host) ->
- Port = 21,
- IpFam = inet,
- Opts = [IpFam, binary, {packet, 0}, {active, false}],
- Timeout = ?SECS(5),
- case gen_tcp:connect(Host, Port, Opts, Timeout) of
- {ok, Sock} ->
- gen_tcp:close(Sock),
- true;
- _Error ->
- false
- end.
-
-
-%%--------------------------------------------------------------------
-
-test_filenames() ->
- {ok, Host} = inet:gethostname(),
- File = Host ++ "_ftp_test.txt",
- NewFile = "new_" ++ File,
- {File, NewFile}.
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(Case, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config)
- when (Case =:= open) orelse
- (Case =:= open_port) ->
- put(ftp_testcase, Case),
- io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
- inets:start(),
- NewConfig = data_dir(Config),
- watch_dog(NewConfig);
-
-init_per_testcase(Case, Config) ->
- put(ftp_testcase, Case),
- do_init_per_testcase(Case, Config).
-
-do_init_per_testcase(Case, Config)
- when (Case =:= passive_user) ->
- io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE,Case]),
- inets:start(),
- NewConfig = close_connection(watch_dog(Config)),
- Host = ftp_host(Config),
- case (catch ?ftp_open(Host, [{mode, passive}])) of
- {ok, Pid} ->
- [{ftp, Pid} | data_dir(NewConfig)];
- {skip, _} = SKIP ->
- SKIP
- end;
-
-do_init_per_testcase(Case, Config)
- when (Case =:= active_user) ->
- io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
- inets:start(),
- NewConfig = close_connection(watch_dog(Config)),
- Host = ftp_host(Config),
- case (catch ?ftp_open(Host, [{mode, active}])) of
- {ok, Pid} ->
- [{ftp, Pid} | data_dir(NewConfig)];
- {skip, _} = SKIP ->
- SKIP
- end;
-
-do_init_per_testcase(Case, Config)
- when (Case =:= progress_report_send) orelse
- (Case =:= progress_report_recv) ->
- inets:start(),
- io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
- NewConfig = close_connection(watch_dog(Config)),
- Host = ftp_host(Config),
- Opts = [{port, ?FTP_PORT},
- {verbose, true},
- {progress, {?MODULE, progress, #progress{}}}],
- case ftp:open(Host, Opts) of
- {ok, Pid} ->
- ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS),
- [{ftp, Pid} | data_dir(NewConfig)];
- {skip, _} = SKIP ->
- SKIP
- end;
-
-do_init_per_testcase(Case, Config) ->
- io:format(user,"~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
- inets:start(),
- NewConfig = close_connection(watch_dog(Config)),
- Host = ftp_host(Config),
- Opts1 =
- if
- ((Case =:= passive_ip_v6_disabled) orelse
- (Case =:= active_ip_v6_disabled)) ->
- [{ipfamily, inet}];
- true ->
- []
- end,
- Opts2 =
- case string:tokens(atom_to_list(Case), [$_]) of
- ["active" | _] ->
- [{mode, active} | Opts1];
- _ ->
- [{mode, passive} | Opts1]
- end,
- case (catch ?ftp_open(Host, Opts2)) of
- {ok, Pid} ->
- ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS),
- [{ftp, Pid} | data_dir(NewConfig)];
- {skip, _} = SKIP ->
- SKIP
- end.
-
-
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(Case, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(_, Config) ->
- NewConfig = close_connection(Config),
- Dog = ?config(watchdog, NewConfig),
- inets:stop(),
- test_server:timetrap_cancel(Dog),
- ok.
-
-
-%%-------------------------------------------------------------------------
-%% Suites similar for all hosts.
-%%-------------------------------------------------------------------------
-
-passive(suite) ->
- [
- passive_user,
- passive_pwd,
- passive_cd,
- passive_lcd,
- passive_ls,
- passive_nlist,
- passive_rename,
- passive_delete,
- passive_mkdir,
- passive_send,
- passive_send_bin,
- passive_send_chunk,
- passive_append,
- passive_append_bin,
- passive_append_chunk,
- passive_recv,
- passive_recv_bin,
- passive_recv_chunk,
- passive_type,
- passive_quote,
- passive_ip_v6_disabled
- ].
-
-active(suite) ->
- [
- active_user,
- active_pwd,
- active_cd,
- active_lcd,
- active_ls,
- active_nlist,
- active_rename,
- active_delete,
- active_mkdir,
- active_send,
- active_send_bin,
- active_send_chunk,
- active_append,
- active_append_bin,
- active_append_chunk,
- active_recv,
- active_recv_bin,
- active_recv_chunk,
- active_type,
- active_quote,
- active_ip_v6_disabled
- ].
-
-
-
-%%-------------------------------------------------------------------------
-%% Test cases starts here.
-%%-------------------------------------------------------------------------
-
-open(doc) ->
- ["Open an ftp connection to a host and close the connection."
- "Also check that !-messages does not disturbe the connection"];
-open(suite) ->
- [];
-open(Config) when is_list(Config) ->
- Host = ftp_host(Config),
- (catch tc_open(Host)).
-
-
-tc_open(Host) ->
- p("tc_open -> entry with"
- "~n Host: ~p", [Host]),
- {ok, Pid} = ?ftp_open(Host, []),
- ok = ftp:close(Pid),
- p("tc_open -> try (ok) open 1"),
- {ok, Pid1} =
- ftp:open({option_list, [{host,Host},
- {port, ?FTP_PORT},
- {flags, [verbose]},
- {timeout, 30000}]}),
- ok = ftp:close(Pid1),
-
- p("tc_open -> try (fail) open 2"),
- {error, ehost} =
- ftp:open({option_list, [{port, ?FTP_PORT}, {flags, [verbose]}]}),
- {ok, Pid2} = ftp:open(Host),
- ok = ftp:close(Pid2),
-
- p("tc_open -> try (ok) open 3"),
- {ok, NewHost} = inet:getaddr(Host, inet),
- {ok, Pid3} = ftp:open(NewHost),
- ftp:user(Pid3, ?FTP_USER, ?FTP_PASS),
- Pid3 ! foobar,
- test_server:sleep(5000),
- {message_queue_len, 0} = process_info(self(), message_queue_len),
- ["200" ++ _] = ftp:quote(Pid3, "NOOP"),
- ok = ftp:close(Pid3),
-
- %% Bad input that has default values are ignored and the defult
- %% is used.
- p("tc_open -> try (ok) open 4"),
- {ok, Pid4} =
- ftp:open({option_list, [{host, Host},
- {port, badarg},
- {flags, [verbose]},
- {timeout, 30000}]}),
- test_server:sleep(100),
- ok = ftp:close(Pid4),
-
- p("tc_open -> try (ok) open 5"),
- {ok, Pid5} =
- ftp:open({option_list, [{host, Host},
- {port, ?FTP_PORT},
- {flags, [verbose]},
- {timeout, -42}]}),
- test_server:sleep(100),
- ok = ftp:close(Pid5),
-
- p("tc_open -> try (ok) open 6"),
- {ok, Pid6} =
- ftp:open({option_list, [{host, Host},
- {port, ?FTP_PORT},
- {flags, [verbose]},
- {mode, cool}]}),
- test_server:sleep(100),
- ok = ftp:close(Pid6),
-
- p("tc_open -> try (ok) open 7"),
- {ok, Pid7} =
- ftp:open(Host, [{port, ?FTP_PORT}, {verbose, true}, {timeout, 30000}]),
- ok = ftp:close(Pid7),
-
- p("tc_open -> try (ok) open 8"),
- {ok, Pid8} =
- ftp:open(Host, ?FTP_PORT),
- ok = ftp:close(Pid8),
-
- p("tc_open -> try (ok) open 9"),
- {ok, Pid9} =
- ftp:open(Host, [{port, ?FTP_PORT},
- {verbose, true},
- {timeout, 30000},
- {dtimeout, -99}]),
- ok = ftp:close(Pid9),
-
- p("tc_open -> try (ok) open 10"),
- {ok, Pid10} =
- ftp:open(Host, [{port, ?FTP_PORT},
- {verbose, true},
- {timeout, 30000},
- {dtimeout, "foobar"}]),
- ok = ftp:close(Pid10),
-
- p("tc_open -> try (ok) open 11"),
- {ok, Pid11} =
- ftp:open(Host, [{port, ?FTP_PORT},
- {verbose, true},
- {timeout, 30000},
- {dtimeout, 1}]),
- ok = ftp:close(Pid11),
-
- p("tc_open -> done"),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-open_port(doc) ->
- ["Open an ftp connection to a host with given port number "
- "and close the connection."]; % See also OTP-3892
-open_port(suite) ->
- [];
-open_port(Config) when is_list(Config) ->
- Host = ftp_host(Config),
- {ok, Pid} = ftp:open(Host, [{port, ?FTP_PORT}]),
- ok = ftp:close(Pid),
- {error, ehost} = ftp:open(?BAD_HOST, []),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-passive_user(doc) ->
- ["Open an ftp connection to a host, and logon as anonymous ftp."];
-passive_user(suite) ->
- [];
-passive_user(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- p("Pid: ~p",[Pid]),
- do_user(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_pwd(doc) ->
- ["Test ftp:pwd/1 & ftp:lpwd/1"];
-passive_pwd(suite) ->
- [];
-passive_pwd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_pwd(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_cd(doc) ->
- ["Open an ftp connection, log on as anonymous ftp, and cd to the"
- "directory \"/pub\" and the to the non-existent directory."];
-passive_cd(suite) ->
- [];
-passive_cd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_cd(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_lcd(doc) ->
- ["Test api function ftp:lcd/2"];
-passive_lcd(suite) ->
- [];
-passive_lcd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- PrivDir = ?config(priv_dir, Config),
- do_lcd(Pid, PrivDir).
-
-
-%%-------------------------------------------------------------------------
-
-passive_ls(doc) ->
- ["Open an ftp connection; ls the current directory, and the "
- "\"incoming\" directory. We assume that ls never fails, since "
- "it's output is meant to be read by humans. "];
-passive_ls(suite) ->
- [];
-passive_ls(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_ls(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_nlist(doc) ->
- ["Open an ftp connection; nlist the current directory, and the "
- "\"incoming\" directory. Nlist does not behave consistenly over "
- "operating systems. On some it is an error to have an empty "
- "directory."];
-passive_nlist(suite) ->
- [];
-passive_nlist(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- WildcardSupport = ?config(wildcard_support, Config),
- do_nlist(Pid, WildcardSupport).
-
-
-%%-------------------------------------------------------------------------
-
-passive_rename(doc) ->
- ["Transfer a file to the server, and rename it; then remove it."];
-passive_rename(suite) ->
- [];
-passive_rename(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_rename(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_delete(doc) ->
- ["Transfer a file to the server, and then delete it"];
-passive_delete(suite) ->
- [];
-passive_delete(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_delete(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_mkdir(doc) ->
- ["Make a remote directory, cd to it, go to parent directory, and "
- "remove the directory."];
-passive_mkdir(suite) ->
- [];
-passive_mkdir(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_mkdir(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_send(doc) ->
- ["Create a local file in priv_dir; open an ftp connection to a host; "
- "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
- "priv_dir; send the file; get a directory listing and check that "
- "the file is on the list;, delete the remote file; get another listing "
- "and check that the file is not on the list; close the session; "
- "delete the local file."];
-passive_send(suite) ->
- [];
-passive_send(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_append(doc) ->
- ["Create a local file in priv_dir; open an ftp connection to a host; "
- "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
- "priv_dir; append the file to a file at the remote side that not exits"
- "this will create the file at the remote side. Then it append the file "
- "again. When this is done it recive the remote file and control that"
- "the content is doubled in it.After that it will remove the files"];
-passive_append(suite) ->
- [];
-passive_append(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_send_bin(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "send a binary; remove file; close the connection."];
-passive_send_bin(suite) ->
- [];
-passive_send_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send_bin(Pid, Config).
-
-%%-------------------------------------------------------------------------
-
-passive_append_bin(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "append a binary twice; get the file and compare the content"
- "remove file; close the connection."];
-passive_append_bin(suite) ->
- [];
-passive_append_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append_bin(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_send_chunk(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "send chunks; remove file; close the connection."];
-passive_send_chunk(suite) ->
- [];
-passive_send_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_append_chunk(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "append chunks;control content remove file; close the connection."];
-passive_append_chunk(suite) ->
- [];
-passive_append_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_recv(doc) ->
- ["Create a local file and transfer it to the remote host into the "
- "the \"incoming\" directory, remove "
- "the local file. Then open a new connection; cd to \"incoming\", "
- "lcd to the private directory; receive the file; delete the "
- "remote file; close connection; check that received file is in "
- "the correct directory; cleanup." ];
-passive_recv(suite) ->
- [];
-passive_recv(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_recv_bin(doc) ->
- ["Send a binary to the remote host; and retreive "
- "the file; then remove the file."];
-passive_recv_bin(suite) ->
- [];
-passive_recv_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv_bin(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_recv_chunk(doc) ->
- ["Send a binary to the remote host; Connect again, and retreive "
- "the file; then remove the file."];
-passive_recv_chunk(suite) ->
- [];
-passive_recv_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_type(doc) ->
- ["Test that we can change btween ASCCI and binary transfer mode"];
-passive_type(suite) ->
- [];
-passive_type(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_type(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_quote(doc) ->
- [""];
-passive_quote(suite) ->
- [];
-passive_quote(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_quote(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_ip_v6_disabled(doc) ->
- ["Test ipv4 command PASV"];
-passive_ip_v6_disabled(suite) ->
- [];
-passive_ip_v6_disabled(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_user(doc) ->
- ["Open an ftp connection to a host, and logon as anonymous ftp."];
-active_user(suite) ->
- [];
-active_user(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_user(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_pwd(doc) ->
- ["Test ftp:pwd/1 & ftp:lpwd/1"];
-active_pwd(suite) ->
- [];
-active_pwd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_pwd(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_cd(doc) ->
- ["Open an ftp connection, log on as anonymous ftp, and cd to the"
- "directory \"/pub\" and to a non-existent directory."];
-active_cd(suite) ->
- [];
-active_cd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_cd(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_lcd(doc) ->
- ["Test api function ftp:lcd/2"];
-active_lcd(suite) ->
- [];
-active_lcd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- PrivDir = ?config(priv_dir, Config),
- do_lcd(Pid, PrivDir).
-
-
-%%-------------------------------------------------------------------------
-
-active_ls(doc) ->
- ["Open an ftp connection; ls the current directory, and the "
- "\"incoming\" directory. We assume that ls never fails, since "
- "it's output is meant to be read by humans. "];
-active_ls(suite) ->
- [];
-active_ls(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_ls(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_nlist(doc) ->
- ["Open an ftp connection; nlist the current directory, and the "
- "\"incoming\" directory. Nlist does not behave consistenly over "
- "operating systems. On some it is an error to have an empty "
- "directory."];
-active_nlist(suite) ->
- [];
-active_nlist(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- WildcardSupport = ?config(wildcard_support, Config),
- do_nlist(Pid, WildcardSupport).
-
-
-%%-------------------------------------------------------------------------
-
-active_rename(doc) ->
- ["Transfer a file to the server, and rename it; then remove it."];
-active_rename(suite) ->
- [];
-active_rename(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_rename(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_delete(doc) ->
- ["Transfer a file to the server, and then delete it"];
-active_delete(suite) ->
- [];
-active_delete(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_delete(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_mkdir(doc) ->
- ["Make a remote directory, cd to it, go to parent directory, and "
- "remove the directory."];
-active_mkdir(suite) ->
- [];
-active_mkdir(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_mkdir(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_send(doc) ->
- ["Create a local file in priv_dir; open an ftp connection to a host; "
- "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
- "priv_dir; send the file; get a directory listing and check that "
- "the file is on the list;, delete the remote file; get another listing "
- "and check that the file is not on the list; close the session; "
- "delete the local file."];
-active_send(suite) ->
- [];
-active_send(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_append(doc) ->
- ["Create a local file in priv_dir; open an ftp connection to a host; "
- "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
- "priv_dir; append the file to a file at the remote side that not exits"
- "this will create the file at the remote side. Then it append the file "
- "again. When this is done it recive the remote file and control that"
- "the content is doubled in it.After that it will remove the files"];
-active_append(suite) ->
- [];
-active_append(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_send_bin(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "send a binary; remove file; close the connection."];
-active_send_bin(suite) ->
- [];
-active_send_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send_bin(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_append_bin(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "append a binary twice; get the file and compare the content"
- "remove file; close the connection."];
-active_append_bin(suite) ->
- [];
-active_append_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append_bin(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_send_chunk(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "send chunks; remove file; close the connection."];
-active_send_chunk(suite) ->
- [];
-active_send_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_append_chunk(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "append chunks;control content remove file; close the connection."];
-active_append_chunk(suite) ->
- [];
-active_append_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_recv(doc) ->
- ["Create a local file and transfer it to the remote host into the "
- "the \"incoming\" directory, remove "
- "the local file. Then open a new connection; cd to \"incoming\", "
- "lcd to the private directory; receive the file; delete the "
- "remote file; close connection; check that received file is in "
- "the correct directory; cleanup." ];
-active_recv(suite) ->
- [];
-active_recv(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_recv_bin(doc) ->
- ["Send a binary to the remote host; and retreive "
- "the file; then remove the file."];
-active_recv_bin(suite) ->
- [];
-active_recv_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv_bin(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_recv_chunk(doc) ->
- ["Send a binary to the remote host; Connect again, and retreive "
- "the file; then remove the file."];
-active_recv_chunk(suite) ->
- [];
-active_recv_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_type(doc) ->
- ["Test that we can change btween ASCCI and binary transfer mode"];
-active_type(suite) ->
- [];
-active_type(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_type(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_quote(doc) ->
- [""];
-active_quote(suite) ->
- [];
-active_quote(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_quote(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_ip_v6_disabled(doc) ->
- ["Test ipv4 command PORT"];
-active_ip_v6_disabled(suite) ->
- [];
-active_ip_v6_disabled(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-api_missuse(doc)->
- ["Test that behaviour of the ftp process if the api is abused"];
-api_missuse(suite) -> [];
-api_missuse(Config) when is_list(Config) ->
- p("api_missuse -> entry"),
- Flag = process_flag(trap_exit, true),
- Pid = ?config(ftp, Config),
- Host = ftp_host(Config),
-
- %% Serious programming fault, connetion will be shut down
- p("api_missuse -> verify bad call termination (~p)", [Pid]),
- case (catch gen_server:call(Pid, {self(), foobar, 10}, infinity)) of
- {error, {connection_terminated, 'API_violation'}} ->
- ok;
- Unexpected1 ->
- exit({unexpected_result, Unexpected1})
- end,
- test_server:sleep(500),
- undefined = process_info(Pid, status),
-
- p("api_missuse -> start new client"),
- {ok, Pid2} = ?ftp_open(Host, []),
- %% Serious programming fault, connetion will be shut down
- p("api_missuse -> verify bad cast termination"),
- gen_server:cast(Pid2, {self(), foobar, 10}),
- test_server:sleep(500),
- undefined = process_info(Pid2, status),
-
- p("api_missuse -> start new client"),
- {ok, Pid3} = ?ftp_open(Host, []),
- %% Could be an innocent misstake the connection lives.
- p("api_missuse -> verify bad bang"),
- Pid3 ! foobar,
- test_server:sleep(500),
- {status, _} = process_info(Pid3, status),
- process_flag(trap_exit, Flag),
- p("api_missuse -> done"),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-not_owner(doc) ->
- ["Test what happens if a process that not owns the connection tries "
- "to use it"];
-not_owner(suite) ->
- [];
-not_owner(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- OtherPid = spawn_link(?MODULE, not_owner, [Pid, self()]),
-
- receive
- {OtherPid, ok} ->
- {ok, _} = ftp:pwd(Pid)
- end,
- ok.
-
-not_owner(FtpPid, Pid) ->
- {error, not_connection_owner} = ftp:pwd(FtpPid),
- ftp:close(FtpPid),
- test_server:sleep(100),
- Pid ! {self(), ok}.
-
-
-%%-------------------------------------------------------------------------
-
-
-progress_report(doc) ->
- ["Solaris 8 sparc test the option progress."];
-progress_report(suite) ->
- [progress_report_send, progress_report_recv].
-
-
-%% --
-
-progress_report_send(doc) ->
- ["Test the option progress for ftp:send/[2,3]"];
-progress_report_send(suite) ->
- [];
-progress_report_send(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- ReportPid =
- spawn_link(?MODULE, progress_report_receiver_init, [self(), 1]),
- do_send(Pid, Config),
- receive
- {ReportPid, ok} ->
- ok
- end.
-
-
-%% --
-
-progress_report_recv(doc) ->
- ["Test the option progress for ftp:recv/[2,3]"];
-progress_report_recv(suite) ->
- [];
-progress_report_recv(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- ReportPid =
- spawn_link(?MODULE, progress_report_receiver_init, [self(), 3]),
- do_recv(Pid, Config),
- receive
- {ReportPid, ok} ->
- ok
- end,
- ok.
-
-progress(#progress{} = Progress , _File, {file_size, Total}) ->
- progress_report_receiver ! start,
- Progress#progress{total = Total};
-progress(#progress{total = Total, current = Current}
- = Progress, _File, {transfer_size, 0}) ->
- progress_report_receiver ! finish,
- case Total of
- unknown ->
- ok;
- Current ->
- ok;
- _ ->
- test_server:fail({error, {progress, {total, Total},
- {current, Current}}})
- end,
- Progress;
-progress(#progress{current = Current} = Progress, _File,
- {transfer_size, Size}) ->
- progress_report_receiver ! update,
- Progress#progress{current = Current + Size}.
-
-progress_report_receiver_init(Pid, N) ->
- register(progress_report_receiver, self()),
- receive
- start ->
- ok
- end,
- progress_report_receiver_loop(Pid, N-1).
-
-progress_report_receiver_loop(Pid, N) ->
- receive
- update ->
- progress_report_receiver_loop(Pid, N);
- finish when N =:= 0 ->
- Pid ! {self(), ok};
- finish ->
- Pid ! {self(), ok},
- receive
- start ->
- ok
- end,
- progress_report_receiver_loop(Pid, N-1)
- end.
-
-
-%%-------------------------------------------------------------------------
-%% Ticket test cases
-%%-------------------------------------------------------------------------
-
-ticket_6035(doc) -> ["Test that owning process that exits with reason "
- "'shutdown' does not cause an error message."];
-ticket_6035(suite) -> [];
-ticket_6035(Config) ->
- p("ticket_6035 -> entry with"
- "~n Config: ~p", [Config]),
- PrivDir = ?config(priv_dir, Config),
- LogFile = filename:join([PrivDir,"ticket_6035.log"]),
- try
- begin
- p("ticket_6035 -> select ftpd host"),
- Host = dirty_select_ftpd_host(Config),
- p("ticket_6035 -> ftpd host selected (~p) => now spawn ftp owner", [Host]),
- Pid = spawn(?MODULE, open_wait_6035, [Host, self()]),
- p("ticket_6035 -> waiter spawned: ~p => now open error logfile (~p)",
- [Pid, LogFile]),
- error_logger:logfile({open, LogFile}),
- p("ticket_6035 -> error logfile open => now kill waiter process"),
- true = kill_ftp_proc_6035(Pid, LogFile),
- p("ticket_6035 -> waiter process killed => now close error logfile"),
- error_logger:logfile(close),
- p("ticket_6035 -> done", []),
- ok
- end
- catch
- throw:{error, not_found} ->
- {skip, "No available FTP servers"}
- end.
-
-kill_ftp_proc_6035(Pid, LogFile) ->
- p("kill_ftp_proc_6035 -> entry"),
- receive
- open ->
- p("kill_ftp_proc_6035 -> received open => now issue shutdown"),
- exit(Pid, shutdown),
- kill_ftp_proc_6035(Pid, LogFile);
- {open_failed, Reason} ->
- p("kill_ftp_proc_6035 -> received open_failed"
- "~n Reason: ~p", [Reason]),
- exit({skip, {failed_openening_server_connection, Reason}})
- after
- 5000 ->
- p("kill_ftp_proc_6035 -> timeout"),
- is_error_report_6035(LogFile)
- end.
-
-open_wait_6035({Tag, FtpServer}, From) ->
- p("open_wait_6035 -> try connect to [~p] ~s for ~p", [Tag, FtpServer, From]),
- case ftp:open(FtpServer, [{timeout, timer:seconds(15)}]) of
- {ok, Pid} ->
- p("open_wait_6035 -> connected (~p), now login", [Pid]),
- LoginResult = ftp:user(Pid,"anonymous","kldjf"),
- p("open_wait_6035 -> login result: ~p", [LoginResult]),
- From ! open,
- receive
- dummy ->
- p("open_wait_6035 -> received dummy"),
- ok
- after
- 10000 ->
- p("open_wait_6035 -> timeout"),
- ok
- end,
- p("open_wait_6035 -> done(ok)"),
- ok;
- {error, Reason} ->
- p("open_wait_6035 -> open failed"
- "~n Reason: ~p", [Reason]),
- From ! {open_failed, {Reason, FtpServer}},
- p("open_wait_6035 -> done(error)"),
- ok
- end.
-
-is_error_report_6035(LogFile) ->
- p("is_error_report_6035 -> entry"),
- Res =
- case file:read_file(LogFile) of
- {ok, Bin} ->
- Txt = binary_to_list(Bin),
- p("is_error_report_6035 -> logfile read: ~n~p", [Txt]),
- read_log_6035(Txt);
- _ ->
- false
- end,
- p("is_error_report_6035 -> logfile read result: "
- "~n ~p", [Res]),
- %% file:delete(LogFile),
- Res.
-
-read_log_6035("=ERROR REPORT===="++_Rest) ->
- p("read_log_6035 -> ERROR REPORT detected"),
- true;
-read_log_6035([H|T]) ->
- p("read_log_6035 -> OTHER: "
- "~p", [H]),
- read_log_6035(T);
-read_log_6035([]) ->
- p("read_log_6035 -> done"),
- false.
-
-
-%%--------------------------------------------------------------------
-%% Internal functions
-%%--------------------------------------------------------------------
-do_user(Pid) ->
- {error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS),
- ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS),
- ok.
-
-do_pwd(Pid) ->
- {ok, "/"} = ftp:pwd(Pid),
- {ok, Path} = ftp:lpwd(Pid),
- {ok, Path} = file:get_cwd(),
- ok.
-
-do_cd(Pid) ->
- ok = ftp:cd(Pid, "/pub"),
- {error, epath} = ftp:cd(Pid, ?BAD_DIR),
- ok.
-
-do_lcd(Pid, Dir) ->
- ok = ftp:lcd(Pid, Dir),
- {error, epath} = ftp:lcd(Pid, ?BAD_DIR),
- ok.
-
-
-do_ls(Pid) ->
- {ok, _} = ftp:ls(Pid),
- {ok, _} = ftp:ls(Pid, "incoming"),
- %% neither nlist nor ls operates on a directory
- %% they operate on a pathname, which *can* be a
- %% directory, but can also be a filename or a group
- %% of files (including wildcards).
- {ok, _} = ftp:ls(Pid, "incom*"),
- ok.
-
-do_nlist(Pid, WildcardSupport) ->
- {ok, _} = ftp:nlist(Pid),
- {ok, _} = ftp:nlist(Pid, "incoming"),
- %% neither nlist nor ls operates on a directory
- %% they operate on a pathname, which *can* be a
- %% directory, but can also be a filename or a group
- %% of files (including wildcards).
- case WildcardSupport of
- true ->
- {ok, _} = ftp:nlist(Pid, "incom*"),
- ok;
- _ ->
- ok
- end.
-
-do_rename(Pid, Config) ->
- PrivDir = ?config(priv_dir, Config),
- LFile = ?config(file, Config),
- NewLFile = ?config(new_file, Config),
- AbsLFile = filename:absname(LFile, PrivDir),
- Contents = "ftp_SUITE test ...",
- ok = file:write_file(AbsLFile, list_to_binary(Contents)),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:lcd(Pid, PrivDir),
- ftp:delete(Pid, LFile), % reset
- ftp:delete(Pid, NewLFile), % reset
- ok = ftp:send(Pid, LFile),
- {error, epath} = ftp:rename(Pid, NewLFile, LFile),
- ok = ftp:rename(Pid, LFile, NewLFile),
- ftp:delete(Pid, LFile), % cleanup
- ftp:delete(Pid, NewLFile), % cleanup
- ok.
-
-do_delete(Pid, Config) ->
- PrivDir = ?config(priv_dir, Config),
- LFile = ?config(file, Config),
- AbsLFile = filename:absname(LFile, PrivDir),
- Contents = "ftp_SUITE test ...",
- ok = file:write_file(AbsLFile, list_to_binary(Contents)),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:lcd(Pid, PrivDir),
- ftp:delete(Pid,LFile), % reset
- ok = ftp:send(Pid, LFile),
- ok = ftp:delete(Pid,LFile),
- ok.
-
-do_mkdir(Pid) ->
- {A, B, C} = erlang:now(),
- NewDir = "nisse_" ++ integer_to_list(A) ++ "_" ++
- integer_to_list(B) ++ "_" ++ integer_to_list(C),
- ok = ftp:cd(Pid, "incoming"),
- {ok, CurrDir} = ftp:pwd(Pid),
- ok = ftp:mkdir(Pid, NewDir),
- ok = ftp:cd(Pid, NewDir),
- ok = ftp:cd(Pid, CurrDir),
- ok = ftp:rmdir(Pid, NewDir),
- ok.
-
-do_send(Pid, Config) ->
- PrivDir = ?config(priv_dir, Config),
- LFile = ?config(file, Config),
- RFile = LFile ++ ".remote",
- AbsLFile = filename:absname(LFile, PrivDir),
- Contents = "ftp_SUITE test ...",
- ok = file:write_file(AbsLFile, list_to_binary(Contents)),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:lcd(Pid, PrivDir),
- ok = ftp:send(Pid, LFile, RFile),
- {ok, RFilesString} = ftp:nlist(Pid),
- RFiles = split(RFilesString),
- true = lists:member(RFile, RFiles),
- ok = ftp:delete(Pid, RFile),
- case ftp:nlist(Pid) of
- {error, epath} ->
- ok; % No files
- {ok, RFilesString1} ->
- RFiles1 = split(RFilesString1),
- false = lists:member(RFile, RFiles1)
- end,
- ok = file:delete(AbsLFile).
-
-do_append(Pid, Config) ->
- PrivDir = ?config(priv_dir, Config),
- LFile = ?config(file, Config),
- RFile = ?config(new_file, Config),
- AbsLFile = filename:absname(LFile, PrivDir),
- Contents = "ftp_SUITE test:appending\r\n",
-
- ok = file:write_file(AbsLFile, list_to_binary(Contents)),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:lcd(Pid, PrivDir),
-
- %% remove files from earlier failed test case
- ftp:delete(Pid, RFile),
- ftp:delete(Pid, LFile),
-
- ok = ftp:append(Pid, LFile, RFile),
- ok = ftp:append(Pid, LFile, RFile),
- ok = ftp:append(Pid, LFile),
-
- %% Control the contents of the file
- {ok, Bin1} = ftp:recv_bin(Pid, RFile),
- ok = ftp:delete(Pid, RFile),
- ok = file:delete(AbsLFile),
- ok = check_content(binary_to_list(Bin1), Contents, double),
-
- {ok, Bin2} = ftp:recv_bin(Pid, LFile),
- ok = ftp:delete(Pid, LFile),
- ok = check_content(binary_to_list(Bin2), Contents, singel),
- ok.
-
-do_send_bin(Pid, Config) ->
- File = ?config(file, Config),
- Contents = "ftp_SUITE test ...",
- Bin = list_to_binary(Contents),
- ok = ftp:cd(Pid, "incoming"),
- {error, enotbinary} = ftp:send_bin(Pid, Contents, File),
- ok = ftp:send_bin(Pid, Bin, File),
- {ok, RFilesString} = ftp:nlist(Pid),
- RFiles = split(RFilesString),
- true = lists:member(File, RFiles),
- ok = ftp:delete(Pid, File),
- ok.
-
-do_append_bin(Pid, Config) ->
- File = ?config(file, Config),
- Contents = "ftp_SUITE test ...",
- Bin = list_to_binary(Contents),
- ok = ftp:cd(Pid, "incoming"),
- {error, enotbinary} = ftp:append_bin(Pid, Contents, File),
- ok = ftp:append_bin(Pid, Bin, File),
- ok = ftp:append_bin(Pid, Bin, File),
- %% Control the contents of the file
- {ok, Bin2} = ftp:recv_bin(Pid, File),
- ok = ftp:delete(Pid,File),
- ok = check_content(binary_to_list(Bin2),binary_to_list(Bin), double).
-
-do_send_chunk(Pid, Config) ->
- File = ?config(file, Config),
- Contents = "ftp_SUITE test ...",
- Bin = list_to_binary(Contents),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:send_chunk_start(Pid, File),
- {error, echunk} = ftp:cd(Pid, "incoming"),
- {error, enotbinary} = ftp:send_chunk(Pid, Contents),
- ok = ftp:send_chunk(Pid, Bin),
- ok = ftp:send_chunk(Pid, Bin),
- ok = ftp:send_chunk_end(Pid),
- {ok, RFilesString} = ftp:nlist(Pid),
- RFiles = split(RFilesString),
- true = lists:member(File, RFiles),
- ok = ftp:delete(Pid, File),
- ok.
-
-do_append_chunk(Pid, Config) ->
- File = ?config(file, Config),
- Contents = ["ER","LE","RL"],
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:append_chunk_start(Pid, File),
- {error, enotbinary} = ftp:append_chunk(Pid, lists:nth(1,Contents)),
- ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(1,Contents))),
- ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(2,Contents))),
- ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(3,Contents))),
- ok = ftp:append_chunk_end(Pid),
- %%Control the contents of the file
- {ok, Bin2} = ftp:recv_bin(Pid, File),
- ok = check_content(binary_to_list(Bin2),"ERL", double),
- ok = ftp:delete(Pid, File),
- ok.
-
-do_recv(Pid, Config) ->
- PrivDir = ?config(priv_dir, Config),
- File = ?config(file, Config),
- Newfile = ?config(new_file, Config),
- AbsFile = filename:absname(File, PrivDir),
- Contents = "ftp_SUITE:recv test ...",
- ok = file:write_file(AbsFile, list_to_binary(Contents)),
- ok = ftp:cd(Pid, "incoming"),
- ftp:delete(Pid, File), % reset
- ftp:lcd(Pid, PrivDir),
- ok = ftp:send(Pid, File),
- ok = file:delete(AbsFile), % cleanup
- test_server:sleep(100),
- ok = ftp:lcd(Pid, PrivDir),
- ok = ftp:recv(Pid, File),
- {ok, Files} = file:list_dir(PrivDir),
- true = lists:member(File, Files),
- ok = file:delete(AbsFile), % cleanup
- ok = ftp:recv(Pid, File, Newfile),
- ok = ftp:delete(Pid, File), % cleanup
- ok.
-
-do_recv_bin(Pid, Config) ->
- File = ?config(file, Config),
- Contents1 = "ftp_SUITE test ...",
- Bin1 = list_to_binary(Contents1),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:send_bin(Pid, Bin1, File),
- test_server:sleep(100),
- {ok, Bin2} = ftp:recv_bin(Pid, File),
- ok = ftp:delete(Pid, File), % cleanup
- Contents2 = binary_to_list(Bin2),
- Contents1 = Contents2,
- ok.
-
-do_recv_chunk(Pid, Config) ->
- File = ?config(file, Config),
- Data = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
- "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
- "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
- "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
- "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
- "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
- "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG"
- "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH"
- "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII",
-
- Contents1 = lists:flatten(lists:duplicate(10, Data)),
- Bin1 = list_to_binary(Contents1),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:type(Pid, binary),
- ok = ftp:send_bin(Pid, Bin1, File),
- test_server:sleep(100),
- {error, "ftp:recv_chunk_start/2 not called"} = recv_chunk(Pid, <<>>),
- ok = ftp:recv_chunk_start(Pid, File),
- {ok, Contents2} = recv_chunk(Pid, <<>>),
- ok = ftp:delete(Pid, File), % cleanup
- ok = find_diff(Contents2, Contents1, 1),
- ok.
-
-do_type(Pid) ->
- ok = ftp:type(Pid, ascii),
- ok = ftp:type(Pid, binary),
- ok = ftp:type(Pid, ascii),
- {error, etype} = ftp:type(Pid, foobar),
- ok.
-
-do_quote(Pid) ->
- ["257 \"/\""++_Rest] = ftp:quote(Pid, "pwd"), %% 257
- [_| _] = ftp:quote(Pid, "help"),
- %% This negativ test causes some ftp servers to hang. This test
- %% is not important for the client, so we skip it for now.
- %%["425 Can't build data connection: Connection refused."]
- %% = ftp:quote(Pid, "list"),
- ok.
-
- watch_dog(Config) ->
- Dog = test_server:timetrap(inets_test_lib:minutes(1)),
- NewConfig = lists:keydelete(watchdog, 1, Config),
- [{watchdog, Dog} | NewConfig].
-
- close_connection(Config) ->
- case ?config(ftp, Config) of
- Pid when is_pid(Pid) ->
- ok = ftp:close(Pid),
- lists:delete({ftp, Pid}, Config);
- _ ->
- Config
- end.
-
-ftp_host(Config) ->
- case ?config(ftp_remote_host, Config) of
- undefined ->
- exit({skip, "No host specified"});
- Host ->
- Host
- end.
-
-check_content(RContent, LContent, Amount) ->
- LContent2 = case Amount of
- double ->
- LContent ++ LContent;
- singel ->
- LContent
- end,
- case string:equal(RContent, LContent2) of
- true ->
- ok;
- false ->
- %% Find where the diff is
- Where = find_diff(RContent, LContent2, 1),
- Where
- end.
-
-find_diff(A, A, _) ->
- ok;
-find_diff([H|T1], [H|T2], Pos) ->
- find_diff(T1, T2, Pos+1);
-find_diff(RC, LC, Pos) ->
- {error, {diff, Pos, RC, LC}}.
-
-recv_chunk(Pid, Acc) ->
- case ftp:recv_chunk(Pid) of
- ok ->
- {ok, binary_to_list(Acc)};
- {ok, Bin} ->
- recv_chunk(Pid, <<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, Opts) ->
- p("do_ftp_open -> entry with"
- "~n Host: ~p"
- "~n Opts: ~p", [Host, Opts]),
- case ftp:open(Host, Opts) of
- {ok, _} = OK ->
- OK;
- {error, Reason} ->
- Str =
- lists:flatten(
- io_lib:format("Unable to reach test FTP server ~p (~p)",
- [Host, Reason])),
- throw({skip, Str})
- end.
-
-
-passwd() ->
- Host =
- case inet:gethostname() of
- {ok, H} ->
- H;
- _ ->
- "localhost"
- end,
- "ftp_SUITE@" ++ Host.
-
-ftpd_hosts(Config) ->
- DataDir = ?config(data_dir, Config),
- FileName = filename:join([DataDir, "../ftp_SUITE_data/", ftpd_hosts]),
- p("FileName: ~p", [FileName]),
- case file:consult(FileName) of
- {ok, [Hosts]} when is_list(Hosts) ->
- Hosts;
- _ ->
- []
- end.
-
-wrapper(Prefix,doc,Func) ->
- Prefix++Func(doc);
-wrapper(_,X,Func) ->
- Func(X).
-
-data_dir(Config) ->
- case ?config(data_dir, Config) of
- List when (length(List) > 0) ->
- PathList = filename:split(List),
- {NewPathList,_} = lists:split((length(PathList)-1), PathList),
- DataDir = filename:join(NewPathList ++ [ftp_SUITE_data]),
- NewConfig =
- lists:keyreplace(data_dir,1,Config, {data_dir,DataDir}),
- NewConfig;
- _ -> Config
- end.
-
-
-
-p(F) ->
- p(F, []).
-
-p(F, A) ->
- case get(ftp_testcase) of
- undefined ->
- io:format("~w [~w] " ++ F ++ "~n", [?MODULE, self() | A]);
- TC when is_atom(TC) ->
- io:format("~w [~w] ~w:" ++ F ++ "~n", [?MODULE, self(), TC | A])
- end.
diff --git a/lib/inets/test/ftp_ticket_test.erl b/lib/inets/test/ftp_ticket_test.erl
deleted file mode 100644
index fe4ab35728..0000000000
--- a/lib/inets/test/ftp_ticket_test.erl
+++ /dev/null
@@ -1,61 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ftp_ticket_test).
-
--compile(export_all).
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Solaris 8 sparc ").
-
-
-%% Test server callbacks
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-
-all() ->
- tickets().
-
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_suite(Config) ->
- ?LIB_MOD:ftpd_init(ticket_test, Config).
-
-tickets() ->
- [ticket_6035].
-
-
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-ticket_6035(X) -> ?LIB_MOD:ticket_6035(X).
diff --git a/lib/inets/test/ftp_windows_2003_server_test.erl b/lib/inets/test/ftp_windows_2003_server_test.erl
deleted file mode 100644
index 32f25713f8..0000000000
--- a/lib/inets/test/ftp_windows_2003_server_test.erl
+++ /dev/null
@@ -1,167 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ftp_windows_2003_server_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Windows 2003 server ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(windows_2003_server, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [
- open,
- open_port,
- {group, passive},
- {group, active},
- api_missuse,
- not_owner,
- {group, progress_report}
- ].
-
-groups() ->
- [
- {passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [], ftp_suite_lib:progress_report(suite)}
- ].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%% Test cases starts here.
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/ftp_windows_xp_test.erl b/lib/inets/test/ftp_windows_xp_test.erl
deleted file mode 100644
index 06d919ba00..0000000000
--- a/lib/inets/test/ftp_windows_xp_test.erl
+++ /dev/null
@@ -1,157 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ftp_windows_xp_test).
-
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(LIB_MOD,ftp_suite_lib).
--define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
--define(PLATFORM,"Windows xp ").
-
-%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- {File, NewFile} = ?LIB_MOD:test_filenames(),
- NewConfig = [{file, File}, {new_file, NewFile} | Config],
- ?LIB_MOD:ftpd_init(windows_xp, NewConfig).
-
-%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
-end_per_suite(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
- ftp_suite_lib:init_per_testcase(Case, Config).
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- ftp_suite_lib:end_per_testcase(Case, Config).
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-all() ->
- [open, open_port, {group, passive}, {group, active},
- api_missuse, not_owner, {group, progress_report}].
-
-groups() ->
- [{passive, [], ftp_suite_lib:passive(suite)},
- {active, [], ftp_suite_lib:active(suite)},
- {progress_report, [],
- ftp_suite_lib:progress_report(suite)}].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
-open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
-not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-
-passive_user(X) -> ?LIB_MOD:passive_user(X).
-passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
-passive_cd(X) -> ?LIB_MOD:passive_cd(X).
-passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
-passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
-passive_rename(X) -> ?LIB_MOD:passive_rename(X).
-passive_delete(X) -> ?LIB_MOD:passive_delete(X).
-passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
-passive_send(X) -> ?LIB_MOD:passive_send(X).
-passive_send_bin(X) -> ?LIB_MOD:passive_send_bin(X).
-passive_send_chunk(X) -> ?LIB_MOD:passive_send_chunk(X).
-passive_append(X) -> ?LIB_MOD:passive_append(X).
-passive_append_bin(X) -> ?LIB_MOD:passive_append_bin(X).
-passive_append_chunk(X) -> ?LIB_MOD:passive_append_chunk(X).
-passive_recv(X) -> ?LIB_MOD:passive_recv(X).
-passive_recv_bin(X) -> ?LIB_MOD:passive_recv_bin(X).
-passive_recv_chunk(X) -> ?LIB_MOD:passive_recv_chunk(X).
-passive_type(X) -> ?LIB_MOD:passive_type(X).
-passive_quote(X) -> ?LIB_MOD:passive_quote(X).
-passive_ip_v6_disabled(X) -> ?LIB_MOD:passive_ip_v6_disabled(X).
-active_user(X) -> ?LIB_MOD:active_user(X).
-active_pwd(X) -> ?LIB_MOD:active_pwd(X).
-active_cd(X) -> ?LIB_MOD:active_cd(X).
-active_lcd(X) -> ?LIB_MOD:active_lcd(X).
-active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
-active_rename(X) -> ?LIB_MOD:active_rename(X).
-active_delete(X) -> ?LIB_MOD:active_delete(X).
-active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
-active_send(X) -> ?LIB_MOD:active_send(X).
-active_send_bin(X) -> ?LIB_MOD:active_send_bin(X).
-active_send_chunk(X) -> ?LIB_MOD:active_send_chunk(X).
-active_append(X) -> ?LIB_MOD:active_append(X).
-active_append_bin(X) -> ?LIB_MOD:active_append_bin(X).
-active_append_chunk(X) -> ?LIB_MOD:active_append_chunk(X).
-active_recv(X) -> ?LIB_MOD:active_recv(X).
-active_recv_bin(X) -> ?LIB_MOD:active_recv_bin(X).
-active_recv_chunk(X) -> ?LIB_MOD:active_recv_chunk(X).
-active_type(X) -> ?LIB_MOD:active_type(X).
-active_quote(X) -> ?LIB_MOD:active_quote(X).
-active_ip_v6_disabled(X) -> ?LIB_MOD:active_ip_v6_disabled(X).
-progress_report_send(X) -> ?LIB_MOD:progress_report_send(X).
-progress_report_recv(X) -> ?LIB_MOD:progress_report_recv(X).
-
-fin(Config) ->
- ?LIB_MOD:ftpd_fin(Config).
diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl
index c5920a3968..a2b463e98c 100644
--- a/lib/inets/test/http_format_SUITE.erl
+++ b/lib/inets/test/http_format_SUITE.erl
@@ -1,44 +1,31 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(http_format_SUITE).
--author('[email protected]').
-include_lib("common_test/include/ct.hrl").
--include("test_server_line.hrl").
-include("http_internal.hrl").
-%% Test server specific exports
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]).
-
-%% Test cases must be exported.
--export([ chunk_decode/1, chunk_encode/1,
- chunk_extensions_otp_6005/1, chunk_decode_otp_6264/1,
- chunk_decode_empty_chunk_otp_6511/1,
- chunk_decode_trailer/1,
- http_response/1, http_request/1, validate_request_line/1,
- esi_parse_headers/1, cgi_parse_headers/1,
- is_absolut_uri/1, convert_netscapecookie_date/1,
- check_content_length_encoding/1]).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
all() ->
[{group, chunk}, http_response, http_request,
@@ -51,7 +38,8 @@ groups() ->
[chunk_decode, chunk_encode, chunk_extensions_otp_6005,
chunk_decode_otp_6264,
chunk_decode_empty_chunk_otp_6511,
- chunk_decode_trailer]}].
+ chunk_whitespace_suffix,
+ chunk_decode_trailer, chunk_max_headersize, chunk_max_bodysize, chunk_not_hex]}].
init_per_suite(Config) ->
Config.
@@ -67,25 +55,17 @@ end_per_group(_GroupName, Config) ->
init_per_testcase(_, Config) ->
- Dog = test_server:timetrap(?t:minutes(1)),
- NewConfig = lists:keydelete(watchdog, 1, Config),
- [{watchdog, Dog} | NewConfig].
+ Config.
-end_per_testcase(_, Config) ->
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
+end_per_testcase(_, _) ->
ok.
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
-
-%%-------------------------------------------------------------------------
-chunk_decode(doc) ->
- ["Test http_chunk:decode/3"];
-chunk_decode(suite) ->
- [];
+chunk_decode() ->
+ [{doc, "Test http_chunk:decode/3"}].
chunk_decode(Config) when is_list(Config) ->
ReqHeaders = #http_request_h{'transfer-encoding' = "chunked"},
ChunkedBody = "A" ++ ?CRLF ++ "1234567890" ++ ?CRLF ++ "4" ++
@@ -108,15 +88,11 @@ chunk_decode(Config) when is_list(Config) ->
?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE),
{_, Body} = parse(Module, Function, Args, tl(NewChunkedBody)),
- "1234567890HEJ!" = binary_to_list(Body),
-
- ok.
+ "1234567890HEJ!" = binary_to_list(Body).
%%-------------------------------------------------------------------------
-chunk_extensions_otp_6005(doc) ->
- ["Make sure so called extensions are ignored"];
-chunk_extensions_otp_6005(suite) ->
- [];
+chunk_extensions_otp_6005() ->
+ [{doc, "Make sure so called extensions are ignored"}].
chunk_extensions_otp_6005(Config) when is_list(Config)->
ChunkedBody = "A;ignore this" ++ ?CRLF ++ "1234567890" ++
?CRLF ++ "4" ++ ?CRLF ++ "HEJ!"++ ?CRLF ++ "0" ++
@@ -135,14 +111,11 @@ chunk_extensions_otp_6005(Config) when is_list(Config)->
?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE),
{_, NewBody} = parse(Module1, Function1, Args1, tl(ChunkedBody1)),
- "1234567890HEJ!" = binary_to_list(NewBody),
- ok.
+ "1234567890HEJ!" = binary_to_list(NewBody).
%%-------------------------------------------------------------------------
-chunk_decode_otp_6264(doc) ->
- ["Check that 0 in the body does not count as the last chunk"];
-chunk_decode_otp_6264(suite) ->
- [];
+chunk_decode_otp_6264() ->
+ [{doc, "Check that 0 in the body does not count as the last chunk"}].
chunk_decode_otp_6264(Config) when is_list(Config)->
ChunkedBody = "A;ignore this" ++ ?CRLF ++ "1234567890" ++
?CRLF ++ "4" ++ ?CRLF ++ "0123"++ ?CRLF ++ "0" ++
@@ -172,27 +145,33 @@ chunk_decode_otp_6264(Config) when is_list(Config)->
?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE),
{_, NewBody} = parse(Module1, Function1, Args1, tl(NewChunkedBody1)),
- "12345678900" = binary_to_list(NewBody),
-
- ok.
+ "12345678900" = binary_to_list(NewBody).
%%-------------------------------------------------------------------------
-chunk_decode_empty_chunk_otp_6511(doc) ->
- [""];
-chunk_decode_empty_chunk_otp_6511(suite) ->
- [];
chunk_decode_empty_chunk_otp_6511(Config) when is_list(Config) ->
ChunkedBody = "0" ++ ?CRLF ++ ?CRLF,
{ok,{["content-length:0"],<<>>}} =
http_chunk:decode(list_to_binary(ChunkedBody),
- ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE),
- ok.
+ ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE).
%%-------------------------------------------------------------------------
-chunk_decode_trailer(doc) ->
- ["Make sure trailers are handled correctly. Trailers should"
- "become new headers"];
-chunk_decode_trailer(suite) ->
- [];
+chunk_whitespace_suffix() ->
+ [{doc, "Test whitespace after chunked length header"}].
+chunk_whitespace_suffix(Config) when is_list(Config) ->
+ ChunkedBody = "1a ; ignore-stuff-here" ++ ?CRLF ++
+ "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10 " ++ ?CRLF
+ ++ "1234567890abcdef" ++ ?CRLF ++ "0 " ++ ?CRLF
+ ++ "some-footer:some-value" ++ ?CRLF
+ ++ "another-footer:another-value" ++ ?CRLF ++ ?CRLF,
+ {ok, {["content-length:42", "another-footer:another-value",
+ "some-footer:some-value", ""],
+ <<"abcdefghijklmnopqrstuvwxyz1234567890abcdef">>}} =
+ http_chunk:decode(list_to_binary(ChunkedBody),
+ ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE).
+
+%%-------------------------------------------------------------------------
+chunk_decode_trailer() ->
+ [{doc,"Make sure trailers are handled correctly. Trailers should"
+ "become new headers"}].
chunk_decode_trailer(Config) when is_list(Config)->
ChunkedBody = "1a; ignore-stuff-here" ++ ?CRLF ++
"abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF
@@ -248,30 +227,79 @@ chunk_decode_trailer(Config) when is_list(Config)->
?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE),
{_, NewBody} = parse(Module1, Function1, Args1, tl(ChunkedBody3)),
- "abcdefghijklmnopqrstuvwxyz1234567890abcdef" = binary_to_list(NewBody),
-
- ok.
+ "abcdefghijklmnopqrstuvwxyz1234567890abcdef" = binary_to_list(NewBody).
%%-------------------------------------------------------------------------
-chunk_encode(doc) ->
- ["Test http_chunk:encode/1 & http_chunk:encode_last/0"];
-chunk_encode(suite) ->
- [];
+chunk_encode() ->
+ [{doc, "Test http_chunk:encode/1 & http_chunk:encode_last/0"}].
chunk_encode(Config) when is_list(Config) ->
<<54, ?CR, ?LF, 102,111,111,98,97,114, ?CR, ?LF>> =
http_chunk:encode(list_to_binary("foobar")),
["6", ?CR, ?LF,"foobar", ?CR, ?LF] = http_chunk:encode("foobar"),
- <<$0, ?CR, ?LF, ?CR, ?LF >> = http_chunk:encode_last(),
- ok.
-
+ <<$0, ?CR, ?LF, ?CR, ?LF >> = http_chunk:encode_last().
+%%-------------------------------------------------------------------------
+chunk_max_headersize() ->
+ [{doc, "Test max header limit"}].
+chunk_max_headersize(Config) when is_list(Config) ->
+ ChunkedBody = "1a; ignore-stuff-here" ++ ?CRLF ++
+ "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF
+ ++ "1234567890abcdef" ++ ?CRLF ++ "0" ++ ?CRLF
+ ++ "some-footer:some-value" ++ ?CRLF
+ ++ "another-footer:another-value" ++ ?CRLF ++ ?CRLF,
+
+ {ok, {_, _}} =
+ http_chunk:decode(list_to_binary(ChunkedBody),
+ ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE),
+
+ %% Too long in length header
+ {error,{header_too_long, {max, 1}}} =
+ (catch http_chunk:decode(list_to_binary(ChunkedBody),
+ ?HTTP_MAX_BODY_SIZE, 1)),
+
+ %% Too long in extension field
+ {error,{header_too_long, {max, 10}}} =
+ (catch http_chunk:decode(list_to_binary(ChunkedBody),
+ ?HTTP_MAX_BODY_SIZE, 10)),
+
+ %% Too long in trailer
+ {error,{header_too_long, {max, 30}}} =
+ (catch http_chunk:decode(list_to_binary(ChunkedBody),
+ ?HTTP_MAX_BODY_SIZE, 30)).
+%%-------------------------------------------------------------------------
+chunk_not_hex() ->
+ [{doc, "Test bad chunked length header"}].
+chunk_not_hex(Config) when is_list(Config) ->
+ ChunkedBody = "åäö; ignore-stuff-here" ++ ?CRLF ++
+ "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF
+ ++ "1234567890abcdef" ++ ?CRLF ++ "0" ++ ?CRLF
+ ++ "some-footer:some-value" ++ ?CRLF
+ ++ "another-footer:another-value" ++ ?CRLF ++ ?CRLF,
+ {error,{chunk_size, "åäö"}} =
+ (catch http_chunk:decode(list_to_binary(ChunkedBody),
+ ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE)).
+%%-------------------------------------------------------------------------
+chunk_max_bodysize() ->
+ [{doc, "Test max body limit"}].
+chunk_max_bodysize(Config) when is_list(Config) ->
+ ChunkedBody = "1a; ignore-stuff-here" ++ ?CRLF ++
+ "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF
+ ++ "1234567890abcdef" ++ ?CRLF ++ "0" ++ ?CRLF
+ ++ "some-footer:some-value" ++ ?CRLF
+ ++ "another-footer:another-value" ++ ?CRLF ++ ?CRLF,
+ {ok, {_, _}} =
+ http_chunk:decode(list_to_binary(ChunkedBody),
+ ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE),
+
+ %% Too long body
+ {error,{body_too_big, {max, 10}}} =
+ (catch http_chunk:decode(list_to_binary(ChunkedBody),
+ 10, ?HTTP_MAX_HEADER_SIZE)).
%%-------------------------------------------------------------------------
-http_response(doc) ->
- ["Test httpc_response:parse*. This test case will simulate that the "
+http_response() ->
+ [{doc, "Test httpc_response:parse*. This test case will simulate that the "
"message will be recived a little at the time on a socket and the "
- "package may be broken up into smaller parts at arbitrary point."];
-http_response(suite) ->
- [];
+ "package may be broken up into smaller parts at arbitrary point."}].
http_response(Config) when is_list(Config) ->
HttpHead1 = ["HTTP", "/1.1 ", "20", "0 ", "ok", [?CR, ?LF],
@@ -339,12 +367,10 @@ http_response(Config) when is_list(Config) ->
[<<>>,Length1], HttpBody1)),
ok.
%%-------------------------------------------------------------------------
-http_request(doc) ->
- ["Test httpd_request:parse* This test case will simulate that the "
+http_request() ->
+ [{doc, "Test httpd_request:parse* This test case will simulate that the "
"message will be recived a little at the time on a socket and the "
- "package may be broken up into smaller parts at arbitrary point."];
-http_request(suite) ->
- [];
+ "package may be broken up into smaller parts at arbitrary point."}].
http_request(Config) when is_list(Config) ->
HttpHead = ["GE", "T ", "http://www.erlang", ".org ", "HTTP",
@@ -355,8 +381,13 @@ http_request(Config) when is_list(Config) ->
"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),
+ [{"te", []}, {"host", "www.erlang.org"}]}, <<>>} =
+ parse(httpd_request, parse, [[{max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version, ?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
+ ]],
+ HttpHead),
HttpHead1 = ["GET http://www.erlang.org HTTP/1.1" ++
[?CR], [?LF, ?CR, ?LF]],
@@ -364,7 +395,11 @@ http_request(Config) when is_list(Config) ->
"http://www.erlang.org",
"HTTP/1.1",
{#http_request_h{}, []}, <<>>} =
- parse(httpd_request, parse, [?HTTP_MAX_HEADER_SIZE], HttpHead1),
+ parse(httpd_request, parse, [[{max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version, ?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
+ ]], HttpHead1),
HttpHead2 = ["GET http://www.erlang.org HTTP/1.1" ++
@@ -373,7 +408,11 @@ http_request(Config) when is_list(Config) ->
"http://www.erlang.org",
"HTTP/1.1",
{#http_request_h{}, []}, <<>>} =
- parse(httpd_request, parse, [?HTTP_MAX_HEADER_SIZE], HttpHead2),
+ parse(httpd_request, parse, [[{max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version, ?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}
+ ]], 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",
@@ -393,15 +432,12 @@ http_request(Config) when is_list(Config) ->
NewBody1 =
binary_to_list(parse
(httpd_request, whole_body,
- [<<>>, Length1], HttpBody1)),
- ok.
+ [<<>>, Length1], HttpBody1)).
%%-------------------------------------------------------------------------
-validate_request_line(doc) ->
- ["Test httpd_request:validate/3. Makes sure you can not get past"
+validate_request_line() ->
+ [{doc, "Test httpd_request:validate/3. Makes sure you can not get past"
" the server_root and that the request is recognized by the server"
- " and protcol version." ];
-validate_request_line(suite) ->
- [];
+ " and protcol version."}].
validate_request_line(Config) when is_list(Config) ->
%% HTTP/0.9 only has GET requests
@@ -454,16 +490,12 @@ validate_request_line(Config) when is_list(Config) ->
NewForbiddenUri1 =
"http://127.0.0.1:8888/../home/ingela/test.html",
{error, {bad_request, {forbidden, NewForbiddenUri1}}} =
- httpd_request:validate("GET", NewForbiddenUri1, "HTTP/1.1"),
-
- ok.
+ httpd_request:validate("GET", NewForbiddenUri1, "HTTP/1.1").
%%-------------------------------------------------------------------------
-check_content_length_encoding(doc) ->
- ["Test http_request:headers/2. Check that the content-length is"
- " encoded even when it is zero." ];
-check_content_length_encoding(suite) ->
- [];
+check_content_length_encoding() ->
+ [{doc, "Test http_request:headers/2. Check that the content-length is"
+ " encoded even when it is zero."}].
check_content_length_encoding(Config) when is_list(Config) ->
%% Check that the content-length is preserved.
@@ -472,16 +504,12 @@ check_content_length_encoding(Config) when is_list(Config) ->
true = (string:str(Header1, "content-length: 123\r\n") > 0),
%% Check that content-length=0 is handled correctly.
Header2 = http_request:http_headers(#http_request_h{'content-length'="0"}),
- true = (string:str(Header2, "content-length: 0\r\n") > 0),
-
- ok.
+ true = (string:str(Header2, "content-length: 0\r\n") > 0).
%%-------------------------------------------------------------------------
-esi_parse_headers(doc) ->
- ["Test httpd_esi:*. All header values are received in the same"
- " erlang message."];
-esi_parse_headers(suite) ->
- [];
+esi_parse_headers() ->
+ [{doc, "Test httpd_esi:*. All header values are received in the same"
+ " erlang message."}].
esi_parse_headers(Config) when is_list(Config) ->
ESIResult = "content-type:text/html\r\ndate:Thu, 28 Oct 2004 07:57:43 "
@@ -508,16 +536,14 @@ esi_parse_headers(Config) when is_list(Config) ->
httpd_esi:handle_headers(Headers2),
{proceed,"/foo/bar.html"} =
- httpd_esi:handle_headers("location:/foo/bar.html\r\n"),
- ok.
+ httpd_esi:handle_headers("location:/foo/bar.html\r\n").
%%--------------------------------------------------------------------
-cgi_parse_headers(doc) ->
- ["Test httpd_cgi:*. This test case will simulate that the "
+cgi_parse_headers() ->
+ [{doc, "Test httpd_cgi:*. This test case will simulate that the "
"message will be recived a little at the time on a socket and the "
- "package may be broken up into smaller parts at arbitrary point."];
-cgi_parse_headers(suite) ->
- [];
+ "package may be broken up into smaller parts at arbitrary point."}].
+
cgi_parse_headers(Config) when is_list(Config) ->
CGIResult = ["content-type:text", "/html\ndate:Thu, 28 Oct 2004 07:57:43 "
@@ -553,26 +579,18 @@ cgi_parse_headers(Config) when is_list(Config) ->
{ok,[{"content-type","text/html"},
{"connection","close"},
{"content-language","en"},
- {"age","4711"}], {200,"ok"}} = httpd_cgi:handle_headers(Headers3),
-
- ok.
-
+ {"age","4711"}], {200,"ok"}} = httpd_cgi:handle_headers(Headers3).
%%-------------------------------------------------------------------------
-is_absolut_uri(doc) ->
- ["Test http_request:is_absolut_uri/1."];
-is_absolut_uri(suite) ->
- [];
+is_absolut_uri() ->
+ [{doc, "Test http_request:is_absolut_uri/1."}].
is_absolut_uri(Config) when is_list(Config) ->
true = http_request:is_absolut_uri("http://www.erlang.org"),
true = http_request:is_absolut_uri("https://www.erlang.org"),
false = http_request:is_absolut_uri("index.html").
-
%%-------------------------------------------------------------------------
-convert_netscapecookie_date(doc) ->
- ["Test http_util:convert_netscapecookie_date/1."];
-convert_netscapecookie_date(suite) ->
- [];
+convert_netscapecookie_date() ->
+ [{doc, "Test http_util:convert_netscapecookie_date/1."}].
convert_netscapecookie_date(Config) when is_list(Config) ->
{{2006,1,6},{8,59,38}} =
http_util:convert_netscapecookie_date("Mon, 06-Jan-2006 08:59:38 GMT"),
@@ -605,9 +623,7 @@ convert_netscapecookie_date(Config) when is_list(Config) ->
{{2006,12,12},{8,59,38}} =
http_util:convert_netscapecookie_date("Sun 12-Dec-06 08:59:38 GMT"),
{{2036,1,1},{8,0,1}} =
- http_util:convert_netscapecookie_date("Tue Jan 01 08:00:01 2036 GMT"),
- ok.
-
+ http_util:convert_netscapecookie_date("Tue Jan 01 08:00:01 2036 GMT").
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index 0c35f284f7..932567ec55 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -27,21 +28,22 @@
-include_lib("kernel/include/file.hrl").
-include_lib("common_test/include/ct.hrl").
-include("inets_test_lib.hrl").
-
+-include("http_internal.hrl").
+-include("httpc_internal.hrl").
%% Note: This directive should only be used in test suites.
-compile(export_all).
-define(URL_START, "http://").
-define(TLS_URL_START, "https://").
-define(NOT_IN_USE_PORT, 8997).
--define(LF, $\n).
--define(HTTP_MAX_HEADER_SIZE, 10240).
+
-record(sslsocket, {fd = nil, pid = nil}).
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
suite() ->
- [{ct_hooks,[ts_install_cth]}].
+ [{ct_hooks,[ts_install_cth]}
+ ].
all() ->
[
@@ -66,7 +68,9 @@ real_requests()->
head,
get,
post,
+ delete,
post_stream,
+ patch,
async,
pipeline,
persistent_connection,
@@ -91,19 +95,29 @@ only_simulated() ->
[
cookie,
cookie_profile,
+ empty_set_cookie,
+ invalid_set_cookie,
trace,
stream_once,
+ stream_single_chunk,
+ stream_no_length,
+ not_streamed_once,
+ stream_large_not_200_or_206,
no_content_204,
tolerate_missing_CR,
userinfo,
bad_response,
internal_server_error,
invalid_http,
+ invalid_chunk_size,
headers_dummy,
+ headers_with_obs_fold,
empty_response_header,
remote_socket_close,
remote_socket_close_async,
+ process_leak_on_keepalive,
transfer_encoding,
+ transfer_encoding_identity,
redirect_loop,
redirect_moved_permanently,
redirect_multiple_choises,
@@ -124,8 +138,9 @@ misc() ->
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- PrivDir = ?config(priv_dir, Config),
- DataDir = ?config(data_dir, Config),
+ ct:timetrap({seconds, 30}),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
inets_test_lib:start_apps([inets]),
ServerRoot = filename:join(PrivDir, "server_root"),
DocRoot = filename:join(ServerRoot, "htdocs"),
@@ -134,7 +149,7 @@ init_per_suite(Config) ->
end_per_suite(Config) ->
inets_test_lib:stop_apps([inets]),
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
inets_test_lib:del_dirs(PrivDir),
ok.
@@ -145,6 +160,23 @@ init_per_group(misc = Group, Config) ->
ok = httpc:set_options([{ipfamily, Inet}]),
Config;
+init_per_group(Group, Config0) when Group =:= sim_https; Group =:= https->
+ ct:timetrap({seconds, 30}),
+ start_apps(Group),
+ StartSsl = try ssl:start()
+ catch
+ Error:Reason ->
+ {skip, lists:flatten(io_lib:format("Failed to start apps for https Error=~p Reason=~p", [Error, Reason]))}
+ end,
+ case StartSsl of
+ {error, {already_started, _}} ->
+ do_init_per_group(Group, Config0);
+ ok ->
+ do_init_per_group(Group, Config0);
+ _ ->
+ StartSsl
+ end;
+
init_per_group(Group, Config0) ->
start_apps(Group),
Config = proplists:delete(port, Config0),
@@ -153,7 +185,10 @@ init_per_group(Group, Config0) ->
end_per_group(_, _Config) ->
ok.
-
+do_init_per_group(Group, Config0) ->
+ Config = proplists:delete(port, Config0),
+ Port = server_start(Group, server_config(Group, Config)),
+ [{port, Port} | Config].
%%--------------------------------------------------------------------
init_per_testcase(pipeline, Config) ->
inets:start(httpc, [{profile, pipeline}]),
@@ -167,7 +202,15 @@ init_per_testcase(persistent_connection, Config) ->
{max_keep_alive_length, 3}], persistent_connection),
Config;
-
+init_per_testcase(wait_for_whole_response, Config) ->
+ ct:timetrap({seconds, 60*3}),
+ Config;
+init_per_testcase(Case, Config) when Case == post;
+ Case == delete;
+ Case == post_delete;
+ Case == post_stream ->
+ ct:timetrap({seconds, 30}),
+ Config;
init_per_testcase(_Case, Config) ->
Config.
@@ -225,6 +268,51 @@ post(Config) when is_list(Config) ->
{ok, {{_,504,_}, [_ | _], []}} =
httpc:request(post, {URL, [{"expect","100-continue"}],
"text/plain", "foobar"}, [], []).
+%%--------------------------------------------------------------------
+delete() ->
+ [{"Test http delete request against local server. We do in this case "
+ "only care about the client side of the the delete. The server "
+ "script will not actually use the delete data."}].
+delete(Config) when is_list(Config) ->
+ CGI = case test_server:os_type() of
+ {win32, _} ->
+ "/cgi-bin/cgi_echo.exe";
+ _ ->
+ "/cgi-bin/cgi_echo"
+ end,
+
+ URL = url(group_name(Config), CGI, Config),
+ Body = lists:duplicate(100, "1"),
+
+ {ok, {{_,200,_}, [_ | _], [_ | _]}} =
+ httpc:request(delete, {URL, [{"expect","100-continue"}],
+ "text/plain", Body}, [], []),
+
+ {ok, {{_,504,_}, [_ | _], []}} =
+ httpc:request(delete, {URL, [{"expect","100-continue"}],
+ "text/plain", "foobar"}, [], []).
+
+%%--------------------------------------------------------------------
+patch() ->
+ [{"Test http patch request against local server. We do in this case "
+ "only care about the client side of the the patch. The server "
+ "script will not actually use the patch data."}].
+patch(Config) when is_list(Config) ->
+ CGI = case test_server:os_type() of
+ {win32, _} ->
+ "/cgi-bin/cgi_echo.exe";
+ _ ->
+ "/cgi-bin/cgi_echo"
+ end,
+
+ URL = url(group_name(Config), CGI, Config),
+
+ %% Cgi-script expects the body length to be 100
+ Body = lists:duplicate(100, "1"),
+
+ {ok, {{_,200,_}, [_ | _], [_ | _]}} =
+ httpc:request(patch, {URL, [{"expect","100-continue"}],
+ "text/plain", Body}, [], []).
%%--------------------------------------------------------------------
post_stream() ->
@@ -277,9 +365,9 @@ trace(Config) when is_list(Config) ->
pipeline(Config) when is_list(Config) ->
Request = {url(group_name(Config), "/dummy.html", Config), []},
{ok, _} = httpc:request(get, Request, [], [], pipeline),
-
+
%% Make sure pipeline session is registerd
- test_server:sleep(4000),
+ ct:sleep(4000),
keep_alive_requests(Request, pipeline).
%%--------------------------------------------------------------------
@@ -289,7 +377,7 @@ persistent_connection(Config) when is_list(Config) ->
{ok, _} = httpc:request(get, Request, [], [], persistent),
%% Make sure pipeline session is registerd
- test_server:sleep(4000),
+ ct:sleep(4000),
keep_alive_requests(Request, persistent).
%%-------------------------------------------------------------------------
@@ -311,18 +399,13 @@ async(Config) when is_list(Config) ->
{ok, NewRequestId} =
httpc:request(get, Request, [], [{sync, false}]),
- ok = httpc:cancel_request(NewRequestId),
- receive
- {http, {NewRequestId, _}} ->
- ct:fail(http_cancel_request_failed)
- after 3000 ->
- ok
- end.
+ ok = httpc:cancel_request(NewRequestId).
+
%%-------------------------------------------------------------------------
save_to_file() ->
[{doc, "Test to save the http body to a file"}].
save_to_file(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
FilePath = filename:join(PrivDir, "dummy.html"),
URL = url(group_name(Config), "/dummy.html", Config),
Request = {URL, []},
@@ -336,7 +419,7 @@ save_to_file(Config) when is_list(Config) ->
save_to_file_async() ->
[{doc,"Test to save the http body to a file"}].
save_to_file_async(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
FilePath = filename:join(PrivDir, "dummy.html"),
URL = url(group_name(Config), "/dummy.html", Config),
Request = {URL, []},
@@ -371,6 +454,37 @@ stream_once(Config) when is_list(Config) ->
Request2 = {url(group_name(Config), "/once_chunked.html", Config), []},
stream_test(Request2, {stream, {self, once}}).
+%%-------------------------------------------------------------------------
+stream_single_chunk() ->
+ [{doc, "Test the option stream for asynchrony requests"}].
+stream_single_chunk(Config) when is_list(Config) ->
+ Request = {url(group_name(Config), "/single_chunk.html", Config), []},
+ stream_test(Request, {stream, self}).
+%%-------------------------------------------------------------------------
+stream_no_length() ->
+ [{doc, "Test the option stream for asynchrony requests with HTTP 1.0 "
+ "body end on closed connection" }].
+stream_no_length(Config) when is_list(Config) ->
+ Request1 = {url(group_name(Config), "/http_1_0_no_length_single.html", Config), []},
+ stream_test(Request1, {stream, self}),
+ Request2 = {url(group_name(Config), "/http_1_0_no_length_multiple.html", Config), []},
+ stream_test(Request2, {stream, self}).
+%%-------------------------------------------------------------------------
+stream_large_not_200_or_206() ->
+ [{doc, "Test the option stream for large responses with status codes "
+ "other than 200 or 206" }].
+stream_large_not_200_or_206(Config) when is_list(Config) ->
+ Request = {url(group_name(Config), "/large_404_response.html", Config), []},
+ {404, _} = not_streamed_test(Request, {stream, self}).
+%%-------------------------------------------------------------------------
+not_streamed_once() ->
+ [{doc, "Test not streamed responses with once streaming"}].
+not_streamed_once(Config) when is_list(Config) ->
+ Request0 = {url(group_name(Config), "/404.html", Config), []},
+ {404, _} = not_streamed_test(Request0, {stream, {self, once}}),
+ Request1 = {url(group_name(Config), "/404_chunked.html", Config), []},
+ {404, _} = not_streamed_test(Request1, {stream, {self, once}}).
+
%%-------------------------------------------------------------------------
redirect_multiple_choises() ->
@@ -522,6 +636,31 @@ cookie_profile(Config) when is_list(Config) ->
inets:stop(httpc, cookie_test).
%%-------------------------------------------------------------------------
+empty_set_cookie() ->
+ [{doc, "Test empty Set-Cookie header."}].
+empty_set_cookie(Config) when is_list(Config) ->
+ ok = httpc:set_options([{cookies, enabled}]),
+
+ Request0 = {url(group_name(Config), "/empty_set_cookie.html", Config), []},
+
+ {ok, {{_,200,_}, [_ | _], [_|_]}}
+ = httpc:request(get, Request0, [], []),
+
+ ok = httpc:set_options([{cookies, disabled}]).
+
+%%-------------------------------------------------------------------------
+invalid_set_cookie(doc) ->
+ ["Test ignoring invalid Set-Cookie header"];
+invalid_set_cookie(Config) when is_list(Config) ->
+ ok = httpc:set_options([{cookies, enabled}]),
+
+ URL = url(group_name(Config), "/invalid_set_cookie.html", Config),
+ {ok, {{_,200,_}, [_|_], [_|_]}} =
+ httpc:request(get, {URL, []}, [], []),
+
+ ok = httpc:set_options([{cookies, disabled}]).
+
+%%-------------------------------------------------------------------------
headers_as_is(doc) ->
["Test the option headers_as_is"];
headers_as_is(Config) when is_list(Config) ->
@@ -616,6 +755,12 @@ transfer_encoding(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
+transfer_encoding_identity(Config) when is_list(Config) ->
+ URL = url(group_name(Config), "/identity_transfer_encoding.html", Config),
+ {ok, {{_,200,_}, [_|_], "IDENTITY"}} = httpc:request(URL).
+
+%%-------------------------------------------------------------------------
+
empty_response_header() ->
[{doc, "Test the case that the HTTP server does not send any headers. Solves OTP-6830"}].
empty_response_header(Config) when is_list(Config) ->
@@ -679,6 +824,22 @@ invalid_http(Config) when is_list(Config) ->
ct:print("Parse error: ~p ~n", [Reason]).
%%-------------------------------------------------------------------------
+
+invalid_chunk_size(doc) ->
+ ["Test parse error of HTTP chunk size"];
+invalid_chunk_size(suite) ->
+ [];
+invalid_chunk_size(Config) when is_list(Config) ->
+
+ URL = url(group_name(Config), "/invalid_chunk_size.html", Config),
+
+ {error, {chunk_size, _} = Reason} =
+ httpc:request(get, {URL, []}, [], []),
+
+ ct:print("Parse error: ~p ~n", [Reason]).
+
+%%-------------------------------------------------------------------------
+
emulate_lower_versions(doc) ->
[{doc, "Perform request as 0.9 and 1.0 clients."}];
emulate_lower_versions(Config) when is_list(Config) ->
@@ -718,7 +879,7 @@ headers() ->
headers(Config) when is_list(Config) ->
URL = url(group_name(Config), "/dummy.html", Config),
- DocRoot = ?config(doc_root, Config),
+ DocRoot = proplists:get_value(doc_root, Config),
{ok, FileInfo} =
file:read_file_info(filename:join([DocRoot,"dummy.html"])),
@@ -825,6 +986,13 @@ headers_dummy(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
+headers_with_obs_fold(Config) when is_list(Config) ->
+ Request = {url(group_name(Config), "/obs_folded_headers.html", Config), []},
+ {ok, {{_,200,_}, Headers, [_|_]}} = httpc:request(get, Request, [], []),
+ "a b" = proplists:get_value("folded", Headers).
+
+%%-------------------------------------------------------------------------
+
invalid_headers(Config) ->
Request = {url(group_name(Config), "/dummy.html", Config), [{"cookie", undefined}]},
{error, _} = httpc:request(get, Request, [], []).
@@ -848,6 +1016,33 @@ remote_socket_close_async(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
+process_leak_on_keepalive(Config) ->
+ {ok, ClosedSocket} = gen_tcp:listen(6666, [{active, false}]),
+ ok = gen_tcp:close(ClosedSocket),
+ Request = {url(group_name(Config), "/dummy.html", Config), []},
+ HttpcHandlers0 = supervisor:which_children(httpc_handler_sup),
+ {ok, {{_, 200, _}, _, Body}} = httpc:request(get, Request, [], []),
+ HttpcHandlers1 = supervisor:which_children(httpc_handler_sup),
+ ChildrenCount = supervisor:count_children(httpc_handler_sup),
+ %% Assuming that the new handler will be selected for keep_alive
+ %% which could not be the case if other handlers existed
+ [{undefined, Pid, worker, [httpc_handler]}] =
+ ordsets:to_list(
+ ordsets:subtract(ordsets:from_list(HttpcHandlers1),
+ ordsets:from_list(HttpcHandlers0))),
+ sys:replace_state(
+ Pid, fun (State) ->
+ Session = element(3, State),
+ setelement(3, State, Session#session{socket=ClosedSocket})
+ end),
+ {ok, {{_, 200, _}, _, Body}} = httpc:request(get, Request, [], []),
+ %% bad handler with the closed socket should get replaced by
+ %% the new one, so children count should stay the same
+ ChildrenCount = supervisor:count_children(httpc_handler_sup),
+ ok.
+
+%%-------------------------------------------------------------------------
+
stream_to_pid(Config) when is_list(Config) ->
ReceiverPid = create_receiver(pid),
Receiver = ReceiverPid,
@@ -1012,14 +1207,27 @@ stream_test(Request, To) ->
ct:fail(Msg)
end,
- Body == binary_to_list(StreamedBody).
+ Body = binary_to_list(StreamedBody).
+
+not_streamed_test(Request, To) ->
+ {ok, {{_,Code,_}, [_ | _], Body}} =
+ httpc:request(get, Request, [], [{body_format, binary}]),
+ {ok, RequestId} =
+ httpc:request(get, Request, [], [{body_format, binary}, {sync, false}, To]),
+
+ receive
+ {http, {RequestId, {{_, Code, _}, _Headers, Body}}} ->
+ {Code, binary_to_list(Body)};
+ {http, Msg} ->
+ ct:fail(Msg)
+ end.
url(http, End, Config) ->
- Port = ?config(port, Config),
+ Port = proplists:get_value(port, Config),
{ok,Host} = inet:gethostname(),
?URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End;
url(https, End, Config) ->
- Port = ?config(port, Config),
+ Port = proplists:get_value(port, Config),
{ok,Host} = inet:gethostname(),
?TLS_URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End;
url(sim_http, End, Config) ->
@@ -1027,10 +1235,10 @@ url(sim_http, End, Config) ->
url(sim_https, End, Config) ->
url(https, End, Config).
url(http, UserInfo, End, Config) ->
- Port = ?config(port, Config),
+ Port = proplists:get_value(port, Config),
?URL_START ++ UserInfo ++ integer_to_list(Port) ++ End;
url(https, UserInfo, End, Config) ->
- Port = ?config(port, Config),
+ Port = proplists:get_value(port, Config),
?TLS_URL_START ++ UserInfo ++ integer_to_list(Port) ++ End;
url(sim_http, UserInfo, End, Config) ->
url(http, UserInfo, End, Config);
@@ -1038,7 +1246,7 @@ url(sim_https, UserInfo, End, Config) ->
url(https, UserInfo, End, Config).
group_name(Config) ->
- GroupProp = ?config(tc_group_properties, Config),
+ GroupProp = proplists:get_value(tc_group_properties, Config),
proplists:get_value(name, GroupProp).
server_start(sim_http, _) ->
@@ -1060,11 +1268,11 @@ server_start(_, HttpdConfig) ->
proplists:get_value(port, Info).
server_config(http, Config) ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
[{port, 0},
{server_name,"httpc_test"},
{server_root, ServerRoot},
- {document_root, ?config(doc_root, Config)},
+ {document_root, proplists:get_value(doc_root, Config)},
{bind_address, any},
{ipfamily, inet_version()},
{mime_type, "text/plain"},
@@ -1080,11 +1288,13 @@ server_config(_, _) ->
start_apps(https) ->
inets_test_lib:start_apps([crypto, public_key, ssl]);
+start_apps(sim_https) ->
+ inets_test_lib:start_apps([crypto, public_key, ssl]);
start_apps(_) ->
ok.
ssl_config(Config) ->
- DataDir = ?config(data_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
[{certfile, filename:join(DataDir, "ssl_server_cert.pem")},
{verify, verify_none}
].
@@ -1149,7 +1359,7 @@ receive_replys([ID|IDs]) ->
{http, {ID, {{_, 200, _}, [_|_], _}}} ->
receive_replys(IDs);
{http, {Other, {{_, 200, _}, [_|_], _}}} ->
- ct:fail({recived_canceld_id, Other})
+ ct:pal({recived_canceld_id, Other})
end.
%% Perform a synchronous stop
@@ -1189,8 +1399,14 @@ dummy_server_init(Caller, ip_comm, Inet, _) ->
{ok, ListenSocket} = gen_tcp:listen(0, [Inet | BaseOpts]),
{ok, Port} = inet:port(ListenSocket),
Caller ! {port, Port},
- dummy_ipcomm_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]},
- [], ListenSocket);
+ dummy_ipcomm_server_loop({httpd_request, parse, [[{max_uri, ?HTTP_MAX_URI_SIZE},
+ {max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version,?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
+ ]]},
+ [], ListenSocket);
dummy_server_init(Caller, ssl, Inet, SSLOptions) ->
BaseOpts = [binary, {reuseaddr,true}, {active, false} |
@@ -1201,7 +1417,13 @@ dummy_ssl_server_init(Caller, BaseOpts, Inet) ->
{ok, ListenSocket} = ssl:listen(0, [Inet | BaseOpts]),
{ok, {_, Port}} = ssl:sockname(ListenSocket),
Caller ! {port, Port},
- dummy_ssl_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]},
+ dummy_ssl_server_loop({httpd_request, parse, [[{max_uri, ?HTTP_MAX_URI_SIZE},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_version,?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
+ ]]},
[], ListenSocket).
dummy_ipcomm_server_loop(MFA, Handlers, ListenSocket) ->
@@ -1231,6 +1453,7 @@ dummy_ssl_server_loop(MFA, Handlers, ListenSocket) ->
From ! {stopped, self()}
after 0 ->
{ok, Socket} = ssl:transport_accept(ListenSocket),
+ ok = ssl:ssl_accept(Socket, infinity),
HandlerPid = dummy_request_handler(MFA, Socket),
ssl:controlling_process(Socket, HandlerPid),
HandlerPid ! ssl_controller,
@@ -1277,10 +1500,22 @@ handle_request(Module, Function, Args, Socket) ->
stop ->
stop;
<<>> ->
- {httpd_request, parse, [[<<>>, ?HTTP_MAX_HEADER_SIZE]]};
+ {httpd_request, parse, [[{max_uri,?HTTP_MAX_URI_SIZE},
+ {max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version,?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
+ ]]};
Data ->
handle_request(httpd_request, parse,
- [Data |[?HTTP_MAX_HEADER_SIZE]], Socket)
+ [Data, [{max_uri, ?HTTP_MAX_URI_SIZE},
+ {max_header, ?HTTP_MAX_HEADER_SIZE},
+ {max_version,?HTTP_MAX_VERSION_STRING},
+ {max_method, ?HTTP_MAX_METHOD_STRING},
+ {max_content_length, ?HTTP_MAX_CONTENT_LENGTH},
+ {customize, httpd_custom}
+ ]], Socket)
end;
NewMFA ->
NewMFA
@@ -1370,7 +1605,7 @@ dummy_ssl_server_hang_loop(_) ->
ensure_host_header_with_port([]) ->
false;
-ensure_host_header_with_port(["host: " ++ Host| _]) ->
+ensure_host_header_with_port([{"host", Host}| _]) ->
case string:tokens(Host, [$:]) of
[_ActualHost, _Port] ->
true;
@@ -1382,7 +1617,7 @@ ensure_host_header_with_port([_|T]) ->
auth_header([]) ->
auth_header_not_found;
-auth_header(["authorization:" ++ Value | _]) ->
+auth_header([{"authorization", Value} | _]) ->
{ok, string:strip(Value)};
auth_header([_ | Tail]) ->
auth_header(Tail).
@@ -1399,7 +1634,7 @@ handle_auth("Basic " ++ UserInfo, Challange, DefaultResponse) ->
check_cookie([]) ->
ct:fail(no_cookie_header);
-check_cookie(["cookie:" ++ _Value | _]) ->
+check_cookie([{"cookie", _} | _]) ->
ok;
check_cookie([_Head | Tail]) ->
check_cookie(Tail).
@@ -1518,6 +1753,11 @@ handle_uri(_,"/307.html",Port,_,Socket,_) ->
"Content-Length:" ++ integer_to_list(length(Body))
++ "\r\n\r\n" ++ Body;
+handle_uri(_,"/404.html",_,_,_,_) ->
+ "HTTP/1.1 404 not found\r\n" ++
+ "Content-Length:14\r\n\r\n" ++
+ "Page not found";
+
handle_uri(_,"/500.html",_,_,_,_) ->
"HTTP/1.1 500 Internal Server Error\r\n" ++
"Content-Length:47\r\n\r\n" ++
@@ -1591,6 +1831,13 @@ handle_uri(_,"/dummy_headers.html",_,_,Socket,_) ->
send(Socket, http_chunk:encode("obar</BODY></HTML>")),
http_chunk:encode_last();
+handle_uri(_,"/obs_folded_headers.html",_,_,_,_) ->
+ "HTTP/1.1 200 ok\r\n"
+ "Content-Length:5\r\n"
+ "Folded: a\r\n"
+ " b\r\n\r\n"
+ "Hello";
+
handle_uri(_,"/capital_transfer_encoding.html",_,_,Socket,_) ->
Head = "HTTP/1.1 200 ok\r\n" ++
"Transfer-Encoding:Chunked\r\n\r\n",
@@ -1599,6 +1846,13 @@ handle_uri(_,"/capital_transfer_encoding.html",_,_,Socket,_) ->
send(Socket, http_chunk:encode("obar</BODY></HTML>")),
http_chunk:encode_last();
+handle_uri(_,"/identity_transfer_encoding.html",_,_,_,_) ->
+ "HTTP/1.0 200 OK\r\n"
+ "Transfer-Encoding:identity\r\n"
+ "Content-Length:8\r\n"
+ "\r\n"
+ "IDENTITY";
+
handle_uri(_,"/cookie.html",_,_,_,_) ->
"HTTP/1.1 200 ok\r\n" ++
"set-cookie:" ++ "test_cookie=true; path=/;" ++
@@ -1606,6 +1860,20 @@ handle_uri(_,"/cookie.html",_,_,_,_) ->
"Content-Length:32\r\n\r\n"++
"<HTML><BODY>foobar</BODY></HTML>";
+handle_uri(_,"/empty_set_cookie.html",_,_,_,_) ->
+ "HTTP/1.1 200 ok\r\n" ++
+ "set-cookie: \r\n" ++
+ "Content-Length:32\r\n\r\n"++
+ "<HTML><BODY>foobar</BODY></HTML>";
+
+handle_uri(_,"/invalid_set_cookie.html",_,_,_,_) ->
+ "HTTP/1.1 200 ok\r\n" ++
+ "set-cookie: =\r\n" ++
+ "set-cookie: name=\r\n" ++
+ "set-cookie: name-or-value\r\n" ++
+ "Content-Length:32\r\n\r\n"++
+ "<HTML><BODY>foobar</BODY></HTML>";
+
handle_uri(_,"/missing_crlf.html",_,_,_,_) ->
"HTTP/1.1 200 ok" ++
"Content-Length:32\r\n" ++
@@ -1625,20 +1893,68 @@ handle_uri(_,"/once_chunked.html",_,_,Socket,_) ->
http_chunk:encode("obar</BODY></HTML>")),
http_chunk:encode_last();
+handle_uri(_,"/404_chunked.html",_,_,Socket,_) ->
+ Head = "HTTP/1.1 404 not found\r\n" ++
+ "Transfer-Encoding:Chunked\r\n\r\n",
+ send(Socket, Head),
+ send(Socket, http_chunk:encode("<HTML><BODY>Not ")),
+ send(Socket,
+ http_chunk:encode("found</BODY></HTML>")),
+ http_chunk:encode_last();
+
+handle_uri(_,"/single_chunk.html",_,_,Socket,_) ->
+ Chunk = "HTTP/1.1 200 ok\r\n" ++
+ "Transfer-Encoding:Chunked\r\n\r\n" ++
+ http_chunk:encode("<HTML><BODY>fo") ++
+ http_chunk:encode("obar</BODY></HTML>") ++
+ http_chunk:encode_last(),
+ send(Socket, Chunk);
+
+handle_uri(_,"/http_1_0_no_length_single.html",_,_,Socket,_) ->
+ Body = "HTTP/1.0 200 ok\r\n"
+ "Content-type:text/plain\r\n\r\n"
+ "single packet",
+ send(Socket, Body),
+ close(Socket);
+
+handle_uri(_,"/http_1_0_no_length_multiple.html",_,_,Socket,_) ->
+ Head = "HTTP/1.0 200 ok\r\n"
+ "Content-type:text/plain\r\n\r\n"
+ "multiple packets, ",
+ send(Socket, Head),
+ %% long body to make sure it will be sent in multiple tcp packets
+ send(Socket, string:copies("other multiple packets ", 200)),
+ close(Socket);
+
+handle_uri(_,"/large_404_response.html",_,_,Socket,_) ->
+ %% long body to make sure it will be sent in multiple tcp packets
+ Body = string:copies("other multiple packets ", 200),
+ Head = io_lib:format("HTTP/1.1 404 not found\r\n"
+ "Content-length: ~B\r\n"
+ "Content-type: text/plain\r\n\r\n",
+ [length(Body)]),
+ send(Socket, Head),
+ send(Socket, Body),
+ close(Socket);
+
handle_uri(_,"/once.html",_,_,Socket,_) ->
Head = "HTTP/1.1 200 ok\r\n" ++
"Content-Length:32\r\n\r\n",
send(Socket, Head),
send(Socket, "<HTML><BODY>fo"),
- test_server:sleep(1000),
+ ct:sleep(1000),
send(Socket, "ob"),
- test_server:sleep(1000),
+ ct:sleep(1000),
send(Socket, "ar</BODY></HTML>");
handle_uri(_,"/invalid_http.html",_,_,_,_) ->
"HTTP/1.1 301\r\nDate:Sun, 09 Dec 2007 13:04:18 GMT\r\n" ++
"Transfer-Encoding:chunked\r\n\r\n";
+handle_uri(_,"/invalid_chunk_size.html",_,_,_,_) ->
+ "HTTP/1.1 200 ok\r\n" ++
+ "Transfer-Encoding:chunked\r\n\r\nåäö\r\n";
+
handle_uri(_,"/missing_reason_phrase.html",_,_,_,_) ->
"HTTP/1.1 200\r\n" ++
"Content-Length: 32\r\n\r\n"
@@ -1764,7 +2080,7 @@ run_clients(NumClients, ServerPort, SeqNumServer) ->
end
end),
MRef = erlang:monitor(process, Pid),
- timer:sleep(10 + random:uniform(1334)),
+ timer:sleep(10 + rand:uniform(1334)),
{Id, Pid, MRef}
end,
lists:seq(1, NumClients)).
@@ -1772,12 +2088,13 @@ run_clients(NumClients, ServerPort, SeqNumServer) ->
wait4clients([], _Timeout) ->
ok;
wait4clients(Clients, Timeout) when Timeout > 0 ->
- Time = now_ms(),
+ Time = erlang:monotonic_time(),
+
receive
{'DOWN', _MRef, process, Pid, normal} ->
{value, {Id, _, _}} = lists:keysearch(Pid, 2, Clients),
NewClients = lists:keydelete(Id, 1, Clients),
- wait4clients(NewClients, Timeout - (now_ms() - Time));
+ wait4clients(NewClients, Timeout - inets_lib:millisec_passed(Time));
{'DOWN', _MRef, process, Pid, Reason} ->
{value, {Id, _, _}} = lists:keysearch(Pid, 2, Clients),
ct:fail({bad_client_termination, Id, Reason})
@@ -1852,7 +2169,7 @@ slowly_send_response(CSock, Answer) ->
[length(Answer), Answer])),
lists:foreach(
fun(Char) ->
- timer:sleep(random:uniform(500)),
+ timer:sleep(rand:uniform(500)),
gen_tcp:send(CSock, <<Char>>)
end,
Response).
@@ -1870,15 +2187,10 @@ parse_connection_type(Request) ->
"keep-alive" -> keep_alive
end.
-%% Time in milli seconds
-now_ms() ->
- {A,B,C} = erlang:now(),
- A*1000000000+B*1000+(C div 1000).
-
set_random_seed() ->
- {_, _, Micros} = now(),
- A = erlang:phash2([make_ref(), self(), Micros]),
- random:seed(A, A, A).
+ Unique = erlang:unique_integer(),
+ A = erlang:phash2([make_ref(), self(), Unique]),
+ rand:seed(exsplus, {A, A, A}).
otp_8739(doc) ->
diff --git a/lib/inets/test/httpc_SUITE_data/ssl_client_cert.pem b/lib/inets/test/httpc_SUITE_data/ssl_client_cert.pem
index f274d2021d..427447958d 100644
--- a/lib/inets/test/httpc_SUITE_data/ssl_client_cert.pem
+++ b/lib/inets/test/httpc_SUITE_data/ssl_client_cert.pem
@@ -1,22 +1,31 @@
-----BEGIN RSA PRIVATE KEY-----
-MIIBOwIBAAJBANz7eFvORmJDi1XJMM2U3uHC5wmp/DXTLMw08XaEvtZ73wgVg84E
-V0oyX3Kh1thRE3Hch9AyrHjgpizCj9/Ra38CAwEAAQJACzpz2SZYCTIpaEh6xFdm
-I86FcsZCXHHIeu/NvRntoHQ+nfM7Np379+z6XNJWIcWh/QgG/jNJalR1BO+eyc6/
-YQIhAP3m8M0LDxJwSgHFtGAGatQqaqw9l48Kq5xdMFqvdpiHAiEA3s7lld6yCJYu
-6q7fZjTH+eKUwgg0vpgJutP7Fsok60kCIHHesQBEhW3vjkFdOZgXSLH+k/jLZr1w
-O6bU5GrHZpjhAiEAyTvGYcjDtTunXjDY9l+fadK6FlEBCk8ZIpNIiTnDhHkCIQDr
-QxxLLuNHRj8iWNbuVVZ99SJy8zC33pMgPFaFKaZesQ==
+MIICXQIBAAKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSVwC+n
+0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53h2Zr
+3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwIDAQAB
+AoGACdIVYe/LTeydUihtInC8lZ2QuPgJmoBNocRjqJFipEihoL4scHAx25n1bBvB
+I0HZphffzBkGp28oBAtl2LRPWXqu527unc/RWRfLMqSK1xNSq1DxD1a30zkrZPna
+QiV65vEJuNSJTtlDy/Zqc/BVZXCpxWlzYQedZgkmf0Qse8ECQQCmaz02Yur8zC9f
+eSQKU5OSzGw3bSIumEzziCfHdTheK6MEoccf5TCAyLXhZwA7QlKja4tFXfeyVxws
+/LlnUJN9AkEA4j+xnOeYUyGKXL5i+BAbnqpI4MzPiq+IoCYkaRlD/wAws24r5HNI
+ZQmEHWqD/NNzOf/A2XuyLtMiTGJPW/DftwJBAKKpJP6Ytuh6xz8BUCnLwO12Y7vV
+LtjuQiCzD3aUa5EYA9HOMqxJPxxRkf0LyR0i2VUkE8+sZiPpov+R0cJa7p0CQQCj
+40GUiArGRSiF7/+e84QeVfl+pb29F1QftiFv5DZmFEwy3Z572KpbTh5edJbxYHY6
+UDHxGHJFCvnwXNJhpkVXAkBJqfEfiMJ3Q/E5Gpf3sQizacouW92iiN8ojlF1oB80
+t34RysJH7SgI3gdMhTribCo2UUaV0StjR6yodPN+TB2J
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
-MIIB7jCCAZgCAQAwDQYJKoZIhvcNAQEEBQAwgYExCzAJBgNVBAYTAlNFMRIwEAYD
-VQQHEwlTdG9ja2hvbG0xETAPBgNVBAoTCEVyaWNzc29uMQwwCgYDVQQLEwNFVFgx
-FjAUBgNVBAMTDUhlbGVuIEFpcml5YW4xJTAjBgkqhkiG9w0BCQEWFmhlbGVuQGVy
-aXguZXJpY3Nzb24uc2UwHhcNOTcwNzI4MDcxNDI1WhcNOTgxMjEwMDcxNDI1WjCB
-gTELMAkGA1UEBhMCU0UxEjAQBgNVBAcTCVN0b2NraG9sbTERMA8GA1UEChMIRXJp
-Y3Nzb24xDDAKBgNVBAsTA0VUWDEWMBQGA1UEAxMNSGVsZW4gQWlyaXlhbjElMCMG
-CSqGSIb3DQEJARYWaGVsZW5AZXJpeC5lcmljc3Nvbi5zZTBcMA0GCSqGSIb3DQEB
-AQUAA0sAMEgCQQDc+3hbzkZiQ4tVyTDNlN7hwucJqfw10yzMNPF2hL7We98IFYPO
-BFdKMl9yodbYURNx3IfQMqx44KYswo/f0Wt/AgMBAAEwDQYJKoZIhvcNAQEEBQAD
-QQC2++hLIaQJ4ChCjFE9UCfXO9cZ3Vq/FT9VjE+G4MRBDo4LQ5mBKNXcPF6EFZmi
-7XrlvopXkVPlRguTi2SLRPkY
+MIIChzCCAfCgAwIBAgIGAIsapa8BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT
+BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE
+BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD
+VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw
+MDAwWjB7MQ8wDQYDVQQDEwZjbGllbnQxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl
+cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD
+VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSV
+wC+n0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53
+h2Zr3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwID
+AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAG8t6f1A
+PF7xayGxtUpG2r6W5ETylC3ZIKPS2kfJk9aYi7AZNTp7/xTU6SgqvFBN8aBPzxCD
+4jHrSNC8DSb4X1x9uimarb6qdZDHEdij+DRAd2eygJHZxEf7+8B4Fx34thQeU9hZ
+S1Izke5AlsyFMkvB7h0anE4k9BfuU70vl6v5
-----END CERTIFICATE-----
diff --git a/lib/inets/test/httpc_SUITE_data/ssl_server_cert.pem b/lib/inets/test/httpc_SUITE_data/ssl_server_cert.pem
index f01b6c992b..4aac86db49 100644
--- a/lib/inets/test/httpc_SUITE_data/ssl_server_cert.pem
+++ b/lib/inets/test/httpc_SUITE_data/ssl_server_cert.pem
@@ -1,22 +1,31 @@
-----BEGIN RSA PRIVATE KEY-----
-MIIBOQIBAAJBAMe2WhP6s+JeKOwWPEjI9susfN4Vjn2dd1X4QUlOETcWVLoF916m
-M4JU+ms7+ciMR8GRNCsIeqZGY8/GSqm74ccCAwEAAQJAF08YKlbLYfM9cXiS5qfV
-7iWemUkIzW5wfC8yZ3zeE4Cp6R9ViUfs/dadQ/23Cw0Bpo2t8UdTUdCa4KpmqOem
-cQIhAOnxTWZ5eo6h6PXDp7L5FZUACg8+wT3qf5f2is2mbSZPAiEA2orUY8JZDTSk
-Rm7q9WxLiLNtORsXdTCmnCWhqBOYpwkCIErdowRxScxNekz0IT3AQqzdR1rbnWHg
-IpcSGhd39CQ3AiA1XvQxjLP8wp9fyBS/bPwhXVhOOuyGpSP7PEF3b5m3KQIgGQWc
-/a5wuWx3pc3mLx0ILwNoJr2ubFEuW1PJPsPJPv0=
+MIICXQIBAAKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9Adq6
+7k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ4UAt
+NHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQIDAQAB
+AoGAQIlma0r6W6bcRj4+Wd4fXCFvHuq5Psu1fYEeC5Yvz8761xVjjSfbrDHJZ9pm
+FjOEgedK+s5lbDXqYVyjbdyZSugStBRocSmbG8SQHcAsxR2ZIkNzX2hYzB+lslWo
+T3YJojDyB134O7XJznCu+ZFXP86jyJ1JT6k6a+OIHcwnJ+ECQQDYn57dY4Px3mEd
+VBLStN3YkRF5oFyT+xk7IaKeLLB6n4gCnoVbBoHut7PFbPYPzoNzEwPk3MQKDIHb
+Kig3S5CpAkEAvPA1VmoJWAlN6kUi+F2L8HXEArzE8x7vwdsslrwMKUe4dFS+ZC/7
+5iDOaxcZ7TYkCgwzBt341++DCgP6j3fY1QJBALB6AcOcwi52m6l4B8mu3ZkEPjdX
+BHTuONTqhv/TqoaLlxODL2NDvvDKqeMp7KBd/srt79swW2lQXS4+fvrlTdkCQQCm
+zxj4O1QWkthkfje6ubSkTwUIOatUzrp1F9GNH2dJRtX2dx9FCwxGCC7WY6XzRXqa
+GF0wsedSllbGD+82nWQlAkAicMGqCqRq4hKR/cVmFatOqKVWCVkx6OFF2FhuiI5Z
+h5eIOPGCt8dVRs1P9DNSld/D98Sfm65m85z8BtXovvYV
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
-MIIB7jCCAZgCAQAwDQYJKoZIhvcNAQEEBQAwgYExCzAJBgNVBAYTAlNFMRIwEAYD
-VQQHEwlTdG9ja2hvbG0xETAPBgNVBAoTCEVyaWNzc29uMQwwCgYDVQQLEwNFVFgx
-FjAUBgNVBAMTDUhlbGVuIEFpcml5YW4xJTAjBgkqhkiG9w0BCQEWFmhlbGVuQGVy
-aXguZXJpY3Nzb24uc2UwHhcNOTcwNzI4MDcyMTAwWhcNOTgxMjEwMDcyMTAwWjCB
-gTELMAkGA1UEBhMCU0UxEjAQBgNVBAcTCVN0b2NraG9sbTERMA8GA1UEChMIRXJp
-Y3Nzb24xDDAKBgNVBAsTA0VUWDEWMBQGA1UEAxMNSGVsZW4gQWlyaXlhbjElMCMG
-CSqGSIb3DQEJARYWaGVsZW5AZXJpeC5lcmljc3Nvbi5zZTBcMA0GCSqGSIb3DQEB
-AQUAA0sAMEgCQQDHtloT+rPiXijsFjxIyPbLrHzeFY59nXdV+EFJThE3FlS6Bfde
-pjOCVPprO/nIjEfBkTQrCHqmRmPPxkqpu+HHAgMBAAEwDQYJKoZIhvcNAQEEBQAD
-QQCnU1TkxmfbLdUwjdECb5x9QHCevAR7AmTms4Csn2oOEyPX+bgF2d94xhrV1sxO
-Rs0yigk1PtN17Ci0Dey0LYkR
+MIIChzCCAfCgAwIBAgIGANUxXM9BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT
+BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE
+BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD
+VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw
+MDAwWjB7MQ8wDQYDVQQDEwZzZXJ2ZXIxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl
+cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD
+VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9
+Adq67k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ
+4UAtNHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQID
+AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAGF5Pfwk
+QDdwJup/mVITPxbBls4Yl7anDooUQsq8066lA1g54H/PRfXscGkyCFGh1ifXvf1L
+psMRoBAdDHL/wSJplk3rRavkC94eBgnTFZmfKL6844g1j53yameiYL8IEVExYMBg
+/XGyc0qwq57WT8B/K4aElrvlBlQ0wF3wN54M
-----END CERTIFICATE-----
diff --git a/lib/inets/test/httpc_cookie_SUITE.erl b/lib/inets/test/httpc_cookie_SUITE.erl
index 80f43ec236..8140967bca 100644
--- a/lib/inets/test/httpc_cookie_SUITE.erl
+++ b/lib/inets/test/httpc_cookie_SUITE.erl
@@ -1,35 +1,30 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(httpc_cookie_SUITE).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
-%% Test server specific exports
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]).
-
-%% Test cases must be exported.
--export([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]).
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
-define(URL, "http://myhost.cookie.test.org").
-define(URL_DOMAIN, "http://myhost2.cookie.test.org").
@@ -37,86 +32,10 @@
%% 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),
- httpc: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),
- httpc: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
-%%--------------------------------------------------------------------
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds,15}}
+ ].
all() ->
[
@@ -147,17 +66,55 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+init_per_testcase(session_cookies_only = Case, Config0) ->
+ Config = init_workdir(Case, Config0),
+ application:start(inets),
+ httpc:set_options([{cookies, verify}]),
+ Config;
+init_per_testcase(cookie_expires = Case, Config0) ->
+ Config = init_workdir(Case, Config0),
+ CaseDir = proplists:get_value(case_top_dir, Config),
+ application:start(inets),
+ application:set_env(inets, services, [{httpc, {default, CaseDir}}]),
+ application:start(inets),
+ httpc:set_options([{cookies, verify}]),
+ Config;
+init_per_testcase(Case, Config0) ->
+ Config = init_workdir(Case, Config0),
+ CaseDir = proplists:get_value(case_top_dir, Config),
+ application:load(inets),
+ application:set_env(inets, services, [{httpc, {default, CaseDir}}]),
+ application:start(inets),
+ httpc:set_options([{cookies, verify}]),
+ Config.
+
+init_workdir(Case, Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ SuiteTopDir = filename:join(PrivDir, ?MODULE),
+ case file:make_dir(SuiteTopDir) of
+ ok ->
+ ok;
+ {error, eexist} ->
+ ok;
+ Error ->
+ ct:fail({failed_creating_subsuite_top_dir, Error})
+ end,
+
+ CaseTopDir = filename:join(SuiteTopDir, Case),
+ ok = file:make_dir(CaseTopDir),
+ [{suite_top_dir, SuiteTopDir},
+ {case_top_dir, CaseTopDir} | Config].
+
+end_per_testcase(_, _) ->
+ application:stop(inets).
+
%% 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() ->
+ [{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(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"}],
httpc:store_cookies(SetCookieHeaders, ?URL),
@@ -165,36 +122,22 @@ session_cookies_only(Config) when is_list(Config) ->
httpc:cookie_header(?URL),
application:stop(inets),
application:start(inets),
- {"cookie", ""} = httpc:cookie_header(?URL),
+ {"cookie", ""} = httpc: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() ->
+ [{doc, "Test that the old (original) format of cookies are accepted."}].
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}],
httpc:store_cookies(SetCookieHeaders, ?URL),
{"cookie", "$Version=0; test_cookie=true; $Path=/"} =
- httpc:cookie_header(?URL),
-
- tsp("netscape_cookies -> Cookies 2: ~p", [httpc:which_cookies()]),
- ok.
+ httpc:cookie_header(?URL).
-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() ->
+ [{doc, "A cookie can be canceld by sending the same cookie with max-age=0 "
+ "this test cheks that cookie is canceled."}].
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"}],
httpc:store_cookies(SetCookieHeaders, ?URL),
@@ -203,155 +146,60 @@ cookie_cancel(Config) when is_list(Config) ->
NewSetCookieHeaders =
[{"set-cookie", "test_cookie=true; path=/;max-age=0"}],
httpc:store_cookies(NewSetCookieHeaders, ?URL),
- {"cookie", ""} = httpc:cookie_header(?URL),
-
- tsp("cookie_cancel -> Cookies 2: ~p", [httpc:which_cookies()]),
- ok.
+ {"cookie", ""} = httpc:cookie_header(?URL).
-cookie_expires(doc) ->
- ["Test that a cookie is not used when it has expired"];
-cookie_expires(suite) ->
- [];
+cookie_expires() ->
+ [{doc, "Test that a cookie is not used when it has expired"}].
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"}],
httpc:store_cookies(SetCookieHeaders, ?URL),
{"cookie", "$Version=0; test_cookie=true; $Path=/"} =
httpc:cookie_header(?URL),
- test_server:sleep(10000),
- {"cookie", ""} = httpc:cookie_header(?URL),
-
- tsp("cookie_expires -> Cookies 2: ~p", [httpc:which_cookies()]),
- ok.
+ timer:sleep(10000),
+ {"cookie", ""} = httpc:cookie_header(?URL).
-persistent_cookie(doc) ->
- ["Test domian cookie attribute"];
-persistent_cookie(suite) ->
- [];
+persistent_cookie() ->
+ [{doc, "Test domian cookie attribute"}].
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"}],
httpc:store_cookies(SetCookieHeaders, ?URL),
{"cookie", "$Version=0; test_cookie=true; $Path=/"} =
httpc:cookie_header(?URL),
- CaseDir = ?config(case_top_dir, Config),
+ CaseDir = proplists:get_value(case_top_dir, Config),
application:stop(inets),
application:load(inets),
application:set_env(inets, services, [{httpc, {default, CaseDir}}]),
application:start(inets),
httpc:set_options([{cookies, enabled}]),
- {"cookie","$Version=0; test_cookie=true; $Path=/"} = httpc:cookie_header(?URL),
-
- tsp("persistent_cookie -> Cookies 2: ~p", [httpc:which_cookies()]),
- ok.
+ {"cookie","$Version=0; test_cookie=true; $Path=/"} = httpc:cookie_header(?URL).
-
-domain_cookie(doc) ->
- ["Test the domian cookie attribute"];
-domain_cookie(suite) ->
- [];
+domain_cookie() ->
+ [{doc, "Test the domian cookie attribute"}].
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"}],
httpc:store_cookies(SetCookieHeaders, ?URL),
{"cookie","$Version=0; test_cookie=true; $Path=/; "
"$Domain=.cookie.test.org"} =
- httpc:cookie_header(?URL_DOMAIN),
+ httpc: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() ->
+ [{doc, "Test the secure cookie attribute"}].
secure_cookie(Config) when is_list(Config) ->
- tsp("secure_cookie -> entry with"
- "~n Config: ~p", [Config]),
-
- %% httpc:reset_cookies(),
-
- tsp("secure_cookie -> Cookies 1: ~p", [httpc:which_cookies()]),
-
SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/; secure"}],
- tsp("secure_cookie -> store cookies (1)"),
ok = httpc:store_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 -> store cookies (2)"),
SetCookieHeaders1 = [{"set-cookie", "test1_cookie=true; path=/; secure"}],
ok = httpc:store_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=/"} = httpc:cookie_header(?URL_SECURE),
-
- tsp("secure_cookie -> Cookies 4: ~p", [httpc:which_cookies()]),
-
- tsp("secure_cookie -> done"),
- ok.
+ ?URL_SECURE).
-expect_cookie_header(No, ExpectedCookie) ->
- case httpc:cookie_header(?URL) of
- {"cookie", ExpectedCookie} ->
- ok;
- {"cookie", BadCookie} ->
- io:format("Bad Cookie ~w: "
- "~n Expected: ~s"
- "~n Received: ~s"
- "~n", [No, ExpectedCookie, BadCookie]),
- exit({bad_cookie_header, No, ExpectedCookie, BadCookie})
- end.
-
-print_cookies(Pre) ->
- io:format("~s: ", [Pre]),
- print_cookies2(httpc:which_cookies()).
-
-print_cookies2([]) ->
- ok;
-print_cookies2([{cookies, Cookies}|Rest]) ->
- print_cookies3("Cookies", Cookies),
- print_cookies2(Rest);
-print_cookies2([{session_cookies, Cookies}|Rest]) ->
- print_cookies3("Session Cookies", Cookies),
- print_cookies2(Rest);
-print_cookies2([_|Rest]) ->
- print_cookies2(Rest).
-
-print_cookies3(Header, []) ->
- io:format(" ~s: []", [Header]);
-print_cookies3(Header, Cookies) ->
- io:format(" ~s: ", [Header]),
- Prefix = " ",
- PrintCookie =
- fun(Cookie) ->
- io:format("~s", [httpc_cookie:image_of(Prefix, Cookie)])
- end,
- lists:foreach(PrintCookie, Cookies).
-
-update_cookie(doc)->
- ["Test that a (plain) cookie can be updated."];
-update_cookie(suite) ->
- [];
+update_cookie()->
+ [{doc, "Test that a (plain) cookie can be updated."}].
update_cookie(Config) when is_list(Config) ->
print_cookies("Cookies before store"),
@@ -376,10 +224,8 @@ update_cookie(Config) when is_list(Config) ->
"test_cookie=false; $Path=/",
expect_cookie_header(2, ExpectCookie2).
-update_cookie_session(doc)->
- ["Test that a session cookie can be updated."];
-update_cookie_session(suite) ->
- [];
+update_cookie_session()->
+ [{doc, "Test that a session cookie can be updated."}].
update_cookie_session(Config) when is_list(Config)->
print_cookies("Cookies before store"),
@@ -399,23 +245,57 @@ update_cookie_session(Config) when is_list(Config)->
expect_cookie_header(2, ExpectedCookie2).
-cookie_attributes(doc) ->
- ["Test attribute not covered by the other test cases"];
-cookie_attributes(suite) ->
- [];
+cookie_attributes() ->
+ [{doc, "Test attribute not covered by the other test cases"}].
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"}],
httpc:store_cookies(SetCookieHeaders, ?URL),
- {"cookie","$Version=1; test_cookie=true"} = httpc:cookie_header(?URL),
- ok.
+ {"cookie","$Version=1; test_cookie=true"} = httpc:cookie_header(?URL).
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+print_cookies(Pre) ->
+ io:format("~s: ", [Pre]),
+ print_cookies2(httpc:which_cookies()).
+
+print_cookies2([]) ->
+ ok;
+print_cookies2([{cookies, Cookies}|Rest]) ->
+ print_cookies3("Cookies", Cookies),
+ print_cookies2(Rest);
+print_cookies2([{session_cookies, Cookies}|Rest]) ->
+ print_cookies3("Session Cookies", Cookies),
+ print_cookies2(Rest);
+print_cookies2([_|Rest]) ->
+ print_cookies2(Rest).
+
+print_cookies3(Header, []) ->
+ io:format(" ~s: []", [Header]);
+print_cookies3(Header, Cookies) ->
+ io:format(" ~s: ", [Header]),
+ Prefix = " ",
+ PrintCookie =
+ fun(Cookie) ->
+ io:format("~s", [httpc_cookie:image_of(Prefix, Cookie)])
+ end,
+ lists:foreach(PrintCookie, Cookies).
+
+expect_cookie_header(No, ExpectedCookie) ->
+ case httpc:cookie_header(?URL) of
+ {"cookie", ExpectedCookie} ->
+ ok;
+ {"cookie", BadCookie} ->
+ io:format("Bad Cookie ~w: "
+ "~n Expected: ~s"
+ "~n Received: ~s"
+ "~n", [No, ExpectedCookie, BadCookie]),
+ exit({bad_cookie_header, No, ExpectedCookie, BadCookie})
+ end.
check_cookie(Expect, URL) ->
case httpc:cookie_header(URL) of
@@ -425,12 +305,12 @@ check_cookie(Expect, URL) ->
case lists:prefix(Expect, Unexpected) of
true ->
Extra = Unexpected -- Expect,
- tsf({extra_cookie_info, Extra});
+ ct:fail({extra_cookie_info, Extra});
false ->
- tsf({unknown_cookie, Expect, Unexpected})
+ ct:fail({unknown_cookie, Expect, Unexpected})
end;
Bad ->
- tsf({bad_cookies, Bad})
+ ct:fail({bad_cookies, Bad})
end.
@@ -508,11 +388,3 @@ 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_proxy_SUITE.erl b/lib/inets/test/httpc_proxy_SUITE.erl
index 84db39e76b..198b245399 100644
--- a/lib/inets/test/httpc_proxy_SUITE.erl
+++ b/lib/inets/test/httpc_proxy_SUITE.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -57,7 +58,7 @@ groups() ->
[http_emulate_lower_versions
|local_proxy_cases()]},
{local_proxy_https,[],
- local_proxy_cases()}].
+ local_proxy_cases() ++ local_proxy_https_cases()}].
%% internal functions
@@ -69,16 +70,20 @@ local_proxy_cases() ->
http_post,
http_put,
http_delete,
+ http_delete_body,
http_headers,
http_proxy_auth,
http_doesnotexist,
http_stream,
http_not_modified_otp_6821].
+local_proxy_https_cases() ->
+ [https_connect_error].
+
%%--------------------------------------------------------------------
init_per_suite(Config0) ->
- case init_apps([crypto,public_key], Config0) of
+ case init_apps(suite_apps(), Config0) of
Config when is_list(Config) ->
make_cert_files(dsa, "server-", Config),
Config;
@@ -93,7 +98,7 @@ end_per_suite(_Config) ->
%% internal functions
suite_apps() ->
- [crypto,public_key].
+ [asn1,crypto,public_key].
%%--------------------------------------------------------------------
@@ -136,7 +141,7 @@ end_per_testcase(_Case, Config) ->
%% internal functions
apps(_Case, Config) ->
- case ?config(protocol, Config) of
+ case proplists:get_value(protocol, Config) of
https ->
[ssl];
_ ->
@@ -262,6 +267,22 @@ http_delete(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
+http_delete_body(doc) ->
+ ["Perform a DELETE request with a content body. The server will not allow it "
+ "but we only test sending the request."];
+http_delete_body(Config) when is_list(Config) ->
+ Method = delete,
+ URL = url("/delete.html", Config),
+ Content = "foo=bar",
+ Request = {URL,[],"application/x-www-form-urlencoded",Content},
+ HttpOpts = [],
+ Opts = [],
+ {ok,{{_,405,_},[_|_],[_|_]}} =
+ httpc:request(Method, Request, HttpOpts, Opts),
+ ok.
+
+%%--------------------------------------------------------------------
+
http_headers(doc) ->
["Use as many request headers as possible"];
http_headers(Config) when is_list(Config) ->
@@ -414,6 +435,21 @@ header_value(Name, [{HeaderName,HeaderValue}|Headers]) ->
end.
%%--------------------------------------------------------------------
+https_connect_error(doc) ->
+ ["Error from CONNECT tunnel should be returned"];
+https_connect_error(Config) when is_list(Config) ->
+ {HttpServer,HttpPort} = proplists:get_value(http, Config),
+ Method = get,
+ %% using HTTPS scheme with HTTP port to trigger connection error
+ URL = "https://" ++ HttpServer ++ ":" ++
+ integer_to_list(HttpPort) ++ "/index.html",
+ Opts = [],
+ HttpOpts = [],
+ Request = {URL,[]},
+ {error,{failed_connect,[_,{tls,_,_}]}} =
+ httpc:request(Method, Request, HttpOpts, Opts).
+
+%%--------------------------------------------------------------------
%% Internal Functions ------------------------------------------------
%%--------------------------------------------------------------------
@@ -441,7 +477,7 @@ app_start(App, Config) ->
inets ->
application:stop(App),
ok = application:start(App),
- case ?config(proxy, Config) of
+ case proplists:get_value(proxy, Config) of
undefined -> ok;
{_,ProxySpec} ->
ok = httpc:set_options([{proxy,ProxySpec}])
@@ -459,7 +495,7 @@ app_stop(App) ->
application:stop(App).
make_cert_files(Alg, Prefix, Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
CaInfo = {CaCert,_} = erl_make_certs:make_cert([{key,Alg}]),
{Cert,CertKey} = erl_make_certs:make_cert([{key,Alg},{issuer,CaInfo}]),
CaCertFile = filename:join(PrivDir, Prefix++"cacerts.pem"),
@@ -477,8 +513,8 @@ der_to_pem(File, Entries) ->
url(AbsPath, Config) ->
- Protocol = ?config(protocol, Config),
- {ServerName,ServerPort} = ?config(Protocol, Config),
+ Protocol = proplists:get_value(protocol, Config),
+ {ServerName,ServerPort} = proplists:get_value(Protocol, Config),
atom_to_list(Protocol) ++ "://" ++
ServerName ++ ":" ++ integer_to_list(ServerPort) ++
AbsPath.
@@ -512,8 +548,8 @@ init_local_proxy_string(String, Config) ->
|Config].
rcmd_local_proxy(Args, Config) ->
- DataDir = ?config(data_dir, Config),
- PrivDir = ?config(priv_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
Script = filename:join(DataDir, ?LOCAL_PROXY_SCRIPT),
rcmd(Script, Args, [{cd,PrivDir}]).
diff --git a/lib/inets/test/httpc_proxy_SUITE_data/apache2/apache2.conf b/lib/inets/test/httpc_proxy_SUITE_data/apache2/apache2.conf
index 37af88c510..b139c4ca51 100644
--- a/lib/inets/test/httpc_proxy_SUITE_data/apache2/apache2.conf
+++ b/lib/inets/test/httpc_proxy_SUITE_data/apache2/apache2.conf
@@ -2,18 +2,19 @@
##
## %CopyrightBegin%
##
-## Copyright Ericsson AB 2012. All Rights Reserved.
+## Copyright Ericsson AB 2012-2016. 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/.
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
##
-## 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.
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
##
## %CopyrightEnd%
##
diff --git a/lib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh b/lib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh
index 4b05ea63ef..e8b690a4a1 100755
--- a/lib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh
+++ b/lib/inets/test/httpc_proxy_SUITE_data/server_proxy.sh
@@ -5,18 +5,19 @@
##
## %CopyrightBegin%
##
-## Copyright Ericsson AB 2012. All Rights Reserved.
+## Copyright Ericsson AB 2012-2016. 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/.
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
##
-## 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.
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
##
## %CopyrightEnd%
##
@@ -168,6 +169,8 @@ MaxRequestsPerChild 0
ViaProxyName "tinyproxy"
ConnectPort $APACHE_HTTPS_PORT
+# to test connect error
+ConnectPort $APACHE_HTTP_PORT
EOF
(tinyproxy -d -c tinyproxy.conf 1>/dev/null 2>&1 </dev/null &)&
wait_for_pidfile tinyproxy.pid
diff --git a/lib/inets/test/httpd_1_0.erl b/lib/inets/test/httpd_1_0.erl
new file mode 100644
index 0000000000..f61dc3c3fd
--- /dev/null
+++ b/lib/inets/test/httpd_1_0.erl
@@ -0,0 +1,39 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(httpd_1_0).
+
+-export([host/4, trace/4]).
+
+%%-------------------------------------------------------------------------
+%% Test cases
+%%-------------------------------------------------------------------------
+host(Type, Port, Host, Node) ->
+ %% No host needed for HTTP/1.0
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET / HTTP/1.0\r\n\r\n",
+ [{statuscode, 200},
+ {version, "HTTP/1.0"}]).
+trace(Type, Port, Host, Node)->
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "TRACE / HTTP/1.0\r\n\r\n",
+ [{statuscode, 501},
+ {version, "HTTP/1.0"}]).
diff --git a/lib/inets/test/httpd_1_1.erl b/lib/inets/test/httpd_1_1.erl
index 07d94ea97a..3755ed117b 100644
--- a/lib/inets/test/httpd_1_1.erl
+++ b/lib/inets/test/httpd_1_1.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -20,12 +21,10 @@
-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]).
+-export([host/4, chunked/4, expect/4, range/4, if_test/5, trace/4,
+ head/4, mod_cgi_chunked_encoding_test/5, mod_esi_chunk_timeout/4]).
%% -define(all_keys_lower_case,true).
-ifndef(all_keys_lower_case).
@@ -40,14 +39,10 @@
%%-------------------------------------------------------------------------
-%% Test cases starts here.
+%% Test cases
%%-------------------------------------------------------------------------
host(Type, Port, Host, Node) ->
- %% No host needed for HTTP/1.0
- ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
- "GET / HTTP/1.0\r\n\r\n",
- [{statuscode, 200},
- {version, "HTTP/1.0"}]),
+
%% No host must generate an error
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET / HTTP/1.1\r\n\r\n",
@@ -158,13 +153,13 @@ if_test(Type, Port, Host, Node, DocRoot)->
calendar:datetime_to_gregorian_seconds(FileInfo#file_info.mtime),
Mod = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime(
- CreatedSec-1)),
+ 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",
+ "\r\nIf-Modified-Since:" ++
+ Mod ++ "\r\n\r\n",
[{statuscode, 200}]),
Mod1 = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime(
CreatedSec+100)),
@@ -174,83 +169,70 @@ if_test(Type, Port, Host, Node, DocRoot)->
++ 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",
+ "\r\nIf-Modified-Since:" ++
+ "AAA[...]AAAA" ++ "\r\n\r\n",
[{statuscode, 400}]),
-
-
- Mod2 = httpd_util:rfc1123_date(calendar:gregorian_seconds_to_datetime(
+
+ 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(
+ %% 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
+ "GET / HTTP/1.1\r\nHost:"
+ ++ Host ++
+ "\r\nIf-Unmodified-Since:"++ Mod3
++"\r\n\r\n",
- [{statuscode, 412}]),
+ [{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}]),
+ %% 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:NotEtag\r\n\r\n",
- [{statuscode, 412}]),
+ "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}]),
+ %% 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",
+ ++ Host ++ "\r\n"++
+ "If-None-Match:NotEtag,"
+ "NeihterEtag\r\n\r\n",
[{statuscode,200}]),
ok.
-
-http_trace(Type, Port, Host, Node)->
+
+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"}]).
+ [{statuscode, 200}]).
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"
@@ -284,12 +266,21 @@ mod_cgi_chunked_encoding_test(Type, Port, Host, Node, [Request| Rest])->
[{statuscode, 200}]),
mod_cgi_chunked_encoding_test(Type, Port, Host, Node, Rest).
+
+mod_esi_chunk_timeout(Type, Port, Host, Node) ->
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /cgi-bin/erl/httpd_example/chunk_timeout?input=20000 HTTP/1.1\r\n"
+ "Host:"++ Host ++"\r\n"
+ "\r\n",
+ [{statuscode, 200},
+ {header, "warning"}]).
+
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
validateRangeRequest(Socket,Response,ValidBody,C,O,DE)->
receive
- {tcp,Socket,Data} ->
+ {_,Socket,Data} ->
case string:str(Data,"\r\n") of
0->
validateRangeRequest(Socket,
@@ -318,7 +309,7 @@ validateRangeRequest1(Socket, Response, ValidBody) ->
case end_of_header(Response) of
false ->
receive
- {tcp,Socket,Data} ->
+ {_,Socket,Data} ->
validateRangeRequest1(Socket, Response ++ Data,
ValidBody);
_->
@@ -337,10 +328,10 @@ validateRangeRequest2(Socket, Head, Body, ValidBody, {multiPart,Boundary})->
validateMultiPartRangeRequest(Body, ValidBody, Boundary);
false->
receive
- {tcp, Socket, Data} ->
+ {_, Socket, Data} ->
validateRangeRequest2(Socket, Head, Body ++ Data,
ValidBody, {multiPart, Boundary});
- {tcp_closed, Socket} ->
+ {_, Socket} ->
error;
_ ->
error
@@ -359,7 +350,7 @@ validateRangeRequest2(Socket, Head, Body, ValidBody, BodySize)
end;
Size when Size < BodySize ->
receive
- {tcp, Socket, Data} ->
+ {_, Socket, Data} ->
validateRangeRequest2(Socket, Head,
Body ++ Data, ValidBody, BodySize);
_ ->
@@ -371,18 +362,18 @@ validateRangeRequest2(Socket, Head, Body, ValidBody, BodySize)
validateMultiPartRangeRequest(Body, ValidBody, Boundary)->
- case inets_regexp:split(Body,"--"++Boundary++"--") of
+ case re:split(Body,"--"++Boundary++"--", [{return, list}]) of
%%Last is the epilogue and must be ignored
- {ok,[First | _Last]}->
+ [First | _Last]->
%%First is now the actuall http request body.
- case inets_regexp:split(First, "--" ++ Boundary) of
+ case re:split(First, "--" ++ Boundary, [{return, list}]) of
%%Parts is now a list of ranges and the heads for each range
%%Gues we try to split out the body
- {ok,Parts}->
+ Parts->
case lists:flatten(lists:map(fun splitRange/1,Parts)) of
ValidBody->
ok;
- ParsedBody->
+ ParsedBody->
error = ParsedBody
end
end;
@@ -392,8 +383,8 @@ validateMultiPartRangeRequest(Body, ValidBody, Boundary)->
splitRange(Part)->
- case inets_regexp:split(Part, "\r\n\r\n") of
- {ok,[_, Body]} ->
+ case re:split(Part, "\r\n\r\n", [{return, list}]) of
+ [_, Body] ->
string:substr(Body, 1, length(Body) - 2);
_ ->
[]
@@ -413,13 +404,13 @@ getRangeSize(Head)->
{multiPart, BoundaryString}->
{multiPart, BoundaryString};
_X1 ->
- case inets_regexp:match(Head, ?CONTENT_RANGE "bytes=.*\r\n") of
- {match, Start, Lenght} ->
+ case re:run(Head, ?CONTENT_RANGE "bytes=.*\r\n", [{capture, first}]) 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);
+ RangeInfo = string:substr(Head, Start + 1 + 20,
+ Lenght - (20 +2)),
+ rangeSize(string:strip(RangeInfo));
_X2 ->
error
end
@@ -455,10 +446,10 @@ num(_CharVal, false) ->
true.
controlMimeType(Head)->
- case inets_regexp:match(Head,?CONTENT_TYPE "multipart/byteranges.*\r\n") of
- {match,Start,Length}->
+ case re:run(Head,?CONTENT_TYPE "multipart/byteranges.*\r\n", [{capture, first}]) of
+ {match, [{Start,Length}]}->
FieldNameLen = length(?CONTENT_TYPE "multipart/byteranges"),
- case clearBoundary(string:substr(Head, Start + FieldNameLen,
+ case clearBoundary(string:substr(Head, Start + 1 + FieldNameLen,
Length - (FieldNameLen+2))) of
error ->
error;
@@ -472,10 +463,10 @@ controlMimeType(Head)->
end.
clearBoundary(Boundary)->
- case inets_regexp:match(Boundary, "boundary=.*\$") of
- {match, Start1, Length1}->
+ case re:run(Boundary, "boundary=.*\$", [{capture, first}]) of
+ {match, [{Start1, Length1}]}->
BoundLen = length("boundary="),
- string:substr(Boundary, Start1 + BoundLen, Length1 - BoundLen);
+ string:substr(Boundary, Start1 + 1 + BoundLen, Length1 - BoundLen);
_ ->
error
end.
@@ -490,12 +481,12 @@ end_of_header(HeaderPart) ->
end.
get_body_size(Head) ->
- case inets_regexp:match(Head,?CONTENT_LENGTH ".*\r\n") of
- {match, Start, Length} ->
+ case re:run(Head,?CONTENT_LENGTH ".*\r\n", [{capture, first}]) 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))),
+ string:strip(string:substr(Head, Start +1 + 15, Length-17))),
S;
_->
0
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index 1efa78a63e..3194b5ad3d 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -1,2229 +1,1865 @@
%%
%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013-2015. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% http://www.apache.org/licenses/LICENSE-2.0
%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
%% %CopyrightEnd%
%%
%%
+%%
+%% ct:run("../inets_test", httpd_SUITE).
+%%
+
-module(httpd_SUITE).
--include_lib("test_server/include/test_server.hrl").
--include("test_server_line.hrl").
+-include_lib("kernel/include/file.hrl").
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
-include("inets_test_lib.hrl").
--include_lib("kernel/include/file.hrl").
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
-%% Test server specific exports
--export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
--export([init_per_testcase/2, end_per_testcase/2,
- init_per_suite/1, end_per_suite/1]).
-
-%% Core Server tests
--export([
- ip_mod_alias/1,
- ip_mod_actions/1,
- ip_mod_security/1,
- ip_mod_auth/1,
- ip_mod_auth_api/1,
- ip_mod_auth_mnesia_api/1,
- ip_mod_htaccess/1,
- ip_mod_cgi/1,
- ip_mod_esi/1,
- ip_mod_get/1,
- ip_mod_head/1,
- ip_mod_all/1,
- ip_load_light/1,
- ip_load_medium/1,
- ip_load_heavy/1,
- ip_dos_hostname/1,
- ip_time_test/1,
- ip_block_disturbing_idle/1,
- ip_block_non_disturbing_idle/1,
- ip_block_503/1,
- ip_block_disturbing_active/1,
- ip_block_non_disturbing_active/1,
- ip_block_disturbing_active_timeout_not_released/1,
- ip_block_disturbing_active_timeout_released/1,
- ip_block_non_disturbing_active_timeout_not_released/1,
- ip_block_non_disturbing_active_timeout_released/1,
- ip_block_disturbing_blocker_dies/1,
- ip_block_non_disturbing_blocker_dies/1,
- ip_restart_no_block/1,
- ip_restart_disturbing_block/1,
- ip_restart_non_disturbing_block/1
- ]).
-
--export([
- essl_mod_alias/1,
- essl_mod_actions/1,
- essl_mod_security/1,
- essl_mod_auth/1,
- essl_mod_auth_api/1,
- essl_mod_auth_mnesia_api/1,
- essl_mod_htaccess/1,
- essl_mod_cgi/1,
- essl_mod_esi/1,
- essl_mod_get/1,
- essl_mod_head/1,
- essl_mod_all/1,
- essl_load_light/1,
- essl_load_medium/1,
- essl_load_heavy/1,
- essl_dos_hostname/1,
- essl_time_test/1,
- essl_restart_no_block/1,
- essl_restart_disturbing_block/1,
- essl_restart_non_disturbing_block/1,
- essl_block_disturbing_idle/1,
- essl_block_non_disturbing_idle/1,
- essl_block_503/1,
- essl_block_disturbing_active/1,
- essl_block_non_disturbing_active/1,
- essl_block_disturbing_active_timeout_not_released/1,
- essl_block_disturbing_active_timeout_released/1,
- essl_block_non_disturbing_active_timeout_not_released/1,
- essl_block_non_disturbing_active_timeout_released/1,
- essl_block_disturbing_blocker_dies/1,
- essl_block_non_disturbing_blocker_dies/1
- ]).
-
-%%% HTTP 1.1 tests
--export([ip_host/1, ip_chunked/1, ip_expect/1, ip_range/1,
- ip_if_test/1, ip_http_trace/1, ip_http1_1_head/1,
- ip_mod_cgi_chunked_encoding_test/1]).
-
-%%% HTTP 1.0 tests
--export([ip_head_1_0/1, ip_get_1_0/1, ip_post_1_0/1]).
-
-%%% HTTP 0.9 tests
--export([ip_get_0_9/1]).
-
-%%% Ticket tests
--export([ticket_5775/1,ticket_5865/1,ticket_5913/1,ticket_6003/1,
- ticket_7304/1]).
-
-%%% IPv6 tests
--export([ipv6_hostname_ipcomm/0, ipv6_hostname_ipcomm/1,
- ipv6_address_ipcomm/0, ipv6_address_ipcomm/1,
- ipv6_hostname_essl/0, ipv6_hostname_essl/1,
- ipv6_address_essl/0, ipv6_address_essl/1]).
-
-%% Help functions
--export([cleanup_mnesia/0, setup_mnesia/0, setup_mnesia/1]).
-
--define(IP_PORT, 8898).
--define(SSL_PORT, 8899).
+-record(httpd_user, {user_name, password, user_data}).
+-record(httpd_group, {group_name, userlist}).
-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}).
-
+-define(URL_START, "http://").
%%--------------------------------------------------------------------
-%% all(Arg) -> [Doc] | [Case] | {skip, Comment}
-%% Arg - doc | suite
-%% Doc - string()
-%% Case - atom()
-%% Name of a test case function.
-%% Comment - string()
-%% Description: Returns documentation/test cases in this test suite
-%% or a skip tuple if the platform is not supported.
+%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds, 120}}
+ ].
-all() ->
+all() ->
[
- {group, ip},
- {group, ssl},
- {group, http_1_1_ip},
- {group, http_1_0_ip},
- {group, http_0_9_ip},
- {group, ipv6},
- {group, tickets}
+ {group, http_basic},
+ {group, https_basic},
+ {group, http_limit},
+ {group, https_limit},
+ {group, http_custom},
+ {group, https_custom},
+ {group, http_basic_auth},
+ {group, https_basic_auth},
+ {group, http_auth_api},
+ {group, https_auth_api},
+ {group, http_auth_api_dets},
+ {group, https_auth_api_dets},
+ {group, http_auth_api_mnesia},
+ {group, https_auth_api_mnesia},
+ {group, http_htaccess},
+ {group, https_htaccess},
+ {group, http_security},
+ {group, https_security},
+ {group, http_reload},
+ {group, https_reload},
+ {group, http_mime_types},
+ mime_types_format
].
-groups() ->
+groups() ->
[
- {ip, [],
- [ip_mod_alias, ip_mod_actions, ip_mod_security,
- ip_mod_auth, ip_mod_auth_api, ip_mod_auth_mnesia_api,
- ip_mod_htaccess, ip_mod_cgi, ip_mod_esi, ip_mod_get,
- ip_mod_head, ip_mod_all, ip_load_light, ip_load_medium,
- ip_load_heavy, ip_dos_hostname, ip_time_test,
- ip_restart_no_block, ip_restart_disturbing_block,
- ip_restart_non_disturbing_block,
- ip_block_disturbing_idle, ip_block_non_disturbing_idle,
- ip_block_503, ip_block_disturbing_active,
- ip_block_non_disturbing_active,
- ip_block_disturbing_active_timeout_not_released,
- ip_block_disturbing_active_timeout_released,
- ip_block_non_disturbing_active_timeout_not_released,
- ip_block_non_disturbing_active_timeout_released,
- ip_block_disturbing_blocker_dies,
- ip_block_non_disturbing_blocker_dies]},
- {ssl, [], [{group, essl}]},
- {essl, [],
- [essl_mod_alias, essl_mod_actions, essl_mod_security,
- essl_mod_auth, essl_mod_auth_api,
- essl_mod_auth_mnesia_api, essl_mod_htaccess,
- essl_mod_cgi, essl_mod_esi, essl_mod_get, essl_mod_head,
- essl_mod_all, essl_load_light, essl_load_medium,
- essl_load_heavy, essl_dos_hostname, essl_time_test,
- essl_restart_no_block, essl_restart_disturbing_block,
- essl_restart_non_disturbing_block,
- essl_block_disturbing_idle,
- essl_block_non_disturbing_idle, essl_block_503,
- essl_block_disturbing_active,
- essl_block_non_disturbing_active,
- essl_block_disturbing_active_timeout_not_released,
- essl_block_disturbing_active_timeout_released,
- essl_block_non_disturbing_active_timeout_not_released,
- essl_block_non_disturbing_active_timeout_released,
- essl_block_disturbing_blocker_dies,
- essl_block_non_disturbing_blocker_dies]},
- {http_1_1_ip, [],
- [ip_host, ip_chunked, ip_expect, ip_range, ip_if_test,
- ip_http_trace, ip_http1_1_head,
- ip_mod_cgi_chunked_encoding_test]},
- {http_1_0_ip, [],
- [ip_head_1_0, ip_get_1_0, ip_post_1_0]},
- {http_0_9_ip, [], [ip_get_0_9]},
- {ipv6, [], [ipv6_hostname_ipcomm, ipv6_address_ipcomm,
- ipv6_hostname_essl, ipv6_address_essl]},
- {tickets, [],
- [ticket_5775, ticket_5865, ticket_5913, ticket_6003,
- ticket_7304]}].
-
-
-init_per_group(ipv6 = _GroupName, Config) ->
- case inets_test_lib:has_ipv6_support() of
- {ok, _} ->
- Config;
- _ ->
- {skip, "Host does not support IPv6"}
- end;
-init_per_group(_GroupName, Config) ->
- Config.
+ {http_basic, [], basic_groups()},
+ {https_basic, [], basic_groups()},
+ {http_limit, [], [{group, limit}]},
+ {https_limit, [], [{group, limit}]},
+ {http_custom, [], [{group, custom}]},
+ {https_custom, [], [{group, custom}]},
+ {http_basic_auth, [], [{group, basic_auth}]},
+ {https_basic_auth, [], [{group, basic_auth}]},
+ {http_auth_api, [], [{group, auth_api}]},
+ {https_auth_api, [], [{group, auth_api}]},
+ {http_auth_api_dets, [], [{group, auth_api_dets}]},
+ {https_auth_api_dets, [], [{group, auth_api_dets}]},
+ {http_auth_api_mnesia, [], [{group, auth_api_mnesia}]},
+ {https_auth_api_mnesia, [], [{group, auth_api_mnesia}]},
+ {http_htaccess, [], [{group, htaccess}]},
+ {https_htaccess, [], [{group, htaccess}]},
+ {http_security, [], [{group, security}]},
+ {https_security, [], [{group, security}]},
+ {http_reload, [], [{group, reload}]},
+ {https_reload, [], [{group, reload}]},
+ {http_mime_types, [], [alias_1_1, alias_1_0, alias_0_9]},
+ {limit, [], [max_clients_1_1, max_clients_1_0, max_clients_0_9]},
+ {custom, [], [customize, add_default]},
+ {reload, [], [non_disturbing_reconfiger_dies,
+ disturbing_reconfiger_dies,
+ non_disturbing_1_1,
+ non_disturbing_1_0,
+ non_disturbing_0_9,
+ disturbing_1_1,
+ disturbing_1_0,
+ disturbing_0_9
+ ]},
+ {basic_auth, [], [basic_auth_1_1, basic_auth_1_0, basic_auth_0_9]},
+ {auth_api, [], [auth_api_1_1, auth_api_1_0, auth_api_0_9
+ ]},
+ {auth_api_dets, [], [auth_api_1_1, auth_api_1_0, auth_api_0_9
+ ]},
+ {auth_api_mnesia, [], [auth_api_1_1, auth_api_1_0, auth_api_0_9
+ ]},
+ {htaccess, [], [htaccess_1_1, htaccess_1_0, htaccess_0_9]},
+ {security, [], [security_1_1, security_1_0]}, %% Skip 0.9 as causes timing issus in test code
+ {http_1_1, [], [host, chunked, expect, cgi, cgi_chunked_encoding_test,
+ trace, range, if_modified_since, mod_esi_chunk_timeout] ++ http_head() ++ http_get() ++ load()},
+ {http_1_0, [], [host, cgi, trace] ++ http_head() ++ http_get() ++ load()},
+ {http_0_9, [], http_head() ++ http_get() ++ load()}
+ ].
-end_per_group(_GroupName, Config) ->
- Config.
+basic_groups ()->
+ [{group, http_1_1},
+ {group, http_1_0},
+ {group, http_0_9}
+ ].
+http_head() ->
+ [head].
+http_get() ->
+ [alias,
+ get,
+ %%actions, Add configuration so that this test mod_action
+ esi,
+ content_length,
+ bad_hex,
+ missing_CR,
+ max_header,
+ max_content_length,
+ ipv6
+ ].
-%%--------------------------------------------------------------------
-%% 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.
-%%--------------------------------------------------------------------
+load() ->
+ [light, medium
+ %%,heavy
+ ].
+
init_per_suite(Config) ->
- io:format(user, "init_per_suite -> entry with"
- "~n Config: ~p"
- "~n", [Config]),
-
- ?PRINT_SYSTEM_INFO([]),
-
- PrivDir = ?config(priv_dir, Config),
- SuiteTopDir = filename:join(PrivDir, ?MODULE),
- case file:make_dir(SuiteTopDir) of
- ok ->
- ok;
- {error, eexist} ->
- ok;
- Error ->
- throw({error, {failed_creating_suite_top_dir, Error}})
- end,
-
- [{has_ipv6_support, inets_test_lib:has_ipv6_support()},
- {suite_top_dir, SuiteTopDir},
+ PrivDir = proplists:get_value(priv_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
+ inets_test_lib:stop_apps([inets]),
+ ServerRoot = filename:join(PrivDir, "server_root"),
+ inets_test_lib:del_dirs(ServerRoot),
+ DocRoot = filename:join(ServerRoot, "htdocs"),
+ setup_server_dirs(ServerRoot, DocRoot, DataDir),
+ {ok, Hostname0} = inet:gethostname(),
+ Inet =
+ case (catch ct:get_config(ipv6_hosts)) of
+ undefined ->
+ inet;
+ Hosts when is_list(Hosts) ->
+ case lists:member(list_to_atom(Hostname0), Hosts) of
+ true ->
+ inet6;
+ false ->
+ inet
+ end;
+ _ ->
+ inet
+ end,
+ [{server_root, ServerRoot},
+ {doc_root, DocRoot},
+ {ipfamily, Inet},
{node, node()},
- {host, inets_test_lib:hostname()},
+ {host, inets_test_lib:hostname()},
{address, getaddr()} | Config].
+end_per_suite(_Config) ->
+ ok.
%%--------------------------------------------------------------------
-%% Function: end_per_suite(Config) -> _
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after the whole suite
-%%--------------------------------------------------------------------
+init_per_group(Group, Config0) when Group == https_basic;
+ Group == https_limit;
+ Group == https_custom;
+ Group == https_basic_auth;
+ Group == https_auth_api;
+ Group == https_auth_api_dets;
+ Group == https_auth_api_mnesia;
+ Group == https_security;
+ Group == https_reload
+ ->
+ init_ssl(Group, Config0);
+init_per_group(Group, Config0) when Group == http_basic;
+ Group == http_limit;
+ Group == http_custom;
+ Group == http_basic_auth;
+ Group == http_auth_api;
+ Group == http_auth_api_dets;
+ Group == http_auth_api_mnesia;
+ Group == http_security;
+ Group == http_reload;
+ Group == http_mime_types
+ ->
+ ok = start_apps(Group),
+ init_httpd(Group, [{type, ip_comm} | Config0]);
+init_per_group(http_1_1, Config) ->
+ [{http_version, "HTTP/1.1"} | Config];
+init_per_group(http_1_0, Config) ->
+ [{http_version, "HTTP/1.0"} | Config];
+init_per_group(http_0_9, Config) ->
+ case {os:type(), os:version()} of
+ {{win32, _}, {5,1,2600}} ->
+ {skip, "eaddrinuse XP problem"};
+ _ ->
+ [{http_version, "HTTP/0.9"} | Config]
+ end;
+init_per_group(http_htaccess = Group, Config) ->
+ Path = proplists:get_value(doc_root, Config),
+ catch remove_htaccess(Path),
+ create_htaccess_data(Path, proplists:get_value(address, Config)),
+ ok = start_apps(Group),
+ init_httpd(Group, [{type, ip_comm} | Config]);
+init_per_group(https_htaccess = Group, Config) ->
+ Path = proplists:get_value(doc_root, Config),
+ catch remove_htaccess(Path),
+ create_htaccess_data(Path, proplists:get_value(address, Config)),
+ init_ssl(Group, Config);
+init_per_group(auth_api, Config) ->
+ [{auth_prefix, ""} | Config];
+init_per_group(auth_api_dets, Config) ->
+ [{auth_prefix, "dets_"} | Config];
+init_per_group(auth_api_mnesia, Config) ->
+ start_mnesia(proplists:get_value(node, Config)),
+ [{auth_prefix, "mnesia_"} | Config];
+init_per_group(_, Config) ->
+ Config.
-end_per_suite(_Config) ->
- %% SuiteTopDir = ?config(suite_top_dir, Config),
- %% inets_test_lib:del_dirs(SuiteTopDir),
+end_per_group(Group, _Config) when Group == http_basic;
+ Group == http_limit;
+ Group == http_basic_auth;
+ Group == http_auth_api;
+ Group == http_auth_api_dets;
+ Group == http_auth_api_mnesia;
+ Group == http_htaccess;
+ Group == http_security;
+ Group == http_reload;
+ Group == http_mime_types
+ ->
+ inets:stop();
+end_per_group(Group, _Config) when Group == https_basic;
+ Group == https_limit;
+ Group == https_basic_auth;
+ Group == https_auth_api;
+ Group == https_auth_api_dets;
+ Group == https_auth_api_mnesia;
+ Group == https_htaccess;
+ Group == https_security;
+ Group == https_reload
+ ->
+ ssl:stop(),
+ inets:stop();
+
+end_per_group(auth_api_mnesia, _) ->
+ cleanup_mnesia();
+
+end_per_group(_, _) ->
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_testcase(Case, Config) when Case == host; Case == trace ->
+ ct:timetrap({seconds, 20}),
+ Prop = proplists:get_value(tc_group_properties, Config),
+ Name = proplists:get_value(name, Prop),
+ Cb = case Name of
+ http_1_0 ->
+ httpd_1_0;
+ http_1_1 ->
+ httpd_1_1
+ end,
+ [{version_cb, Cb} | proplists:delete(version_cb, Config)];
+
+init_per_testcase(range, Config) ->
+ ct:timetrap({seconds, 20}),
+ DocRoot = proplists:get_value(doc_root, Config),
+ create_range_data(DocRoot),
+ Config;
+
+init_per_testcase(_, Config) ->
+ ct:timetrap({seconds, 20}),
+ Config.
+end_per_testcase(_Case, _Config) ->
+ ok.
-init_per_testcase2(Case, Config) ->
+%%-------------------------------------------------------------------------
+%% Test cases starts here.
+%%-------------------------------------------------------------------------
- %% tsp("init_per_testcase2 -> entry with"
- %% "~n Config: ~p", [Config]),
+head() ->
+ [{doc, "HTTP HEAD request for static page"}].
+
+head(Config) when is_list(Config) ->
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config), proplists:get_value(node, Config),
+ http_request("HEAD /index.html ", Version, Host),
+ [{statuscode, head_status(Version)},
+ {version, Version}]).
+
+get() ->
+ [{doc, "HTTP GET request for static page"}].
+
+get(Config) when is_list(Config) ->
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Type = proplists:get_value(type, Config),
+ ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config),
+ transport_opts(Type, Config),
+ proplists:get_value(node, Config),
+ http_request("GET /index.html ", Version, Host),
+ [{statuscode, 200},
+ {header, "Content-Type", "text/html"},
+ {header, "Date"},
+ {header, "Server"},
+ {version, Version}]).
+
+basic_auth_1_1(Config) when is_list(Config) ->
+ basic_auth([{http_version, "HTTP/1.1"} | Config]).
+
+basic_auth_1_0(Config) when is_list(Config) ->
+ basic_auth([{http_version, "HTTP/1.0"} | Config]).
+
+basic_auth_0_9(Config) when is_list(Config) ->
+ basic_auth([{http_version, "HTTP/0.9"} | Config]).
+
+basic_auth() ->
+ [{doc, "Test Basic authentication with WWW-Authenticate header"}].
+
+basic_auth(Config) ->
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ basic_auth_requiered(Config),
+ %% Authentication OK! ["one:OnePassword" user first in user list]
+ ok = auth_status(auth_request("/open/dummy.html", "one", "onePassword", Version, Host), Config,
+ [{statuscode, 200}]),
+ %% Authentication OK and a directory listing is supplied!
+ %% ["Aladdin:open sesame" user second in user list]
+ ok = auth_status(auth_request("/open/", "Aladdin", "AladdinPassword", Version, Host), Config,
+ [{statuscode, 200}]),
+ %% User correct but wrong password! ["one:one" user first in user list]
+ ok = auth_status(auth_request("/open/dummy.html", "one", "one", Version, Host), Config,
+ [{statuscode, 401},
+ {header, "WWW-Authenticate"}]),
+ %% Make sure Authenticate header is received even the second time
+ %% we try a incorrect password! Otherwise a browser client will hang!
+ ok = auth_status(auth_request("/open/dummy.html", "one", "one", Version, Host), Config,
+ [{statuscode, 401},
+ {header, "WWW-Authenticate"}]),
+ %% Neither user or password correct! ["dummy:dummy"]
+ ok = auth_status(auth_request("/open/dummy.html", "dummy", "dummy", Version, Host), Config,
+ [{statuscode, 401}]),
+ %% Nested secret/top_secret OK! ["Aladdin:open sesame"]
+ ok = http_status(auth_request("/secret/top_secret/", "Aladdin", "AladdinPassword", Version, Host),
+ Config, [{statuscode, 200}]),
+ %% Authentication still required!
+ basic_auth_requiered(Config).
+
+auth_api_1_1(Config) when is_list(Config) ->
+ auth_api([{http_version, "HTTP/1.1"} | Config]).
+
+auth_api_1_0(Config) when is_list(Config) ->
+ auth_api([{http_version, "HTTP/1.0"} | Config]).
+
+auth_api_0_9(Config) when is_list(Config) ->
+ auth_api([{http_version, "HTTP/0.9"} | Config]).
+
+auth_api() ->
+ [{doc, "Test mod_auth API"}].
+
+auth_api(Config) when is_list(Config) ->
+ Prefix = proplists:get_value(auth_prefix, Config),
+ do_auth_api(Prefix, Config).
+
+do_auth_api(AuthPrefix, Config) ->
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Port = proplists:get_value(port, Config),
+ Node = proplists:get_value(node, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
+ ok = http_status("GET / ", Config,
+ [{statuscode, 200}]),
+ ok = auth_status(auth_request("/", "one", "WrongPassword", Version, Host), Config,
+ [{statuscode, 200}]),
+
+ %% Make sure Authenticate header is received even the second time
+ %% we try a incorrect password! Otherwise a browser client will hang!
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "dummy", "WrongPassword", Version, Host), Config,
+ [{statuscode, 401},
+ {header, "WWW-Authenticate"}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/", "dummy", "WrongPassword",
+ Version, Host), Config, [{statuscode, 401},
+ {header, "WWW-Authenticate"}]),
- IpNormal = integer_to_list(?IP_PORT) ++ ".conf",
- IpHtaccess = integer_to_list(?IP_PORT) ++ "htaccess.conf",
- SslNormal = integer_to_list(?SSL_PORT) ++ ".conf",
- SslHtaccess = integer_to_list(?SSL_PORT) ++ "htaccess.conf",
-
- DataDir = ?config(data_dir, Config),
- SuiteTopDir = ?config(suite_top_dir, Config),
-
- %% tsp("init_per_testcase2 -> "
- %% "~n SuiteDir: ~p"
- %% "~n DataDir: ~p", [SuiteTopDir, DataDir]),
+ %% 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, AuthPrefix,
+ "open", "NoPassword", "DummyPassword"),
+ {error,bad_password} =
+ add_user(Node, ServerRoot, Port, AuthPrefix, "open", "one",
+ "onePassword", []),
+ ok = update_password(Node, ServerRoot, Host, Port, AuthPrefix, "open",
+ "DummyPassword", "NoPassword"),
+
+ %% Test /*open, require user one Aladdin
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "open"),
- TcTopDir = filename:join(SuiteTopDir, Case),
- ?line ok = file:make_dir(TcTopDir),
-
- %% tsp("init_per_testcase2 -> "
- %% "~n TcTopDir: ~p", [TcTopDir]),
-
- DataSrc = filename:join([DataDir, "server_root"]),
- ServerRoot = filename:join([TcTopDir, "server_root"]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "one", "onePassword", Version, Host), Config,
+ [{statuscode, 401}]),
- %% tsp("init_per_testcase2 -> "
- %% "~n DataSrc: ~p"
- %% "~n ServerRoot: ~p", [DataSrc, ServerRoot]),
-
- ok = file:make_dir(ServerRoot),
- ok = file:make_dir(filename:join([TcTopDir, "logs"])),
-
- NewConfig = [{tc_top_dir, TcTopDir}, {server_root, ServerRoot} | Config],
-
- %% tsp("init_per_testcase2 -> copy DataSrc to ServerRoot"),
-
- inets_test_lib:copy_dirs(DataSrc, ServerRoot),
-
- %% tsp("init_per_testcase2 -> fix cgi"),
- EnvCGI = filename:join([ServerRoot, "cgi-bin", "printenv.sh"]),
- {ok, FileInfo} = file:read_file_info(EnvCGI),
- ok = file:write_file_info(EnvCGI,
- FileInfo#file_info{mode = 8#00755}),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "two", "twoPassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "Aladdin", "onePassword", Version, Host),
+ Config, [{statuscode, 401}]),
- 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}),
+ true = add_user(Node, ServerRoot, Port, AuthPrefix, "open", "one",
+ "onePassword", []),
+ true = add_user(Node, ServerRoot, Port, AuthPrefix, "open", "two",
+ "twoPassword", []),
+ true = add_user(Node, ServerRoot, Port, AuthPrefix, "open", "Aladdin",
+ "AladdinPassword", []),
+ {ok, [_|_]} = list_users(Node, ServerRoot, Host, Port,
+ AuthPrefix, "open"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "one", "WrongPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "one", "onePassword", Version, Host),
+ Config, [{statuscode, 200}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "two", "twoPassword", Version, Host),
+ Config,[{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "Aladdin", "WrongPassword", Version, Host),
+ Config,[{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "open/",
+ "Aladdin", "AladdinPassword", Version, Host),
+ Config, [{statuscode, 200}]),
- %% To be used by IP test cases
- %% tsp("init_per_testcase2 -> ip testcase setups"),
- create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig],
- normal_access, IpNormal),
- create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig],
- mod_htaccess, IpHtaccess),
-
- %% To be used by SSL test cases
- %% tsp("init_per_testcase2 -> ssl testcase setups"),
- SocketType =
- case atom_to_list(Case) of
- [X, $s, $s, $l | _] ->
- case X of
- $p -> ssl;
- $e -> essl
- end;
- _ ->
- ssl
- end,
-
- create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig],
- normal_access, SslNormal),
- create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig],
- mod_htaccess, SslHtaccess),
-
- %% To be used by IPv6 test cases. Case-clause is so that
- %% you can do ts:run(inets, httpd_SUITE, <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.
-
- tsp("init_per_testcase2 -> maybe generate IPv6 config file(s)"),
- NewConfig2 =
- case atom_to_list(Case) of
- "ipv6_" ++ _ ->
- case (catch inets_test_lib:has_ipv6_support(NewConfig)) of
- {ok, IPv6Address0} ->
- {ok, Hostname} = inet:gethostname(),
- IPv6Address = http_transport:ipv6_name(IPv6Address0),
- create_ipv6_config([{port, ?IP_PORT},
- {sock_type, ip_comm},
- {ipv6_host, IPv6Address} |
- NewConfig],
- "ipv6_hostname_ipcomm.conf",
- Hostname),
- create_ipv6_config([{port, ?IP_PORT},
- {sock_type, ip_comm},
- {ipv6_host, IPv6Address} |
- NewConfig],
- "ipv6_address_ipcomm.conf",
- IPv6Address),
- create_ipv6_config([{port, ?SSL_PORT},
- {sock_type, essl},
- {ipv6_host, IPv6Address} |
- NewConfig],
- "ipv6_hostname_essl.conf",
- Hostname),
- create_ipv6_config([{port, ?SSL_PORT},
- {sock_type, essl},
- {ipv6_host, IPv6Address} |
- NewConfig],
- "ipv6_address_essl.conf",
- IPv6Address),
- [{ipv6_host, IPv6Address} | NewConfig];
- _ ->
- NewConfig
- end;
-
- _ ->
- NewConfig
- end,
-
- %% tsp("init_per_testcase2 -> done when"
- %% "~n NewConfig2: ~p", [NewConfig2]),
-
- NewConfig2.
-
-
-init_per_testcase3(Case, Config) ->
- tsp("init_per_testcase3(~w) -> entry with"
- "~n Config: ~p", [Case, Config]),
-
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "open"),
+ {ok, []} = list_users(Node, ServerRoot, Host, Port,
+ AuthPrefix, "open"),
-%% %% Create a new fresh node to be used by the server in this test-case
+ %% Phase 2
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "secret"),
+ {ok, []} = list_users(Node, ServerRoot, Host, Port, AuthPrefix,
+ "secret"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "one", "onePassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "two", "twoPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "three", "threePassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ add_user(Node, ServerRoot, Port, AuthPrefix, "secret", "one",
+ "onePassword",
+ []),
+ add_user(Node, ServerRoot, Port, AuthPrefix, "secret",
+ "two", "twoPassword", []),
+ add_user(Node, ServerRoot, Port, AuthPrefix, "secret", "Aladdin",
+ "AladdinPassword",[]),
+ add_group_member(Node, ServerRoot, Port, AuthPrefix, "secret",
+ "one", "group1"),
+ add_group_member(Node, ServerRoot, Port, AuthPrefix, "secret",
+ "two", "group1"),
+ add_group_member(Node, ServerRoot, Port, AuthPrefix,
+ "secret", "Aladdin", "group2"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "one", "onePassword", Version, Host),
+ Config, [{statuscode, 200}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "two", "twoPassword", Version, Host),
+ Config,[{statuscode, 200}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "Aladdin", "AladdinPassword", Version, Host),
+ Config, [{statuscode, 200}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++ "secret/",
+ "three", "threePassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "secret"),
+ {ok, []} = list_users(Node, ServerRoot, Host, Port,
+ AuthPrefix, "secret"),
+ remove_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret"),
-%% NodeName = list_to_atom(atom_to_list(Case) ++ "_httpd"),
-%% Node = inets_test_lib:start_node(NodeName),
+ {ok, []} = list_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret"),
- %% Clean up (we do not want this clean up in end_per_testcase
- %% if init_per_testcase crashes for some testcase it will
- %% have contaminated the environment and there will be no clean up.)
- %% This init can take a few different paths so that one crashes
- %% does not mean that all invocations will.
-
- application:unset_env(inets, services),
- application:stop(inets),
- application:stop(ssl),
- cleanup_mnesia(),
-
- %% Start initialization
- tsp("init_per_testcase3(~w) -> start init", [Case]),
-
- Dog = test_server:timetrap(inets_test_lib:minutes(10)),
- NewConfig = lists:keydelete(watchdog, 1, Config),
- TcTopDir = ?config(tc_top_dir, Config),
-
- CaseRest =
- case atom_to_list(Case) of
- "ip_mod_htaccess" ->
- inets_test_lib:start_http_server(
- filename:join(TcTopDir,
- integer_to_list(?IP_PORT) ++
- "htaccess.conf")),
- "mod_htaccess";
- "ip_" ++ Rest ->
- inets_test_lib:start_http_server(
- filename:join(TcTopDir,
- integer_to_list(?IP_PORT) ++ ".conf")),
- Rest;
- "ticket_5913" ->
- HttpdOptions =
- [{file,
- filename:join(TcTopDir,
- integer_to_list(?IP_PORT) ++ ".conf")},
- {accept_timeout,30000},
- {debug,[{exported_functions,
- [httpd_manager,httpd_request_handler]}]}],
- inets_test_lib:start_http_server(HttpdOptions);
- "ticket_"++Rest ->
- %% OTP-5913 use the new syntax of inets.config
- inets_test_lib:start_http_server([{file,
- filename:join(TcTopDir,
- integer_to_list(?IP_PORT) ++ ".conf")}]),
- Rest;
-
- [X, $s, $s, $l, $_, $m, $o, $d, $_, $h, $t, $a, $c, $c, $e, $s, $s] ->
- ?ENSURE_STARTED([crypto, public_key, ssl]),
- SslTag =
- case X of
- $p -> ssl; % Plain
- $e -> essl % Erlang based ssl
- end,
- case inets_test_lib:start_http_server_ssl(
- filename:join(TcTopDir,
- integer_to_list(?SSL_PORT) ++
- "htaccess.conf"), SslTag) of
- ok ->
- "mod_htaccess";
- Other ->
- error_logger:info_msg("Other: ~p~n", [Other]),
- {skip, "SSL does not seem to be supported"}
- end;
- [X, $s, $s, $l, $_ | Rest] ->
- ?ENSURE_STARTED([crypto, public_key, ssl]),
- SslTag =
- case X of
- $p -> ssl;
- $e -> essl
- end,
- case inets_test_lib:start_http_server_ssl(
- filename:join(TcTopDir,
- integer_to_list(?SSL_PORT) ++
- ".conf"), SslTag) of
- ok ->
- Rest;
- Other ->
- error_logger:info_msg("Other: ~p~n", [Other]),
- {skip, "SSL does not seem to be supported"}
- end;
- "ipv6_" ++ _ = TestCaseStr ->
- case inets_test_lib:has_ipv6_support() of
- {ok, _} ->
- inets_test_lib:start_http_server(
- filename:join(TcTopDir,
- TestCaseStr ++ ".conf"));
-
- _ ->
- {skip, "Host does not support IPv6"}
- end
- end,
-
- InitRes =
- case CaseRest of
- {skip, _} = Skip ->
- Skip;
- "mod_auth_" ++ _ ->
- start_mnesia(?config(node, Config)),
- [{watchdog, Dog} | NewConfig];
- "mod_htaccess" ->
- ServerRoot = ?config(server_root, Config),
- Path = filename:join([ServerRoot, "htdocs"]),
- catch remove_htaccess(Path),
- create_htaccess_data(Path, ?config(address, Config)),
- [{watchdog, Dog} | NewConfig];
- "range" ->
- ServerRoot = ?config(server_root, Config),
- Path = filename:join([ServerRoot, "htdocs"]),
- create_range_data(Path),
- [{watchdog, Dog} | NewConfig];
- _ ->
- [{watchdog, Dog} | NewConfig]
- end,
+ %% Phase 3
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
+ remove_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
- tsp("init_per_testcase3(~w) -> done when"
- "~n InitRes: ~p", [Case, InitRes]),
-
- InitRes.
-
-
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(Case, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- end_per_testcase2(Case, lists:keydelete(watchdog, 1, Config)),
- ok.
-
-end_per_testcase2(Case, Config) ->
- tsp("end_per_testcase2(~w) -> entry with"
- "~n Config: ~p", [Case, Config]),
- application:unset_env(inets, services),
- application:stop(inets),
- application:stop(ssl),
- application:stop(crypto), % used by the new ssl (essl test cases)
- cleanup_mnesia(),
- tsp("end_per_testcase2(~w) -> done", [Case]),
- ok.
-
-
-%%-------------------------------------------------------------------------
-%% Test cases starts here.
-%%-------------------------------------------------------------------------
-
-%%-------------------------------------------------------------------------
-ip_mod_alias(doc) ->
- ["Module test: mod_alias"];
-ip_mod_alias(suite) ->
- [];
-ip_mod_alias(Config) when is_list(Config) ->
- httpd_mod:alias(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-ip_mod_actions(doc) ->
- ["Module test: mod_actions"];
-ip_mod_actions(suite) ->
- [];
-ip_mod_actions(Config) when is_list(Config) ->
- httpd_mod:actions(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-ip_mod_security(doc) ->
- ["Module test: mod_security"];
-ip_mod_security(suite) ->
- [];
-ip_mod_security(Config) when is_list(Config) ->
- ServerRoot = ?config(server_root, Config),
- httpd_mod:security(ServerRoot, ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-ip_mod_auth(doc) ->
- ["Module test: mod_auth"];
-ip_mod_auth(suite) ->
- [];
-ip_mod_auth(Config) when is_list(Config) ->
- httpd_mod:auth(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-ip_mod_auth_api(doc) ->
- ["Module test: mod_auth_api"];
-ip_mod_auth_api(suite) ->
- [];
-ip_mod_auth_api(Config) when is_list(Config) ->
- ServerRoot = ?config(server_root, Config),
- Host = ?config(host, Config),
- Node = ?config(node, Config),
- httpd_mod:auth_api(ServerRoot, "", ip_comm, ?IP_PORT, Host, Node),
- httpd_mod:auth_api(ServerRoot, "dets_", ip_comm, ?IP_PORT, Host, Node),
- httpd_mod:auth_api(ServerRoot, "mnesia_", ip_comm, ?IP_PORT, Host, Node),
- ok.
-%%-------------------------------------------------------------------------
-ip_mod_auth_mnesia_api(doc) ->
- ["Module test: mod_auth_mnesia_api"];
-ip_mod_auth_mnesia_api(suite) ->
- [];
-ip_mod_auth_mnesia_api(Config) when is_list(Config) ->
- httpd_mod:auth_mnesia_api(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_mod_htaccess(doc) ->
- ["Module test: mod_htaccess"];
-ip_mod_htaccess(suite) ->
- [];
-ip_mod_htaccess(Config) when is_list(Config) ->
- httpd_mod:htaccess(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_mod_cgi(doc) ->
- ["Module test: mod_cgi"];
-ip_mod_cgi(suite) ->
- [];
-ip_mod_cgi(Config) when is_list(Config) ->
- httpd_mod:cgi(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_mod_esi(doc) ->
- ["Module test: mod_esi"];
-ip_mod_esi(suite) ->
- [];
-ip_mod_esi(Config) when is_list(Config) ->
- httpd_mod:esi(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-ip_mod_get(doc) ->
- ["Module test: mod_get"];
-ip_mod_get(suite) ->
- [];
-ip_mod_get(Config) when is_list(Config) ->
- httpd_mod:get(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
- ok.
-
-%%-------------------------------------------------------------------------
-ip_mod_head(doc) ->
- ["Module test: mod_head"];
-ip_mod_head(suite) ->
- [];
-ip_mod_head(Config) when is_list(Config) ->
- httpd_mod:head(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_mod_all(doc) ->
- ["All modules test"];
-ip_mod_all(suite) ->
- [];
-ip_mod_all(Config) when is_list(Config) ->
- httpd_mod:all(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_load_light(doc) ->
- ["Test light load"];
-ip_load_light(suite) ->
- [];
-ip_load_light(Config) when is_list(Config) ->
- httpd_load:load_test(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config),
- get_nof_clients(ip_comm, light)),
- ok.
-%%-------------------------------------------------------------------------
-ip_load_medium(doc) ->
- ["Test medium load"];
-ip_load_medium(suite) ->
- [];
-ip_load_medium(Config) when is_list(Config) ->
- httpd_load:load_test(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config),
- get_nof_clients(ip_comm, medium)),
- ok.
-%%-------------------------------------------------------------------------
-ip_load_heavy(doc) ->
- ["Test heavy load"];
-ip_load_heavy(suite) ->
- [];
-ip_load_heavy(Config) when is_list(Config) ->
- httpd_load:load_test(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config),
- get_nof_clients(ip_comm, heavy)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-ip_dos_hostname(doc) ->
- ["Denial Of Service (DOS) attack test case"];
-ip_dos_hostname(suite) ->
- [];
-ip_dos_hostname(Config) when is_list(Config) ->
- dos_hostname(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config), ?MAX_HEADER_SIZE),
- ok.
-
-
-%%-------------------------------------------------------------------------
-ip_time_test(doc) ->
- [""];
-ip_time_test(suite) ->
- [];
-ip_time_test(Config) when is_list(Config) ->
- %% <CONDITIONAL-SKIP>
- Skippable = [win32],
- Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
- ?NON_PC_TC_MAYBE_SKIP(Config, Condition),
- %% </CONDITIONAL-SKIP>
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/",
+ "three", "threePassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/", "two", "twoPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ add_user(Node, ServerRoot, Port, AuthPrefix,
+ "secret/top_secret","three",
+ "threePassword",[]),
+ add_user(Node, ServerRoot, Port, AuthPrefix, "secret/top_secret",
+ "two","twoPassword", []),
+ add_group_member(Node, ServerRoot, Port, AuthPrefix, "secret/top_secret", "three", "group3"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/", "three", "threePassword",
+ Version, Host),
+ Config, [{statuscode, 200}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/", "two", "twoPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ add_group_member(Node, ServerRoot, Port, AuthPrefix, "secret/top_secret", "two", "group3"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/",
+ "two", "twoPassword", Version, Host),
+ Config, [{statuscode, 200}]),
+ remove_users(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
+ {ok, []} = list_users(Node, ServerRoot, Host, Port,
+ AuthPrefix, "secret/top_secret"),
+ remove_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
+ {ok, []} = list_groups(Node, ServerRoot, Host, Port, AuthPrefix, "secret/top_secret"),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/", "two", "twoPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+ ok = auth_status(auth_request("/" ++ AuthPrefix ++
+ "secret/top_secret/","three", "threePassword", Version, Host),
+ Config, [{statuscde, 401}]).
+%%-------------------------------------------------------------------------
+ipv6() ->
+ [{require, ipv6_hosts},
+ {doc,"Test ipv6."}].
+ipv6(Config) when is_list(Config) ->
+ {ok, Hostname0} = inet:gethostname(),
+ case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of
+ true ->
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ URI = http_request("GET / ", Version, Host),
+ httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config), [inet6],
+ proplists:get_value(code, Config),
+ URI,
+ [{statuscode, 200}, {version, Version}]);
+ false ->
+ {skip, "Host does not support IPv6"}
+ end.
+
+%%-------------------------------------------------------------------------
+htaccess_1_1(Config) when is_list(Config) ->
+ htaccess([{http_version, "HTTP/1.1"} | Config]).
+
+htaccess_1_0(Config) when is_list(Config) ->
+ htaccess([{http_version, "HTTP/1.0"} | Config]).
+
+htaccess_0_9(Config) when is_list(Config) ->
+ htaccess([{http_version, "HTTP/0.9"} | Config]).
+
+htaccess() ->
+ [{doc, "Test mod_auth API"}].
+
+htaccess(Config) when is_list(Config) ->
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Type = proplists:get_value(type, Config),
+ Port = proplists:get_value(port, Config),
+ Node = proplists:get_value(node, Config),
+ %% 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,
+ http_request("GET /ht/open/ ", Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("GET /ht/secret/ ", Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("GET /ht/secret/top_secret/ ",
+ Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]),
+
+ %% Make sure Authenticate header is received even the second time
+ %% we try a incorrect password! Otherwise a browser client will hang!
+ ok = auth_status(auth_request("/ht/open/",
+ "dummy", "WrongPassword", Version, Host), Config,
+ [{statuscode, 401},
+ {header, "WWW-Authenticate"}]),
+ ok = auth_status(auth_request("/ht/open/",
+ "dummy", "WrongPassword", Version, Host), Config,
+ [{statuscode, 401},
+ {header, "WWW-Authenticate"}]),
- 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.
+ %% Control that not just the first user in the list is valid
+ %% Control the first user
+ %% Authennticating ["one:OnePassword" user first in user list]
+ ok = auth_status(auth_request("/ht/open/dummy.html", "one", "OnePassword",
+ Version, Host), Config,
+ [{statuscode, 200}]),
+
+ %% Control the second user
+ %% Authentication OK and a directory listing is supplied!
+ %% ["Aladdin:open sesame" user second in user list]
+ ok = auth_status(auth_request("/ht/open/","Aladdin",
+ "AladdinPassword", Version, Host), Config,
+ [{statuscode, 200}]),
+
+ %% Contro that bad passwords and userids get a good denial
+ %% User correct but wrong password! ["one:one" user first in user list]
+ ok = auth_status(auth_request("/ht/open/", "one", "one", Version, Host), Config,
+ [{statuscode, 401}]),
+ %% Neither user or password correct! ["dummy:dummy"]
+ ok = auth_status(auth_request("/ht/open/", "dummy", "dummy", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ %% Control that authetication still works, even if its a member in a group
+ %% Authentication OK! ["two:TwoPassword" user in first group]
+ ok = auth_status(auth_request("/ht/secret/dummy.html", "two",
+ "TwoPassword", Version, Host), Config,
+ [{statuscode, 200}]),
+
+ %% Authentication OK and a directory listing is supplied!
+ %% ["three:ThreePassword" user in second group]
+ ok = auth_status(auth_request("/ht/secret/", "three",
+ "ThreePassword", Version, Host), Config,
+ [{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]
+ ok = auth_status(auth_request("/ht/secret/", "two", "two", Version, Host), Config,
+ [{statuscode, 401}]),
+ %% Neither user or password correct! ["dummy:dummy"]
+ ok = auth_status(auth_request("/ht/secret/", "dummy", "dummy", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ %% control that we deny the users that are in subnet above the allowed
+ ok = auth_status(auth_request("/ht/blocknet/dummy.html", "four",
+ "FourPassword", Version, Host), Config,
+ [{statuscode, 403}]),
+ %% Control that we only applies the rules to the right methods
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("HEAD /ht/blocknet/dummy.html ", Version, Host),
+ [{statuscode, head_status(Version)},
+ {version, Version}]),
+
+ %% Control that the rerquire directive can be overrideen
+ ok = auth_status(auth_request("/ht/secret/top_secret/ ", "Aladdin", "AladdinPassword",
+ Version, Host), Config,
+ [{statuscode, 401}]),
+
+ %% Authentication still required!
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("GET /ht/open/ ", Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("GET /ht/secret/ ", Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ http_request("GET /ht/secret/top_secret/ ", Version, Host),
+ [{statuscode, 401},
+ {version, Version},
+ {header, "WWW-Authenticate"}]).
+
+%%-------------------------------------------------------------------------
+host() ->
+ [{doc, "Test host header"}].
+
+host(Config) when is_list(Config) ->
+ Cb = proplists:get_value(version_cb, Config),
+ Cb:host(proplists:get_value(type, Config), proplists:get_value(port, Config),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)).
+%%-------------------------------------------------------------------------
+chunked() ->
+ [{doc, "Check that the server accepts chunked requests."}].
+
+chunked(Config) when is_list(Config) ->
+ httpd_1_1:chunked(proplists:get_value(type, Config), proplists:get_value(port, Config),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)).
+%%-------------------------------------------------------------------------
+expect() ->
+ ["Check that the server handles request with the expect header "
+ "field appropiate"].
+expect(Config) when is_list(Config) ->
+ httpd_1_1:expect(proplists:get_value(type, Config), proplists:get_value(port, Config),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)).
+%%-------------------------------------------------------------------------
+max_clients_1_1() ->
+ [{doc, "Test max clients limit"}].
+
+max_clients_1_1(Config) when is_list(Config) ->
+ do_max_clients([{http_version, "HTTP/1.1"} | Config]).
+
+max_clients_1_0() ->
+ [{doc, "Test max clients limit"}].
+
+max_clients_1_0(Config) when is_list(Config) ->
+ do_max_clients([{http_version, "HTTP/1.0"} | Config]).
+
+max_clients_0_9() ->
+ [{doc, "Test max clients limit"}].
+
+max_clients_0_9(Config) when is_list(Config) ->
+ do_max_clients([{http_version, "HTTP/0.9"} | Config]).
+%%-------------------------------------------------------------------------
+esi() ->
+ [{doc, "Test mod_esi"}].
+
+esi(Config) when is_list(Config) ->
+ ok = http_status("GET /eval?httpd_example:print(\"Hi!\") ",
+ Config, [{statuscode, 200}]),
+ ok = http_status("GET /eval?not_allowed:print(\"Hi!\") ",
+ Config, [{statuscode, 403}]),
+ ok = http_status("GET /eval?httpd_example:undef(\"Hi!\") ",
+ Config, [{statuscode, 500}]),
+ ok = http_status("GET /cgi-bin/erl/httpd_example ",
+ Config, [{statuscode, 400}]),
+ ok = http_status("GET /cgi-bin/erl/httpd_example:get ",
+ Config, [{statuscode, 200}]),
+ ok = http_status("GET /cgi-bin/erl/httpd_example:"
+ "get?input=4711 ", Config,
+ [{statuscode, 200}]),
+ ok = http_status("GET /cgi-bin/erl/httpd_example:post ",
+ Config, [{statuscode, 200}]),
+ ok = http_status("GET /cgi-bin/erl/not_allowed:post ",
+ Config, [{statuscode, 403}]),
+ ok = http_status("GET /cgi-bin/erl/httpd_example:undef ",
+ Config, [{statuscode, 404}]),
+ ok = http_status("GET /cgi-bin/erl/httpd_example/yahoo ",
+ Config, [{statuscode, 302}]),
+ %% Check "ErlScriptNoCache" directive (default: false)
+ ok = http_status("GET /cgi-bin/erl/httpd_example:get ",
+ Config, [{statuscode, 200},
+ {no_header, "cache-control"}]),
+ ok = http_status("GET /cgi-bin/erl/httpd_example:peer ",
+ Config, [{statuscode, 200},
+ {header, "peer-cert-exist", peer(Config)}]).
+
%%-------------------------------------------------------------------------
-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.
+mod_esi_chunk_timeout(Config) when is_list(Config) ->
+ ok = httpd_1_1:mod_esi_chunk_timeout(proplists:get_value(type, Config),
+ proplists:get_value(port, Config),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)).
%%-------------------------------------------------------------------------
-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.
+cgi() ->
+ [{doc, "Test mod_cgi"}].
-%%-------------------------------------------------------------------------
-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
+cgi(Config) when is_list(Config) ->
+ {Script, Script2, Script3} =
+ case test_server:os_type() of
+ {win32, _} ->
+ {"printenv.bat", "printenv.sh", "cgi_echo.exe"};
+ _ ->
+ {"printenv.sh", "printenv.bat", "cgi_echo"}
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.
+ %%The length (> 100) is intentional
+ ok = http_status("POST /cgi-bin/" ++ Script3 ++ " ",
+ {"Content-Length:100 \r\n",
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"},
+ Config,
+ [{statuscode, 200},
+ {header, "content-type", "text/plain"}]),
+
+ ok = http_status("GET /cgi-bin/"++ Script ++ " ", Config, [{statuscode, 200}]),
-%%-------------------------------------------------------------------------
-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
+ ok = http_status("GET /cgi-bin/not_there ", Config,
+ [{statuscode, 404}, {statuscode, 500}]),
+
+ ok = http_status("GET /cgi-bin/"++ Script ++ "?Nisse:kkk?sss/lll ",
+ Config,
+ [{statuscode, 200}]),
+
+ ok = http_status("POST /cgi-bin/"++ Script ++ " ", Config,
+ [{statuscode, 200}]),
+
+ ok = http_status("GET /htbin/"++ Script ++ " ", Config,
+ [{statuscode, 200}]),
+
+ ok = http_status("GET /htbin/not_there ", Config,
+ [{statuscode, 404},{statuscode, 500}]),
+
+ ok = http_status("GET /htbin/"++ Script ++ "?Nisse:kkk?sss/lll ", Config,
+ [{statuscode, 200}]),
+
+ ok = http_status("POST /htbin/"++ Script ++ " ", Config,
+ [{statuscode, 200}]),
+
+ ok = http_status("POST /htbin/"++ Script ++ " ", Config,
+ [{statuscode, 200}]),
+
+ %% Execute an existing, but bad CGI script..
+ ok = http_status("POST /htbin/"++ Script2 ++ " ", Config,
+ [{statuscode, 404}]),
+
+ ok = http_status("POST /cgi-bin/"++ Script2 ++ " ", Config,
+ [{statuscode, 404}]),
+
+ %% Check "ScriptNoCache" directive (default: false)
+ ok = http_status("GET /cgi-bin/" ++ Script ++ " ", Config,
+ [{statuscode, 200},
+ {no_header, "cache-control"}]).
+%%-------------------------------------------------------------------------
+cgi_chunked_encoding_test() ->
+ [{doc, "Test chunked encoding together with mod_cgi "}].
+cgi_chunked_encoding_test(Config) when is_list(Config) ->
+ Host = proplists:get_value(host, Config),
+ Script =
+ case test_server:os_type() of
+ {win32, _} ->
+ "/cgi-bin/printenv.bat";
+ _ ->
+ "/cgi-bin/printenv.sh"
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.
-
-%%-------------------------------------------------------------------------
-
-essl_mod_alias(doc) ->
- ["Module test: mod_alias - using new of configure new SSL"];
-essl_mod_alias(suite) ->
- [];
-essl_mod_alias(Config) when is_list(Config) ->
- ssl_mod_alias(essl, Config).
-
-
-ssl_mod_alias(Tag, Config) ->
- httpd_mod:alias(Tag, ?SSL_PORT,
- ?config(host, Config), ?config(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_actions(doc) ->
- ["Module test: mod_actions - using new of configure new SSL"];
-essl_mod_actions(suite) ->
- [];
-essl_mod_actions(Config) when is_list(Config) ->
- ssl_mod_actions(essl, Config).
-
-
-ssl_mod_actions(Tag, Config) ->
- httpd_mod:actions(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_security(doc) ->
- ["Module test: mod_security - using new of configure new SSL"];
-essl_mod_security(suite) ->
- [];
-essl_mod_security(Config) when is_list(Config) ->
- ssl_mod_security(essl, Config).
-
-ssl_mod_security(Tag, Config) ->
- ServerRoot = ?config(server_root, Config),
- httpd_mod:security(ServerRoot,
- Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_auth(doc) ->
- ["Module test: mod_auth - using new of configure new SSL"];
-essl_mod_auth(suite) ->
- [];
-essl_mod_auth(Config) when is_list(Config) ->
- ssl_mod_auth(essl, Config).
-
-ssl_mod_auth(Tag, Config) ->
- httpd_mod:auth(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-
-essl_mod_auth_api(doc) ->
- ["Module test: mod_auth - using new of configure new SSL"];
-essl_mod_auth_api(suite) ->
- [];
-essl_mod_auth_api(Config) when is_list(Config) ->
- ssl_mod_auth_api(essl, Config).
-
-ssl_mod_auth_api(Tag, Config) ->
- ServerRoot = ?config(server_root, Config),
- Host = ?config(host, Config),
- Node = ?config(node, Config),
- httpd_mod:auth_api(ServerRoot, "", Tag, ?SSL_PORT, Host, Node),
- httpd_mod:auth_api(ServerRoot, "dets_", Tag, ?SSL_PORT, Host, Node),
- httpd_mod:auth_api(ServerRoot, "mnesia_", Tag, ?SSL_PORT, Host, Node),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-
-essl_mod_auth_mnesia_api(doc) ->
- ["Module test: mod_auth_mnesia_api - using new of configure new SSL"];
-essl_mod_auth_mnesia_api(suite) ->
- [];
-essl_mod_auth_mnesia_api(Config) when is_list(Config) ->
- ssl_mod_auth_mnesia_api(essl, Config).
-
-ssl_mod_auth_mnesia_api(Tag, Config) ->
- httpd_mod:auth_mnesia_api(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_htaccess(doc) ->
- ["Module test: mod_htaccess - using new of configure new SSL"];
-essl_mod_htaccess(suite) ->
- [];
-essl_mod_htaccess(Config) when is_list(Config) ->
- ssl_mod_htaccess(essl, Config).
-
-ssl_mod_htaccess(Tag, Config) ->
- httpd_mod:htaccess(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_cgi(doc) ->
- ["Module test: mod_cgi - using new of configure new SSL"];
-essl_mod_cgi(suite) ->
- [];
-essl_mod_cgi(Config) when is_list(Config) ->
- ssl_mod_cgi(essl, Config).
-
-ssl_mod_cgi(Tag, Config) ->
- httpd_mod:cgi(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_esi(doc) ->
- ["Module test: mod_esi - using new of configure new SSL"];
-essl_mod_esi(suite) ->
- [];
-essl_mod_esi(Config) when is_list(Config) ->
- ssl_mod_esi(essl, Config).
-
-ssl_mod_esi(Tag, Config) ->
- httpd_mod:esi(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_get(doc) ->
- ["Module test: mod_get - using new of configure new SSL"];
-essl_mod_get(suite) ->
- [];
-essl_mod_get(Config) when is_list(Config) ->
- ssl_mod_get(essl, Config).
-
-ssl_mod_get(Tag, Config) ->
- httpd_mod:get(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_head(doc) ->
- ["Module test: mod_head - using new of configure new SSL"];
-essl_mod_head(suite) ->
- [];
-essl_mod_head(Config) when is_list(Config) ->
- ssl_mod_head(essl, Config).
-
-ssl_mod_head(Tag, Config) ->
- httpd_mod:head(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_mod_all(doc) ->
- ["All modules test - using new of configure new SSL"];
-essl_mod_all(suite) ->
- [];
-essl_mod_all(Config) when is_list(Config) ->
- ssl_mod_all(essl, Config).
-
-ssl_mod_all(Tag, Config) ->
- httpd_mod:all(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_load_light(doc) ->
- ["Test light load - using new of configure new SSL"];
-essl_load_light(suite) ->
- [];
-essl_load_light(Config) when is_list(Config) ->
- ssl_load_light(essl, Config).
-
-ssl_load_light(Tag, Config) ->
- httpd_load:load_test(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config),
- get_nof_clients(ssl, light)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_load_medium(doc) ->
- ["Test medium load - using new of configure new SSL"];
-essl_load_medium(suite) ->
- [];
-essl_load_medium(Config) when is_list(Config) ->
- ssl_load_medium(essl, Config).
-
-ssl_load_medium(Tag, Config) ->
- %% <CONDITIONAL-SKIP>
- Skippable = [win32],
- Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
- ?NON_PC_TC_MAYBE_SKIP(Config, Condition),
- %% </CONDITIONAL-SKIP>
-
- httpd_load:load_test(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config),
- get_nof_clients(ssl, medium)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_load_heavy(doc) ->
- ["Test heavy load - using new of configure new SSL"];
-essl_load_heavy(suite) ->
- [];
-essl_load_heavy(Config) when is_list(Config) ->
- ssl_load_heavy(essl, Config).
-
-ssl_load_heavy(Tag, Config) ->
- %% <CONDITIONAL-SKIP>
- Skippable = [win32],
- Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
- ?NON_PC_TC_MAYBE_SKIP(Config, Condition),
- %% </CONDITIONAL-SKIP>
-
- httpd_load:load_test(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config),
- get_nof_clients(ssl, heavy)),
- ok.
-
-
+ 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(proplists:get_value(type, Config), proplists:get_value(port, Config),
+ Host,
+ proplists:get_value(node, Config),
+ Requests).
%%-------------------------------------------------------------------------
+alias_1_1() ->
+ [{doc, "Test mod_alias"}].
+alias_1_1(Config) when is_list(Config) ->
+ alias([{http_version, "HTTP/1.1"} | Config]).
-essl_dos_hostname(doc) ->
- ["Denial Of Service (DOS) attack test case - using new of configure new SSL"];
-essl_dos_hostname(suite) ->
- [];
-essl_dos_hostname(Config) when is_list(Config) ->
- ssl_dos_hostname(essl, Config).
-
-ssl_dos_hostname(Tag, Config) ->
- dos_hostname(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config),
- ?MAX_HEADER_SIZE),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
+alias_1_0() ->
+ [{doc, "Test mod_alias"}].
+
+alias_1_0(Config) when is_list(Config) ->
+ alias([{http_version, "HTTP/1.0"} | Config]).
-essl_time_test(doc) ->
- ["using new of configure new SSL"];
-essl_time_test(suite) ->
- [];
-essl_time_test(Config) when is_list(Config) ->
- ssl_time_test(essl, Config).
-
-ssl_time_test(Tag, Config) when is_list(Config) ->
- %% <CONDITIONAL-SKIP>
- FreeBSDVersionVerify =
- fun() ->
- case os:version() of
- {7, 1, _} -> % We only have one such machine, so...
- true;
- _ ->
- false
- end
- end,
- Skippable = [win32, {unix, [{freebsd, FreeBSDVersionVerify}]}],
- Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
- ?NON_PC_TC_MAYBE_SKIP(Config, Condition),
- %% </CONDITIONAL-SKIP>
+alias_0_9() ->
+ [{doc, "Test mod_alias"}].
+
+alias_0_9(Config) when is_list(Config) ->
+ alias([{http_version, "HTTP/0.9"} | Config]).
+
+alias() ->
+ [{doc, "Test mod_alias"}].
+
+alias(Config) when is_list(Config) ->
+ ok = http_status("GET /pics/icon.sheet.gif ", Config,
+ [{statuscode, 200},
+ {header, "Content-Type","image/gif"},
+ {header, "Server"},
+ {header, "Date"}]),
- httpd_time_test:t(Tag,
- ?config(host, Config),
- ?SSL_PORT),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-
-essl_block_503(doc) ->
- ["Check that you will receive status code 503 when the server"
- " is blocked and 200 when its not blocked - using new of configure new SSL."];
-essl_block_503(suite) ->
- [];
-essl_block_503(Config) when is_list(Config) ->
- ssl_block_503(essl, Config).
-
-ssl_block_503(Tag, Config) ->
- httpd_block:block_503(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_block_disturbing_idle(doc) ->
- ["Check that you can block/unblock an idle server. The strategy "
- "distribing does not really make a difference in this case."
- "Using new of configure new SSL"];
-essl_block_disturbing_idle(suite) ->
- [];
-essl_block_disturbing_idle(Config) when is_list(Config) ->
- ssl_block_disturbing_idle(essl, Config).
-
-ssl_block_disturbing_idle(Tag, Config) ->
- httpd_block:block_disturbing_idle(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-essl_block_non_disturbing_idle(doc) ->
- ["Check that you can block/unblock an idle server. The strategy "
- "non distribing does not really make a difference in this case."
- "Using new of configure new SSL"];
-essl_block_non_disturbing_idle(suite) ->
- [];
-essl_block_non_disturbing_idle(Config) when is_list(Config) ->
- ssl_block_non_disturbing_idle(essl, Config).
-
-ssl_block_non_disturbing_idle(Tag, Config) ->
- httpd_block:block_non_disturbing_idle(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
-
+ ok = http_status("GET / ", Config,
+ [{statuscode, 200},
+ {header, "Content-Type","text/html"},
+ {header, "Server"},
+ {header, "Date"}]),
+
+ ok = http_status("GET /misc/ ", Config,
+ [{statuscode, 200},
+ {header, "Content-Type","text/html"},
+ {header, "Server"},
+ {header, "Date"}]),
+
+ %% Check redirection if trailing slash is missing.
+ ok = http_status("GET /misc ", Config,
+ [{statuscode, 301},
+ {header, "Location"},
+ {header, "Content-Type","text/html"}]).
+%%-------------------------------------------------------------------------
+actions() ->
+ [{doc, "Test mod_actions"}].
+
+actions(Config) when is_list(Config) ->
+ ok = http_status("GET /", Config, [{statuscode, 200}]).
+
+%%-------------------------------------------------------------------------
+range() ->
+ [{doc, "Test Range header"}].
+
+range(Config) when is_list(Config) ->
+ httpd_1_1:range(proplists:get_value(type, Config), proplists:get_value(port, Config),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)).
+
+%%-------------------------------------------------------------------------
+if_modified_since() ->
+ [{doc, "Test If-Modified-Since header"}].
+
+if_modified_since(Config) when is_list(Config) ->
+ httpd_1_1:if_test(proplists:get_value(type, Config), proplists:get_value(port, Config),
+ proplists:get_value(host, Config), proplists:get_value(node, Config),
+ proplists:get_value(doc_root, Config)).
+%%-------------------------------------------------------------------------
+trace() ->
+ [{doc, "Test TRACE method"}].
+
+trace(Config) when is_list(Config) ->
+ Cb = proplists:get_value(version_cb, Config),
+ Cb:trace(proplists:get_value(type, Config), proplists:get_value(port, Config),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)).
+%%-------------------------------------------------------------------------
+light() ->
+ ["Test light load"].
+light(Config) when is_list(Config) ->
+ httpd_load:load_test(proplists:get_value(type, Config), proplists:get_value(port, Config), proplists:get_value(host, Config),
+ proplists:get_value(node, Config), 10).
+%%-------------------------------------------------------------------------
+medium() ->
+ ["Test medium load"].
+medium(Config) when is_list(Config) ->
+ httpd_load:load_test(proplists:get_value(type, Config), proplists:get_value(port, Config), proplists:get_value(host, Config),
+ proplists:get_value(node, Config), 100).
+%%-------------------------------------------------------------------------
+heavy() ->
+ ["Test heavy load"].
+heavy(Config) when is_list(Config) ->
+ httpd_load:load_test(proplists:get_value(type, Config), proplists:get_value(port, Config), proplists:get_value(host, Config),
+ proplists:get_value(node, Config),
+ 1000).
+%%-------------------------------------------------------------------------
+content_length() ->
+ ["Tests that content-length is correct OTP-5775"].
+content_length(Config) ->
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config), proplists:get_value(node, Config),
+ http_request("GET /cgi-bin/erl/httpd_example:get_bin ",
+ Version, Host),
+ [{statuscode, 200},
+ {content_length, 274},
+ {version, Version}]).
+%%-------------------------------------------------------------------------
+bad_hex() ->
+ ["Tests that a URI with a bad hexadecimal code is handled OTP-6003"].
+bad_hex(Config) ->
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config), proplists:get_value(node, Config),
+ http_request("GET http://www.erlang.org/%skalle ",
+ Version, Host),
+ [{statuscode, 400},
+ {version, Version}]).
+%%-------------------------------------------------------------------------
+missing_CR() ->
+ ["Tests missing CR in delimiter OTP-7304"].
+missing_CR(Config) ->
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config), proplists:get_value(node, Config),
+ http_request_missing_CR("GET /index.html ", Version, Host),
+ [{statuscode, 200},
+ {version, Version}]).
%%-------------------------------------------------------------------------
+customize() ->
+ [{doc, "Test filtering of headers with custom callback"}].
-essl_block_disturbing_active(doc) ->
- ["Check that you can block/unblock an active server. The strategy "
- "distribing means ongoing requests should be terminated."
- "Using new of configure new SSL"];
-essl_block_disturbing_active(suite) ->
- [];
-essl_block_disturbing_active(Config) when is_list(Config) ->
- ssl_block_disturbing_active(essl, Config).
-
-ssl_block_disturbing_active(Tag, Config) ->
- httpd_block:block_disturbing_active(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
-
+customize(Config) when is_list(Config) ->
+ Version = "HTTP/1.1",
+ Host = proplists:get_value(host, Config),
+ Type = proplists:get_value(type, Config),
+ ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config),
+ transport_opts(Type, Config),
+ proplists:get_value(node, Config),
+ http_request("GET /index.html ", Version, Host),
+ [{statuscode, 200},
+ {header, "Content-Type", "text/html"},
+ {header, "Date"},
+ {no_header, "Server"},
+ {version, Version}]).
+
+add_default() ->
+ [{doc, "Test adding default header with custom callback"}].
+
+add_default(Config) when is_list(Config) ->
+ Version = "HTTP/1.1",
+ Host = proplists:get_value(host, Config),
+ Type = proplists:get_value(type, Config),
+ ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config),
+ transport_opts(Type, Config),
+ proplists:get_value(node, Config),
+ http_request("GET /index.html ", Version, Host),
+ [{statuscode, 200},
+ {header, "Content-Type", "text/html"},
+ {header, "Date", "Override-date"},
+ {header, "X-Frame-Options"},
+ {version, Version}]).
+
+%%-------------------------------------------------------------------------
+max_header() ->
+ ["Denial Of Service (DOS) attack, prevented by max_header"].
+max_header(Config) when is_list(Config) ->
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ case Version of
+ "HTTP/0.9" ->
+ {skip, not_implemented};
+ _ ->
+ dos_hostname(proplists:get_value(type, Config), proplists:get_value(port, Config), Host,
+ proplists:get_value(node, Config), Version, ?MAX_HEADER_SIZE)
+ end.
%%-------------------------------------------------------------------------
-
-essl_block_non_disturbing_active(doc) ->
- ["Check that you can block/unblock an idle server. The strategy "
- "non distribing means the ongoing requests should be compleated."
- "Using new of configure new SSL"];
-essl_block_non_disturbing_active(suite) ->
- [];
-essl_block_non_disturbing_active(Config) when is_list(Config) ->
- ssl_block_non_disturbing_active(essl, Config).
-
-ssl_block_non_disturbing_active(Tag, Config) ->
- httpd_block:block_non_disturbing_idle(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
-
+max_content_length() ->
+ ["Denial Of Service (DOS) attack, prevented by max_content_length"].
+max_content_length(Config) when is_list(Config) ->
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ garbage_content_length(proplists:get_value(type, Config), proplists:get_value(port, Config), Host,
+ proplists:get_value(node, Config), Version).
%%-------------------------------------------------------------------------
+security_1_1(Config) when is_list(Config) ->
+ security([{http_version, "HTTP/1.1"} | Config]).
-essl_block_disturbing_active_timeout_not_released(doc) ->
- ["Check that you can block an active server. The strategy "
- "distribing means ongoing requests should be compleated"
- "if the timeout does not occur."
- "Using new of configure new SSL"];
-essl_block_disturbing_active_timeout_not_released(suite) ->
- [];
-essl_block_disturbing_active_timeout_not_released(Config)
- when is_list(Config) ->
- ssl_block_disturbing_active_timeout_not_released(essl, Config).
-
-ssl_block_disturbing_active_timeout_not_released(Tag, Config) ->
- Port = ?SSL_PORT,
- Host = ?config(host, Config),
- Node = ?config(node, Config),
- httpd_block:block_disturbing_active_timeout_not_released(Tag,
- Port, Host, Node),
- ok.
+security_1_0(Config) when is_list(Config) ->
+ security([{http_version, "HTTP/1.0"} | Config]).
+security() ->
+ ["Test mod_security"].
+security(Config) ->
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Port = proplists:get_value(port, Config),
+ Node = proplists:get_value(node, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
-%%-------------------------------------------------------------------------
+ global:register_name(mod_security_test, self()), % Receive events
-essl_block_disturbing_active_timeout_released(doc) ->
- ["Check that you can block an active server. The strategy "
- "distribing means ongoing requests should be terminated when"
- "the timeout occurs."
- "Using new of configure new SSL"];
-essl_block_disturbing_active_timeout_released(suite) ->
- [];
-essl_block_disturbing_active_timeout_released(Config)
- when is_list(Config) ->
- ssl_block_disturbing_active_timeout_released(essl, Config).
-
-ssl_block_disturbing_active_timeout_released(Tag, Config) ->
- Port = ?SSL_PORT,
- Host = ?config(host, Config),
- Node = ?config(node, Config),
- httpd_block:block_disturbing_active_timeout_released(Tag,
- Port,
- Host,
- Node),
- ok.
+ ct:sleep(5000),
+ OpenDir = filename:join([ServerRoot, "htdocs", "open"]),
-%%-------------------------------------------------------------------------
-
-essl_block_non_disturbing_active_timeout_not_released(doc) ->
- ["Check that you can block an active server. The strategy "
- "non non distribing means ongoing requests should be completed."
- "Using new of configure new SSL"];
-essl_block_non_disturbing_active_timeout_not_released(suite) ->
- [];
-essl_block_non_disturbing_active_timeout_not_released(Config)
- when is_list(Config) ->
- ssl_block_non_disturbing_active_timeout_not_released(essl, Config).
-
-ssl_block_non_disturbing_active_timeout_not_released(Tag, Config) ->
- Port = ?SSL_PORT,
- Host = ?config(host, Config),
- Node = ?config(node, Config),
- httpd_block:block_non_disturbing_active_timeout_not_released(Tag,
- Port,
- Host,
- Node),
- ok.
+ %% Test blocking / unblocking of users.
+ %% /open, require user one Aladdin
+ remove_users(Node, ServerRoot, Host, Port, "", "open"),
-%%-------------------------------------------------------------------------
+ ok = auth_status(auth_request("/open/",
+ "one", "onePassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ receive_security_event({event, auth_fail, Port, OpenDir,
+ [{user, "one"}, {password, "onePassword"}]},
+ Node, Port),
+
+ ok = auth_status(auth_request("/open/",
+ "two", "twoPassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ receive_security_event({event, auth_fail, Port, OpenDir,
+ [{user, "two"}, {password, "twoPassword"}]},
+ Node, Port),
+ ok = auth_status(auth_request("/open/",
+ "Aladdin", "AladdinPassword", Version, Host),
+ Config, [{statuscode, 401}]),
+
+ receive_security_event({event, auth_fail, Port, OpenDir,
+ [{user, "Aladdin"},
+ {password, "AladdinPassword"}]},
+ Node, Port),
-essl_block_non_disturbing_active_timeout_released(doc) ->
- ["Check that you can block an active server. The strategy "
- "non distribing means ongoing requests should be completed. "
- "When the timeout occurs the block operation sohould be canceled."
- "Using new of configure new SSL"];
-essl_block_non_disturbing_active_timeout_released(suite) ->
- [];
-essl_block_non_disturbing_active_timeout_released(Config)
- when is_list(Config) ->
- ssl_block_non_disturbing_active_timeout_released(essl, Config).
-
-ssl_block_non_disturbing_active_timeout_released(Tag, Config)
- when is_list(Config) ->
- Port = ?SSL_PORT,
- Host = ?config(host, Config),
- Node = ?config(node, Config),
- httpd_block:block_non_disturbing_active_timeout_released(Tag,
- Port,
- Host,
- Node),
+ add_user(Node, ServerRoot, Port, "", "open", "one", "onePassword", []),
+ add_user(Node, ServerRoot, Port, "", "open", "two", "twoPassword", []),
- ok.
+ ok = auth_status(auth_request("/open/", "one", "WrongPassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ receive_security_event({event, auth_fail, Port, OpenDir,
+ [{user, "one"}, {password, "WrongPassword"}]},
+ Node, Port),
+ ok = auth_status(auth_request("/open/", "one", "WrongPassword", Version, Host), Config,
+ [{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.
+
+ ok = auth_status(auth_request("/open/", "one", "WrongPassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ %% User "one" should be blocked now..
+ case list_blocked_users(Node, Port) of
+ [{"one",_, Port, OpenDir,_}] ->
+ ok;
+ Blocked ->
+ ct:fail({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.
-essl_block_disturbing_blocker_dies(doc) ->
- ["using new of configure new SSL"];
-essl_block_disturbing_blocker_dies(suite) ->
- [];
-essl_block_disturbing_blocker_dies(Config) when is_list(Config) ->
- ssl_block_disturbing_blocker_dies(essl, Config).
+ [] = list_blocked_users(Node, Port),
-ssl_block_disturbing_blocker_dies(Tag, Config) ->
- httpd_block:disturbing_blocker_dies(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
+ ok = auth_status(auth_request("/open/", "one", "onePassword", Version, Host), Config,
+ [{statuscode, 200}]),
+ %% Test list_auth_users & auth_timeout
-%%-------------------------------------------------------------------------
+ ["one"] = list_auth_users(Node, Port),
-essl_block_non_disturbing_blocker_dies(doc) ->
- ["using new of configure new SSL"];
-essl_block_non_disturbing_blocker_dies(suite) ->
- [];
-essl_block_non_disturbing_blocker_dies(Config) when is_list(Config) ->
- ssl_block_non_disturbing_blocker_dies(essl, Config).
-
-ssl_block_non_disturbing_blocker_dies(Tag, Config) ->
- httpd_block:non_disturbing_blocker_dies(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
+ ok = auth_status(auth_request("/open/", "two", "onePassword", Version, Host), Config,
+ [{statuscode, 401}]),
+ ["one"] = list_auth_users(Node, Port),
-%%-------------------------------------------------------------------------
+
+ ["one"] = list_auth_users(Node, Port, OpenDir),
+
+ ok = auth_status(auth_request("/open/", "two", "twoPassword", Version, Host), Config,
+ [{statuscode, 401}]),
-essl_restart_no_block(doc) ->
- ["using new of configure new SSL"];
-essl_restart_no_block(suite) ->
- [];
-essl_restart_no_block(Config) when is_list(Config) ->
- ssl_restart_no_block(essl, Config).
+ ["one"] = list_auth_users(Node, Port),
-ssl_restart_no_block(Tag, Config) ->
- httpd_block:restart_no_block(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
+
+ ["one"] = list_auth_users(Node, Port, OpenDir),
+ %% Wait for successful auth to timeout.
+ ct:sleep(?AUTH_TIMEOUT*1001),
-%%-------------------------------------------------------------------------
+ [] = list_auth_users(Node, Port),
+ [] = list_auth_users(Node, Port, OpenDir),
-essl_restart_disturbing_block(doc) ->
- ["using new of configure new SSL"];
-essl_restart_disturbing_block(suite) ->
- [];
-essl_restart_disturbing_block(Config) when is_list(Config) ->
- ssl_restart_disturbing_block(essl, Config).
-
-ssl_restart_disturbing_block(Tag, Config) ->
- %% <CONDITIONAL-SKIP>
- Condition =
- fun() ->
- case os:type() of
- {unix, linux} ->
- case ?OSCMD("uname -m") of
- "ppc" ->
- case file:read_file_info("/etc/fedora-release") of
- {ok, _} ->
- case ?OSCMD("awk '{print $2}' /etc/fedora-release") of
- "release" ->
- %% Fedora 7 and later
- case ?OSCMD("awk '{print $3}' /etc/fedora-release") of
- "7" ->
- true;
- _ ->
- false
- end;
- _ ->
- false
- end;
- _ ->
- false
- end;
- _ ->
- false
- end;
- _ ->
- false
- end
- end,
- ?NON_PC_TC_MAYBE_SKIP(Config, Condition),
- %% </CONDITIONAL-SKIP>
+ %% "two" is blocked.
- httpd_block:restart_disturbing_block(Tag, ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
+ true = unblock_user(Node, "two", Port, OpenDir),
-%%-------------------------------------------------------------------------
+ %% Test explicit blocking. Block user 'two'.
+ [] = list_blocked_users(Node,Port,OpenDir),
-essl_restart_non_disturbing_block(doc) ->
- ["using new of configure new SSL"];
-essl_restart_non_disturbing_block(suite) ->
- [];
-essl_restart_non_disturbing_block(Config) when is_list(Config) ->
- ssl_restart_non_disturbing_block(essl, Config).
-
-ssl_restart_non_disturbing_block(Tag, Config) ->
- %% <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>
+ true = block_user(Node, "two", Port, OpenDir, 10),
- httpd_block:restart_non_disturbing_block(Tag,
- ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
- ok.
+ ok = auth_status(auth_request("/open/", "two", "twoPassword", Version, Host), Config,
+ [{statuscode, 401}]),
+
+ true = unblock_user(Node, "two", Port, OpenDir).
+
+%%-------------------------------------------------------------------------
+non_disturbing_reconfiger_dies(Config) when is_list(Config) ->
+ do_reconfiger_dies([{http_version, "HTTP/1.1"} | Config], non_disturbing).
+disturbing_reconfiger_dies(Config) when is_list(Config) ->
+ do_reconfiger_dies([{http_version, "HTTP/1.1"} | Config], disturbing).
+
+do_reconfiger_dies(Config, DisturbingType) ->
+ Server = proplists:get_value(server_pid, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Port = proplists:get_value(port, Config),
+ Type = proplists:get_value(type, Config),
+
+ HttpdConfig = httpd:info(Server),
+ BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
+ {ok, Socket} = inets_test_lib:connect_bin(Type, Host, Port, transport_opts(Type, Config)),
+ inets_test_lib:send(Type, Socket, BlockRequest),
+ ct:sleep(100), %% Avoid possible timing issues
+ Pid = spawn(fun() -> httpd:reload_config([{server_name, "httpd_kill_" ++ Version},
+ {port, Port}|
+ proplists:delete(server_name, HttpdConfig)], DisturbingType)
+ end),
+
+ monitor(process, Pid),
+ exit(Pid, kill),
+ receive
+ {'DOWN', _, _, _, _} ->
+ ok
+ end,
+ inets_test_lib:close(Type, Socket),
+ [{server_name, "httpd_test"}] = httpd:info(Server, [server_name]).
+%%-------------------------------------------------------------------------
+disturbing_1_1(Config) when is_list(Config) ->
+ disturbing([{http_version, "HTTP/1.1"} | Config]).
+
+disturbing_1_0(Config) when is_list(Config) ->
+ disturbing([{http_version, "HTTP/1.0"} | Config]).
+
+disturbing_0_9(Config) when is_list(Config) ->
+ disturbing([{http_version, "HTTP/0.9"} | Config]).
+
+disturbing(Config) when is_list(Config)->
+ Server = proplists:get_value(server_pid, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Port = proplists:get_value(port, Config),
+ Type = proplists:get_value(type, Config),
+ HttpdConfig = httpd:info(Server),
+ BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
+ {ok, Socket} = inets_test_lib:connect_bin(Type, Host, Port, transport_opts(Type, Config)),
+ inets_test_lib:send(Type, Socket, BlockRequest),
+ ct:sleep(100), %% Avoid possible timing issues
+ ok = httpd:reload_config([{server_name, "httpd_disturbing_" ++ Version}, {port, Port}|
+ proplists:delete(server_name, HttpdConfig)], disturbing),
+ Close = list_to_atom((typestr(Type)) ++ "_closed"),
+ receive
+ {Close, Socket} ->
+ ok;
+ Msg ->
+ ct:fail({{expected, {Close, Socket}}, {got, Msg}})
+ end,
+ inets_test_lib:close(Type, Socket),
+ [{server_name, "httpd_disturbing_" ++ Version}] = httpd:info(Server, [server_name]).
+%%-------------------------------------------------------------------------
+non_disturbing_1_1(Config) when is_list(Config) ->
+ non_disturbing([{http_version, "HTTP/1.1"} | Config]).
+
+non_disturbing_1_0(Config) when is_list(Config) ->
+ non_disturbing([{http_version, "HTTP/1.0"} | Config]).
+
+non_disturbing_0_9(Config) when is_list(Config) ->
+ non_disturbing([{http_version, "HTTP/0.9"} | Config]).
+
+non_disturbing(Config) when is_list(Config)->
+ Server = proplists:get_value(server_pid, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Port = proplists:get_value(port, Config),
+ Type = proplists:get_value(type, Config),
+
+ HttpdConfig = httpd:info(Server),
+ BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
+ {ok, Socket} = inets_test_lib:connect_bin(Type, Host, Port, transport_opts(Type, Config)),
+ inets_test_lib:send(Type, Socket, BlockRequest),
+ ct:sleep(100), %% Avoid possible timing issues
+ ok = httpd:reload_config([{server_name, "httpd_non_disturbing_" ++ Version}, {port, Port}|
+ proplists:delete(server_name, HttpdConfig)], non_disturbing),
+ Transport = type(Type),
+ receive
+ {Transport, Socket, Msg} ->
+ ct:pal("Received message ~p~n", [Msg]),
+ ok
+ after 2000 ->
+ ct:fail(timeout)
+ end,
+ inets_test_lib:close(Type, Socket),
+ [{server_name, "httpd_non_disturbing_" ++ Version}] = httpd:info(Server, [server_name]).
+
+%%-------------------------------------------------------------------------
+mime_types_format(Config) when is_list(Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ MimeTypes = filename:join(DataDir, "mime_types.txt"),
+ {ok,[{"wrl","x-world/x-vrml"},
+ {"vrml","x-world/x-vrml"},
+ {"ice","x-conference/x-cooltalk"},
+ {"movie","video/x-sgi-movie"},
+ {"avi","video/x-msvideo"},
+ {"qt","video/quicktime"},
+ {"mov","video/quicktime"},
+ {"mpeg","video/mpeg"},
+ {"mpg","video/mpeg"},
+ {"mpe","video/mpeg"},
+ {"sgml","text/x-sgml"},
+ {"sgm","text/x-sgml"},
+ {"etx","text/x-setext"},
+ {"tsv","text/tab-separated-values"},
+ {"rtx","text/richtext"},
+ {"txt","text/plain"},
+ {"html","text/html"},
+ {"htm","text/html"},
+ {"css","text/css"},
+ {"xwd","image/x-xwindowdump"},
+ {"xpm","image/x-xpixmap"},
+ {"xbm","image/x-xbitmap"},
+ {"rgb","image/x-rgb"},
+ {"ppm","image/x-portable-pixmap"},
+ {"pgm","image/x-portable-graymap"},
+ {"pbm","image/x-portable-bitmap"},
+ {"pnm","image/x-portable-anymap"},
+ {"ras","image/x-cmu-raster"},
+ {"tiff","image/tiff"},
+ {"tif","image/tiff"},
+ {"png","image/png"},
+ {"jpeg","image/jpeg"},
+ {"jpg","image/jpeg"},
+ {"jpe","image/jpeg"},
+ {"ief","image/ief"},
+ {"gif","image/gif"},
+ {"pdb","chemical/x-pdb"},
+ {"xyz","chemical/x-pdb"},
+ {"wav","audio/x-wav"},
+ {"ra","audio/x-realaudio"},
+ {"rpm","audio/x-pn-realaudio-plugin"},
+ {"ram","audio/x-pn-realaudio"},
+ {"aif","audio/x-aiff"},
+ {"aiff","audio/x-aiff"},
+ {"aifc","audio/x-aiff"},
+ {"mpga","audio/mpeg"},
+ {"mp2","audio/mpeg"},
+ {"au","audio/basic"},
+ {"snd","audio/basic"},
+ {"zip","application/zip"},
+ {"src","application/x-wais-source"},
+ {"ustar","application/x-ustar"},
+ {"ms","application/x-troff-ms"},
+ {"me","application/x-troff-me"},
+ {"man","application/x-troff-man"},
+ {"t","application/x-troff"},
+ {"tr","application/x-troff"},
+ {"roff","application/x-troff"},
+ {"texinfo","application/x-texinfo"},
+ {"texi","application/x-texinfo"},
+ {"tex","application/x-tex"},
+ {"tcl","application/x-tcl"},
+ {"tar","application/x-tar"},
+ {"sv4crc","application/x-sv4crc"},
+ {"sv4cpio","application/x-sv4cpio"},
+ {"sit","application/x-stuffit"},
+ {"shar","application/x-shar"},
+ {"sh","application/x-sh"},
+ {"nc","application/x-netcdf"},
+ {"cdf","application/x-netcdf"},
+ {"mif","application/x-mif"},
+ {"latex","application/x-latex"},
+ {"skp","application/x-koan"},
+ {"skd","application/x-koan"},
+ {"skt","application/x-koan"},
+ {"skm","application/x-koan"},
+ {"cgi","application/x-httpd-cgi"},
+ {"hdf","application/x-hdf"},
+ {"gz","application/x-gzip"},
+ {"gtar","application/x-gtar"},
+ {"dvi","application/x-dvi"},
+ {"dcr","application/x-director"},
+ {"dir","application/x-director"},
+ {"dxr","application/x-director"},
+ {"csh","application/x-csh"},
+ {"cpio","application/x-cpio"},
+ {"Z","application/x-compress"},
+ {"vcd","application/x-cdlink"},
+ {"bcpio","application/x-bcpio"},
+ {"rtf","application/rtf"},
+ {"ppt","application/powerpoint"},
+ {"ai","application/postscript"},
+ {"eps","application/postscript"},
+ {"ps","application/postscript"},
+ {"pdf","application/pdf"},
+ {"oda","application/oda"},
+ {"bin","application/octet-stream"},
+ {"dms","application/octet-stream"},
+ {"lha","application/octet-stream"},
+ {"lzh","application/octet-stream"},
+ {"exe","application/octet-stream"},
+ {"class","application/octet-stream"},
+ {"doc","application/msword"},
+ {"cpt","application/mac-compactpro"},
+ {"hqx","application/mac-binhex40"}]} = httpd_conf:load_mime_types(MimeTypes).
+%%--------------------------------------------------------------------
+%% Internal functions -----------------------------------
+%%--------------------------------------------------------------------
+url(http, End, Config) ->
+ Port = proplists:get_value(port, Config),
+ {ok,Host} = inet:gethostname(),
+ ?URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End.
+
+do_max_clients(Config) ->
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Port = proplists:get_value(port, Config),
+ Type = proplists:get_value(type, Config),
+
+ Request = http_request("GET /index.html ", Version, Host),
+ BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
+ {ok, Socket} = inets_test_lib:connect_bin(Type, Host, Port, transport_opts(Type, Config)),
+ inets_test_lib:send(Type, Socket, BlockRequest),
+ ct:sleep(100), %% Avoid possible timing issues
+ ok = httpd_test_lib:verify_request(Type, Host,
+ Port,
+ transport_opts(Type, Config),
+ proplists:get_value(node, Config),
+ Request,
+ [{statuscode, 503},
+ {version, Version}]),
+ receive
+ {_, Socket, _Msg} ->
+ ok
+ end,
+ inets_test_lib:close(Type, Socket),
+ ct:sleep(100), %% Avoid possible timing issues
+ ok = httpd_test_lib:verify_request(Type, Host,
+ Port,
+ transport_opts(Type, Config),
+ proplists:get_value(node, Config),
+ Request,
+ [{statuscode, 200},
+ {version, Version}]).
-%%-------------------------------------------------------------------------
-ip_host(doc) ->
- ["Control that the server accepts/rejects requests with/ without host"];
-ip_host(suite)->
- [];
-ip_host(Config) when is_list(Config) ->
- httpd_1_1:host(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_chunked(doc) ->
- ["Control that the server accepts chunked requests"];
-ip_chunked(suite) ->
- [];
-ip_chunked(Config) when is_list(Config) ->
- httpd_1_1:chunked(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_expect(doc) ->
- ["Control that the server handles request with the expect header "
- "field appropiate"];
-ip_expect(suite)->
- [];
-ip_expect(Config) when is_list(Config) ->
- httpd_1_1:expect(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_range(doc) ->
- ["Control that the server can handle range requests to plain files"];
-ip_range(suite)->
- [];
-ip_range(Config) when is_list(Config) ->
- httpd_1_1:range(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_if_test(doc) ->
- ["Test that the if - request header fields is handled correclty"];
-ip_if_test(suite) ->
- [];
-ip_if_test(Config) when is_list(Config) ->
- ServerRoot = ?config(server_root, Config),
- DocRoot = filename:join([ServerRoot, "htdocs"]),
- httpd_1_1:if_test(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config), DocRoot),
- ok.
-%%-------------------------------------------------------------------------
-ip_http_trace(doc) ->
- ["Test the trace module "];
-ip_http_trace(suite) ->
- [];
-ip_http_trace(Config) when is_list(Config) ->
- httpd_1_1:http_trace(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config)),
- ok.
-%%-------------------------------------------------------------------------
-ip_http1_1_head(doc) ->
- ["Test the trace module "];
-ip_http1_1_head(suite)->
- [];
-ip_http1_1_head(Config) when is_list(Config) ->
- httpd_1_1:head(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config)),
- ok.
+setup_server_dirs(ServerRoot, DocRoot, DataDir) ->
+ CgiDir = filename:join(ServerRoot, "cgi-bin"),
+ AuthDir = filename:join(ServerRoot, "auth"),
+ PicsDir = filename:join(ServerRoot, "icons"),
+ ConfigDir = filename:join(ServerRoot, "config"),
-%%-------------------------------------------------------------------------
-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 = file:make_dir(ServerRoot),
+ ok = file:make_dir(DocRoot),
+ ok = file:make_dir(CgiDir),
+ ok = file:make_dir(AuthDir),
+ ok = file:make_dir(PicsDir),
+ ok = file:make_dir(ConfigDir),
+
+ DocSrc = filename:join(DataDir, "server_root/htdocs"),
+ AuthSrc = filename:join(DataDir, "server_root/auth"),
+ CgiSrc = filename:join(DataDir, "server_root/cgi-bin"),
+ PicsSrc = filename:join(DataDir, "server_root/icons"),
+ ConfigSrc = filename:join(DataDir, "server_root/config"),
- 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"}]),
+ inets_test_lib:copy_dirs(DocSrc, DocRoot),
+ inets_test_lib:copy_dirs(AuthSrc, AuthDir),
+ inets_test_lib:copy_dirs(CgiSrc, CgiDir),
+ inets_test_lib:copy_dirs(PicsSrc, PicsDir),
+ inets_test_lib:copy_dirs(ConfigSrc, ConfigDir),
+
+ Cgi = case test_server:os_type() of
+ {win32, _} ->
+ "cgi_echo.exe";
+ _ ->
+ "cgi_echo"
+ end,
- 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"}]),
+ inets_test_lib:copy_file(Cgi, DataDir, CgiDir),
+ AbsCgi = filename:join([CgiDir, Cgi]),
+ {ok, FileInfo} = file:read_file_info(AbsCgi),
+ ok = file:write_file_info(AbsCgi, FileInfo#file_info{mode = 8#00755}),
- ok.
-%%-------------------------------------------------------------------------
-ip_mod_cgi_chunked_encoding_test(doc) ->
- ["Test the trace module "];
-ip_mod_cgi_chunked_encoding_test(suite)->
- [];
-ip_mod_cgi_chunked_encoding_test(Config) when is_list(Config) ->
- Host = ?config(host, Config),
- Script =
- case test_server:os_type() of
- {win32, _} ->
- "/cgi-bin/printenv.bat";
- _ ->
- "/cgi-bin/printenv.sh"
- end,
- Requests =
- ["GET " ++ Script ++ " HTTP/1.1\r\nHost:"++ Host ++"\r\n\r\n",
- "GET /cgi-bin/erl/httpd_example/newformat HTTP/1.1\r\nHost:"
- ++ Host ++"\r\n\r\n"],
- httpd_1_1:mod_cgi_chunked_encoding_test(ip_comm, ?IP_PORT,
- Host,
- ?config(node, Config),
- Requests),
- ok.
-
-%-------------------------------------------------------------------------
-
-ipv6_hostname_ipcomm() ->
- [{require, ipv6_hosts}].
-ipv6_hostname_ipcomm(X) ->
- SocketType = ip_comm,
- Port = ?IP_PORT,
- ipv6_hostname(SocketType, Port, X).
-
-ipv6_hostname_essl() ->
- [{require, ipv6_hosts}].
-ipv6_hostname_essl(X) ->
- SocketType = essl,
- Port = ?SSL_PORT,
- ipv6_hostname(SocketType, Port, X).
-
-ipv6_hostname(_SocketType, _Port, doc) ->
- ["Test standard ipv6 address"];
-ipv6_hostname(_SocketType, _Port, suite)->
- [];
-ipv6_hostname(SocketType, Port, Config) when is_list(Config) ->
- tsp("ipv6_hostname -> entry with"
- "~n SocketType: ~p"
- "~n Port: ~p"
- "~n Config: ~p", [SocketType, Port, Config]),
- Host = ?config(host, Config),
- URI = "GET HTTP://" ++
- Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n",
- tsp("ipv6_hostname -> Host: ~p", [Host]),
- httpd_test_lib:verify_request(SocketType, Host, Port, [inet6],
- node(),
- URI,
- [{statuscode, 200}, {version, "HTTP/1.1"}]),
- ok.
-
-%%-------------------------------------------------------------------------
-
-ipv6_address_ipcomm() ->
- [{require, ipv6_hosts}].
-ipv6_address_ipcomm(X) ->
- SocketType = ip_comm,
- Port = ?IP_PORT,
- ipv6_address(SocketType, Port, X).
-
-ipv6_address_essl() ->
- [{require, ipv6_hosts}].
-ipv6_address_essl(X) ->
- SocketType = essl,
- Port = ?SSL_PORT,
- ipv6_address(SocketType, Port, X).
-
-ipv6_address(_SocketType, _Port, doc) ->
- ["Test standard ipv6 address"];
-ipv6_address(_SocketType, _Port, suite)->
- [];
-ipv6_address(SocketType, Port, Config) when is_list(Config) ->
- tsp("ipv6_address -> entry with"
- "~n SocketType: ~p"
- "~n Port: ~p"
- "~n Config: ~p", [SocketType, Port, Config]),
- Host = ?config(host, Config),
- tsp("ipv6_address -> Host: ~p", [Host]),
- URI = "GET HTTP://" ++
- Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n",
- httpd_test_lib:verify_request(SocketType, Host, Port, [inet6],
- node(),
- URI,
- [{statuscode, 200}, {version, "HTTP/1.1"}]),
- ok.
-
-
-%%--------------------------------------------------------------------
-ticket_5775(doc) ->
- ["Tests that content-length is correct"];
-ticket_5775(suite) ->
- [];
-ticket_5775(Config) ->
- ok=httpd_test_lib:verify_request(ip_comm, ?config(host, Config),
- ?IP_PORT, ?config(node, Config),
- "GET /cgi-bin/erl/httpd_example:get_bin "
- "HTTP/1.0\r\n\r\n",
- [{statuscode, 200},
- {version, "HTTP/1.0"}]),
- ok.
-ticket_5865(doc) ->
- ["Tests that a header without last-modified is handled"];
-ticket_5865(suite) ->
- [];
-ticket_5865(Config) ->
- ?SKIP(as_of_r15_behaviour_of_calendar_has_changed),
- Host = ?config(host,Config),
- ServerRoot = ?config(server_root, Config),
- DocRoot = filename:join([ServerRoot, "htdocs"]),
- File = filename:join([DocRoot,"last_modified.html"]),
-
- Bad_mtime = case test_server:os_type() of
- {win32, _} ->
- {{1600,12,31},{23,59,59}};
- {unix, _} ->
- {{1969,12,31},{23,59,59}}
- end,
+ EnvCGI = filename:join([ServerRoot, "cgi-bin", "printenv.sh"]),
+ {ok, FileInfo1} = file:read_file_info(EnvCGI),
+ ok = file:write_file_info(EnvCGI,
+ FileInfo1#file_info{mode = 8#00755}).
- {ok,FI}=file:read_file_info(File),
+start_apps(Group) when Group == https_basic;
+ Group == https_limit;
+ Group == https_custom;
+ Group == https_basic_auth;
+ Group == https_auth_api;
+ Group == https_auth_api_dets;
+ Group == https_auth_api_mnesia;
+ Group == https_htaccess;
+ Group == https_security;
+ Group == https_reload
+ ->
+ inets_test_lib:start_apps([inets, asn1, crypto, public_key, ssl]);
+start_apps(Group) when Group == http_basic;
+ Group == http_limit;
+ Group == http_custom;
+ Group == http_basic_auth;
+ Group == http_auth_api;
+ Group == http_auth_api_dets;
+ Group == http_auth_api_mnesia;
+ Group == http_htaccess;
+ Group == http_security;
+ Group == http_reload;
+ Group == http_mime_types->
+ inets_test_lib:start_apps([inets]).
+
+server_start(_, HttpdConfig) ->
+ {ok, Pid} = inets:start(httpd, HttpdConfig),
+ Serv = inets:services_info(),
+ {value, {_, _, Info}} = lists:keysearch(Pid, 2, Serv),
+ {Pid, proplists:get_value(port, Info)}.
+
+init_ssl(Group, Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ CaKey = {_Trusted,_} =
+ erl_make_certs:make_cert([{key, dsa},
+ {subject,
+ [{name, "Public Key"},
+ {?'id-at-name',
+ {printableString, "public_key"}},
+ {?'id-at-pseudonym',
+ {printableString, "pubkey"}},
+ {city, "Stockholm"},
+ {country, "SE"},
+ {org, "erlang"},
+ {org_unit, "testing dep"}
+ ]}
+ ]),
+ ok = erl_make_certs:write_pem(PrivDir, "public_key_cacert", CaKey),
- case file:write_file_info(File,FI#file_info{mtime=Bad_mtime}) of
+ CertK1 = {_Cert1, _} = erl_make_certs:make_cert([{issuer, CaKey}]),
+ CertK2 = {_Cert2,_} = erl_make_certs:make_cert([{issuer, CertK1},
+ {digest, md5},
+ {extensions, false}]),
+ ok = erl_make_certs:write_pem(PrivDir, "public_key_cert", CertK2),
+
+ case start_apps(Group) 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}
+ init_httpd(Group, [{type, ssl} | Config]);
+ _ ->
+ {skip, "Could not start https apps"}
end.
-ticket_5913(doc) ->
- ["Tests that a header without last-modified is handled"];
-ticket_5913(suite) -> [];
-ticket_5913(Config) ->
- ok = httpd_test_lib:verify_request(ip_comm, ?config(host, Config),
- ?IP_PORT, ?config(node, Config),
- "GET /cgi-bin/erl/httpd_example:get_bin "
- "HTTP/1.0\r\n\r\n",
- [{statuscode, 200},
- {version, "HTTP/1.0"}]),
- ok.
-
-ticket_6003(doc) ->
- ["Tests that a URI with a bad hexadecimal code is handled"];
-ticket_6003(suite) -> [];
-ticket_6003(Config) ->
- ok = httpd_test_lib:verify_request(ip_comm, ?config(host, Config),
- ?IP_PORT, ?config(node, Config),
- "GET http://www.erlang.org/%skalle "
- "HTTP/1.0\r\n\r\n",
- [{statuscode, 400},
- {version, "HTTP/1.0"}]),
- ok.
-
-ticket_7304(doc) ->
- ["Tests missing CR in delimiter"];
-ticket_7304(suite) ->
- [];
-ticket_7304(Config) ->
- ok = httpd_test_lib:verify_request(ip_comm, ?config(host, Config),
- ?IP_PORT, ?config(node, Config),
- "GET / HTTP/1.0\r\n\n",
- [{statuscode, 200},
- {version, "HTTP/1.0"}]),
- ok.
-
-%%--------------------------------------------------------------------
-%% Internal functions
-%%--------------------------------------------------------------------
-dos_hostname(Type, Port, Host, Node, Max) ->
- H1 = {"", 200},
- H2 = {"dummy-host.ericsson.se", 200},
- TooLongHeader = lists:append(lists:duplicate(Max + 1, "a")),
- H3 = {TooLongHeader, 403},
- Hosts = [H1,H2,H3],
- dos_hostname_poll(Type, Host, Port, Node, Hosts).
-
-%% make_ipv6(T) when is_tuple(T) andalso (size(T) =:= 8) ->
-%% make_ipv6(tuple_to_list(T));
-
-%% make_ipv6([_, _, _, _, _, _, _, _] = IPV6) ->
-%% lists:flatten(io_lib:format("~s:~s:~s:~s:~s:~s:~s:~s", IPV6)).
-
-
-%%--------------------------------------------------------------------
-%% Other help functions
-create_config(Config, Access, FileName) ->
- ServerRoot = ?config(server_root, Config),
- TcTopDir = ?config(tc_top_dir, Config),
- Port = ?config(port, Config),
- Type = ?config(sock_type, Config),
- Host = ?config(host, Config),
- Mods = io_lib:format("~p", [httpd_mod]),
- Funcs = io_lib:format("~p", [ssl_password_cb]),
- MaxHdrSz = io_lib:format("~p", [256]),
- MaxHdrAct = io_lib:format("~p", [close]),
-
- io:format(user,
- "create_config -> "
- "~n ServerRoot: ~p"
- "~n TcTopDir: ~p"
- "~n Type: ~p"
- "~n Port: ~p"
- "~n Host: ~p"
- "~n", [ServerRoot, TcTopDir, Type, Port, Host]),
-
- SSL =
- if
- (Type =:= ssl) orelse
- (Type =:= essl) ->
- [cline(["SSLCertificateFile ",
- filename:join(ServerRoot, "ssl/ssl_server.pem")]),
- cline(["SSLCertificateKeyFile ",
- filename:join(ServerRoot, "ssl/ssl_server.pem")]),
- cline(["SSLCACertificateFile ",
- filename:join(ServerRoot, "ssl/ssl_server.pem")]),
- cline(["SSLPasswordCallbackModule ", Mods]),
- cline(["SSLPasswordCallbackFunction ", Funcs]),
- cline(["SSLVerifyClient 0"]),
- cline(["SSLVerifyDepth 1"])];
- true ->
- []
- end,
- ModOrder =
- case Access of
- mod_htaccess ->
- "Modules mod_alias mod_htaccess mod_auth "
- "mod_security "
- "mod_responsecontrol mod_trace mod_esi "
- "mod_actions mod_cgi mod_include mod_dir "
- "mod_range mod_get "
- "mod_head mod_log mod_disk_log";
- _ ->
- "Modules mod_alias mod_auth mod_security "
- "mod_responsecontrol mod_trace mod_esi "
- "mod_actions mod_cgi mod_include mod_dir "
- "mod_range mod_get "
- "mod_head mod_log mod_disk_log"
- end,
-
- %% The test suite currently does not handle an explicit BindAddress.
- %% They assume any has been used, that is Addr is always set to undefined!
-
- %% {ok, Hostname} = inet:gethostname(),
- %% {ok, Addr} = inet:getaddr(Hostname, inet6),
- %% AddrStr = make_ipv6(Addr),
- %% BindAddress = lists:flatten(io_lib:format("~s|inet6", [AddrStr])),
-
- BindAddress = "*|inet",
- %% BindAddress = "*",
-
- HttpConfig = [
- cline(["Port ", integer_to_list(Port)]),
- cline(["ServerName ", Host]),
- cline(["SocketType ", atom_to_list(Type)]),
- cline([ModOrder]),
- %% cline(["LogFormat ", "erlang"]),
- cline(["ServerAdmin [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),
+server_config(http_basic, Config) ->
+ basic_conf() ++ server_config(http, Config);
+server_config(https_basic, Config) ->
+ basic_conf() ++ server_config(https, Config);
+server_config(http_reload, Config) ->
+ [{keep_alive_timeout, 2}] ++ server_config(http, Config);
+server_config(https_reload, Config) ->
+ [{keep_alive_timeout, 2}] ++ server_config(https, Config);
+server_config(http_limit, Config) ->
+ Conf = [{max_clients, 1},
+ %% Make sure option checking code is run
+ {max_content_length, 100000002}] ++ server_config(http, Config),
+ ct:pal("Received message ~p~n", [Conf]),
+ Conf;
+server_config(http_custom, Config) ->
+ [{customize, ?MODULE}] ++ server_config(http, Config);
+server_config(https_custom, Config) ->
+ [{customize, ?MODULE}] ++ server_config(https, Config);
+server_config(https_limit, Config) ->
+ [{max_clients, 1}] ++ server_config(https, Config);
+server_config(http_basic_auth, Config) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ auth_conf(ServerRoot) ++ server_config(http, Config);
+server_config(https_basic_auth, Config) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ auth_conf(ServerRoot) ++ server_config(https, Config);
+server_config(http_auth_api, Config) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ auth_api_conf(ServerRoot, plain) ++ server_config(http, Config);
+server_config(https_auth_api, Config) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ auth_api_conf(ServerRoot, plain) ++ server_config(https, Config);
+server_config(http_auth_api_dets, Config) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ auth_api_conf(ServerRoot, dets) ++ server_config(http, Config);
+server_config(https_auth_api_dets, Config) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ auth_api_conf(ServerRoot, dets) ++ server_config(https, Config);
+server_config(http_auth_api_mnesia, Config) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ auth_api_conf(ServerRoot, mnesia) ++ server_config(http, Config);
+server_config(https_auth_api_mnesia, Config) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ auth_api_conf(ServerRoot, mnesia) ++ server_config(https, Config);
+server_config(http_htaccess, Config) ->
+ auth_access_conf() ++ server_config(http, Config);
+server_config(https_htaccess, Config) ->
+ auth_access_conf() ++ server_config(https, Config);
+server_config(http_security, Config) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ tl(auth_conf(ServerRoot)) ++ security_conf(ServerRoot) ++ server_config(http, Config);
+server_config(https_security, Config) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ tl(auth_conf(ServerRoot)) ++ security_conf(ServerRoot) ++ server_config(https, Config);
+server_config(http_mime_types, Config0) ->
+ Config1 = basic_conf() ++ server_config(http, Config0),
+ ServerRoot = proplists:get_value(server_root, Config0),
+ MimeTypesFile = filename:join([ServerRoot,"config", "mime.types"]),
+ [{mime_types, MimeTypesFile} | proplists:delete(mime_types, Config1)];
+
+server_config(http, Config) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ [{port, 0},
+ {socket_type, {ip_comm, [{nodelay, true}]}},
+ {server_name,"httpd_test"},
+ {server_root, ServerRoot},
+ {document_root, proplists:get_value(doc_root, Config)},
+ {bind_address, any},
+ {ipfamily, proplists:get_value(ipfamily, Config)},
+ {max_header_size, 256},
+ {max_header_action, close},
+ {directory_index, ["index.html", "welcome.html"]},
+ {mime_types, [{"html","text/html"},{"htm","text/html"}, {"shtml","text/html"},
+ {"gif", "image/gif"}]},
+ {alias, {"/icons/", filename:join(ServerRoot,"icons") ++ "/"}},
+ {alias, {"/pics/", filename:join(ServerRoot,"icons") ++ "/"}},
+ {script_alias, {"/cgi-bin/", filename:join(ServerRoot, "cgi-bin") ++ "/"}},
+ {script_alias, {"/htbin/", filename:join(ServerRoot, "cgi-bin") ++ "/"}},
+ {erl_script_alias, {"/cgi-bin/erl", [httpd_example, io]}},
+ {eval_script_alias, {"/eval", [httpd_example, io]}}
+ ];
+
+server_config(https, Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ [{socket_type, {essl,
+ [{nodelay, true},
+ {cacertfile,
+ filename:join(PrivDir, "public_key_cacert.pem")},
+ {certfile,
+ filename:join(PrivDir, "public_key_cert.pem")},
+ {keyfile,
+ filename:join(PrivDir, "public_key_cert_key.pem")}
+ ]}}] ++ proplists:delete(socket_type, server_config(http, Config)).
+
+init_httpd(Group, Config0) ->
+ Config1 = proplists:delete(port, Config0),
+ Config = proplists:delete(server_pid, Config1),
+ {Pid, Port} = server_start(Group, server_config(Group, Config)),
+ [{server_pid, Pid}, {port, Port} | Config].
+
+http_request(Request, "HTTP/1.1" = Version, Host, {Headers, Body}) ->
+ Request ++ Version ++ "\r\nhost:" ++ Host ++ "\r\n" ++ Headers ++ "\r\n" ++ Body;
+http_request(Request, Version, _, {Headers, Body}) ->
+ Request ++ Version ++ "\r\n" ++ Headers ++ "\r\n" ++ Body.
+
+http_request(Request, "HTTP/1.1" = Version, Host) ->
+ Request ++ Version ++ "\r\nhost:" ++ Host ++ "\r\n\r\n";
+http_request(Request, Version, _) ->
+ Request ++ Version ++ "\r\n\r\n".
+
+auth_request(Path, User, Passwd, "HTTP/1.1" = Version, Host) ->
+ "GET " ++ Path ++ " " ++ Version ++ "\r\nhost:" ++ Host ++
+ "\r\nAuthorization: Basic " ++
+ base64:encode_to_string(User++":"++Passwd) ++
+ "\r\n\r\n";
+auth_request(Path, User, Passwd, Version, _Host) ->
+ "GET " ++ Path ++ " " ++ Version ++
+ "\r\nAuthorization: Basic " ++
+ base64:encode_to_string(User++":"++Passwd) ++
+ "\r\n\r\n".
+
+http_request_missing_CR(Request, "HTTP/1.1" = Version, Host) ->
+ Request ++ Version ++ "\r\nhost:" ++ Host ++ "\r\n\r\n\n";
+http_request_missing_CR(Request, Version, _) ->
+ Request ++ Version ++ "\r\n\n".
+
+head_status("HTTP/0.9") ->
+ 501; %% Not implemented in HTTP/0.9
+head_status(_) ->
+ 200.
+
+basic_conf() ->
+ [{modules, [mod_alias, mod_range, mod_responsecontrol,
+ mod_trace, mod_esi, mod_cgi, mod_dir, mod_get, mod_head]}].
+
+auth_access_conf() ->
+ [{modules, [mod_alias, mod_htaccess, mod_dir, mod_get, mod_head]},
+ {access_files, [".htaccess"]}].
+
+auth_conf(Root) ->
+ [{modules, [mod_alias, mod_auth, mod_dir, mod_get, mod_head]},
+ {directory, {filename:join(Root, "htdocs/open"),
+ [{auth_type, plain},
+ {auth_name, "Open Area"},
+ {auth_user_file, filename:join(Root, "auth/passwd")},
+ {auth_group_file, filename:join(Root, "auth/group")},
+ {require_user, ["one", "Aladdin"]}]}},
+ {directory, {filename:join(Root, "htdocs/secret"),
+ [{auth_type, plain},
+ {auth_name, "Secret Area"},
+ {auth_user_file, filename:join(Root, "auth/passwd")},
+ {auth_group_file, filename:join(Root, "auth/group")},
+ {require_group, ["group1", "group2"]}]}},
+ {directory, {filename:join(Root, "htdocs/secret/top_secret"),
+ [{auth_type, plain},
+ {auth_name, "Top Secret Area"},
+ {auth_user_file, filename:join(Root, "auth/passwd")},
+ {auth_group_file, filename:join(Root, "auth/group")},
+ {require_group, ["group3"]}]}}].
+
+auth_api_conf(Root, plain) ->
+ [{modules, [mod_alias, mod_auth, mod_dir, mod_get, mod_head]},
+ {directory, {filename:join(Root, "htdocs/open"),
+ [{auth_type, plain},
+ {auth_name, "Open Area"},
+ {auth_user_file, filename:join(Root, "auth/passwd")},
+ {auth_group_file, filename:join(Root, "auth/group")},
+ {require_user, ["one", "Aladdin"]}]}},
+ {directory, {filename:join(Root, "htdocs/secret"),
+ [{auth_type, plain},
+ {auth_name, "Secret Area"},
+ {auth_user_file, filename:join(Root, "auth/passwd")},
+ {auth_group_file, filename:join(Root, "auth/group")},
+ {require_group, ["group1", "group2"]}]}},
+ {directory, {filename:join(Root, "htdocs/secret/top_secret"),
+ [{auth_type, plain},
+ {auth_name, "Top Secret Area"},
+ {auth_user_file, filename:join(Root, "auth/passwd")},
+ {auth_group_file, filename:join(Root, "auth/group")},
+ {require_group, ["group3"]}]}}];
+
+auth_api_conf(Root, dets) ->
[
- 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"])
+ {modules, [mod_alias, mod_auth, mod_dir, mod_get, mod_head]},
+ {directory, {filename:join(Root, "htdocs/dets_open"),
+ [{auth_type, dets},
+ {auth_name, "Dets Open Area"},
+ {auth_user_file, filename:join(Root, "passwd")},
+ {auth_group_file, filename:join(Root, "group")},
+ {require_user, ["one", "Aladdin"]}]}},
+ {directory, {filename:join(Root, "htdocs/dets_secret"),
+ [{auth_type, dets},
+ {auth_name, "Dests Secret Area"},
+ {auth_user_file, filename:join(Root, "passwd")},
+ {auth_group_file, filename:join(Root, "group")},
+ {require_group, ["group1", "group2"]}]}},
+ {directory, {filename:join(Root, "htdocs/dets_secret/top_secret"),
+ [{auth_type, dets},
+ {auth_name, "Dets Top Secret Area"},
+ {auth_user_file, filename:join(Root, "passwd")},
+ {auth_group_file, filename:join(Root, "group")},
+ {require_group, ["group3"]}]}}
+ ];
+
+auth_api_conf(Root, mnesia) ->
+ [{modules, [mod_alias, mod_auth, mod_dir, mod_get, mod_head]},
+ {directory, {filename:join(Root, "htdocs/mnesia_open"),
+ [{auth_type, mnesia},
+ {auth_name, "Mnesia Open Area"},
+ {require_user, ["one", "Aladdin"]}]}},
+ {directory, {filename:join(Root, "htdocs/mnesia_secret"),
+ [{auth_type, mnesia},
+ {auth_name, "Mnesia Secret Area"},
+ {require_group, ["group1", "group2"]}]}},
+ {directory, {filename:join(Root, "htdocs/mnesia_secret/top_secret"),
+ [{auth_type, mnesia},
+ {auth_name, "Mnesia Top Secret Area"},
+ {require_group, ["group3"]}]}}].
+
+security_conf(Root) ->
+ SecFile = filename:join(Root, "security_data"),
+ Open = filename:join(Root, "htdocs/open"),
+ Secret = filename:join(Root, "htdocs/secret"),
+ TopSecret = filename:join(Root, "htdocs/secret/top_secret"),
+
+ [{modules, [mod_alias, mod_auth, mod_security, mod_dir, mod_get, mod_head]},
+ {security_directory, {Open,
+ [{auth_name, "Open Area"},
+ {auth_user_file, filename:join(Root, "auth/passwd")},
+ {auth_group_file, filename:join(Root, "auth/group")},
+ {require_user, ["one", "Aladdin"]} |
+ mod_security_conf(SecFile, Open)]}},
+ {security_directory, {Secret,
+ [{auth_name, "Secret Area"},
+ {auth_user_file, filename:join(Root, "auth/passwd")},
+ {auth_group_file, filename:join(Root, "auth/group")},
+ {require_group, ["group1", "group2"]} |
+ mod_security_conf(SecFile, Secret)]}},
+ {security_directory, {TopSecret,
+ [{auth_name, "Top Secret Area"},
+ {auth_user_file, filename:join(Root, "auth/passwd")},
+ {auth_group_file, filename:join(Root, "auth/group")},
+ {require_group, ["group3"]} |
+ mod_security_conf(SecFile, TopSecret)]}}].
+
+mod_security_conf(SecFile, Dir) ->
+ [{data_file, SecFile},
+ {max_retries, 3},
+ {fail_expire_time, ?FAIL_EXPIRE_TIME},
+ {block_time, 1},
+ {auth_timeout, ?AUTH_TIMEOUT},
+ {callback_module, ?MODULE},
+ {path, Dir} %% This is should not be needed, but is atm, awful design!
].
+
-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])).
+http_status(Request, Config, Expected) ->
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Type = proplists:get_value(type, Config),
+ httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config),
+ transport_opts(Type, Config),
+ proplists:get_value(node, Config),
+ http_request(Request, Version, Host),
+ Expected ++ [{version, Version}]).
+
+http_status(Request, HeadersAndBody, Config, Expected) ->
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Type = proplists:get_value(type, Config),
+ httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config),
+ transport_opts(Type, Config),
+ proplists:get_value(node, Config),
+ http_request(Request, Version, Host, HeadersAndBody),
+ Expected ++ [{version, Version}]).
+
+auth_status(AuthRequest, Config, Expected) ->
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Type = proplists:get_value(type, Config),
+ httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config),
+ transport_opts(Type, Config),
+ proplists:get_value(node, Config),
+ AuthRequest,
+ Expected ++ [{version, Version}]).
+
+basic_auth_requiered(Config) ->
+ ok = http_status("GET /open/ ", Config, [{statuscode, 401},
+ {header, "WWW-Authenticate"}]),
+ ok = http_status("GET /secret/ ", Config, [{statuscode, 401},
+ {header, "WWW-Authenticate"}]),
+ ok = http_status("GET /secret/top_secret/ ", Config, [{statuscode, 401},
+ {header, "WWW-Authenticate"}]).
start_mnesia(Node) ->
case rpc:call(Node, ?MODULE, cleanup_mnesia, []) of
ok ->
ok;
Other ->
- tsf({failed_to_cleanup_mnesia, Other})
+ ct:fail({failed_to_cleanup_mnesia, Other})
end,
case rpc:call(Node, ?MODULE, setup_mnesia, []) of
{atomic, ok} ->
ok;
Other2 ->
- tsf({failed_to_setup_mnesia, Other2})
+ ct:fail({failed_to_setup_mnesia, Other2})
end,
ok.
@@ -2251,6 +1887,29 @@ cleanup_mnesia() ->
mnesia:delete_schema([node()]),
ok.
+transport_opts(ssl, Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ [proplists:get_value(ipfamily, Config),
+ {cacertfile, filename:join(PrivDir, "public_key_cacert.pem")}];
+transport_opts(_, Config) ->
+ [proplists:get_value(ipfamily, Config)].
+
+
+%%% mod_range
+create_range_data(Path) ->
+ PathAndFileName=filename:join([Path,"range.txt"]),
+ case file:read_file(PathAndFileName) of
+ {error, enoent} ->
+ file:write_file(PathAndFileName,list_to_binary(["12345678901234567890",
+ "12345678901234567890",
+ "12345678901234567890",
+ "12345678901234567890",
+ "12345678901234567890"]));
+ _ ->
+ ok
+ end.
+
+%%% mod_htaccess
create_htaccess_data(Path, IpAddress)->
create_htaccess_dirs(Path),
@@ -2347,99 +2006,189 @@ remove_htaccess(Path)->
file:delete(filename:join([Path,"ht","groups.file"])),
remove_htaccess_dirs(Path).
+dos_hostname(Type, Port, Host, Node, Version, Max) ->
+ TooLongHeader = lists:append(lists:duplicate(Max + 1, "a")),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ dos_hostname_request("", Version),
+ [{statuscode, 200},
+ {version, Version}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ dos_hostname_request("dummy-host.ericsson.se", Version),
+ [{statuscode, 200},
+ {version, Version}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ dos_hostname_request(TooLongHeader, Version),
+ [{statuscode, request_entity_too_large_code(Version)},
+ {version, Version}]).
+dos_hostname_request(Host, Version) ->
+ dos_http_request("GET / ", Version, Host).
+
+dos_http_request(Request, "HTTP/1.1" = Version, Host) ->
+ http_request(Request, Version, Host);
+dos_http_request(Request, Version, Host) ->
+ Request ++ Version ++ "\r\nhost:" ++ Host ++ "\r\n\r\n".
+
+request_entity_too_large_code("HTTP/1.0") ->
+ 403; %% 413 not defined in HTTP/1.0
+request_entity_too_large_code(_) ->
+ 413.
+
+length_required_code("HTTP/1.0") ->
+ 403; %% 411 not defined in HTTP/1.0
+length_required_code(_) ->
+ 411.
+
+garbage_content_length(Type, Port, Host, Node, Version) ->
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ garbage_content_length_request("GET / ", Version, Host, "aaaa"),
+ [{statuscode, length_required_code(Version)},
+ {version, Version}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ garbage_content_length_request("GET / ", Version, Host,
+ lists:duplicate($a, 100)),
+ [{statuscode, request_entity_too_large_code(Version)},
+ {version, Version}]).
+
+garbage_content_length_request(Request, Version, Host, Garbage) ->
+ http_request(Request, Version, Host,
+ {"content-length:" ++ Garbage, "Body with garbage content length indicator"}).
+
+
+update_password(Node, ServerRoot, _Address, Port, AuthPrefix, Dir, Old, New)->
+ Directory = filename:join([ServerRoot, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, update_password,
+ [undefined, Port, Directory, Old, New, New]).
+
+add_user(Node, Root, Port, AuthPrefix, Dir, User, Password, UserData) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, add_user,
+ [User, Password, UserData, Addr, Port, Directory]).
+
+
+delete_user(Node, Root, _Host, Port, AuthPrefix, Dir, User) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, delete_user, [User, Addr, Port, Directory]).
+remove_users(Node, ServerRoot, Host, Port, AuthPrefix, Dir) ->
+ %% List users, delete them, and make sure they are gone.
+ case list_users(Node, ServerRoot, Host, Port, AuthPrefix, Dir) of
+ {ok, Users} ->
+ lists:foreach(fun(User) ->
+ delete_user(Node, ServerRoot, Host,
+ Port, AuthPrefix, Dir, User)
+ end,
+ Users),
+ {ok, []} = list_users(Node, ServerRoot, Host, Port, AuthPrefix, Dir);
+ _ ->
+ ok
+ end.
+
+list_users(Node, Root, _Host, Port, AuthPrefix, Dir) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, list_users, [Addr, Port, Directory]).
+
+remove_groups(Node, ServerRoot, Host, Port, AuthPrefix, Dir) ->
+ {ok, Groups} = list_groups(Node, ServerRoot, Host, Port, AuthPrefix, Dir),
+ lists:foreach(fun(Group) ->
+ delete_group(Node, Group, Port, ServerRoot, AuthPrefix, Dir)
+ end,
+ Groups),
+ {ok, []} = list_groups(Node, ServerRoot, Host, Port, AuthPrefix, Dir).
+
+delete_group(Node, Group, Port, Root, AuthPrefix, Dir) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, delete_group, [Group, Addr, Port, Directory]).
+
+list_groups(Node, Root, _, Port, AuthPrefix, Dir) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, list_groups, [Addr, Port, Directory]).
+
+add_group_member(Node, Root, Port, AuthPrefix, Dir, User, Group) ->
+ Addr = undefined,
+ Directory = filename:join([Root, "htdocs", AuthPrefix ++ Dir]),
+ rpc:call(Node, mod_auth, add_group_member, [Group, User, Addr, Port,
+ Directory]).
+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])).
+
+receive_security_event(Event, Node, Port) ->
+ receive
+ Event ->
+ ok;
+ {'EXIT', _, _} ->
+ receive_security_event(Event, Node, Port)
+ after 5000 ->
+ %% Flush the message queue, to see if we got something...
+ inets_test_lib:flush()
+ end.
-dos_hostname_poll(Type, Host, Port, Node, Hosts) ->
- [dos_hostname_poll1(Type, Host, Port, Node, Host1, Code)
- || {Host1,Code} <- Hosts].
+list_blocked_users(Node,Port) ->
+ Addr = undefined, % Assumed to be on the same host
+ rpc:call(Node, mod_security, list_blocked_users, [Addr,Port]).
-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".
+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]).
-get_nof_clients(Mode, Load) ->
- get_nof_clients(test_server:os_type(), Mode, Load).
+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]).
-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.
+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]).
-%% Make a file 100 bytes long containing 012...9*10
-create_range_data(Path) ->
- PathAndFileName=filename:join([Path,"range.txt"]),
- file:write_file(PathAndFileName,list_to_binary(["12345678901234567890",
- "12345678901234567890",
- "12345678901234567890",
- "12345678901234567890",
- "12345678901234567890"])).
-
-create_ipv6_config(Config, FileName, Ipv6Address) ->
- ServerRoot = ?config(server_root, Config),
- TcTopDir = ?config(tc_top_dir, Config),
- Port = ?config(port, Config),
- SockType = ?config(sock_type, Config),
- Mods = io_lib:format("~p", [httpd_mod]),
- Funcs = io_lib:format("~p", [ssl_password_cb]),
- Host = ?config(ipv6_host, Config),
-
- MaxHdrSz = io_lib:format("~p", [256]),
- MaxHdrAct = io_lib:format("~p", [close]),
-
- Mod_order = "Modules mod_alias mod_auth mod_esi mod_actions mod_cgi"
- " mod_include mod_dir mod_get mod_head"
- " mod_log mod_disk_log mod_trace",
-
- SSL =
- if
- (SockType =:= ssl) orelse
- (SockType =:= essl) ->
- [cline(["SSLCertificateFile ",
- filename:join(ServerRoot, "ssl/ssl_server.pem")]),
- cline(["SSLCertificateKeyFile ",
- filename:join(ServerRoot, "ssl/ssl_server.pem")]),
- cline(["SSLCACertificateFile ",
- filename:join(ServerRoot, "ssl/ssl_server.pem")]),
- cline(["SSLPasswordCallbackModule ", Mods]),
- cline(["SSLPasswordCallbackFunction ", Funcs]),
- cline(["SSLVerifyClient 0"]),
- cline(["SSLVerifyDepth 1"])];
- true ->
- []
- end,
+list_auth_users(Node,Port) ->
+ Addr = undefined, % Assumed to be on the same host
+ rpc:call(Node, mod_security, list_auth_users, [Addr,Port]).
- BindAddress = "[" ++ Ipv6Address ++"]|inet6",
-
- HttpConfig =
- [cline(["BindAddress ", BindAddress]),
- cline(["Port ", integer_to_list(Port)]),
- cline(["ServerName ", Host]),
- cline(["SocketType ", atom_to_list(SockType)]),
- cline([Mod_order]),
- cline(["ServerRoot ", ServerRoot]),
- cline(["DocumentRoot ", filename:join(ServerRoot, "htdocs")]),
- cline(["MaxHeaderSize ",MaxHdrSz]),
- cline(["MaxHeaderAction ",MaxHdrAct]),
- cline(["DirectoryIndex ", "index.html "]),
- cline(["DefaultType ", "text/plain"]),
- SSL],
- ConfigFile = filename:join([TcTopDir,FileName]),
- {ok, Fd} = file:open(ConfigFile, [write]),
- ok = file:write(Fd, lists:flatten(HttpConfig)),
- ok = file:close(Fd).
-
-
-tsp(F) ->
- inets_test_lib:tsp("[~w]" ++ F, [?MODULE]).
-tsp(F, A) ->
- inets_test_lib:tsp("[~w]" ++ F, [?MODULE|A]).
-
-tsf(Reason) ->
- inets_test_lib:tsf(Reason).
+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]).
+
+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.
+type(ip_comm) ->
+ tcp;
+type(_) ->
+ ssl.
+
+typestr(ip_comm) ->
+ "tcp";
+typestr(_) ->
+ "ssl".
+
+response_header({"server", _}) ->
+ false;
+response_header(Header) ->
+ {true, Header}.
+
+response_default_headers() ->
+ [%% Add new header
+ {"X-Frame-Options", "SAMEORIGIN"},
+ %% Override built-in default
+ {"Date", "Override-date"}].
+
+peer(Config) ->
+ case proplists:get_value(type, Config) of
+ ssl ->
+ "true";
+ _ ->
+ "false"
+ end.
diff --git a/lib/inets/test/httpd_SUITE_data/Makefile.src b/lib/inets/test/httpd_SUITE_data/Makefile.src
index b0fdb43d8d..cea40dd8cb 100644
--- a/lib/inets/test/httpd_SUITE_data/Makefile.src
+++ b/lib/inets/test/httpd_SUITE_data/Makefile.src
@@ -10,5 +10,10 @@ all: $(PROGS)
cgi_echo@exe@: cgi_echo@obj@
$(LD) $(CROSSLDFLAGS) -o cgi_echo cgi_echo@obj@ @LIBS@
+@IFEQ@ (@CC@, cl -nologo)
+cgi_echo@obj@: cgi_echo.c
+ $(CC) /c /Focgi_echo@obj@ $(CFLAGS) cgi_echo.c
+@ELSE@
cgi_echo@obj@: cgi_echo.c
$(CC) -c -o cgi_echo@obj@ $(CFLAGS) cgi_echo.c
+@ENDIF@
diff --git a/lib/inets/test/httpd_SUITE_data/mime_types.txt b/lib/inets/test/httpd_SUITE_data/mime_types.txt
new file mode 100644
index 0000000000..3149a119d5
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/mime_types.txt
@@ -0,0 +1,100 @@
+# This is a comment. I love comments.
+
+
+application/activemessage
+application/andrew-inset
+application/applefile
+application/atomicmail
+application/dca-rft
+application/dec-dx
+application/mac-binhex40 hqx
+application/mac-compactpro cpt
+application/macwriteii
+application/msword doc
+application/news-message-id
+application/news-transmission
+application/octet-stream bin dms lha lzh exe class
+application/oda oda
+application/pdf pdf
+application/postscript ai eps ps
+application/powerpoint ppt
+application/remote-printing
+application/rtf rtf
+application/slate
+application/wita
+application/wordperfect5.1
+application/x-bcpio bcpio
+application/x-cdlink vcd
+application/x-compress Z
+application/x-cpio cpio
+application/x-csh csh
+application/x-director dcr dir dxr
+application/x-dvi dvi
+application/x-gtar gtar
+application/x-gzip gz
+application/x-hdf hdf
+application/x-httpd-cgi cgi
+application/x-koan skp skd skt skm
+application/x-latex latex
+application/x-mif mif
+application/x-netcdf nc cdf
+application/x-sh sh
+application/x-shar shar
+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/zip zip
+audio/basic au snd
+audio/mpeg mpga mp2
+audio/x-aiff aif aiff aifc
+audio/x-pn-realaudio ram
+audio/x-pn-realaudio-plugin rpm
+audio/x-realaudio ra
+audio/x-wav wav
+chemical/x-pdb pdb xyz
+image/gif gif
+image/ief ief
+image/jpeg jpeg jpg jpe
+image/png png
+image/tiff tiff tif
+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/external-body
+message/news
+message/partial
+message/rfc822
+multipart/alternative
+multipart/appledouble
+multipart/digest
+multipart/mixed
+multipart/parallel
+text/css css
+text/html html htm
+text/plain txt
+text/richtext rtx
+text/tab-separated-values tsv
+text/x-setext etx
+text/x-sgml sgml sgm
+video/mpeg mpeg mpg mpe
+video/quicktime qt mov
+video/x-msvideo avi
+video/x-sgi-movie movie
+x-conference/x-cooltalk ice
+x-world/x-vrml wrl vrml
diff --git a/lib/inets/test/httpd_SUITE_data/server_root/config/mime.types b/lib/inets/test/httpd_SUITE_data/server_root/config/mime.types
new file mode 100644
index 0000000000..b68cff21a6
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/config/mime.types
@@ -0,0 +1,4 @@
+text/html html
+text/html htm
+text/html shtml
+image/gif gif
diff --git a/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_client.pem b/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_client.pem
deleted file mode 100644
index 8221139eb4..0000000000
--- a/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_client.pem
+++ /dev/null
@@ -1,22 +0,0 @@
------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
deleted file mode 100644
index fe739c15f7..0000000000
--- a/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_server.pem
+++ /dev/null
@@ -1,22 +0,0 @@
------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_all.erl b/lib/inets/test/httpd_all.erl
new file mode 100644
index 0000000000..9be02e3fd8
--- /dev/null
+++ b/lib/inets/test/httpd_all.erl
@@ -0,0 +1,240 @@
+alias(Version, Type, Port, Host, Node) ->
+ Opts = [],
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node,
+ "GET /pics/icon.sheet.gif "
+ ++ Version ++ "\r\n\r\n",
+ [{statuscode, 200},
+ {header, "Content-Type","image/gif"},
+ {header, "Server"},
+ {header, "Date"},
+ {version, Version}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node,
+ "GET / " ++ Version ++ "\r\n\r\n",
+ [{statuscode, 200},
+ {header, "Content-Type","text/html"},
+ {header, "Server"},
+ {header, "Date"},
+ {version, Version}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node,
+ "GET /misc/ " ++ Version ++ "\r\n\r\n",
+ [{statuscode, 200},
+ {header, "Content-Type","text/html"},
+ {header, "Server"},
+ {header, "Date"},
+ {version, Version}]),
+
+ %% Check redirection if trailing slash is missing.
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node,
+ "GET /misc "++ Version ++ "\r\n\r\n",
+ [{statuscode, 301},
+ {header, "Location"},
+ {header, "Content-Type","text/html"},
+ {version, Version}]).
+
+
+head(Version, Type, Port, Host, Node) ->
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "HEAD /index.html " ++ Version ++ "\r\n\r\n",
+ [{statuscode, 200},
+ {version, Version}]).
+
+
+get(Version, Type, Port, Host, Node) ->
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /index.html " ++ Version ++ "\r\n\r\n",
+ [{statuscode, 200},
+ {header, "Content-Type", "text/html"},
+ {header, "Date"},
+ {header, "Server"},
+ {version, Version}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /fsize.shtml " ++ Version ++ "\r\nHost:"
+ ++ Host ++ "\r\n\r\n",
+ [{statuscode, 200},
+ {header, "Content-Type", "text/html"},
+ {header, "Date"},
+ {header, "Server"}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /secret/dummy.html "
+ ++ Version ++ "\r\n\r\n",
+ [{statuscode, 401},
+ {header, "WWW-Authenticate"},
+ {version, Version}]).
+
+esi(Version, Type, Port, Host, Node) ->
+ %% Check "ErlScriptAlias" and "EvalScriptAlias" directives
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /eval?httpd_example:print(\"Hi!\") "
+ ++ Version ++ "\r\n\r\n",
+ [{statuscode, 200},
+ {version, Version}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /eval?not_allowed:print(\"Hi!\") "
+ ++ Version ++ "\r\n\r\n",
+ [{statuscode, 403},
+ {version, Version}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /eval?httpd_example:undef(\"Hi!\") "
+ ++ Version ++ "\r\n\r\n",
+ [{statuscode, 500},
+ {version, Version}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /cgi-bin/erl/httpd_example "
+ ++ Version ++ "\r\n\r\n",
+ [{statuscode, 400},
+ {version, Version}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /cgi-bin/erl/httpd_example:get "
+ ++ Version ++ "\r\n\r\n",
+ [{statuscode, 200},
+ {version, Version}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /cgi-bin/erl/httpd_example:"
+ "get?input=4711"
+ " HTTP/1.0\r\n\r\n",
+ [{statuscode, 200},
+ {version, "HTTP/1.0"}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /cgi-bin/erl/httpd_example:"
+ "post " ++ Version ++ "\r\n\r\n",
+ [{statuscode, 200},
+ {version, Version}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /cgi-bin/erl/not_allowed:post "
+ ++ Version ++ "\r\n\r\n",
+ [{statuscode, 403},
+ {version, Version}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /cgi-bin/erl/httpd_example:undef "
+ ++ Version ++ "\r\n\r\n",
+ [{statuscode, 404},
+ {version, Version}]),
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /cgi-bin/erl/httpd_example/yahoo "
+ ++ Version ++ "\r\n\r\n",
+ [{statuscode, 302},
+ {version, Version}]),
+ %% Check "ErlScriptNoCache" directive (default: false)
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /cgi-bin/erl/httpd_example:get "
+ ++ Version ++ "\r\n\r\n",
+ [{statuscode, 200},
+ {no_header, "cache-control"},
+ {version, "HTTP/1.0"}]).
+
+cgi(Version, Type, Port, Host, Node) ->
+ {Script, Script2, Script3} =
+ case test_server:os_type() of
+ {win32, _} ->
+ {"printenv.bat", "printenv.sh", "cgi_echo.exe"};
+ _ ->
+ {"printenv.sh", "printenv.bat", "cgi_echo"}
+ end,
+
+ %% The length (> 100) is intentional
+ ok = httpd_test_lib:
+ verify_request(Type, Host, Port, Node,
+ "POST /cgi-bin/" ++ Script3 ++
+ Version ++ " \r\n"
+ "Content-Length:100 \r\n\r\n "
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ " \r\n\r\n",
+ [{statuscode, 200},
+ {version, Version},
+ {header, "content-type", "text/plain"}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /cgi-bin/"++ Script ++
+ " " ++ Version ++ "\r\n\r\n",
+ [{statuscode, 200},
+ {version, Version}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /cgi-bin/not_there " ++
+ Version ++ "\r\n\r\n",
+ [{statuscode, 404},{statuscode, 500},
+ {version, Version}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /cgi-bin/"++ Script ++
+ "?Nisse:kkk?sss/lll " ++ Version ++ "\r\n\r\n",
+ [{statuscode, 200},
+ {version, Version}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "POST /cgi-bin/"++ Script ++
+ " HTTP/1.0\r\n\r\n",
+ [{statuscode, 200},
+ {version, "HTTP/1.0"}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /htbin/"++ Script ++
+ " HTTP/1.0\r\n\r\n",
+ [{statuscode, 200},
+ {version, "HTTP/1.0"}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /htbin/not_there "
+ "HTTP/1.0\r\n\r\n",
+ [{statuscode, 404},{statuscode, 500},
+ {version, "HTTP/1.0"}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /htbin/"++ Script ++
+ "?Nisse:kkk?sss/lll HTTP/1.0\r\n\r\n",
+ [{statuscode, 200},
+ {version, "HTTP/1.0"}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "POST /htbin/"++ Script ++
+ " HTTP/1.0\r\n\r\n",
+ [{statuscode, 200},
+ {version, "HTTP/1.0"}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "POST /htbin/"++ Script ++
+ " HTTP/1.0\r\n\r\n",
+ [{statuscode, 200},
+ {version, "HTTP/1.0"}]),
+
+ %% Execute an existing, but bad CGI script..
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "POST /htbin/"++ Script2 ++
+ " HTTP/1.0\r\n\r\n",
+ [{statuscode, 404},
+ {version, "HTTP/1.0"}]),
+
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "POST /cgi-bin/"++ Script2 ++
+ " HTTP/1.0\r\n\r\n",
+ [{statuscode, 404},
+ {version, "HTTP/1.0"}]),
+
+ %% Check "ScriptNoCache" directive (default: false)
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /cgi-bin/" ++ Script ++
+ " HTTP/1.0\r\n\r\n",
+ [{statuscode, 200},
+ {no_header, "cache-control"},
+ {version, "HTTP/1.0"}]).
+
diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl
index 523cf9d38c..f413248092 100644
--- a/lib/inets/test/httpd_basic_SUITE.erl
+++ b/lib/inets/test/httpd_basic_SUITE.erl
@@ -1,24 +1,26 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(httpd_basic_SUITE).
+-include_lib("kernel/include/file.hrl").
-include_lib("common_test/include/ct.hrl").
-include("inets_test_lib.hrl").
@@ -28,14 +30,19 @@
-define(URL_START, "http://localhost:").
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() -> [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, 30}}].
all() ->
- [
- uri_too_long_414,
- header_too_long_413,
+ [uri_too_long_414,
+ header_too_long_413,
+ entity_too_long,
+ erl_script_nocache_opt,
+ script_nocache,
escaped_url_in_error_body,
- slowdose
+ script_timeout,
+ slowdose,
+ keep_alive_timeout
].
groups() ->
@@ -58,13 +65,13 @@ end_per_group(_GroupName, Config) ->
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- tsp("init_per_suite -> entry with"
- "~n Config: ~p", [Config]),
- ok = inets:start(),
- PrivDir = ?config(priv_dir, Config),
-
+ inets_test_lib:stop_apps([inets]),
+ inets_test_lib:start_apps([inets]),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
+
Dummy =
-"<HTML>
+ "<HTML>
<HEAD>
<TITLE>/index.html</TITLE>
</HEAD>
@@ -72,8 +79,23 @@ init_per_suite(Config) ->
DUMMY
</BODY>
</HTML>",
-
+
DummyFile = filename:join([PrivDir,"dummy.html"]),
+ CgiDir = filename:join(PrivDir, "cgi-bin"),
+ ok = file:make_dir(CgiDir),
+ {CgiPrintEnv, CgiSleep} = case test_server:os_type() of
+ {win32, _} ->
+ {"printenv.bat", "cgi_sleep.exe"};
+ _ ->
+ {"printenv.sh", "cgi_sleep"}
+ end,
+ lists:foreach(
+ fun(Cgi) ->
+ inets_test_lib:copy_file(Cgi, DataDir, CgiDir),
+ AbsCgi = filename:join([CgiDir, Cgi]),
+ {ok, FileInfo} = file:read_file_info(AbsCgi),
+ ok = file:write_file_info(AbsCgi, FileInfo#file_info{mode = 8#00755})
+ end, [CgiPrintEnv, CgiSleep]),
{ok, Fd} = file:open(DummyFile, [write]),
ok = file:write(Fd, Dummy),
ok = file:close(Fd),
@@ -84,7 +106,8 @@ DUMMY
{document_root, PrivDir},
{bind_address, "localhost"}],
- [{httpd_conf, HttpdConf} | Config].
+ [{httpd_conf, HttpdConf}, {cgi_dir, CgiDir},
+ {cgi_printenv, CgiPrintEnv}, {cgi_sleep, CgiSleep} | Config].
%%--------------------------------------------------------------------
%% Function: end_per_suite(Config) -> _
@@ -93,8 +116,6 @@ DUMMY
%% Description: Cleanup after the whole suite
%%--------------------------------------------------------------------
end_per_suite(_Config) ->
- tsp("end_per_suite -> entry with"
- "~n Config: ~p", [_Config]),
inets:stop(),
ok.
@@ -110,9 +131,7 @@ end_per_suite(_Config) ->
%% 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) ->
- tsp("init_per_testcase(~w) -> entry with"
- "~n Config: ~p", [Case, Config]),
+init_per_testcase(_Case, Config) ->
Config.
@@ -124,22 +143,18 @@ init_per_testcase(Case, Config) ->
%% 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(~w) -> entry with"
- "~n Config: ~p", [Case, Config]),
+end_per_testcase(_Case, 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() ->
+ [{doc, "Test that too long uri's get 414 HTTP code"}].
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 = proplists:get_value(httpd_conf, Config),
+ {ok, Pid} = inets:start(httpd, [{max_uri_size, 10}
| HttpdConf]),
Info = httpd:info(Pid),
Port = proplists:get_value(port, Info),
@@ -155,17 +170,12 @@ uri_too_long_414(Config) when is_list(Config) ->
{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() ->
+ [{doc,"Test that too long headers's get 413 HTTP code"}].
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 = proplists:get_value(httpd_conf, Config),
+ {ok, Pid} = inets:start(httpd, [{max_header_size, 10}
| HttpdConf]),
Info = httpd:info(Pid),
Port = proplists:get_value(port, Info),
@@ -178,111 +188,191 @@ header_too_long_413(Config) when is_list(Config) ->
{version, "HTTP/1.1"}]),
inets:stop(httpd, Pid).
-
%%-------------------------------------------------------------------------
+
+entity_too_long() ->
+ [{doc, "Test that too long versions and method strings are rejected"}].
+entity_too_long(Config) when is_list(Config) ->
+ HttpdConf = proplists:get_value(httpd_conf, Config),
+ {ok, Pid} = inets:start(httpd, HttpdConf),
+ Info = httpd:info(Pid),
+ Port = proplists:get_value(port, Info),
+ Address = proplists:get_value(bind_address, Info),
+
+ %% Not so long but wrong
+ ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(),
+ "GET / " ++
+ lists:duplicate(5, $A) ++ "\r\n\r\n",
+ [{statuscode, 400},
+ %% Server will send lowest version
+ %% as it will not get to the
+ %% client version
+ %% before aborting
+ {version, "HTTP/0.9"}]),
+
+ %% Too long
+ ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(),
+ "GET / " ++
+ lists:duplicate(100, $A) ++ "\r\n\r\n",
+ [{statuscode, 413},
+ %% Server will send lowest version
+ %% as it will not get to the
+ %% client version
+ %% before aborting
+ {version, "HTTP/0.9"}]),
+ %% Not so long but wrong
+ ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(),
+ lists:duplicate(5, $A) ++ " / "
+ "HTTP/1.1\r\n\r\n",
+ [{statuscode, 501},
+ %% Server will send lowest version
+ %% as it will not get to the
+ %% client version
+ %% before aborting
+ {version, "HTTP/1.1"}]),
+ %% Too long
+ ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(),
+ lists:duplicate(100, $A) ++ " / "
+ "HTTP/1.1\r\n\r\n",
+ [{statuscode, 413},
+ %% Server will send lowest version
+ %% as it will not get to the
+ %% client version
+ %% before aborting
+ {version, "HTTP/0.9"}]),
+ inets:stop(httpd, Pid).
+
%%-------------------------------------------------------------------------
-escaped_url_in_error_body(doc) ->
- ["Test Url-encoding see OTP-8940"];
-escaped_url_in_error_body(suite) ->
+script_nocache() ->
+ [{doc,"Test nocache option for mod_cgi and mod_esi"}].
+script_nocache(Config) when is_list(Config) ->
+ Normal = {no_header, "cache-control"},
+ NoCache = {header, "cache-control", "no-cache"},
+ verify_script_nocache(Config, false, false, Normal, Normal),
+ verify_script_nocache(Config, true, false, NoCache, Normal),
+ verify_script_nocache(Config, false, true, Normal, NoCache),
+ verify_script_nocache(Config, true, true, NoCache, NoCache).
+
+%%-------------------------------------------------------------------------
+erl_script_nocache_opt(doc) ->
+ ["Test that too long headers's get 413 HTTP code"];
+erl_script_nocache_opt(suite) ->
[];
-escaped_url_in_error_body(Config) when is_list(Config) ->
- %% <CONDITIONAL-SKIP>
- %% This skip is due to a problem on windows with long path's
- %% If a path is too long file:open fails with, for example, eio.
- %% Until that problem is fixed, we skip this case...
- Skippable = [win32],
- Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
- ?NON_PC_TC_MAYBE_SKIP(Config, Condition),
- %% </CONDITIONAL-SKIP>
-
- tsp("escaped_url_in_error_body -> entry"),
- HttpdConf = ?config(httpd_conf, Config),
- {ok, Pid} = inets:start(httpd, [{port, 0} | HttpdConf]),
+erl_script_nocache_opt(Config) when is_list(Config) ->
+ HttpdConf = proplists:get_value(httpd_conf, Config),
+ {ok, Pid} = inets:start(httpd, [{port, 0}, {erl_script_nocache, true} | HttpdConf]),
Info = httpd:info(Pid),
Port = proplists:get_value(port, Info),
_Address = proplists:get_value(bind_address, Info),
-
- %% Request 1
- tss(1000),
- 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, {StatusCode1, Body1}} ->
- tsf({unexpected_ok_1, StatusCode1, Body1})
+ ok
end,
+ inets:stop(httpd, Pid).
+
+%%-------------------------------------------------------------------------
- %% Request 2
- tss(1000),
- 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, {StatusCode2, Body2}} ->
- tsf({unexpected_ok_2, StatusCode2, Body2})
- end,
- %% Request 3
- tss(1000),
- tsp("escaped_url_in_error_body -> request 3"),
+%%-------------------------------------------------------------------------
+
+escaped_url_in_error_body() ->
+ [{doc, "Test Url-encoding see OTP-8940"}].
+escaped_url_in_error_body(Config) when is_list(Config) ->
+ HttpdConf = proplists:get_value(httpd_conf, Config),
+ {ok, Pid} = inets:start(httpd, [{port, 0} | HttpdConf]),
+ Info = httpd:info(Pid),
+ Port = proplists:get_value(port, Info),
+ URL1 = ?URL_START ++ integer_to_list(Port),
+
+ %% Sanity check
+ {ok, {200, _}} = httpc:request(get, {URL1 ++ "/dummy.html", []},
+ [{url_encode, false},
+ {version, "HTTP/1.0"}],
+ [{full_result, false}]),
+ {ok, {200, _}} = httpc:request(get, {URL1 ++ "/dummy.html", []},
+ [{url_encode, true},
+ {version, "HTTP/1.0"}],
+ [{full_result, false}]),
+
%% 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_3, UnexpectedOK3})
+ {ok, {404, Body3}} = httpc:request(get, {URL2, []},
+ [{url_encode, true},
+ {version, "HTTP/1.0"}],
+ [{full_result, false}]),
+
+ HTMLEncodedPath = find_URL_path(string:tokens(Body3, " ")),
+
+ {ok, {404, Body4}} = httpc:request(get, {URL2, []},
+ [{url_encode, false},
+ {version, "HTTP/1.0"}],
+ [{full_result, false}]),
+
+ HTMLEncodedPath = find_URL_path(string:tokens(Body4, " ")),
+ inets:stop(httpd, Pid).
+
+%%-------------------------------------------------------------------------
+
+keep_alive_timeout(doc) ->
+ ["Test the keep_alive_timeout option"];
+keep_alive_timeout(suite) ->
+ [];
+keep_alive_timeout(Config) when is_list(Config) ->
+ HttpdConf = proplists:get_value(httpd_conf, Config),
+ {ok, Pid} = inets:start(httpd, [{port, 0}, {keep_alive, true}, {keep_alive_timeout, 2} | HttpdConf]),
+ Info = httpd:info(Pid),
+ Port = proplists:get_value(port, Info),
+ _Address = proplists:get_value(bind_address, Info),
+ {ok, S} = gen_tcp:connect("localhost", Port, []),
+ receive
+ after 3000 ->
+ {error, closed} = gen_tcp:send(S, "hey")
end,
+ inets:stop(httpd, Pid).
- %% Request 4
- tss(1000),
- 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_4, HTMLEncodedPath, BadPath4})
- end;
- {ok, UnexpectedOK4} ->
- tsf({unexpected_ok_4, UnexpectedOK4})
- end,
- tss(1000),
- tsp("escaped_url_in_error_body -> stop inets"),
- inets:stop(httpd, Pid),
- tsp("escaped_url_in_error_body -> done"),
+%%-------------------------------------------------------------------------
+
+script_timeout(doc) ->
+ ["Test the httpd script_timeout option"];
+script_timeout(suite) ->
+ [];
+script_timeout(Config) when is_list(Config) ->
+ verify_script_timeout(Config, 20, 200),
+ verify_script_timeout(Config, 5, 403),
ok.
-slowdose(doc) ->
- ["Testing minimum bytes per second option"];
+
+verify_script_timeout(Config, ScriptTimeout, StatusCode) ->
+ HttpdConf = proplists:get_value(httpd_conf, Config),
+ CgiScript = proplists:get_value(cgi_sleep, Config),
+ CgiDir = proplists:get_value(cgi_dir, Config),
+ {ok, Pid} = inets:start(httpd, [{port, 0},
+ {script_alias,
+ {"/cgi-bin/", CgiDir ++ "/"}},
+ {script_timeout, ScriptTimeout}
+ | HttpdConf]),
+ Info = httpd:info(Pid),
+ Port = proplists:get_value(port, Info),
+ Address = proplists:get_value(bind_address, Info),
+ ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(),
+ "GET /cgi-bin/" ++ CgiScript ++
+ " HTTP/1.0\r\n\r\n",
+ [{statuscode, StatusCode},
+ {version, "HTTP/1.0"}]),
+ inets:stop(httpd, Pid).
+
+%%-------------------------------------------------------------------------
+
+slowdose() ->
+ [{doc, "Testing minimum bytes per second option"}].
slowdose(Config) when is_list(Config) ->
- HttpdConf = ?config(httpd_conf, Config),
+ HttpdConf = proplists:get_value(httpd_conf, Config),
{ok, Pid} = inets:start(httpd, [{port, 0}, {minimum_bytes_per_second, 200}|HttpdConf]),
Info = httpd:info(Pid),
Port = proplists:get_value(port, Info),
@@ -291,6 +381,40 @@ slowdose(Config) when is_list(Config) ->
after 6000 ->
{error, closed} = gen_tcp:send(Socket, "Hey")
end.
+
+%%-------------------------------------------------------------------------
+%% Internal functions
+%%-------------------------------------------------------------------------
+
+verify_script_nocache(Config, CgiNoCache, EsiNoCache, CgiOption, EsiOption) ->
+ HttpdConf = proplists:get_value(httpd_conf, Config),
+ CgiScript = proplists:get_value(cgi_printenv, Config),
+ CgiDir = proplists:get_value(cgi_dir, Config),
+ {ok, Pid} = inets:start(httpd, [{port, 0},
+ {script_alias,
+ {"/cgi-bin/", CgiDir ++ "/"}},
+ {script_nocache, CgiNoCache},
+ {erl_script_alias,
+ {"/cgi-bin/erl", [httpd_example,io]}},
+ {erl_script_nocache, EsiNoCache}
+ | HttpdConf]),
+ Info = httpd:info(Pid),
+ Port = proplists:get_value(port, Info),
+ Address = proplists:get_value(bind_address, Info),
+ ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(),
+ "GET /cgi-bin/" ++ CgiScript ++
+ " HTTP/1.0\r\n\r\n",
+ [{statuscode, 200},
+ CgiOption,
+ {version, "HTTP/1.0"}]),
+ ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(),
+ "GET /cgi-bin/erl/httpd_example:get "
+ "HTTP/1.0\r\n\r\n",
+ [{statuscode, 200},
+ EsiOption,
+ {version, "HTTP/1.0"}]),
+ inets:stop(httpd, Pid).
+
find_URL_path([]) ->
"";
find_URL_path(["URL", URL | _]) ->
@@ -298,21 +422,6 @@ find_URL_path(["URL", URL | _]) ->
find_URL_path([_ | Rest]) ->
find_URL_path(Rest).
-
-tsp(F) ->
- inets_test_lib:tsp(F).
-tsp(F, A) ->
- inets_test_lib:tsp(F, A).
-
-tsf(Reason) ->
- inets_test_lib:tsf(Reason).
-
-tss(Time) ->
- inets_test_lib:tss(Time).
-
-
-
-
skip(Reason) ->
{skip, Reason}.
diff --git a/lib/inets/test/httpd_basic_SUITE_data/Makefile.src b/lib/inets/test/httpd_basic_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..9da2ed583f
--- /dev/null
+++ b/lib/inets/test/httpd_basic_SUITE_data/Makefile.src
@@ -0,0 +1,14 @@
+CC = @CC@
+LD = @LD@
+CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@
+CROSSLDFLAGS = @CROSSLDFLAGS@
+
+PROGS = cgi_sleep@exe@
+
+all: $(PROGS)
+
+cgi_sleep@exe@: cgi_sleep@obj@
+ $(LD) $(CROSSLDFLAGS) -o cgi_sleep cgi_sleep@obj@ @LIBS@
+
+cgi_sleep@obj@: cgi_sleep.c
+ $(CC) -c -o cgi_sleep@obj@ $(CFLAGS) cgi_sleep.c
diff --git a/lib/inets/test/httpd_basic_SUITE_data/cgi_sleep.c b/lib/inets/test/httpd_basic_SUITE_data/cgi_sleep.c
new file mode 100644
index 0000000000..126bb23987
--- /dev/null
+++ b/lib/inets/test/httpd_basic_SUITE_data/cgi_sleep.c
@@ -0,0 +1,26 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef __WIN32__
+#include <windows.h>
+#include <fcntl.h>
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+int main(void)
+{
+ unsigned int seconds = 10;
+
+#ifdef __WIN32__
+ Sleep(seconds * 1000);
+ _setmode(_fileno(stdout), _O_BINARY);
+#else
+ sleep(seconds);
+#endif
+
+ printf("Content-type: text/plain\r\n\r\n");
+ printf("Slept for %u seconds.\r\n", seconds);
+ exit(EXIT_SUCCESS);
+}
diff --git a/lib/inets/test/httpd_basic_SUITE_data/printenv.bat b/lib/inets/test/httpd_basic_SUITE_data/printenv.bat
new file mode 120000
index 0000000000..1bc8e52059
--- /dev/null
+++ b/lib/inets/test/httpd_basic_SUITE_data/printenv.bat
@@ -0,0 +1 @@
+../httpd_SUITE_data/server_root/cgi-bin/printenv.bat \ No newline at end of file
diff --git a/lib/inets/test/httpd_basic_SUITE_data/printenv.sh b/lib/inets/test/httpd_basic_SUITE_data/printenv.sh
new file mode 120000
index 0000000000..0136a3fa23
--- /dev/null
+++ b/lib/inets/test/httpd_basic_SUITE_data/printenv.sh
@@ -0,0 +1 @@
+../httpd_SUITE_data/server_root/cgi-bin/printenv.sh \ No newline at end of file
diff --git a/lib/inets/test/httpd_block.erl b/lib/inets/test/httpd_block.erl
index ac1bf43ff5..45547e6d4e 100644
--- a/lib/inets/test/httpd_block.erl
+++ b/lib/inets/test/httpd_block.erl
@@ -1,26 +1,26 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(httpd_block).
--include("test_server.hrl").
--include("test_server_line.hrl").
+-include_lib("common_test/include/ct.hrl").
%% General testcases bodies called from httpd_SUITE
-export([block_disturbing_idle/4, block_non_disturbing_idle/4,
@@ -88,7 +88,7 @@ block_503(Type, Port, Host, Node) ->
block_disturbing_active(Type, Port, Host, Node) ->
process_flag(trap_exit, true),
Pid = long_poll(Type, Host, Port, Node, 200, 60000),
- test_server:sleep(15000),
+ ct:sleep(15000),
block_server(Node, Host, Port),
await_suite_failed_process_exit(Pid, "poller", 60000,
connection_closed),
@@ -100,7 +100,7 @@ block_disturbing_active(Type, Port, Host, Node) ->
block_non_disturbing_active(Type, Port, Host, Node) ->
process_flag(trap_exit, true),
Poller = long_poll(Type, Host, Port, Node, 200, 60000),
- test_server:sleep(15000),
+ ct:sleep(15000),
ok = block_nd_server(Node, Host, Port),
await_normal_process_exit(Poller, "poller", 60000),
blocked = get_admin_state(Node, Host, Port),
@@ -111,9 +111,8 @@ block_non_disturbing_active(Type, Port, Host, Node) ->
block_disturbing_active_timeout_not_released(Type, Port, Host, Node) ->
process_flag(trap_exit, true),
Poller = long_poll(Type, Host, Port, Node, 200, 60000),
- test_server:sleep(15000),
- Blocker = blocker(Node, Host, Port, 50000),
- await_normal_process_exit(Blocker, "blocker", 50000),
+ ct:sleep(15000),
+ ok = httpd_block(undefined, Port, disturbing, 50000),
await_normal_process_exit(Poller, "poller", 30000),
blocked = get_admin_state(Node, Host, Port),
process_flag(trap_exit, false),
@@ -123,9 +122,8 @@ block_disturbing_active_timeout_not_released(Type, Port, Host, Node) ->
block_disturbing_active_timeout_released(Type, Port, Host, Node) ->
process_flag(trap_exit, true),
Poller = long_poll(Type, Host, Port, Node, 200, 40000),
- test_server:sleep(5000),
- Blocker = blocker(Node, Host, Port, 10000),
- await_normal_process_exit(Blocker, "blocker", 15000),
+ ct:sleep(5000),
+ ok = httpd_block(undefined, Port, disturbing, 10000),
await_suite_failed_process_exit(Poller, "poller", 40000,
connection_closed),
blocked = get_admin_state(Node, Host, Port),
@@ -135,7 +133,7 @@ block_disturbing_active_timeout_released(Type, Port, Host, Node) ->
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),
+ ct:sleep(5000),
ok = block_nd_server(Node, Host, Port, 40000),
await_normal_process_exit(Poller, "poller", 60000),
blocked = get_admin_state(Node, Host, Port),
@@ -146,7 +144,7 @@ block_non_disturbing_active_timeout_not_released(Type, Port, Host, Node) ->
block_non_disturbing_active_timeout_released(Type, Port, Host, Node) ->
process_flag(trap_exit, true),
Poller = long_poll(Type, Host, Port, Node, 200, 45000),
- test_server:sleep(5000),
+ ct:sleep(5000),
Blocker = blocker_nd(Node, Host, Port ,10000, {error,timeout}),
await_normal_process_exit(Blocker, "blocker", 15000),
await_normal_process_exit(Poller, "poller", 50000),
@@ -157,9 +155,9 @@ block_non_disturbing_active_timeout_released(Type, Port, Host, Node) ->
disturbing_blocker_dies(Type, Port, Host, Node) ->
process_flag(trap_exit, true),
Poller = long_poll(Type, Host, Port, Node, 200, 60000),
- test_server:sleep(5000),
+ ct:sleep(5000),
Blocker = blocker(Node, Host, Port, 10000),
- test_server:sleep(5000),
+ ct:sleep(5000),
exit(Blocker,simulate_blocker_crash),
await_normal_process_exit(Poller, "poller", 60000),
unblocked = get_admin_state(Node, Host, Port),
@@ -170,9 +168,9 @@ disturbing_blocker_dies(Type, Port, Host, Node) ->
non_disturbing_blocker_dies(Type, Port, Host, Node) ->
process_flag(trap_exit, true),
Poller = long_poll(Type, Host, Port, Node, 200, 60000),
- test_server:sleep(5000),
+ ct:sleep(5000),
Blocker = blocker_nd(Node, Host, Port, 10000, ok),
- test_server:sleep(5000),
+ ct:sleep(5000),
exit(Blocker, simulate_blocker_crash),
await_normal_process_exit(Poller, "poller", 60000),
unblocked = get_admin_state(Node, Host, Port),
@@ -295,11 +293,14 @@ httpd_restart(Addr, Port) ->
end.
make_name(Addr, Port) ->
- httpd_util:make_name("httpd", Addr, Port).
+ httpd_util:make_name("httpd", Addr, Port, default).
-get_admin_state(Node, _Host, Port) ->
- Addr = undefined,
- rpc:call(Node, httpd, get_admin_state, [Addr, Port]).
+get_admin_state(_, _Host, Port) ->
+ Name = make_name(undefined, Port),
+ {status, _, _, StatusInfo} = sys:get_status(whereis(Name)),
+ [_, _,_, _, Prop] = StatusInfo,
+ State = state(Prop),
+ element(6, State).
validate_admin_state(Node, Host, Port, Expect) ->
io:format("try validating server admin state: ~p~n", [Expect]),
@@ -323,15 +324,15 @@ await_normal_process_exit(Pid, Name, Timeout) ->
io_lib:format("expected normal exit, "
"unexpected exit of ~s process: ~p",
[Name, Reason])),
- test_server:fail(Err)
+ ct:fail(Err)
after Timeout ->
- test_server:fail("timeout while waiting for " ++ Name)
+ ct:fail("timeout while waiting for " ++ Name)
end.
await_suite_failed_process_exit(Pid, Name, Timeout, Why) ->
receive
- {'EXIT', Pid, {suite_failed, Why}} ->
+ {'EXIT', Pid, {test_failed, Why}} ->
ok;
{'EXIT', Pid, Reason} ->
Err =
@@ -339,9 +340,9 @@ await_suite_failed_process_exit(Pid, Name, Timeout, Why) ->
io_lib:format("expected connection_closed, "
"unexpected exit of ~s process: ~p",
[Name, Reason])),
- test_server:fail(Err)
+ ct:fail(Err)
after Timeout ->
- test_server:fail("timeout while waiting for " ++ Name)
+ ct:fail("timeout while waiting for " ++ Name)
end.
long_poll(Type, Host, Port, Node, StatusCode, Timeout) ->
@@ -359,10 +360,13 @@ do_long_poll(Type, Host, Port, Node, StatusCode, Timeout) ->
ok ->
exit(normal);
Reason ->
- test_server:fail(Reason)
+ exit({test_failed, Reason})
end.
-
-
-
+state([{data,[{"State", State}]} | _]) ->
+ State;
+state([{data,[{"StateData", State}]} | _]) ->
+ State;
+state([_ | Rest]) ->
+ state(Rest).
diff --git a/lib/inets/test/httpd_load.erl b/lib/inets/test/httpd_load.erl
index 83520033dc..7f4d16139f 100644
--- a/lib/inets/test/httpd_load.erl
+++ b/lib/inets/test/httpd_load.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -20,8 +21,7 @@
-module(httpd_load).
--include("test_server.hrl").
--include("test_server_line.hrl").
+-include_lib("common_test/include/ct.hrl").
%% General testcases bodies called from httpd_SUITE
-export([load_test/5]).
@@ -63,7 +63,7 @@ load_test(Fun, URIs, Type, Host, Port, Node, 0, List) ->
{'EXIT', Pid, Reason} ->
Str = lists:flatten(io_lib:format("client ~p exited: ~p",
[Pid,Reason])),
- test_server:fail(Str);
+ ct:fail(Str);
_ ->
load_test(Fun, URIs, Type, Host, Port, Node, 0, List)
end;
@@ -86,12 +86,11 @@ load_test_client(Fun, [URI|URIs], Type, Host, Port, Node, Boss, Timeout) ->
{'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),
+ ct:sleep(Timeout1),
load_test_client(Fun, URIs, Type, Host, Port, Node, Boss, Timeout1).
load_test_client_done(Boss) ->
diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl
index df4ed6b179..d9118aa1a4 100644
--- a/lib/inets/test/httpd_mod.erl
+++ b/lib/inets/test/httpd_mod.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -20,8 +21,7 @@
-module(httpd_mod).
--include("test_server.hrl").
--include("test_server_line.hrl").
+-include_lib("common_test/include/ct.hrl").
%% General testcases bodies called from httpd_SUITE
-export([alias/4, actions/4, security/5, auth/4, auth_api/6,
@@ -86,240 +86,116 @@ actions(Type, Port, Host, Node) ->
security(ServerRoot, Type, Port, Host, Node) ->
global:register_name(mod_security_test, self()), % Receive events
-
- tsp("security -> "
- "sleep"),
- test_server:sleep(5000),
+
+ ct:sleep(5000),
OpenDir = filename:join([ServerRoot, "htdocs", "open"]),
%% Test blocking / unblocking of users.
%% /open, require user one Aladdin
- tsp("security -> "
- "blocking and unblocking of users - "
- "remove all existing users"),
+
remove_users(Node, ServerRoot, Host, Port, "open"),
- tsp("security -> "
- "blocking and unblocking of users - "
- "auth request for nonex user 'one' - expect 401"),
auth_request(Type, Host, Port, Node, "/open/", "one", "onePassword",
[{statuscode, 401}]),
- tsp("security -> "
- "blocking and unblocking of users - "
- "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "one"}, {password, "onePassword"}]},
Node, Port),
- tsp("security -> "
- "blocking and unblocking of users - "
- "auth request for nonex user 'two' - expect 401"),
auth_request(Type,Host,Port,Node,"/open/", "two", "twoPassword",
[{statuscode, 401}]),
- tsp("security -> "
- "blocking and unblocking of users - "
- "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "two"}, {password, "twoPassword"}]},
Node, Port),
-
- tsp("security -> "
- "blocking and unblocking of users - "
- "auth request for nonex user 'Alladin' - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "Aladdin",
"AladdinPassword", [{statuscode, 401}]),
- tsp("security -> "
- "blocking and unblocking of users - "
- "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "Aladdin"},
{password, "AladdinPassword"}]},
Node, Port),
-
- tsp("security -> "
- "blocking and unblocking of users - "
- "add user 'one'"),
add_user(Node, ServerRoot, Port, "open", "one", "onePassword", []),
- tsp("security -> "
- "blocking and unblocking of users - "
- "add user 'two'"),
add_user(Node, ServerRoot, Port, "open", "two", "twoPassword", []),
- tsp("security -> "
- "blocking and unblocking of users - "
- "auth request 1 for user 'one' with wrong password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
[{statuscode, 401}]),
-
- tsp("security -> "
- "blocking and unblocking of users - "
- "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "one"}, {password, "WrongPassword"}]},
Node, Port),
-
- tsp("security -> "
- "blocking and unblocking of users - "
- "auth request 2 for user 'one' with wrong password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
[{statuscode, 401}]),
- tsp("security -> "
- "blocking and unblocking of users - "
- "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "one"}, {password, "WrongPassword"}]},
Node, Port),
-
- tsp("security -> "
- "blocking and unblocking of users - "
- "await block security event (two failed attempts)"),
- receive_security_event({event, user_block, Port, OpenDir,
+ receive_security_event({event, user_block, Port, OpenDir,
[{user, "one"}]}, Node, Port),
- tsp("security -> "
- "blocking and unblocking of users - "
- "unregister - no more security events"),
global:unregister_name(mod_security_test), % No more events.
- tsp("security -> "
- "blocking and unblocking of users - "
- "auth request for user 'one' with wrong password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
[{statuscode, 401}]),
- tsp("security -> "
- "blocking and unblocking of users - "
- "auth request for user 'one' with correct password - expect 403"),
auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword",
[{statuscode, 403}]),
%% User "one" should be blocked now..
- tsp("security -> "
- "blocking and unblocking of users - "
- "list blocked users - 'one' should be the only one"),
case list_blocked_users(Node, Port) of
[{"one",_, Port, OpenDir,_}] ->
ok;
Blocked ->
- tsp(" *** unexpected blocked users ***"
- "~n Blocked: ~p", [Blocked]),
exit({unexpected_blocked, Blocked})
end,
- tsp("security -> "
- "blocking and unblocking of users - "
- "list users blocked for dir '~p' - "
- "user 'one' should be the only one", [OpenDir]),
[{"one",_, Port, OpenDir,_}] = list_blocked_users(Node, Port, OpenDir),
- tsp("security -> "
- "blocking and unblocking of users - "
- "unblock user 'one' for dir '~p'", [OpenDir]),
true = unblock_user(Node, "one", Port, OpenDir),
%% User "one" should not be blocked any more.
- tsp("security -> "
- "blocking and unblocking of users - "
- "ensure user 'one' is no longer blocked"),
[] = list_blocked_users(Node, Port),
-
- tsp("security -> "
- "blocking and unblocking of users - "
- "auth request for user 'one' with correct password - expect 200"),
auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword",
[{statuscode, 200}]),
%% Test list_auth_users & auth_timeout
-
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "list auth users - expect user 'one'"),
["one"] = list_auth_users(Node, Port),
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "auth request for user 'two' with wrong password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "two", "onePassword",
[{statuscode, 401}]),
-
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "list auth users - expect user 'one'"),
["one"] = list_auth_users(Node, Port),
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "list auth users for dir '~p' - expect user 'one'", [OpenDir]),
["one"] = list_auth_users(Node, Port, OpenDir),
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "auth request for user 'two' with correct password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword",
[{statuscode, 401}]),
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "list auth users - expect user 'one'"),
["one"] = list_auth_users(Node, Port),
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "list auth users for dir '~p' - expect user 'one'", [OpenDir]),
["one"] = list_auth_users(Node, Port, OpenDir),
%% Wait for successful auth to timeout.
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "wait for successful auth to timeout"),
- test_server:sleep(?AUTH_TIMEOUT*1001),
-
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "list auth users - expect none"),
+ ct:sleep(?AUTH_TIMEOUT*1001),
+
[] = list_auth_users(Node, Port),
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "list auth users for dir '~p'~n - expect none", [OpenDir]),
+
[] = list_auth_users(Node, Port, OpenDir),
%% "two" is blocked.
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "unblock user 'two' for dir '~p'", [OpenDir]),
true = unblock_user(Node, "two", Port, OpenDir),
-
-
%% Test explicit blocking. Block user 'two'.
- tsp("security -> "
- "explicit blocking - list blocked users - should be none"),
[] = list_blocked_users(Node,Port,OpenDir),
- tsp("security -> "
- "explicit blocking - "
- "block user 'two' for dir '~p'", [OpenDir]),
true = block_user(Node, "two", Port, OpenDir, 10),
-
- tsp("security -> "
- "explicit blocking - "
- "auth request for user 'two' with correct password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword",
- [{statuscode, 401}]),
- tsp("security -> "
- "done").
-
+ [{statuscode, 401}]).
%%-------------------------------------------------------------------------
auth(Type, Port, Host, Node) ->
@@ -743,7 +619,6 @@ cgi(Type, Port, Host, Node) ->
end,
%% The length (> 100) is intentional
-%% tsp("cgi -> request 01 with length > 100"),
ok = httpd_test_lib:
verify_request(Type, Host, Port, Node,
"POST /cgi-bin/" ++ Script3 ++
@@ -771,55 +646,51 @@ cgi(Type, Port, Host, Node) ->
{version, "HTTP/1.0"},
{header, "content-type", "text/plain"}]),
-%% tsp("cgi -> request 02"),
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"}]),
-%% tsp("cgi -> request 03"),
+
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"}]),
-%% tsp("cgi -> request 04"),
+
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"}]),
-%% tsp("cgi -> request 04"),
+
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"}]),
-%% tsp("cgi -> request 05"),
+
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"}]),
-%% tsp("cgi -> request 06"),
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"}]),
-%% tsp("cgi -> request 07"),
+
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"}]),
-%% tsp("cgi -> request 08"),
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"}]),
-%% tsp("cgi -> request 09"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"POST /htbin/"++ Script ++
" HTTP/1.0\r\n\r\n",
@@ -827,23 +698,25 @@ cgi(Type, Port, Host, Node) ->
{version, "HTTP/1.0"}]),
%% Execute an existing, but bad CGI script..
-%% tsp("cgi -> request 10 - bad 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"}]),
-%% tsp("cgi -> request 11 - bad script"),
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"}]),
-%% tsp("cgi -> done"),
- ok.
-
+ %% Check "ScriptNoCache" directive (default: false)
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /cgi-bin/" ++ Script ++
+ " HTTP/1.0\r\n\r\n",
+ [{statuscode, 200},
+ {no_header, "cache-control"},
+ {version, "HTTP/1.0"}]).
%%--------------------------------------------------------------------
esi(Type, Port, Host, Node) ->
@@ -899,6 +772,13 @@ esi(Type, Port, Host, Node) ->
" HTTP/1.0\r\n\r\n",
[{statuscode, 302},
{version, "HTTP/1.0"}]),
+ %% Check "ErlScriptNoCache" directive (default: false)
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /cgi-bin/erl/httpd_example:get"
+ " HTTP/1.0\r\n\r\n",
+ [{statuscode, 200},
+ {no_header, "cache-control"},
+ {version, "HTTP/1.0"}]),
ok.
@@ -1003,18 +883,15 @@ list_users(Node, Root, _Host, Port, Dir) ->
receive_security_event(Event, Node, Port) ->
- tsp("receive_security_event -> await ~w event", [element(2, Event)]),
receive
Event ->
- tsp("receive_security_event -> "
- "received expected ~w event", [element(2, Event)]),
ok;
{'EXIT', _, _} ->
receive_security_event(Event, Node, Port)
after 5000 ->
%% Flush the message queue, to see if we got something...
Msgs = inets_test_lib:flush(),
- tsf({expected_event_not_received, Msgs})
+ ct:fail({expected_event_not_received, Msgs})
end.
@@ -1030,10 +907,10 @@ receive_security_event(Event, Node, Port) ->
%% {'EXIT', _, _} ->
%% receive_security_event(Event, Node, Port);
%% Other ->
-%% test_server:fail({unexpected_event,
+%% ct:fail({unexpected_event,
%% {expected, Event}, {received, Other}})
%% after 5000 ->
-%% test_server:fail(no_event_recived)
+%% ct:fail(no_event_recived)
%% end.
@@ -1115,17 +992,4 @@ check_lists_members1(L1,L2) ->
{error,{lists_not_equal,L1,L2}}.
-%% p(F) ->
-%% p(F, []).
-
-%% p(F, A) ->
-%% io:format(user, "~w:" ++ F ++ "~n", [?MODULE|A]).
-
-tsp(F) ->
- inets_test_lib:tsp(F).
-tsp(F, A) ->
- inets_test_lib:tsp(F, A).
-
-tsf(Reason) ->
- test_server:fail(Reason).
diff --git a/lib/inets/test/httpd_mod_SUITE.erl b/lib/inets/test/httpd_mod_SUITE.erl
new file mode 100644
index 0000000000..5ec4e0856d
--- /dev/null
+++ b/lib/inets/test/httpd_mod_SUITE.erl
@@ -0,0 +1,77 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+%%
+%% ct:run("../inets_test", httpd_mod_SUITE).
+-module(httpd_mod_SUITE).
+
+-include_lib("kernel/include/file.hrl").
+-include_lib("common_test/include/ct.hrl").
+-include("inets_test_lib.hrl").
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ {group, http},
+ {group, https}
+ ].
+
+groups() ->
+ [
+ {http, [], all_version_groups()},
+ {https, [], all_version_groups()}
+ {http_1_1, [], []},
+ {http_1_0, [], []},
+ {http_0_9, [], []},
+ {mod_alias, [], []},
+ {mod_actions, [], []},
+ {mod_security, [], []},
+ {mod_auth, [], []},
+ {mod_htaccess, [], []},
+ {mod_cgi, [], []},
+ {mod_esi, [], []},
+ {mod_head, [], []},
+ {configure, [], []}
+ ].
+
+all_version_groups ()->
+ [
+ {group, mod_alias},
+ {group, mod_actions},
+ {group, mod_security},
+ {group, mod_auth},
+ {group, mod_htaccess},
+ {group, mod_cgi},
+ {group, mod_esi},
+ {group, mod_head}
+ ].
+
+%%-------------------------------------------------------------------------
+%% Test cases starts here.
+%%-------------------------------------------------------------------------
diff --git a/lib/inets/test/httpd_poll.erl b/lib/inets/test/httpd_poll.erl
index 32335cabcf..f86e3a979b 100644
--- a/lib/inets/test/httpd_poll.erl
+++ b/lib/inets/test/httpd_poll.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -258,11 +259,11 @@ validate(ExpStatusCode,Socket,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|_]} ->
+ case re:split(Response," ", [{return, list}]) of
+ ["HTTP/1.0",ExpStatusCode|_] ->
vlog("response (~p bytes) was ok",[Size]),
ok;
- {ok,["HTTP/1.0",StatusCode|_]} ->
+ ["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",
diff --git a/lib/inets/test/httpd_test_data/server_root/Makefile b/lib/inets/test/httpd_test_data/server_root/Makefile
index d7a3231068..ed4d63a3bb 100644
--- a/lib/inets/test/httpd_test_data/server_root/Makefile
+++ b/lib/inets/test/httpd_test_data/server_root/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. 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/.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# 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.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
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
index 48e66f0114..7b1b4a15b2 100644
--- a/lib/inets/test/httpd_test_data/server_root/conf/8080.conf
+++ b/lib/inets/test/httpd_test_data/server_root/conf/8080.conf
@@ -1,7 +1,7 @@
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
+Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log
ServerAdmin [email protected]
ServerRoot /var/tmp/server_root
ErrorLog logs/error_log_8080
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
index 79bb7fcca4..042779fcd0 100644
--- a/lib/inets/test/httpd_test_data/server_root/conf/8888.conf
+++ b/lib/inets/test/httpd_test_data/server_root/conf/8888.conf
@@ -1,7 +1,7 @@
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
+Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log
ServerAdmin [email protected]
ServerRoot /var/tmp/server_root
ErrorLog logs/error_log_8888
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
index ceb94237d2..3f9fde03b5 100644
--- a/lib/inets/test/httpd_test_data/server_root/conf/httpd.conf
+++ b/lib/inets/test/httpd_test_data/server_root/conf/httpd.conf
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
@@ -63,7 +64,7 @@ SocketType ip_comm
# 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
+Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_responsecontrol mod_trace mod_range mod_head mod_dir mod_get mod_log mod_disk_log
# ServerAdmin: Your address, where problems with the server should be
# e-mailed.
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
index 8b8c57a98b..de49ceafd0 100644
--- a/lib/inets/test/httpd_test_data/server_root/conf/ssl.conf
+++ b/lib/inets/test/httpd_test_data/server_root/conf/ssl.conf
@@ -1,7 +1,7 @@
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
+Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log
ServerAdmin [email protected]
ServerRoot /var/tmp/server_root
ErrorLog logs/error_log_8088
diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl
index 13584c50f6..1cecd2642c 100644
--- a/lib/inets/test/httpd_test_lib.erl
+++ b/lib/inets/test/httpd_test_lib.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -91,73 +92,38 @@ verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut)
when (is_integer(TimeOut) orelse (TimeOut =:= infinity)) ->
verify_request(SocketType, Host, Port, [], Node, RequestStr, Options, TimeOut).
-verify_request(SocketType, Host, Port, TranspOpts0, Node, RequestStr, Options, TimeOut) ->
- tsp("verify_request -> entry with"
- "~n SocketType: ~p"
- "~n Host: ~p"
- "~n Port: ~p"
- "~n TranspOpts: ~p"
- "~n Node: ~p"
- "~n Options: ~p"
- "~n TimeOut: ~p",
- [SocketType, Host, Port, TranspOpts0, Node, Options, TimeOut]),
-
- %% For now, until we modernize the httpd tests
- TranspOpts =
- case lists:member(inet6, TranspOpts0) of
- true ->
- TranspOpts0;
- false ->
- [inet | TranspOpts0]
- end,
-
+verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, TimeOut) ->
try inets_test_lib:connect_bin(SocketType, Host, Port, TranspOpts) of
{ok, Socket} ->
- tsp("verify_request -> connected - now send message"),
- SendRes = inets_test_lib:send(SocketType, Socket, RequestStr),
- tsp("verify_request -> send result: "
- "~n ~p", [SendRes]),
- State = case inets_regexp:match(RequestStr, "printenv") of
+ ok = inets_test_lib:send(SocketType, Socket, RequestStr),
+ State = case re:run(RequestStr, "printenv", [{capture, none}]) of
nomatch ->
#state{};
- _ ->
+ match ->
#state{print = true}
end,
case request(State#state{request = RequestStr,
socket = Socket}, TimeOut) of
{error, Reason} ->
- tsp("verify_request -> request failed: "
- "~n Reason: ~p", [Reason]),
{error, Reason};
NewState ->
- tsp("verify_request -> validate reply: "
- "~n NewState: ~p", [NewState]),
ValidateResult =
validate(RequestStr, NewState, Options, Node, Port),
- tsp("verify_request -> validation result: "
- "~n ~p", [ValidateResult]),
inets_test_lib:close(SocketType, Socket),
ValidateResult
end;
ConnectError ->
- tsp("verify_request -> connect error: "
- "~n ~p"
- "~n", [ConnectError]),
- tsf({connect_error, ConnectError,
- [SocketType, Host, Port, TranspOpts]})
+ ct:fail({connect_error, ConnectError,
+ [SocketType, Host, Port, TranspOpts]})
catch
T:E ->
- tsp("verify_request -> connect failed: "
- "~n E: ~p"
- "~n T: ~p"
- "~n", [E, T]),
- tsf({connect_failure,
- [{type, T},
- {error, E},
- {stacktrace, erlang:get_stacktrace()},
- {args, [SocketType, Host, Port, TranspOpts]}]})
+ ct:fail({connect_failure,
+ [{type, T},
+ {error, E},
+ {stacktrace, erlang:get_stacktrace()},
+ {args, [SocketType, Host, Port, TranspOpts]}]})
end.
request(#state{mfa = {Module, Function, Args},
@@ -166,10 +132,6 @@ request(#state{mfa = {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);
@@ -179,22 +141,12 @@ request(#state{mfa = {Module, Function, Args},
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);
+ exit({test_failed, connection_closed});
{tcp_error, Socket, Reason} ->
- io:format("~p ~w[~w]request -> received (tcp) error"
- "~n Reason: ~p"
- "~n", [self(), ?MODULE, ?LINE, Reason]),
- test_server:fail({tcp_error, Reason});
+ ct:fail({tcp_error, Reason});
{ssl, Socket, Data} ->
- print(ssl, Data, State),
case Module:Function([Data | Args]) of
{ok, Parsed} ->
handle_http_msg(Parsed, State);
@@ -204,28 +156,19 @@ request(#state{mfa = {Module, Function, Args},
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);
+ exit({test_failed, connection_closed});
{ssl_error, Socket, Reason} ->
- test_server:fail({ssl_error, Reason})
+ ct:fail({ssl_error, Reason})
after TimeOut ->
- io:format("~p ~w[~w]request -> timeout"
- "~n", [self(), ?MODULE, ?LINE]),
- test_server:fail(connection_timed_out)
+ ct:pal("~p ~w[~w]request -> timeout"
+ "~n", [self(), ?MODULE, ?LINE]),
+ ct: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,
@@ -277,7 +220,7 @@ handle_http_body(Body, State = #state{headers = Headers,
request(State#state{mfa = MFA}, 5000)
end;
false ->
- test_server:fail(body_too_big)
+ ct:fail(body_too_big)
end
end.
@@ -285,11 +228,6 @@ validate(RequestStr, #state{status_line = {Version, StatusCode, _},
headers = Headers,
body = Body}, Options, N, P) ->
- tsp("validate -> entry with"
- "~n StatusCode: ~p"
- "~n Headers: ~p"
- "~n Body: ~p", [StatusCode, Headers, Body]),
-
check_version(Version, Options),
case lists:keysearch(statuscode, 1, Options) of
{value, _} ->
@@ -297,11 +235,17 @@ validate(RequestStr, #state{status_line = {Version, StatusCode, _},
_ ->
ok
end,
- do_validate(http_response:header_list(Headers), Options, N, P),
- check_body(RequestStr, StatusCode,
- Headers#http_response_h.'content-type',
- list_to_integer(Headers#http_response_h.'content-length'),
- Body).
+ HList = http_response:header_list(Headers),
+ do_validate(HList, Options, N, P),
+ case lists:keysearch("warning", 1, HList) of
+ {value, _} ->
+ ok;
+ _ ->
+ check_body(RequestStr, StatusCode,
+ Headers#http_response_h.'content-type',
+ list_to_integer(Headers#http_response_h.'content-length'),
+ Body)
+ end.
%--------------------------------------------------------------------
%% Internal functions
@@ -311,20 +255,20 @@ check_version(Version, Options) ->
{value, {version, Version}} ->
ok;
{value, {version, Ver}} ->
- tsf({wrong_version, [{got, Version},
- {expected, Ver}]});
+ ct:fail({wrong_version, [{got, Version},
+ {expected, Ver}]});
_ ->
- case Version of
- "HTTP/1.1" ->
- ok;
+ case Version of
+ "HTTP/1.1" ->
+ ok;
_ ->
- tsf({wrong_version, [{got, Version},
- {expected, "HTTP/1.1"}]})
- end
+ ct:fail({wrong_version, [{got, Version},
+ {expected, "HTTP/1.1"}]})
+ end
end.
check_status_code(StatusCode, [], Options) ->
- tsf({wrong_status_code, [{got, StatusCode}, {expected, Options}]});
+ ct:fail({wrong_status_code, [{got, StatusCode}, {expected, Options}]});
check_status_code(StatusCode, Current = [_ | Rest], Options) ->
case lists:keysearch(statuscode, 1, Current) of
{value, {statuscode, StatusCode}} ->
@@ -332,7 +276,7 @@ check_status_code(StatusCode, Current = [_ | Rest], Options) ->
{value, {statuscode, _OtherStatus}} ->
check_status_code(StatusCode, Rest, Options);
false ->
- tsf({wrong_status_code, [{got, StatusCode}, {expected, Options}]})
+ ct:fail({wrong_status_code, [{got, StatusCode}, {expected, Options}]})
end.
do_validate(_, [], _, _) ->
@@ -345,9 +289,9 @@ do_validate(Header, [{header, HeaderField}|Rest], N, P) ->
{value, {LowerHeaderField, _Value}} ->
ok;
false ->
- tsf({missing_header_field, LowerHeaderField, Header});
+ ct:fail({missing_header_field, LowerHeaderField, Header});
_ ->
- tsf({missing_header_field, LowerHeaderField, Header})
+ ct:fail({missing_header_field, LowerHeaderField, Header})
end,
do_validate(Header, Rest, N, P);
do_validate(Header, [{header, HeaderField, Value}|Rest],N,P) ->
@@ -356,15 +300,15 @@ do_validate(Header, [{header, HeaderField, Value}|Rest],N,P) ->
{value, {LowerHeaderField, Value}} ->
ok;
false ->
- tsf({wrong_header_field_value, LowerHeaderField, Header});
+ ct:fail({wrong_header_field_value, LowerHeaderField, Header, Value});
_ ->
- tsf({wrong_header_field_value, LowerHeaderField, Header})
+ ct:fail({wrong_header_field_value, LowerHeaderField, Header, Value})
end,
do_validate(Header, Rest, N, P);
-do_validate(Header,[{no_last_modified, HeaderField}|Rest],N,P) ->
+do_validate(Header,[{no_header, HeaderField}|Rest],N,P) ->
case lists:keysearch(HeaderField,1,Header) of
{value,_} ->
- tsf({wrong_header_field_value, HeaderField, Header});
+ ct:fail({wrong_header_field_value, HeaderField, Header});
_ ->
ok
end,
@@ -373,23 +317,23 @@ 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, _, _}->
+ case re:run(RequestStr, "xpect:100-continue", [{capture, none}]) of
+ match->
true;
- _ ->
+ nomatch ->
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);
+ ct:fail(content_length_error);
check_body("GET /cgi-bin/cgi_echo HTTP/1.0\r\n\r\n", 200, "text/plain",
_, Body) ->
case size(Body) of
100 ->
ok;
_ ->
- tsf(content_length_error)
+ ct:fail(content_length_error)
end;
check_body(RequestStr, 200, "text/html", _, Body) ->
@@ -404,16 +348,3 @@ check_body(RequestStr, 200, "text/html", _, Body) ->
check_body(_, _, _, _,_) ->
ok.
-print(Proto, Data, #state{print = true}) ->
- test_server:format("Received ~p: ~p~n", [Proto, Data]);
-print(_, _, #state{print = false}) ->
- ok.
-
-
-tsp(F) ->
- inets_test_lib:tsp(F).
-tsp(F, A) ->
- inets_test_lib:tsp(F, A).
-
-tsf(Reason) ->
- inets_test_lib:tsf(Reason).
diff --git a/lib/inets/test/httpd_time_test.erl b/lib/inets/test/httpd_time_test.erl
index 0bb457f9b9..e858ddf4f6 100644
--- a/lib/inets/test/httpd_time_test.erl
+++ b/lib/inets/test/httpd_time_test.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -116,13 +117,14 @@ main(N, SocketType, Host, Port, Time)
loop(Pollers, Timeout) ->
d("loop -> entry when"
"~n Timeout: ~p", [Timeout]),
- Start = t(),
+ Start = erlang:monotonic_time(),
+
receive
{'EXIT', Pid, {poller_stat_failure, SocketType, Host, Port, Time, Reason}} ->
case is_poller(Pid, Pollers) of
true ->
error_msg("received unexpected exit from poller ~p~n"
- "befor completion of test "
+ "before completion of test "
"after ~p micro sec"
"~n SocketType: ~p"
"~n Host: ~p"
@@ -133,7 +135,7 @@ loop(Pollers, Timeout) ->
false ->
error_msg("received unexpected ~p from ~p"
"befor completion of test", [Reason, Pid]),
- loop(Pollers, to(Timeout, Start))
+ loop(Pollers, Timeout - inets_lib:millisec_passed(Start))
end;
{poller_stat_failure, Pid, {SocketType, Host, Port, Time, Reason}} ->
@@ -384,63 +386,34 @@ 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|_]} ->
+ case re:split(Response," ", [{return, list}]) of
+ ["HTTP/1.0", ExpStatusCode|_] ->
ok;
- {ok, ["HTTP/1.0", StatusCode|_]} ->
+ ["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|_]} ->
+ ["HTTP/1.1", ExpStatusCode|_] ->
ok;
- {ok, ["HTTP/1.1", StatusCode|_]} ->
+ ["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});
- {ok, Unexpected} ->
- error_msg("Unexpected response split: ~p (~s)",
- [Unexpected, Response]),
- exit({unexpected_response, Unexpected, Response});
- {error, Reason} ->
+ {error, Reason} ->
error_msg("Failed processing response: ~p (~s)",
[Reason, Response]),
- exit({failed_response_processing, Reason, Response})
- 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})
+ exit({failed_response_processing, Reason, Response});
+ Unexpected ->
+ error_msg("Unexpected response split: ~p (~s)",
+ [Unexpected, Response]),
+ exit({unexpected_response, Unexpected, Response})
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) ->
@@ -505,17 +478,6 @@ 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) ->
diff --git a/lib/inets/test/inets_SUITE.erl b/lib/inets/test/inets_SUITE.erl
index 6510c70d08..5eaf3a28a0 100644
--- a/lib/inets/test/inets_SUITE.erl
+++ b/lib/inets/test/inets_SUITE.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -20,7 +21,6 @@
-module(inets_SUITE).
-include_lib("common_test/include/ct.hrl").
--include("test_server_line.hrl").
-include("inets_test_lib.hrl").
%% Note: This directive should only be used in test suites.
@@ -28,18 +28,24 @@
-define(NUM_DEFAULT_SERVICES, 1).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds,5}}
+ ].
all() ->
- [{group, app_test}, {group, appup_test},
+ [{group, app_test},
{group, services_test}, httpd_reload].
groups() ->
[{services_test, [],
- [start_inets, start_httpc, start_httpd, start_ftpc,
- start_tftpd]},
- {app_test, [], [{inets_app_test, all}]},
- {appup_test, [], [{inets_appup_test, all}]}].
+ [start_inets,
+ start_httpc,
+ start_httpd,
+ start_ftpc,
+ start_tftpd
+ ]},
+ {app_test, [], [app, appup]}].
init_per_group(_GroupName, Config) ->
Config.
@@ -47,9 +53,6 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-
-
-
%%--------------------------------------------------------------------
%% Function: init_per_suite(Config) -> Config
%% Config - [tuple()]
@@ -83,6 +86,10 @@ end_per_suite(_Config) ->
%% 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_reload, Config) ->
+ inets:stop(),
+ ct:timetrap({seconds, 40}),
+ Config;
init_per_testcase(_Case, Config) ->
inets:stop(),
Config.
@@ -101,15 +108,18 @@ end_per_testcase(_, Config) ->
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
+app() ->
+ [{doc, "Test that the inets app file is ok"}].
+app(Config) when is_list(Config) ->
+ ok = ?t:app_test(inets).
+%%--------------------------------------------------------------------
+appup() ->
+ [{doc, "Test that the inets appup file is ok"}].
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(inets).
-
-
-%%-------------------------------------------------------------------------
-
-start_inets(doc) ->
- ["Test inets API functions"];
-start_inets(suite) ->
- [];
+start_inets() ->
+ [{doc, "Test inets API functions"}].
start_inets(Config) when is_list(Config) ->
[_|_] = inets:service_names(),
@@ -133,142 +143,92 @@ start_inets(Config) when is_list(Config) ->
ok = inets:start(permanent),
ok = inets:stop().
-
%%-------------------------------------------------------------------------
-start_httpc(doc) ->
- ["Start/stop of httpc service"];
-start_httpc(suite) ->
- [];
+start_httpc() ->
+ [{doc, "Start/stop of httpc service"}].
start_httpc(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- tsp("start_httpc -> entry with"
- "~n Config: ~p", [Config]),
+ PrivDir = proplists:get_value(priv_dir, 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),
+ ct:sleep(100),
- tsp("start_httpc -> check running services"),
Pids1 = [ServicePid || {_, ServicePid} <- inets:services()],
false = lists:member(Pid0, Pids1),
- tsp("start_httpc -> start httpc (stand-alone) with profile bar"),
{ok, Pid1} = inets:start(httpc, [{profile, bar}], stand_alone),
- tsp("start_httpc -> check running services"),
Pids2 = [ServicePid || {_, ServicePid} <- inets:services()],
false = lists:member(Pid1, Pids2),
- tsp("start_httpc -> stop httpc"),
ok = inets:stop(stand_alone, Pid1),
receive
{'EXIT', Pid1, shutdown} ->
ok
after 100 ->
- tsf(stand_alone_not_shutdown)
+ ct:fail(stand_alone_not_shutdown)
end,
- tsp("start_httpc -> stop inets"),
ok = inets:stop(),
- tsp("start_httpc -> unload inets"),
application:load(inets),
-
- tsp("start_httpc -> set inets environment (httpc profile foo)"),
application:set_env(inets, services, [{httpc,[{profile, foo},
{data_dir, PrivDir}]}]),
-
- tsp("start_httpc -> start inets"),
ok = inets:start(),
- tsp("start_httpc -> check running services"),
(?NUM_DEFAULT_SERVICES + 1) = length(inets:services()),
- tsp("start_httpc -> unset inets env"),
application:unset_env(inets, services),
-
- tsp("start_httpc -> stop inets"),
ok = inets:stop(),
-
- tsp("start_httpc -> start (empty) inets"),
ok = inets:start(),
- tsp("start_httpc -> start inets httpc service with profile foo"),
{ok, Pid3} = inets:start(httpc, [{profile, foo}]),
-
- tsp("start_httpc -> stop inets service httpc with profile foo"),
ok = inets:stop(httpc, foo),
-
- tsp("start_httpc -> check running services"),
Pids3 = [ServicePid || {_, ServicePid} <- inets:services()],
false = lists:member(Pid3, Pids3),
-
- tsp("start_httpc -> stop inets"),
- ok = inets:stop(),
-
- tsp("start_httpc -> done"),
- ok.
-
+ ok = inets:stop().
%%-------------------------------------------------------------------------
-start_httpd(doc) ->
- ["Start/stop of httpd service"];
-start_httpd(suite) ->
- [];
+start_httpd() ->
+ [{doc, "Start/stop of httpd service"}].
start_httpd(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- i("start_httpd -> entry with"
- "~n Config: ~p", [Config]),
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
HttpdConf = [{server_name, "httpd_test"}, {server_root, PrivDir},
- {document_root, PrivDir}, {bind_address, "localhost"}],
+ {document_root, PrivDir}, {bind_address, any}],
- i("start_httpd -> start inets"),
ok = inets:start(),
-
- i("start_httpd -> start httpd service"),
{ok, Pid0} = inets:start(httpd, [{port, 0}, {ipfamily, inet} | HttpdConf]),
Pids0 = [ServicePid || {_, ServicePid} <- inets:services()],
true = lists:member(Pid0, Pids0),
[_|_] = inets:services_info(),
- i("start_httpd -> stop httpd service"),
inets:stop(httpd, Pid0),
- test_server:sleep(500),
+ ct: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)
+ ct: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]),
@@ -276,17 +236,12 @@ start_httpd(Config) when is_list(Config) ->
ok = file:write(Fd0, Str),
file:close(Fd0),
- i("start_httpd -> [application] load inets"),
application:load(inets),
- i("start_httpd -> [application] set httpd services env with proplist-file"),
application:set_env(inets,
services, [{httpd, [{proplist_file, File0}]}]),
- i("start_httpd -> start inets"),
ok = inets:start(),
(?NUM_DEFAULT_SERVICES + 1) = length(inets:services()),
- i("start_httpd -> [application] unset services env"),
application:unset_env(inets, services),
- i("start_httpd -> stop inets"),
ok = inets:stop(),
File1 = filename:join(PrivDir, "httpd_apache.conf"),
@@ -299,116 +254,84 @@ start_httpd(Config) when is_list(Config) ->
file:write(Fd1, "Port 0\r\n"),
file:close(Fd1),
- i("start_httpd -> [application] load inets"),
application:load(inets),
- i("start_httpd -> [application] set httpd services env with file"),
application:set_env(inets,
services, [{httpd, [{file, File1}]}]),
- i("start_httpd -> start inets"),
ok = inets:start(),
(?NUM_DEFAULT_SERVICES + 1) = length(inets:services()),
- i("start_httpd -> [application] unset services env"),
application:unset_env(inets, services),
- i("start_httpd -> stop inets"),
ok = inets:stop(),
%% OLD format
- i("start_httpd -> [application] load inets"),
application:load(inets),
- i("start_httpd -> [application] set httpd services OLD env"),
application:set_env(inets,
services, [{httpd, File1}]),
- i("start_httpd -> start inets"),
ok = inets:start(),
(?NUM_DEFAULT_SERVICES + 1) = length(inets:services()),
- i("start_httpd -> [application] unset services enc"),
application:unset_env(inets, services),
- i("start_httpd -> stop inets"),
ok = inets:stop(),
-
- i("start_httpd -> start inets"),
ok = inets:start(),
- i("start_httpd -> try (and fail) start httpd service - server_name"),
{error, {missing_property, server_name}} =
inets:start(httpd, [{port, 0},
{server_root, PrivDir},
{document_root, PrivDir},
{bind_address, "localhost"}]),
- i("start_httpd -> try (and fail) start httpd service - missing document_root"),
{error, {missing_property, document_root}} =
inets:start(httpd, [{port, 0},
{server_name, "httpd_test"},
{server_root, PrivDir},
{bind_address, "localhost"}]),
- i("start_httpd -> try (and fail) start httpd service - missing server_root"),
{error, {missing_property, server_root}} =
inets:start(httpd, [{port, 0},
{server_name, "httpd_test"},
{document_root, PrivDir},
{bind_address, "localhost"}]),
- i("start_httpd -> try (and fail) start httpd service - missing port"),
{error, {missing_property, port}} =
inets:start(httpd, HttpdConf),
- i("start_httpd -> stop inets"),
- ok = inets:stop(),
- i("start_httpd -> done"),
- ok.
-
+ ok = inets:stop().
%%-------------------------------------------------------------------------
start_ftpc(doc) ->
- ["Start/stop of ftpc service"];
-start_ftpc(suite) ->
- [];
-start_ftpc(Config) when is_list(Config) ->
+ [{doc, "Start/stop of ftpc service"}];
+start_ftpc(Config0) when is_list(Config0) ->
process_flag(trap_exit, true),
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(),
- ok;
- _ ->
- {skip, "Unable to reach selected FTP server " ++ FtpdHost}
- end
- end
- catch
- throw:{error, not_found} ->
- {skip, "No available FTP servers"}
+ case ftp_SUITE:init_per_suite(Config0) of
+ {skip, _} = Skip ->
+ Skip;
+ Config ->
+ FtpdHost = proplists:get_value(ftpd_host,Config),
+ {ok, Pid0} = inets:start(ftpc, [{host, FtpdHost}]),
+ Pids0 = [ServicePid || {_, ServicePid} <-
+ inets:services()],
+ true = lists:member(Pid0, Pids0),
+ [_|_] = inets:services_info(),
+ inets:stop(ftpc, Pid0),
+ ct: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 ->
+ ct:fail(stand_alone_not_shutdown)
+ end,
+ ok = inets:stop(),
+ catch ftp_SUITE:end_per_SUITE(Config)
end.
-
-
%%-------------------------------------------------------------------------
-start_tftpd(doc) ->
- ["Start/stop of tfpd service"];
-start_tftpd(suite) ->
- [];
+start_tftpd() ->
+ [{doc, "Start/stop of tfpd service"}].
start_tftpd(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ok = inets:start(),
@@ -417,7 +340,7 @@ start_tftpd(Config) when is_list(Config) ->
true = lists:member(Pid0, Pids0),
[_|_] = inets:services_info(),
inets:stop(tftpd, Pid0),
- test_server:sleep(100),
+ ct:sleep(100),
Pids1 = [ServicePid || {_, ServicePid} <- inets:services()],
false = lists:member(Pid0, Pids1),
{ok, Pid1} =
@@ -429,7 +352,7 @@ start_tftpd(Config) when is_list(Config) ->
{'EXIT', Pid1, shutdown} ->
ok
after 100 ->
- test_server:fail(stand_alone_not_shutdown)
+ ct:fail(stand_alone_not_shutdown)
end,
ok = inets:stop(),
application:load(inets),
@@ -440,52 +363,40 @@ start_tftpd(Config) when is_list(Config) ->
application:unset_env(inets, services),
ok = inets:stop().
-
%%-------------------------------------------------------------------------
-httpd_reload(doc) ->
- ["Reload httpd configuration without restarting service"];
-httpd_reload(suite) ->
- [];
+httpd_reload() ->
+ [{doc, "Reload httpd configuration without restarting service"}].
httpd_reload(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- i("httpd_reload -> starting"),
- PrivDir = ?config(priv_dir, Config),
- DataDir = ?config(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
HttpdConf = [{server_name, "httpd_test"},
{server_root, PrivDir},
{document_root, PrivDir},
{bind_address, "localhost"}],
- i("httpd_reload -> start inets"),
-
ok = inets:start(),
- test_server:sleep(5000),
- i("httpd_reload -> inets started - start httpd service"),
+ ct:sleep(5000),
- {ok, Pid0} = inets:start(httpd, [{port, 0}, {ipfamily, inet} | HttpdConf]),
- test_server:sleep(5000),
- i("httpd_reload -> httpd service started (~p) - get port", [Pid0]),
+ {ok, Pid0} = inets:start(httpd, [{port, 0},
+ {ipfamily, inet} | HttpdConf]),
+ ct:sleep(5000),
[{port, Port0}] = httpd:info(Pid0, [port]),
- test_server:sleep(5000),
- i("httpd_reload -> Port: ~p - get document root", [Port0]),
+ ct:sleep(5000),
[{document_root, PrivDir}] = httpd:info(Pid0, [document_root]),
- test_server:sleep(5000),
- i("httpd_reload -> document root: ~p - reload config", [PrivDir]),
+ ct:sleep(5000),
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]),
-
+ ct:sleep(5000),
[{document_root, DataDir}] = httpd:info(Pid0, [document_root]),
- test_server:sleep(5000),
- i("httpd_reload -> document root: ~p - reload config", [DataDir]),
+ ct:sleep(5000),
ok = httpd:reload_config([{port, Port0}, {ipfamily, inet},
{server_name, "httpd_test"},
@@ -538,36 +449,5 @@ httpd_reload(Config) when is_list(Config) ->
ok = inets:stop(httpd, Pid1),
application:unset_env(inets, services),
- ok = inets:stop(),
- i("httpd_reload -> starting"),
- ok.
-
-
-tsf(Reason) ->
- test_server:fail(Reason).
-
-tsp(F) ->
- tsp(F, []).
-tsp(F, A) ->
- Timestamp = 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).
+ ok = inets:stop().
diff --git a/lib/inets/test/inets_app_test.erl b/lib/inets/test/inets_app_test.erl
deleted file mode 100644
index eabfa69f7c..0000000000
--- a/lib/inets/test/inets_app_test.erl
+++ /dev/null
@@ -1,307 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2002-2012. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-%%----------------------------------------------------------------------
-%% 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)),
-
- %% We need to check if there is a point to run this test.
- %% On some platforms, crypto will not build, which in turn
- %% causes ssl to not build (at this time, this will
- %% change in the future).
- %% So, we first check if we can start crypto, and if not,
- %% we skip this test case!
- ?ENSURE_STARTED(crypto),
-
- [{watchdog, Dog}| NewConfig];
-init_per_testcase(_, Config) ->
- Config.
-
-end_per_testcase(_Case, Config) ->
- Config.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-all() ->
- [fields, modules, exportall, app_depend, undef_funcs].
-
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-init_per_suite(suite) -> [];
-init_per_suite(doc) -> [];
-init_per_suite(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.
-
-
-end_per_suite(suite) -> [];
-end_per_suite(doc) -> [];
-end_per_suite(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
deleted file mode 100644
index d563b52ae7..0000000000
--- a/lib/inets/test/inets_appup_test.erl
+++ /dev/null
@@ -1,358 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2002-2013. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% 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_appup_test).
-
--compile(export_all).
--compile({no_auto_import,[error/1]}).
-
--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.
-
-end_per_testcase(_Case, Config) ->
- Config.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-all() ->
- [appup].
-
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-init_per_suite(suite) -> [];
-init_per_suite(doc) -> [];
-init_per_suite(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]).
-
-
-end_per_suite(suite) -> [];
-end_per_suite(doc) -> [];
-end_per_suite(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(REBin) when is_binary(REBin) ->
- try
- begin
- RE = binary_to_list(REBin),
- case re:compile(RE) of
- {ok, _} ->
- ok;
- {error, _} ->
- error({bad_version, REBin})
- end
- end
- catch
- _T:_E ->
- error({bad_version, REBin})
- end;
-check_version(V) ->
- error({bad_version, V}).
-
-
-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_socketwrap_SUITE.erl b/lib/inets/test/inets_socketwrap_SUITE.erl
new file mode 100644
index 0000000000..18df995215
--- /dev/null
+++ b/lib/inets/test/inets_socketwrap_SUITE.erl
@@ -0,0 +1,154 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2015. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(inets_socketwrap_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include("inets_test_lib.hrl").
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [start_httpd_fd, start_tftpd_fd].
+
+init_per_suite(Config) ->
+ case os:type() of
+ {unix, linux} ->
+ Config;
+ _ ->
+ {skip, linux_feature}
+ end.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+init_per_testcase(Case, Config) ->
+ end_per_testcase(Case, Config),
+ Config.
+
+end_per_testcase(_, Config) ->
+ inets:stop(),
+ Config.
+
+%%-------------------------------------------------------------------------
+start_httpd_fd() ->
+ [{doc, "Start/stop of httpd service with socket wrapper"}].
+start_httpd_fd(Config) when is_list(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
+ HttpdConf = [{port, 80}, {ipfamily, inet},
+ {server_name, "httpd_fd_test"}, {server_root, PrivDir},
+ {document_root, PrivDir}, {bind_address, any}],
+ case setup_node_info(node()) of
+ {skip, _} = Skip ->
+ Skip;
+ {Node, NodeArg} ->
+ InetPort = inets_test_lib:inet_port(node()),
+ ct:pal("Node: ~p Port ~p~n", [Node, InetPort]),
+ Wrapper = filename:join(DataDir, "setuid_socket_wrap"),
+ Cmd = Wrapper ++
+ " -s -httpd_80,0:" ++ integer_to_list(InetPort)
+ ++ " -p " ++ os:find_executable("erl") ++
+ " -- " ++ NodeArg,
+ ct:pal("cmd: ~p~n", [Cmd]),
+ case open_port({spawn, Cmd}, [stderr_to_stdout]) of
+ Port when is_port(Port) ->
+ wait_node_up(Node, 10),
+ ct:pal("~p", [rpc:call(Node, init, get_argument, [httpd_80])]),
+ ok = rpc:call(Node, inets, start, []),
+ {ok, Pid} = rpc:call(Node, inets, start, [httpd, HttpdConf]),
+ [{port, InetPort}] = rpc:call(Node, httpd, info, [Pid, [port]]),
+ rpc:call(Node, erlang, halt, []);
+ _ ->
+ ct:fail(open_port_failed)
+ end
+ end.
+%%-------------------------------------------------------------------------
+start_tftpd_fd() ->
+ [{doc, "Start/stop of tfpd service with socket wrapper"}].
+start_tftpd_fd(Config) when is_list(Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ case setup_node_info(node()) of
+ {skip, _} = Skip ->
+ Skip;
+ {Node, NodeArg} ->
+ InetPort = inets_test_lib:inet_port(node()),
+ ct:pal("Node: ~p~n", [Node]),
+ Wrapper = filename:join(DataDir, "setuid_socket_wrap"),
+ Cmd = Wrapper ++
+ " -s -tftpd_69,0:" ++ integer_to_list(InetPort)
+ ++ " -p " ++ os:find_executable("erl") ++
+ " -- " ++ NodeArg,
+ ct:pal("cmd: ~p~n", [Cmd]),
+ case open_port({spawn, Cmd}, [stderr_to_stdout]) of
+ Port when is_port(Port) ->
+ wait_node_up(Node, 10),
+ ct:pal("~p", [rpc:call(Node, init, get_argument, [tftpd_69])]),
+ ok = rpc:call(Node, inets, start, []),
+ {ok, Pid} = rpc:call(Node, inets, start,
+ [tftpd,[{host, "localhost"}]]),
+ {ok, Info} = rpc:call(Node, tftp, info, [Pid]),
+ {value,{port, InetPort}} = lists:keysearch(port, 1, Info),
+ rpc:call(Node, erlang, halt, []);
+ _ ->
+ ct:fail(open_port_failed)
+ end
+ end.
+%%-------------------------------------------------------------------------
+%% Internal functions
+%%-------------------------------------------------------------------------
+setup_node_info(nonode@nohost) ->
+ {skip, needs_distributed_node};
+setup_node_info(Node) ->
+ Static = "-detached -noinput",
+ Name = "inets_fd_test",
+ NameSw = case net_kernel:longnames() of
+ false -> "-sname ";
+ _ -> "-name "
+ end,
+ StrNode =
+ Static ++ " "
+ ++ NameSw ++ " " ++ Name ++ " "
+ ++ "-setcookie " ++ atom_to_list(erlang:get_cookie()),
+ [_, Location] = string:tokens(atom_to_list(Node), "$@"),
+ TestNode = Name ++ "@" ++ Location,
+ {list_to_atom(TestNode), StrNode}.
+
+wait_node_up(Node, 0) ->
+ ct:fail({failed_to_start_node, Node});
+wait_node_up(Node, N) ->
+ ct:pal("(Node ~p: net_adm:ping(~p)~n", [node(), Node]),
+ case net_adm:ping(Node) of
+ pong ->
+ ok;
+ pang ->
+ ct:sleep(5000),
+ wait_node_up(Node, N-1)
+ end.
diff --git a/lib/inets/test/inets_socketwrap_SUITE_data/Makefile.src b/lib/inets/test/inets_socketwrap_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..0933815b58
--- /dev/null
+++ b/lib/inets/test/inets_socketwrap_SUITE_data/Makefile.src
@@ -0,0 +1,39 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2015-2015. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+
+CC = @CC@
+LD = @LD@
+CFLAGS = @CFLAGS@
+CROSSLDFLAGS = @CROSSLDFLAGS@
+
+PROGS = setuid_socket_wrap@exe@
+
+.PHONY: all
+@IFEQ@ (@os@, linux-gnu)
+all: $(PROGS)
+@ELSE@
+all:
+@ENDIF@
+
+setuid_socket_wrap@exe@: setuid_socket_wrap@obj@
+ $(LD) $(CROSSLDFLAGS) -o setuid_socket_wrap setuid_socket_wrap@obj@ @LIBS@
+
+setuid_socket_wrap@obj@: setuid_socket_wrap.c
+ $(CC) -c $(CFLAGS) -o setuid_socket_wrap@obj@ setuid_socket_wrap.c
diff --git a/lib/inets/test/inets_socketwrap_SUITE_data/setuid_socket_wrap.c b/lib/inets/test/inets_socketwrap_SUITE_data/setuid_socket_wrap.c
new file mode 100644
index 0000000000..018d398956
--- /dev/null
+++ b/lib/inets/test/inets_socketwrap_SUITE_data/setuid_socket_wrap.c
@@ -0,0 +1,259 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1999-2016. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * setuid_socket_wrap.c
+ *
+ * ./a.out [-s [tag,][addr]:[port]]* [-d [tag,][addr]:[port]]*
+ * [-r [tag,]proto]* [-p erl_path]* -- program args
+ *
+ * Where: -s = stream socket, -d datagram socket and -r means raw socket.
+ *
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+struct sock_list {
+ struct sock_list *next;
+ int fd;
+ int type;
+ int protocol;
+ struct sockaddr_in addr;
+ char *arg;
+};
+
+int parse_addr(addr, str)
+ struct sockaddr_in *addr;
+ char *str;
+{
+ int port = 0;
+ char *cp;
+ struct hostent *hp;
+ struct servent *se;
+
+ if ((cp = strrchr(str, (int)':')) != NULL)
+ *cp++ = '\0';
+ if (cp) {
+ if (!isdigit((int)cp[0])) {
+ if ((se = getservbyname(cp, "tcp")) != NULL) {
+ port = ntohs(se->s_port);
+ } else {
+ fprintf(stderr, "unknown port %s\n", cp);
+ return -1;
+ }
+ } else {
+ port = atoi(cp);
+ }
+ }
+ if (port < 0 || port > 0xffff) {
+ fprintf(stderr, "bad port number %d\n", port);
+ return -1;
+ }
+
+ bzero(addr, sizeof(*addr));
+ addr->sin_family = AF_INET;
+ addr->sin_port = htons(port);
+ if (*str == '\000') {
+ addr->sin_addr.s_addr = INADDR_ANY;
+ } else {
+ if ((addr->sin_addr.s_addr = inet_addr(str)) == INADDR_NONE) {
+ if ((hp = gethostbyname(str)) == NULL) {
+ fprintf(stderr, "\"%s\" unknown host or address!\n", str);
+ return -1;
+ } else {
+ bcopy(hp->h_addr_list[0], &addr->sin_addr.s_addr,hp->h_length);
+ }
+ }
+ }
+ return 0;
+}
+
+struct sock_list *new_entry(type, argstr)
+ int type;
+ char *argstr;
+{
+ struct sock_list *sle;
+ char *cp;
+
+ sle = (struct sock_list *)malloc(sizeof(struct sock_list));
+ if (!sle)
+ return NULL;
+ sle->next = NULL;
+ sle->fd = -1;
+
+ if ((cp = strchr(argstr, (int)',')) != NULL) {
+ *cp++ = '\0';
+ sle->arg = argstr;
+ argstr = cp;
+ } else {
+ sle->arg = "-fd";
+ }
+ sle->type = type;
+ switch (type) {
+ case SOCK_RAW: {
+ struct protoent *pe;
+ pe = getprotobyname(argstr);
+ if (!pe) {
+ fprintf(stderr, "Unknown protocol: %s\n", argstr);
+ free(sle);
+ return NULL;
+ }
+ sle->protocol = pe->p_proto;
+ break;
+ }
+ case SOCK_STREAM:
+ case SOCK_DGRAM:
+ sle->protocol = 0;
+ if (parse_addr(&sle->addr, argstr) < 0) {
+ free(sle);
+ return NULL;
+ }
+ break;
+ }
+ return sle;
+}
+
+int open_socket(sle)
+ struct sock_list *sle;
+{
+ sle->fd = socket(AF_INET, sle->type, sle->protocol);
+ if (sle->fd < 0) {
+ perror("socket");
+ return -1;
+ }
+ if (sle->type != SOCK_RAW) {
+#if 0
+ printf("binding fd %d to %s:%d\n", sle->fd,
+ inet_ntoa(sle->addr.sin_addr), ntohs(sle->addr.sin_port));
+#endif
+ if (bind(sle->fd, (struct sockaddr *)&sle->addr, sizeof(sle->addr))<0){
+ perror("bind");
+ close(sle->fd);
+ return -1;
+ }
+ }
+ return sle->fd;
+}
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct sock_list *sl = NULL, *sltmp = NULL;
+ int count = 0;
+ int c;
+ char *run_prog = NULL;
+
+ while ((c = getopt(argc, argv, "s:d:r:p:")) != EOF)
+ switch (c) {
+ case 's':
+ sltmp = new_entry(SOCK_STREAM, optarg);
+ if (!sltmp) {
+ exit(1);
+ }
+ sltmp->next = sl;
+ sl = sltmp;
+ count++;
+ break;
+ case 'd':
+ sltmp = new_entry(SOCK_DGRAM, optarg);
+ if (!sltmp) {
+ exit(1);
+ }
+ sltmp->next = sl;
+ sl = sltmp;
+ count++;
+ break;
+ case 'r':
+ sltmp = new_entry(SOCK_RAW, optarg);
+ if (!sltmp) {
+ exit(1);
+ }
+ sltmp->next = sl;
+ sl = sltmp;
+ count++;
+ break;
+ case 'p':
+ run_prog = optarg;
+ break;
+ default:
+ exit(1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ for(sltmp = sl; sltmp != NULL; sltmp = sltmp->next)
+ if (open_socket(sltmp) < 0) {
+ fprintf(stderr, "failed to create socket!\n");
+ exit(1);
+ }
+
+ setuid(getuid());
+
+ {
+ int i;
+ char **newargv;
+ char *run_prog_name;
+
+ newargv = (char **)malloc((1 + 2*count + argc + 1) * sizeof(char*));
+
+ if ((run_prog_name = strrchr(run_prog, (int)'/')) == NULL)
+ run_prog_name = run_prog;
+ else
+ run_prog_name++;
+
+ i = 0;
+ newargv[i++] = run_prog_name;
+
+ for (; argc; argc--, argv++, i++)
+ newargv[i] = *argv;
+ for(sltmp = sl; sltmp != NULL; ) {
+ char *fd_str = (char *)malloc(8);
+ if (!fd_str) exit(1);
+ sprintf(fd_str, "%d", sltmp->fd);
+ if (sltmp->arg && *(sltmp->arg))
+ newargv[i++] = sltmp->arg;
+ newargv[i++] = fd_str;
+ sl = sltmp;
+ sltmp = sltmp->next;
+ free(sl);
+ }
+ newargv[i] = (char *)NULL;
+ execv(run_prog, newargv);
+ perror("exec");
+ exit(1);
+ }
+ exit(0);
+}
diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl
index 0ac940fd3e..5b8b1463c8 100644
--- a/lib/inets/test/inets_sup_SUITE.erl
+++ b/lib/inets/test/inets_sup_SUITE.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -21,15 +22,18 @@
-module(inets_sup_SUITE).
-include_lib("common_test/include/ct.hrl").
--include("test_server_line.hrl").
%% Note: This directive should only be used in test suites.
-compile(export_all).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds, 10}}
+ ].
all() ->
- [default_tree, ftpc_worker, tftpd_worker, httpd_subtree,
+ [default_tree, ftpc_worker, tftpd_worker,
+ httpd_subtree, httpd_subtree_profile,
httpc_subtree].
groups() ->
@@ -41,149 +45,83 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-
-%%--------------------------------------------------------------------
-%% Function: init_per_suite(Config) -> Config
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Initiation before the whole suite
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
init_per_suite(Config) ->
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}]),
-
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Dir = filename:join(PrivDir, "root"),
+ ok = file:make_dir(Dir),
+
+ SimpleConfig = [{port, 0},
+ {server_name,"www.test"},
+ {modules, [mod_get]},
+ {server_root, Dir},
+ {document_root, Dir},
+ {bind_address, any},
+ {ipfamily, inet}],
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]
+ inets:stop(),
+ inets:start(),
+ inets:start(httpd, SimpleConfig),
+ Config
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),
+ inets:stop(),
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]),
+init_per_testcase(httpd_subtree_profile, Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Dir = filename:join(PrivDir, "root"),
+ ok = file:make_dir(Dir),
+
+ SimpleConfig = [{port, 0},
+ {server_name,"www.test"},
+ {modules, [mod_get]},
+ {server_root, Dir},
+ {document_root, Dir},
+ {bind_address, any},
+ {profile, test_profile},
+ {ipfamily, inet}],
+ try
+ inets:stop(),
+ inets:start(),
+ {ok, _} = inets:start(httpd, SimpleConfig),
+ Config
+ catch
+ _:Reason ->
+ inets:stop(),
+ exit({failed_starting_inets, Reason})
+ end;
+
+
+init_per_testcase(_Case, Config) ->
+ inets: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),
+ Config.
+
+end_per_testcase(Case, Config) when Case == httpd_subtree;
+ Case == httpd_subtree_profile ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Dir = filename:join(PrivDir, "root"),
+ inets_test_lib:del_dirs(Dir),
ok;
-end_per_testcase(_, Config) ->
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
+end_per_testcase(_, _) ->
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() ->
+ [{doc, "Makes sure the correct processes are started and linked,"
+ "in the default case."}].
default_tree(Config) when is_list(Config) ->
TopSupChildren = supervisor:which_children(inets_sup),
4 = length(TopSupChildren),
@@ -216,94 +154,111 @@ default_tree(Config) when is_list(Config) ->
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) ->
+ftpc_worker() ->
+ [{doc, "Makes sure the ftp worker processes are added and removed "
+ "appropriatly to/from the supervison tree."}].
+ftpc_worker(Config0) when is_list(Config0) ->
[] = 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),
- ok;
- Children ->
- exit({unexpected_children, Children})
- end;
- _ ->
- {skip, "Unable to reach test FTP server"}
+ case ftp_SUITE:init_per_suite(Config0) of
+ {skip, _} = Skip ->
+ Skip;
+ Config ->
+ FtpdHost = proplists:get_value(ftpd_host,Config),
+ {ok, Pid} = inets:start(ftpc, [{host, FtpdHost}]),
+ case supervisor:which_children(ftp_sup) of
+ [{_,_, worker, [ftp]}] ->
+ inets:stop(ftpc, Pid),
+ ct:sleep(5000),
+ [] = supervisor:which_children(ftp_sup),
+ catch ftp_SUITE:end_per_SUITE(Config),
+ ok;
+ Children ->
+ catch ftp_SUITE:end_per_SUITE(Config),
+ exit({unexpected_children, Children})
end
- end
- catch
- throw:{error, not_found} ->
- {skip, "No available FTP servers"}
end.
-
-%%-------------------------------------------------------------------------
-%% tftpd_worker
-%%-------------------------------------------------------------------------
-tftpd_worker(doc) ->
- ["Makes sure the tftp sub tree is correct."];
-tftpd_worker(suite) ->
- [];
+tftpd_worker() ->
+ [{doc, "Makes sure the tftp sub tree is correct."}].
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),
+ {ok, Pid0} = inets:start(tftpd, [{host, inets_test_lib:hostname()},
+ {port, 0}]),
+ {ok, _Pid1} = inets:start(tftpd, [{host, inets_test_lib:hostname()},
+ {port, 0}], stand_alone),
[{_,Pid0, worker, _}] = supervisor:which_children(tftp_sup),
inets:stop(tftpd, Pid0),
- test_server:sleep(5000),
+ ct:sleep(5000),
[] = supervisor:which_children(tftp_sup),
ok.
+httpd_subtree() ->
+ [{doc, "Makes sure the httpd sub tree is correct."}].
+httpd_subtree(Config) when is_list(Config) ->
+ do_httpd_subtree(Config, default).
+
+httpd_subtree_profile(doc) ->
+ ["Makes sure the httpd sub tree is correct when using a profile"];
+httpd_subtree_profile(Config) when is_list(Config) ->
+ do_httpd_subtree(Config, test_profile).
+
+httpc_subtree() ->
+ [{doc, "Makes sure the httpd sub tree is correct."}].
+httpc_subtree(Config) when is_list(Config) ->
+ {ok, Foo} = inets:start(httpc, [{profile, foo}]),
+
+ {ok, Bar} = inets:start(httpc, [{profile, bar}], stand_alone),
+
+ HttpcChildren = supervisor:which_children(httpc_profile_sup),
+
+ {value, {httpc_manager, _, worker, [httpc_manager]}} =
+ lists:keysearch(httpc_manager, 1, HttpcChildren),
+
+ {value,{{httpc,foo}, _Pid, worker, [httpc_manager]}} =
+ lists:keysearch({httpc, foo}, 1, HttpcChildren),
+ false = lists:keysearch({httpc, bar}, 1, HttpcChildren),
+
+ inets:stop(httpc, Foo),
+ exit(Bar, normal).
%%-------------------------------------------------------------------------
-%% httpd_subtree
+%% Internal functions
%%-------------------------------------------------------------------------
-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", []),
+verify_child(Parent, Child, Type) ->
+ Children = supervisor:which_children(Parent),
+ 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) ->
+ {ok, Id};
+ true when (Type2 =/= Type) ->
+ {error, {wrong_type, Type2, Child, Parent}};
+ false ->
+ verify_child(Children, Parent, Child, Type)
+ end.
+
+do_httpd_subtree(_Config, Profile) ->
+ %% Check that we have the httpd top supervisor
{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),
+ {httpd_instance_sup, Addr, Port, Profile} = Id,
+ Instance = httpd_util:make_name("httpd_instance_sup", Addr, Port, Profile),
%% Check that we have the expected httpd instance children
- io:format("httpd_subtree -> verify httpd instance children "
- "(acceptor, misc and manager)~n", []),
+ {ok, _} = verify_child(Instance, httpd_connection_sup, supervisor),
{ok, _} = verify_child(Instance, httpd_acceptor_sup, supervisor),
{ok, _} = verify_child(Instance, httpd_misc_sup, supervisor),
{ok, _} = verify_child(Instance, httpd_manager, worker),
%% Check that the httpd instance acc supervisor has children
- io:format("httpd_subtree -> verify acc~n", []),
- InstanceAcc = httpd_util:make_name("httpd_acc_sup", Addr, Port),
+ InstanceAcc = httpd_util:make_name("httpd_acceptor_sup", Addr, Port, Profile),
case supervisor:which_children(InstanceAcc) of
[_ | _] ->
ok;
@@ -314,7 +269,7 @@ httpd_subtree(Config) when is_list(Config) ->
%% 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),
+ InstanceMisc = httpd_util:make_name("httpd_misc_sup", Addr, Port, Profile),
case supervisor:which_children(InstanceMisc) of
[] ->
ok;
@@ -324,90 +279,3 @@ httpd_subtree(Config) when is_list(Config) ->
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
deleted file mode 100644
index e52d345ff7..0000000000
--- a/lib/inets/test/inets_sup_SUITE_data/mime.types
+++ /dev/null
@@ -1,3 +0,0 @@
-# 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
deleted file mode 100644
index e1429b4a28..0000000000
--- a/lib/inets/test/inets_sup_SUITE_data/simple.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-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
index 6ccc7b0da1..f1185f7574 100644
--- a/lib/inets/test/inets_test_lib.erl
+++ b/lib/inets/test/inets_test_lib.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -287,7 +288,9 @@ print(F, A, Mod, Line) ->
print("", F, A, Mod, Line).
hostname() ->
- from($@, atom_to_list(node())).
+ {ok, Name} = inet:gethostname(),
+ Name.
+
from(H, [H | T]) -> T;
from(H, [_ | T]) -> from(H, T);
from(_, []) -> [].
@@ -496,13 +499,6 @@ close(essl,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).
@@ -544,31 +540,19 @@ flush() ->
tsp(F) ->
tsp(F, []).
tsp(F, A) ->
- Timestamp = formated_timestamp(),
- test_server:format("*** ~s ~p ~p " ++ F ++ "~n",
+ Timestamp = inets_lib:formated_timestamp(),
+ ct:pal("*** ~s ~p ~p " ++ F ++ "~n",
[Timestamp, node(), self() | A]).
tsf(Reason) ->
- test_server:fail(Reason).
+ ct:fail(Reason).
tss(Time) ->
- test_server:sleep(Time).
+ ct:sleep(Time).
timestamp() ->
http_util:timestamp().
-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).
-
start_apps(Apps) ->
lists:foreach(fun(App) ->
application:stop(App),
@@ -579,3 +563,12 @@ stop_apps(Apps) ->
application:stop(App)
end, Apps).
+inet_port(Node) ->
+ {Port, Socket} = do_inet_port(Node),
+ rpc:call(Node, gen_tcp, close, [Socket]),
+ Port.
+
+do_inet_port(Node) ->
+ {ok, Socket} = rpc:call(Node, gen_tcp, listen, [0, [{reuseaddr, true}]]),
+ {ok, Port} = rpc:call(Node, inet, port, [Socket]),
+ {Port, Socket}.
diff --git a/lib/inets/test/inets_test_lib.hrl b/lib/inets/test/inets_test_lib.hrl
index 6a86b1b764..d436395290 100644
--- a/lib/inets/test/inets_test_lib.hrl
+++ b/lib/inets/test/inets_test_lib.hrl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -20,96 +21,8 @@
%%----------------------------------------------------------------------
%% 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.
-
-
-%% - OS Command and stuff
-
--define(OSCMD(Cmd), inets_test_lib:oscmd(Cmd)).
-
--define(PRINT_SYSTEM_INFO(P), inets_test_lib:print_system_info(P)).
-
--define(RUN_ON_OS(OS, FUN), inets_test_lib:run_on_os(OS, FUN)).
--define(RUN_ON_WINDOWS(FUN), inets_test_lib:run_on_windows(FUN)).
-
-
-%% - 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(ENSURE_STARTED(A), inets_test_lib:ensure_started(A)).
--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, ?MODULE, ?LINE)).
--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/old_httpd_SUITE.erl b/lib/inets/test/old_httpd_SUITE.erl
new file mode 100644
index 0000000000..172db53844
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE.erl
@@ -0,0 +1,2347 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(old_httpd_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include("inets_test_lib.hrl").
+
+-include_lib("kernel/include/file.hrl").
+
+%% Test server specific exports
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
+-export([init_per_testcase/2, end_per_testcase/2,
+ init_per_suite/1, end_per_suite/1]).
+
+%% Core Server tests
+-export([
+ ip_mod_alias/1,
+ ip_mod_actions/1,
+ ip_mod_security/1,
+ ip_mod_auth/1,
+ ip_mod_auth_api/1,
+ ip_mod_auth_mnesia_api/1,
+ ip_mod_htaccess/1,
+ ip_mod_cgi/1,
+ ip_mod_esi/1,
+ ip_mod_get/1,
+ ip_mod_head/1,
+ ip_mod_all/1,
+ ip_load_light/1,
+ ip_load_medium/1,
+ ip_load_heavy/1,
+ ip_dos_hostname/1,
+ ip_time_test/1,
+ ip_block_disturbing_idle/1,
+ ip_block_non_disturbing_idle/1,
+ ip_block_503/1,
+ ip_block_disturbing_active/1,
+ ip_block_non_disturbing_active/1,
+ ip_block_disturbing_active_timeout_not_released/1,
+ ip_block_disturbing_active_timeout_released/1,
+ ip_block_non_disturbing_active_timeout_not_released/1,
+ ip_block_non_disturbing_active_timeout_released/1,
+ ip_block_disturbing_blocker_dies/1,
+ ip_block_non_disturbing_blocker_dies/1,
+ ip_restart_no_block/1,
+ ip_restart_disturbing_block/1,
+ ip_restart_non_disturbing_block/1
+ ]).
+
+-export([
+ essl_mod_alias/1,
+ essl_mod_actions/1,
+ essl_mod_security/1,
+ essl_mod_auth/1,
+ essl_mod_auth_api/1,
+ essl_mod_auth_mnesia_api/1,
+ essl_mod_htaccess/1,
+ essl_mod_cgi/1,
+ essl_mod_esi/1,
+ essl_mod_get/1,
+ essl_mod_head/1,
+ essl_mod_all/1,
+ essl_load_light/1,
+ essl_load_medium/1,
+ essl_load_heavy/1,
+ essl_dos_hostname/1,
+ essl_time_test/1,
+ essl_restart_no_block/1,
+ essl_restart_disturbing_block/1,
+ essl_restart_non_disturbing_block/1,
+ essl_block_disturbing_idle/1,
+ essl_block_non_disturbing_idle/1,
+ essl_block_503/1,
+ essl_block_disturbing_active/1,
+ essl_block_non_disturbing_active/1,
+ essl_block_disturbing_active_timeout_not_released/1,
+ essl_block_disturbing_active_timeout_released/1,
+ essl_block_non_disturbing_active_timeout_not_released/1,
+ essl_block_non_disturbing_active_timeout_released/1,
+ essl_block_disturbing_blocker_dies/1,
+ essl_block_non_disturbing_blocker_dies/1
+ ]).
+
+%%% HTTP 1.1 tests
+-export([ip_host/1, ip_chunked/1, ip_expect/1, ip_range/1,
+ ip_if_test/1, ip_http_trace/1, ip_http1_1_head/1,
+ ip_mod_cgi_chunked_encoding_test/1]).
+
+%%% HTTP 1.0 tests
+-export([ip_head_1_0/1, ip_get_1_0/1, ip_post_1_0/1]).
+
+%%% HTTP 0.9 tests
+-export([ip_get_0_9/1]).
+
+%%% Ticket tests
+-export([ticket_5775/1,ticket_5865/1,ticket_5913/1,ticket_6003/1,
+ ticket_7304/1]).
+
+%%% IPv6 tests
+-export([ipv6_hostname_ipcomm/0, ipv6_hostname_ipcomm/1,
+ ipv6_address_ipcomm/0, ipv6_address_ipcomm/1,
+ ipv6_hostname_essl/0, ipv6_hostname_essl/1,
+ ipv6_address_essl/0, ipv6_address_essl/1]).
+
+%% Help functions
+-export([cleanup_mnesia/0, setup_mnesia/0, setup_mnesia/1]).
+
+-define(IP_PORT, 8898).
+-define(SSL_PORT, 8899).
+-define(MAX_HEADER_SIZE, 256).
+-define(IPV6_LOCAL_HOST, "0:0:0:0:0:0:0:1").
+
+%% Minutes before failed auths timeout.
+-define(FAIL_EXPIRE_TIME,1).
+
+%% Seconds before successful auths timeout.
+-define(AUTH_TIMEOUT,5).
+
+-record(httpd_user, {user_name, password, user_data}).
+-record(httpd_group, {group_name, userlist}).
+
+
+%%--------------------------------------------------------------------
+%% all(Arg) -> [Doc] | [Case] | {skip, Comment}
+%% Arg - doc | suite
+%% Doc - string()
+%% Case - atom()
+%% Name of a test case function.
+%% Comment - string()
+%% Description: Returns documentation/test cases in this test suite
+%% or a skip tuple if the platform is not supported.
+%%--------------------------------------------------------------------
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ {group, ip},
+ {group, ssl},
+ %%{group, http_1_1_ip},
+ %%{group, http_1_0_ip},
+ %%{group, http_0_9_ip},
+ %%{group, ipv6},
+ {group, tickets}
+ ].
+
+groups() ->
+ [
+ {ip, [],
+ [
+ %%ip_mod_alias,
+ ip_mod_actions,
+ %%ip_mod_security,
+ %% ip_mod_auth,
+ %% ip_mod_auth_api,
+ ip_mod_auth_mnesia_api,
+ %%ip_mod_htaccess,
+ %%ip_mod_cgi,
+ %%ip_mod_esi,
+ %%ip_mod_get,
+ %%ip_mod_head,
+ %%ip_mod_all,
+ %% ip_load_light,
+ %% ip_load_medium,
+ %% ip_load_heavy,
+ %%ip_dos_hostname,
+ ip_time_test,
+ %% Only used through load_config
+ %% but we still need these tests
+ %% should be cleaned up and moved to new test suite
+ %%ip_restart_no_block,
+ %%ip_restart_disturbing_block,
+ %%ip_restart_non_disturbing_block,
+ %% Tested in inets_SUITE
+ %%ip_block_disturbing_idle,
+ %%ip_block_non_disturbing_idle,
+ ip_block_503
+ %% Tested in new httpd_SUITE
+ %%ip_block_disturbing_active,
+ %%ip_block_non_disturbing_active,
+ %%ip_block_disturbing_blocker_dies,
+ %%ip_block_non_disturbing_blocker_dies
+ %% No longer relevant
+ %%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,
+ ]},
+ {ssl, [], [{group, essl}]},
+ {essl, [],
+ [
+ %%essl_mod_alias,
+ essl_mod_actions,
+ %% essl_mod_security,
+ %% essl_mod_auth,
+ %% essl_mod_auth_api,
+ essl_mod_auth_mnesia_api,
+ %%essl_mod_htaccess,
+ %%essl_mod_cgi,
+ %%essl_mod_esi,
+ %%essl_mod_get,
+ %%essl_mod_head,
+ %% essl_mod_all,
+ %% essl_load_light,
+ %% essl_load_medium,
+ %% essl_load_heavy,
+ %%essl_dos_hostname,
+ essl_time_test
+ %% Replaced by load_config
+ %% essl_restart_no_block,
+ %% essl_restart_disturbing_block,
+ %% essl_restart_non_disturbing_block,
+ %% essl_block_disturbing_idle,
+ %% essl_block_non_disturbing_idle, essl_block_503,
+ %% essl_block_disturbing_active,
+ %% essl_block_non_disturbing_active,
+ %% essl_block_disturbing_active_timeout_not_released,
+ %% essl_block_disturbing_active_timeout_released,
+ %% essl_block_non_disturbing_active_timeout_not_released,
+ %% essl_block_non_disturbing_active_timeout_released,
+ %% essl_block_disturbing_blocker_dies,
+ %% essl_block_non_disturbing_blocker_dies
+ ]},
+ %% {http_1_1_ip, [],
+ %% [
+ %% %%ip_host, ip_chunked, ip_expect,
+ %% %%ip_range,
+ %% %%ip_if_test
+ %% %%ip_http_trace, ip_http1_1_head,
+ %% %%ip_mod_cgi_chunked_encoding_test
+ %% ]},
+ %%{http_1_0_ip, [],
+ %%[ip_head_1_0, ip_get_1_0, ip_post_1_0]},
+ %%{http_0_9_ip, [], [ip_get_0_9]},
+ %% {ipv6, [], [ipv6_hostname_ipcomm, ipv6_address_ipcomm,
+ %% ipv6_hostname_essl, ipv6_address_essl]},
+ {tickets, [],
+ [%%ticket_5775, ticket_5865,
+ ticket_5913%%, ticket_6003,
+ %%ticket_7304
+ ]}].
+
+init_per_group(ipv6 = _GroupName, Config) ->
+ case inets_test_lib:has_ipv6_support() of
+ {ok, _} ->
+ Config;
+ _ ->
+ {skip, "Host does not support IPv6"}
+ end;
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config) -> Config
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Initiation before the whole suite
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ io:format(user, "init_per_suite -> entry with"
+ "~n Config: ~p"
+ "~n", [Config]),
+
+ PrivDir = proplists:get_value(priv_dir, Config),
+ SuiteTopDir = filename:join(PrivDir, ?MODULE),
+ case file:make_dir(SuiteTopDir) of
+ ok ->
+ ok;
+ {error, eexist} ->
+ ok;
+ Error ->
+ throw({error, {failed_creating_suite_top_dir, Error}})
+ end,
+
+ [{has_ipv6_support, inets_test_lib:has_ipv6_support()},
+ {suite_top_dir, SuiteTopDir},
+ {node, node()},
+ {host, inets_test_lib:hostname()},
+ {address, getaddr()} | Config].
+
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config) -> _
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Cleanup after the whole suite
+%%--------------------------------------------------------------------
+
+end_per_suite(_Config) ->
+ %% SuiteTopDir = proplists:get_value(suite_top_dir, Config),
+ %% inets_test_lib:del_dirs(SuiteTopDir),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(Case, Config) -> Config
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Initiation before each test case
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_testcase(Case, Config) ->
+ NewConfig = init_per_testcase2(Case, Config),
+ init_per_testcase3(Case, NewConfig).
+
+
+init_per_testcase2(Case, Config) ->
+
+ %% tsp("init_per_testcase2 -> entry with"
+ %% "~n Config: ~p", [Config]),
+
+ IpNormal = integer_to_list(?IP_PORT) ++ ".conf",
+ IpHtaccess = integer_to_list(?IP_PORT) ++ "htaccess.conf",
+ SslNormal = integer_to_list(?SSL_PORT) ++ ".conf",
+ SslHtaccess = integer_to_list(?SSL_PORT) ++ "htaccess.conf",
+
+ DataDir = proplists:get_value(data_dir, Config),
+ SuiteTopDir = proplists:get_value(suite_top_dir, Config),
+
+ %% tsp("init_per_testcase2 -> "
+ %% "~n SuiteDir: ~p"
+ %% "~n DataDir: ~p", [SuiteTopDir, DataDir]),
+
+ TcTopDir = filename:join(SuiteTopDir, Case),
+ ?line ok = file:make_dir(TcTopDir),
+
+ %% tsp("init_per_testcase2 -> "
+ %% "~n TcTopDir: ~p", [TcTopDir]),
+
+ DataSrc = filename:join([DataDir, "server_root"]),
+ ServerRoot = filename:join([TcTopDir, "server_root"]),
+
+ %% tsp("init_per_testcase2 -> "
+ %% "~n DataSrc: ~p"
+ %% "~n ServerRoot: ~p", [DataSrc, ServerRoot]),
+
+ ok = file:make_dir(ServerRoot),
+ ok = file:make_dir(filename:join([TcTopDir, "logs"])),
+
+ NewConfig = [{tc_top_dir, TcTopDir}, {server_root, ServerRoot} | Config],
+
+ %% tsp("init_per_testcase2 -> copy DataSrc to ServerRoot"),
+
+ inets_test_lib:copy_dirs(DataSrc, ServerRoot),
+
+ %% tsp("init_per_testcase2 -> fix cgi"),
+ EnvCGI = filename:join([ServerRoot, "cgi-bin", "printenv.sh"]),
+ {ok, FileInfo} = file:read_file_info(EnvCGI),
+ ok = file:write_file_info(EnvCGI,
+ FileInfo#file_info{mode = 8#00755}),
+
+ EchoCGI = case test_server:os_type() of
+ {win32, _} ->
+ "cgi_echo.exe";
+ _ ->
+ "cgi_echo"
+ end,
+ CGIDir = filename:join([ServerRoot, "cgi-bin"]),
+ inets_test_lib:copy_file(EchoCGI, DataDir, CGIDir),
+ NewEchoCGI = filename:join([CGIDir, EchoCGI]),
+ {ok, FileInfo1} = file:read_file_info(NewEchoCGI),
+ ok = file:write_file_info(NewEchoCGI,
+ FileInfo1#file_info{mode = 8#00755}),
+
+ %% To be used by IP test cases
+ %% tsp("init_per_testcase2 -> ip testcase setups"),
+ create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig],
+ normal_access, IpNormal),
+ create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig],
+ mod_htaccess, IpHtaccess),
+
+ %% To be used by SSL test cases
+ %% tsp("init_per_testcase2 -> ssl testcase setups"),
+ SocketType =
+ case atom_to_list(Case) of
+ [X, $s, $s, $l | _] ->
+ case X of
+ $p -> ssl;
+ $e -> essl
+ end;
+ _ ->
+ ssl
+ end,
+
+ create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig],
+ normal_access, SslNormal),
+ create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig],
+ mod_htaccess, SslHtaccess),
+
+ %% To be used by IPv6 test cases. Case-clause is so that
+ %% you can do ts:run(inets, httpd_SUITE, <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.
+
+ tsp("init_per_testcase2 -> maybe generate IPv6 config file(s)"),
+ NewConfig2 =
+ case atom_to_list(Case) of
+ "ipv6_" ++ _ ->
+ case (catch inets_test_lib:has_ipv6_support(NewConfig)) of
+ {ok, IPv6Address0} ->
+ {ok, Hostname} = inet:gethostname(),
+ IPv6Address = http_transport:ipv6_name(IPv6Address0),
+ create_ipv6_config([{port, ?IP_PORT},
+ {sock_type, ip_comm},
+ {ipv6_host, IPv6Address} |
+ NewConfig],
+ "ipv6_hostname_ipcomm.conf",
+ Hostname),
+ create_ipv6_config([{port, ?IP_PORT},
+ {sock_type, ip_comm},
+ {ipv6_host, IPv6Address} |
+ NewConfig],
+ "ipv6_address_ipcomm.conf",
+ IPv6Address),
+ create_ipv6_config([{port, ?SSL_PORT},
+ {sock_type, essl},
+ {ipv6_host, IPv6Address} |
+ NewConfig],
+ "ipv6_hostname_essl.conf",
+ Hostname),
+ create_ipv6_config([{port, ?SSL_PORT},
+ {sock_type, essl},
+ {ipv6_host, IPv6Address} |
+ NewConfig],
+ "ipv6_address_essl.conf",
+ IPv6Address),
+ [{ipv6_host, IPv6Address} | NewConfig];
+ _ ->
+ NewConfig
+ end;
+
+ _ ->
+ NewConfig
+ end,
+
+ %% tsp("init_per_testcase2 -> done when"
+ %% "~n NewConfig2: ~p", [NewConfig2]),
+
+ NewConfig2.
+
+
+init_per_testcase3(Case, Config) ->
+ tsp("init_per_testcase3(~w) -> entry with"
+ "~n Config: ~p", [Case, Config]),
+
+
+%% %% Create a new fresh node to be used by the server in this test-case
+
+%% NodeName = list_to_atom(atom_to_list(Case) ++ "_httpd"),
+%% Node = inets_test_lib:start_node(NodeName),
+
+ %% Clean up (we do not want this clean up in end_per_testcase
+ %% if init_per_testcase crashes for some testcase it will
+ %% have contaminated the environment and there will be no clean up.)
+ %% This init can take a few different paths so that one crashes
+ %% does not mean that all invocations will.
+
+ application:unset_env(inets, services),
+ application:stop(inets),
+ application:stop(ssl),
+ cleanup_mnesia(),
+
+ %% Start initialization
+ tsp("init_per_testcase3(~w) -> start init", [Case]),
+
+ Dog = test_server:timetrap(inets_test_lib:minutes(10)),
+ NewConfig = lists:keydelete(watchdog, 1, Config),
+ TcTopDir = proplists:get_value(tc_top_dir, Config),
+
+ CaseRest =
+ case atom_to_list(Case) of
+ "ip_mod_htaccess" ->
+ inets_test_lib:start_http_server(
+ filename:join(TcTopDir,
+ integer_to_list(?IP_PORT) ++
+ "htaccess.conf")),
+ "mod_htaccess";
+ "ip_" ++ Rest ->
+ inets_test_lib:start_http_server(
+ filename:join(TcTopDir,
+ integer_to_list(?IP_PORT) ++ ".conf")),
+ Rest;
+ "ticket_5913" ->
+ HttpdOptions =
+ [{file,
+ filename:join(TcTopDir,
+ integer_to_list(?IP_PORT) ++ ".conf")},
+ {accept_timeout,30000},
+ {debug,[{exported_functions,
+ [httpd_manager,httpd_request_handler]}]}],
+ inets_test_lib:start_http_server(HttpdOptions);
+ "ticket_"++Rest ->
+ %% OTP-5913 use the new syntax of inets.config
+ inets_test_lib:start_http_server([{file,
+ filename:join(TcTopDir,
+ integer_to_list(?IP_PORT) ++ ".conf")}]),
+ Rest;
+
+ [X, $s, $s, $l, $_, $m, $o, $d, $_, $h, $t, $a, $c, $c, $e, $s, $s] ->
+ ?ENSURE_STARTED([crypto, public_key, ssl]),
+ SslTag =
+ case X of
+ $p -> ssl; % Plain
+ $e -> essl % Erlang based ssl
+ end,
+ case inets_test_lib:start_http_server_ssl(
+ filename:join(TcTopDir,
+ integer_to_list(?SSL_PORT) ++
+ "htaccess.conf"), SslTag) of
+ ok ->
+ "mod_htaccess";
+ Other ->
+ error_logger:info_msg("Other: ~p~n", [Other]),
+ {skip, "SSL does not seem to be supported"}
+ end;
+ [X, $s, $s, $l, $_ | Rest] ->
+ ?ENSURE_STARTED([crypto, public_key, ssl]),
+ SslTag =
+ case X of
+ $p -> ssl;
+ $e -> essl
+ end,
+ case inets_test_lib:start_http_server_ssl(
+ filename:join(TcTopDir,
+ integer_to_list(?SSL_PORT) ++
+ ".conf"), SslTag) of
+ ok ->
+ Rest;
+ Other ->
+ error_logger:info_msg("Other: ~p~n", [Other]),
+ {skip, "SSL does not seem to be supported"}
+ end;
+ "ipv6_" ++ _ = TestCaseStr ->
+ case inets_test_lib:has_ipv6_support() of
+ {ok, _} ->
+ inets_test_lib:start_http_server(
+ filename:join(TcTopDir,
+ TestCaseStr ++ ".conf"));
+
+ _ ->
+ {skip, "Host does not support IPv6"}
+ end
+ end,
+
+ InitRes =
+ case CaseRest of
+ {skip, _} = Skip ->
+ Skip;
+ "mod_auth_" ++ _ ->
+ start_mnesia(proplists:get_value(node, Config)),
+ [{watchdog, Dog} | NewConfig];
+ "mod_htaccess" ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ Path = filename:join([ServerRoot, "htdocs"]),
+ catch remove_htaccess(Path),
+ create_htaccess_data(Path, proplists:get_value(address, Config)),
+ [{watchdog, Dog} | NewConfig];
+ "range" ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ Path = filename:join([ServerRoot, "htdocs"]),
+ create_range_data(Path),
+ [{watchdog, Dog} | NewConfig];
+ _ ->
+ [{watchdog, Dog} | NewConfig]
+ end,
+
+ tsp("init_per_testcase3(~w) -> done when"
+ "~n InitRes: ~p", [Case, InitRes]),
+
+ InitRes.
+
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(Case, Config) -> _
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Cleanup after each test case
+%%--------------------------------------------------------------------
+end_per_testcase(Case, Config) ->
+ Dog = proplists:get_value(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ end_per_testcase2(Case, lists:keydelete(watchdog, 1, Config)),
+ ok.
+
+end_per_testcase2(Case, Config) ->
+ tsp("end_per_testcase2(~w) -> entry with"
+ "~n Config: ~p", [Case, Config]),
+ application:unset_env(inets, services),
+ application:stop(inets),
+ application:stop(ssl),
+ application:stop(crypto), % used by the new ssl (essl test cases)
+ cleanup_mnesia(),
+ tsp("end_per_testcase2(~w) -> done", [Case]),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+%% Test cases starts here.
+%%-------------------------------------------------------------------------
+
+%%-------------------------------------------------------------------------
+ip_mod_alias(doc) ->
+ ["Module test: mod_alias"];
+ip_mod_alias(suite) ->
+ [];
+ip_mod_alias(Config) when is_list(Config) ->
+ httpd_mod:alias(ip_comm, ?IP_PORT,
+ proplists:get_value(host, Config), proplists:get_value(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,
+ proplists:get_value(host, Config), proplists:get_value(node, Config)),
+ ok.
+
+%%-------------------------------------------------------------------------
+ip_mod_security(doc) ->
+ ["Module test: mod_security"];
+ip_mod_security(suite) ->
+ [];
+ip_mod_security(Config) when is_list(Config) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ httpd_mod:security(ServerRoot, ip_comm, ?IP_PORT,
+ proplists:get_value(host, Config), proplists:get_value(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,
+ proplists:get_value(host, Config), proplists:get_value(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 = proplists:get_value(server_root, Config),
+ Host = proplists:get_value(host, Config),
+ Node = proplists:get_value(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,
+ proplists:get_value(host, Config), proplists:get_value(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,
+ proplists:get_value(host, Config), proplists:get_value(node, Config)),
+ ok.
+%%-------------------------------------------------------------------------
+ip_mod_cgi(doc) ->
+ ["Module test: mod_cgi"];
+ip_mod_cgi(suite) ->
+ [];
+ip_mod_cgi(Config) when is_list(Config) ->
+ httpd_mod:cgi(ip_comm, ?IP_PORT,
+ proplists:get_value(host, Config), proplists:get_value(node, Config)),
+ ok.
+%%-------------------------------------------------------------------------
+ip_mod_esi(doc) ->
+ ["Module test: mod_esi"];
+ip_mod_esi(suite) ->
+ [];
+ip_mod_esi(Config) when is_list(Config) ->
+ httpd_mod:esi(ip_comm, ?IP_PORT,
+ proplists:get_value(host, Config), proplists:get_value(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,
+ proplists:get_value(host, Config), proplists:get_value(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,
+ proplists:get_value(host, Config), proplists:get_value(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,
+ proplists:get_value(host, Config), proplists:get_value(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, proplists:get_value(host, Config),
+ proplists:get_value(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, proplists:get_value(host, Config),
+ proplists:get_value(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, proplists:get_value(host, Config),
+ proplists:get_value(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, proplists:get_value(host, Config),
+ proplists:get_value(node, Config), ?MAX_HEADER_SIZE),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+ip_time_test(doc) ->
+ [""];
+ip_time_test(suite) ->
+ [];
+ip_time_test(Config) when is_list(Config) ->
+ httpd_time_test:t(ip_comm, proplists:get_value(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, proplists:get_value(host, Config),
+ proplists:get_value(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,
+ proplists:get_value(host, Config),
+ proplists:get_value(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,
+ proplists:get_value(host, Config),
+ proplists:get_value(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,
+ proplists:get_value(host, Config),
+ proplists:get_value(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,
+ proplists:get_value(host, Config),
+ proplists:get_value(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,
+ proplists:get_value(host,
+ Config),
+ proplists:get_value(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,
+ proplists:get_value(host,
+ Config),
+ proplists:get_value(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,
+ proplists:get_value(host,
+ Config),
+ proplists:get_value(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,
+ proplists:get_value(host,
+ Config),
+ proplists:get_value(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,
+ proplists:get_value(host, Config),
+ proplists:get_value(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,
+ proplists:get_value(host, Config),
+ proplists:get_value(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, proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+%%-------------------------------------------------------------------------
+ip_restart_disturbing_block(doc) ->
+ [""];
+ip_restart_disturbing_block(suite) ->
+ [];
+ip_restart_disturbing_block(Config) when is_list(Config) ->
+ httpd_block:restart_disturbing_block(ip_comm, ?IP_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(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) ->
+ httpd_block:restart_non_disturbing_block(ip_comm, ?IP_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+%%-------------------------------------------------------------------------
+
+essl_mod_alias(doc) ->
+ ["Module test: mod_alias - using new of configure new SSL"];
+essl_mod_alias(suite) ->
+ [];
+essl_mod_alias(Config) when is_list(Config) ->
+ ssl_mod_alias(essl, Config).
+
+
+ssl_mod_alias(Tag, Config) ->
+ httpd_mod:alias(Tag, ?SSL_PORT,
+ proplists:get_value(host, Config), proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_mod_actions(doc) ->
+ ["Module test: mod_actions - using new of configure new SSL"];
+essl_mod_actions(suite) ->
+ [];
+essl_mod_actions(Config) when is_list(Config) ->
+ ssl_mod_actions(essl, Config).
+
+
+ssl_mod_actions(Tag, Config) ->
+ httpd_mod:actions(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_mod_security(doc) ->
+ ["Module test: mod_security - using new of configure new SSL"];
+essl_mod_security(suite) ->
+ [];
+essl_mod_security(Config) when is_list(Config) ->
+ ssl_mod_security(essl, Config).
+
+ssl_mod_security(Tag, Config) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ httpd_mod:security(ServerRoot,
+ Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_mod_auth(doc) ->
+ ["Module test: mod_auth - using new of configure new SSL"];
+essl_mod_auth(suite) ->
+ [];
+essl_mod_auth(Config) when is_list(Config) ->
+ ssl_mod_auth(essl, Config).
+
+ssl_mod_auth(Tag, Config) ->
+ httpd_mod:auth(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+
+essl_mod_auth_api(doc) ->
+ ["Module test: mod_auth - using new of configure new SSL"];
+essl_mod_auth_api(suite) ->
+ [];
+essl_mod_auth_api(Config) when is_list(Config) ->
+ ssl_mod_auth_api(essl, Config).
+
+ssl_mod_auth_api(Tag, Config) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ Host = proplists:get_value(host, Config),
+ Node = proplists:get_value(node, Config),
+ httpd_mod:auth_api(ServerRoot, "", Tag, ?SSL_PORT, Host, Node),
+ httpd_mod:auth_api(ServerRoot, "dets_", Tag, ?SSL_PORT, Host, Node),
+ httpd_mod:auth_api(ServerRoot, "mnesia_", Tag, ?SSL_PORT, Host, Node),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+
+essl_mod_auth_mnesia_api(doc) ->
+ ["Module test: mod_auth_mnesia_api - using new of configure new SSL"];
+essl_mod_auth_mnesia_api(suite) ->
+ [];
+essl_mod_auth_mnesia_api(Config) when is_list(Config) ->
+ ssl_mod_auth_mnesia_api(essl, Config).
+
+ssl_mod_auth_mnesia_api(Tag, Config) ->
+ httpd_mod:auth_mnesia_api(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_mod_htaccess(doc) ->
+ ["Module test: mod_htaccess - using new of configure new SSL"];
+essl_mod_htaccess(suite) ->
+ [];
+essl_mod_htaccess(Config) when is_list(Config) ->
+ ssl_mod_htaccess(essl, Config).
+
+ssl_mod_htaccess(Tag, Config) ->
+ httpd_mod:htaccess(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_mod_cgi(doc) ->
+ ["Module test: mod_cgi - using new of configure new SSL"];
+essl_mod_cgi(suite) ->
+ [];
+essl_mod_cgi(Config) when is_list(Config) ->
+ ssl_mod_cgi(essl, Config).
+
+ssl_mod_cgi(Tag, Config) ->
+ httpd_mod:cgi(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_mod_esi(doc) ->
+ ["Module test: mod_esi - using new of configure new SSL"];
+essl_mod_esi(suite) ->
+ [];
+essl_mod_esi(Config) when is_list(Config) ->
+ ssl_mod_esi(essl, Config).
+
+ssl_mod_esi(Tag, Config) ->
+ httpd_mod:esi(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_mod_get(doc) ->
+ ["Module test: mod_get - using new of configure new SSL"];
+essl_mod_get(suite) ->
+ [];
+essl_mod_get(Config) when is_list(Config) ->
+ ssl_mod_get(essl, Config).
+
+ssl_mod_get(Tag, Config) ->
+ httpd_mod:get(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_mod_head(doc) ->
+ ["Module test: mod_head - using new of configure new SSL"];
+essl_mod_head(suite) ->
+ [];
+essl_mod_head(Config) when is_list(Config) ->
+ ssl_mod_head(essl, Config).
+
+ssl_mod_head(Tag, Config) ->
+ httpd_mod:head(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_mod_all(doc) ->
+ ["All modules test - using new of configure new SSL"];
+essl_mod_all(suite) ->
+ [];
+essl_mod_all(Config) when is_list(Config) ->
+ ssl_mod_all(essl, Config).
+
+ssl_mod_all(Tag, Config) ->
+ httpd_mod:all(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_load_light(doc) ->
+ ["Test light load - using new of configure new SSL"];
+essl_load_light(suite) ->
+ [];
+essl_load_light(Config) when is_list(Config) ->
+ ssl_load_light(essl, Config).
+
+ssl_load_light(Tag, Config) ->
+ httpd_load:load_test(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config),
+ get_nof_clients(ssl, light)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_load_medium(doc) ->
+ ["Test medium load - using new of configure new SSL"];
+essl_load_medium(suite) ->
+ [];
+essl_load_medium(Config) when is_list(Config) ->
+ ssl_load_medium(essl, Config).
+
+ssl_load_medium(Tag, Config) ->
+ httpd_load:load_test(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config),
+ get_nof_clients(ssl, medium)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_load_heavy(doc) ->
+ ["Test heavy load - using new of configure new SSL"];
+essl_load_heavy(suite) ->
+ [];
+essl_load_heavy(Config) when is_list(Config) ->
+ ssl_load_heavy(essl, Config).
+
+ssl_load_heavy(Tag, Config) ->
+ httpd_load:load_test(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config),
+ get_nof_clients(ssl, heavy)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+
+essl_dos_hostname(doc) ->
+ ["Denial Of Service (DOS) attack test case - using new of configure new SSL"];
+essl_dos_hostname(suite) ->
+ [];
+essl_dos_hostname(Config) when is_list(Config) ->
+ ssl_dos_hostname(essl, Config).
+
+ssl_dos_hostname(Tag, Config) ->
+ dos_hostname(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config),
+ ?MAX_HEADER_SIZE),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+
+essl_time_test(doc) ->
+ ["using new of configure new SSL"];
+essl_time_test(suite) ->
+ [];
+essl_time_test(Config) when is_list(Config) ->
+ ssl_time_test(essl, Config).
+
+ssl_time_test(Tag, Config) when is_list(Config) ->
+ httpd_time_test:t(Tag,
+ proplists:get_value(host, Config),
+ ?SSL_PORT),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+
+essl_block_503(doc) ->
+ ["Check that you will receive status code 503 when the server"
+ " is blocked and 200 when its not blocked - using new of configure new SSL."];
+essl_block_503(suite) ->
+ [];
+essl_block_503(Config) when is_list(Config) ->
+ ssl_block_503(essl, Config).
+
+ssl_block_503(Tag, Config) ->
+ httpd_block:block_503(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_block_disturbing_idle(doc) ->
+ ["Check that you can block/unblock an idle server. The strategy "
+ "distribing does not really make a difference in this case."
+ "Using new of configure new SSL"];
+essl_block_disturbing_idle(suite) ->
+ [];
+essl_block_disturbing_idle(Config) when is_list(Config) ->
+ ssl_block_disturbing_idle(essl, Config).
+
+ssl_block_disturbing_idle(Tag, Config) ->
+ httpd_block:block_disturbing_idle(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_block_non_disturbing_idle(doc) ->
+ ["Check that you can block/unblock an idle server. The strategy "
+ "non distribing does not really make a difference in this case."
+ "Using new of configure new SSL"];
+essl_block_non_disturbing_idle(suite) ->
+ [];
+essl_block_non_disturbing_idle(Config) when is_list(Config) ->
+ ssl_block_non_disturbing_idle(essl, Config).
+
+ssl_block_non_disturbing_idle(Tag, Config) ->
+ httpd_block:block_non_disturbing_idle(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_block_disturbing_active(doc) ->
+ ["Check that you can block/unblock an active server. The strategy "
+ "distribing means ongoing requests should be terminated."
+ "Using new of configure new SSL"];
+essl_block_disturbing_active(suite) ->
+ [];
+essl_block_disturbing_active(Config) when is_list(Config) ->
+ ssl_block_disturbing_active(essl, Config).
+
+ssl_block_disturbing_active(Tag, Config) ->
+ httpd_block:block_disturbing_active(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_block_non_disturbing_active(doc) ->
+ ["Check that you can block/unblock an idle server. The strategy "
+ "non distribing means the ongoing requests should be compleated."
+ "Using new of configure new SSL"];
+essl_block_non_disturbing_active(suite) ->
+ [];
+essl_block_non_disturbing_active(Config) when is_list(Config) ->
+ ssl_block_non_disturbing_active(essl, Config).
+
+ssl_block_non_disturbing_active(Tag, Config) ->
+ httpd_block:block_non_disturbing_idle(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_block_disturbing_active_timeout_not_released(doc) ->
+ ["Check that you can block an active server. The strategy "
+ "distribing means ongoing requests should be compleated"
+ "if the timeout does not occur."
+ "Using new of configure new SSL"];
+essl_block_disturbing_active_timeout_not_released(suite) ->
+ [];
+essl_block_disturbing_active_timeout_not_released(Config)
+ when is_list(Config) ->
+ ssl_block_disturbing_active_timeout_not_released(essl, Config).
+
+ssl_block_disturbing_active_timeout_not_released(Tag, Config) ->
+ Port = ?SSL_PORT,
+ Host = proplists:get_value(host, Config),
+ Node = proplists:get_value(node, Config),
+ httpd_block:block_disturbing_active_timeout_not_released(Tag,
+ Port, Host, Node),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_block_disturbing_active_timeout_released(doc) ->
+ ["Check that you can block an active server. The strategy "
+ "distribing means ongoing requests should be terminated when"
+ "the timeout occurs."
+ "Using new of configure new SSL"];
+essl_block_disturbing_active_timeout_released(suite) ->
+ [];
+essl_block_disturbing_active_timeout_released(Config)
+ when is_list(Config) ->
+ ssl_block_disturbing_active_timeout_released(essl, Config).
+
+ssl_block_disturbing_active_timeout_released(Tag, Config) ->
+ Port = ?SSL_PORT,
+ Host = proplists:get_value(host, Config),
+ Node = proplists:get_value(node, Config),
+ httpd_block:block_disturbing_active_timeout_released(Tag,
+ Port,
+ Host,
+ Node),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_block_non_disturbing_active_timeout_not_released(doc) ->
+ ["Check that you can block an active server. The strategy "
+ "non non distribing means ongoing requests should be completed."
+ "Using new of configure new SSL"];
+essl_block_non_disturbing_active_timeout_not_released(suite) ->
+ [];
+essl_block_non_disturbing_active_timeout_not_released(Config)
+ when is_list(Config) ->
+ ssl_block_non_disturbing_active_timeout_not_released(essl, Config).
+
+ssl_block_non_disturbing_active_timeout_not_released(Tag, Config) ->
+ Port = ?SSL_PORT,
+ Host = proplists:get_value(host, Config),
+ Node = proplists:get_value(node, Config),
+ httpd_block:block_non_disturbing_active_timeout_not_released(Tag,
+ Port,
+ Host,
+ Node),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+
+essl_block_non_disturbing_active_timeout_released(doc) ->
+ ["Check that you can block an active server. The strategy "
+ "non distribing means ongoing requests should be completed. "
+ "When the timeout occurs the block operation sohould be canceled."
+ "Using new of configure new SSL"];
+essl_block_non_disturbing_active_timeout_released(suite) ->
+ [];
+essl_block_non_disturbing_active_timeout_released(Config)
+ when is_list(Config) ->
+ ssl_block_non_disturbing_active_timeout_released(essl, Config).
+
+ssl_block_non_disturbing_active_timeout_released(Tag, Config)
+ when is_list(Config) ->
+ Port = ?SSL_PORT,
+ Host = proplists:get_value(host, Config),
+ Node = proplists:get_value(node, Config),
+ httpd_block:block_non_disturbing_active_timeout_released(Tag,
+ Port,
+ Host,
+ Node),
+
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+
+essl_block_disturbing_blocker_dies(doc) ->
+ ["using new of configure new SSL"];
+essl_block_disturbing_blocker_dies(suite) ->
+ [];
+essl_block_disturbing_blocker_dies(Config) when is_list(Config) ->
+ ssl_block_disturbing_blocker_dies(essl, Config).
+
+ssl_block_disturbing_blocker_dies(Tag, Config) ->
+ httpd_block:disturbing_blocker_dies(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+essl_block_non_disturbing_blocker_dies(doc) ->
+ ["using new of configure new SSL"];
+essl_block_non_disturbing_blocker_dies(suite) ->
+ [];
+essl_block_non_disturbing_blocker_dies(Config) when is_list(Config) ->
+ ssl_block_non_disturbing_blocker_dies(essl, Config).
+
+ssl_block_non_disturbing_blocker_dies(Tag, Config) ->
+ httpd_block:non_disturbing_blocker_dies(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+
+essl_restart_no_block(doc) ->
+ ["using new of configure new SSL"];
+essl_restart_no_block(suite) ->
+ [];
+essl_restart_no_block(Config) when is_list(Config) ->
+ ssl_restart_no_block(essl, Config).
+
+ssl_restart_no_block(Tag, Config) ->
+ httpd_block:restart_no_block(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+
+essl_restart_disturbing_block(doc) ->
+ ["using new of configure new SSL"];
+essl_restart_disturbing_block(suite) ->
+ [];
+essl_restart_disturbing_block(Config) when is_list(Config) ->
+ ssl_restart_disturbing_block(essl, Config).
+
+ssl_restart_disturbing_block(Tag, Config) ->
+ httpd_block:restart_disturbing_block(Tag, ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+
+
+essl_restart_non_disturbing_block(doc) ->
+ ["using new of configure new SSL"];
+essl_restart_non_disturbing_block(suite) ->
+ [];
+essl_restart_non_disturbing_block(Config) when is_list(Config) ->
+ ssl_restart_non_disturbing_block(essl, Config).
+
+ssl_restart_non_disturbing_block(Tag, Config) ->
+ httpd_block:restart_non_disturbing_block(Tag,
+ ?SSL_PORT,
+ proplists:get_value(host, Config),
+ proplists:get_value(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, proplists:get_value(host, Config),
+ proplists:get_value(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, proplists:get_value(host, Config),
+ proplists:get_value(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, proplists:get_value(host, Config),
+ proplists:get_value(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, proplists:get_value(host, Config),
+ proplists:get_value(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 = proplists:get_value(server_root, Config),
+ DocRoot = filename:join([ServerRoot, "htdocs"]),
+ httpd_1_1:if_test(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
+ proplists:get_value(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, proplists:get_value(host, Config),
+ proplists:get_value(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, proplists:get_value(host, Config),
+ proplists:get_value(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 = proplists:get_value(host, Config),
+ Node = proplists:get_value(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 = proplists:get_value(host, Config),
+ Node = proplists:get_value(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 = proplists:get_value(host, Config),
+ Node = proplists:get_value(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 = proplists:get_value(host, Config),
+ Node = proplists:get_value(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 = proplists:get_value(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,
+ proplists:get_value(node, Config),
+ Requests),
+ ok.
+
+%-------------------------------------------------------------------------
+
+ipv6_hostname_ipcomm() ->
+ [{require, ipv6_hosts}].
+ipv6_hostname_ipcomm(X) ->
+ SocketType = ip_comm,
+ Port = ?IP_PORT,
+ ipv6_hostname(SocketType, Port, X).
+
+ipv6_hostname_essl() ->
+ [{require, ipv6_hosts}].
+ipv6_hostname_essl(X) ->
+ SocketType = essl,
+ Port = ?SSL_PORT,
+ ipv6_hostname(SocketType, Port, X).
+
+ipv6_hostname(_SocketType, _Port, doc) ->
+ ["Test standard ipv6 address"];
+ipv6_hostname(_SocketType, _Port, suite)->
+ [];
+ipv6_hostname(SocketType, Port, Config) when is_list(Config) ->
+ tsp("ipv6_hostname -> entry with"
+ "~n SocketType: ~p"
+ "~n Port: ~p"
+ "~n Config: ~p", [SocketType, Port, Config]),
+ Host = proplists:get_value(host, Config),
+ URI = "GET HTTP://" ++
+ Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n",
+ tsp("ipv6_hostname -> Host: ~p", [Host]),
+ httpd_test_lib:verify_request(SocketType, Host, Port, [inet6],
+ node(),
+ URI,
+ [{statuscode, 200}, {version, "HTTP/1.1"}]),
+ ok.
+
+%%-------------------------------------------------------------------------
+
+ipv6_address_ipcomm() ->
+ [{require, ipv6_hosts}].
+ipv6_address_ipcomm(X) ->
+ SocketType = ip_comm,
+ Port = ?IP_PORT,
+ ipv6_address(SocketType, Port, X).
+
+ipv6_address_essl() ->
+ [{require, ipv6_hosts}].
+ipv6_address_essl(X) ->
+ SocketType = essl,
+ Port = ?SSL_PORT,
+ ipv6_address(SocketType, Port, X).
+
+ipv6_address(_SocketType, _Port, doc) ->
+ ["Test standard ipv6 address"];
+ipv6_address(_SocketType, _Port, suite)->
+ [];
+ipv6_address(SocketType, Port, Config) when is_list(Config) ->
+ tsp("ipv6_address -> entry with"
+ "~n SocketType: ~p"
+ "~n Port: ~p"
+ "~n Config: ~p", [SocketType, Port, Config]),
+ Host = proplists:get_value(host, Config),
+ tsp("ipv6_address -> Host: ~p", [Host]),
+ URI = "GET HTTP://" ++
+ Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n",
+ httpd_test_lib:verify_request(SocketType, Host, Port, [inet6],
+ node(),
+ URI,
+ [{statuscode, 200}, {version, "HTTP/1.1"}]),
+ ok.
+
+
+%%--------------------------------------------------------------------
+ticket_5775(doc) ->
+ ["Tests that content-length is correct"];
+ticket_5775(suite) ->
+ [];
+ticket_5775(Config) ->
+ ok=httpd_test_lib:verify_request(ip_comm, proplists:get_value(host, Config),
+ ?IP_PORT, proplists:get_value(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) ->
+ ct:skip(as_of_r15_behaviour_of_calendar_has_changed),
+ Host = proplists:get_value(host,Config),
+ ServerRoot = proplists:get_value(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, proplists:get_value(node, Config),
+ "GET /last_modified.html"
+ " HTTP/1.1\r\nHost:"
+ ++Host++"\r\n\r\n",
+ [{statuscode, 200},
+ {no_header,
+ "last-modified"}]),
+ ok;
+ {error, Reason} ->
+ Fault =
+ io_lib:format("Attempt to change the file info to set the"
+ " preconditions of the test case failed ~p~n",
+ [Reason]),
+ {skip, Fault}
+ end.
+
+ticket_5913(doc) ->
+ ["Tests that a header without last-modified is handled"];
+ticket_5913(suite) -> [];
+ticket_5913(Config) ->
+ ok = httpd_test_lib:verify_request(ip_comm, proplists:get_value(host, Config),
+ ?IP_PORT, proplists:get_value(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, proplists:get_value(host, Config),
+ ?IP_PORT, proplists:get_value(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, proplists:get_value(host, Config),
+ ?IP_PORT, proplists:get_value(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 = proplists:get_value(server_root, Config),
+ TcTopDir = proplists:get_value(tc_top_dir, Config),
+ Port = proplists:get_value(port, Config),
+ Type = proplists:get_value(sock_type, Config),
+ Host = proplists:get_value(host, Config),
+ Mods = io_lib:format("~p", [httpd_mod]),
+ Funcs = io_lib:format("~p", [ssl_password_cb]),
+ MaxHdrSz = io_lib:format("~p", [256]),
+ MaxHdrAct = io_lib:format("~p", [close]),
+
+ io:format(user,
+ "create_config -> "
+ "~n ServerRoot: ~p"
+ "~n TcTopDir: ~p"
+ "~n Type: ~p"
+ "~n Port: ~p"
+ "~n Host: ~p"
+ "~n", [ServerRoot, TcTopDir, Type, Port, Host]),
+
+ SSL =
+ if
+ (Type =:= ssl) orelse
+ (Type =:= essl) ->
+ [cline(["SSLCertificateFile ",
+ filename:join(ServerRoot, "ssl/ssl_server.pem")]),
+ cline(["SSLCertificateKeyFile ",
+ filename:join(ServerRoot, "ssl/ssl_server.pem")]),
+ cline(["SSLCACertificateFile ",
+ filename:join(ServerRoot, "ssl/ssl_server.pem")]),
+ cline(["SSLPasswordCallbackModule ", Mods]),
+ cline(["SSLPasswordCallbackFunction ", Funcs]),
+ cline(["SSLVerifyClient 0"]),
+ cline(["SSLVerifyDepth 1"])];
+ true ->
+ []
+ end,
+ ModOrder =
+ case Access of
+ mod_htaccess ->
+ "Modules mod_alias mod_htaccess mod_auth "
+ "mod_security "
+ "mod_responsecontrol mod_trace mod_esi "
+ "mod_actions mod_cgi mod_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_dir "
+ "mod_range mod_get "
+ "mod_head mod_log mod_disk_log"
+ end,
+
+ %% The test suite currently does not handle an explicit BindAddress.
+ %% They assume any has been used, that is Addr is always set to undefined!
+
+ %% {ok, Hostname} = inet:gethostname(),
+ %% {ok, Addr} = inet:getaddr(Hostname, inet6),
+ %% AddrStr = make_ipv6(Addr),
+ %% BindAddress = lists:flatten(io_lib:format("~s|inet6", [AddrStr])),
+
+ BindAddress = "*|inet",
+ %% BindAddress = "*",
+
+ HttpConfig = [
+ cline(["Port ", integer_to_list(Port)]),
+ cline(["ServerName ", Host]),
+ cline(["SocketType ", atom_to_list(Type)]),
+ cline([ModOrder]),
+ %% cline(["LogFormat ", "erlang"]),
+ cline(["ServerAdmin [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 ->
+ tsf({failed_to_cleanup_mnesia, Other})
+ end,
+ case rpc:call(Node, ?MODULE, setup_mnesia, []) of
+ {atomic, ok} ->
+ ok;
+ Other2 ->
+ tsf({failed_to_setup_mnesia, Other2})
+ end,
+ ok.
+
+setup_mnesia() ->
+ setup_mnesia([node()]).
+
+setup_mnesia(Nodes) ->
+ ok = mnesia:create_schema(Nodes),
+ ok = mnesia:start(),
+ {atomic, ok} = mnesia:create_table(httpd_user,
+ [{attributes,
+ record_info(fields, httpd_user)},
+ {disc_copies,Nodes}, {type, set}]),
+ {atomic, ok} = mnesia:create_table(httpd_group,
+ [{attributes,
+ record_info(fields,
+ httpd_group)},
+ {disc_copies,Nodes}, {type,bag}]).
+
+cleanup_mnesia() ->
+ mnesia:start(),
+ mnesia:delete_table(httpd_user),
+ mnesia:delete_table(httpd_group),
+ stopped = mnesia:stop(),
+ mnesia:delete_schema([node()]),
+ ok.
+
+create_htaccess_data(Path, IpAddress)->
+ create_htaccess_dirs(Path),
+
+ create_html_file(filename:join([Path,"ht/open/dummy.html"])),
+ create_html_file(filename:join([Path,"ht/blocknet/dummy.html"])),
+ create_html_file(filename:join([Path,"ht/secret/dummy.html"])),
+ create_html_file(filename:join([Path,"ht/secret/top_secret/dummy.html"])),
+
+ create_htaccess_file(filename:join([Path,"ht/open/.htaccess"]),
+ Path, "user one Aladdin"),
+ create_htaccess_file(filename:join([Path,"ht/secret/.htaccess"]),
+ Path, "group group1 group2"),
+ create_htaccess_file(filename:join([Path,
+ "ht/secret/top_secret/.htaccess"]),
+ Path, "user four"),
+ create_htaccess_file(filename:join([Path,"ht/blocknet/.htaccess"]),
+ Path, nouser, IpAddress),
+
+ create_user_group_file(filename:join([Path,"ht","users.file"]),
+ "one:OnePassword\ntwo:TwoPassword\nthree:"
+ "ThreePassword\nfour:FourPassword\nAladdin:"
+ "AladdinPassword"),
+ create_user_group_file(filename:join([Path,"ht","groups.file"]),
+ "group1: two one\ngroup2: two three").
+
+create_html_file(PathAndFileName)->
+ file:write_file(PathAndFileName,list_to_binary(
+ "<html><head><title>test</title></head>
+ <body>testar</body></html>")).
+
+create_htaccess_file(PathAndFileName, BaseDir, RequireData)->
+ file:write_file(PathAndFileName,
+ list_to_binary(
+ "AuthUserFile "++ BaseDir ++
+ "/ht/users.file\nAuthGroupFile "++ BaseDir
+ ++ "/ht/groups.file\nAuthName Test\nAuthType"
+ " Basic\n<Limit>\nrequire " ++ RequireData ++
+ "\n</Limit>")).
+
+create_htaccess_file(PathAndFileName, BaseDir, nouser, IpAddress)->
+ file:write_file(PathAndFileName,list_to_binary(
+ "AuthUserFile "++ BaseDir ++
+ "/ht/users.file\nAuthGroupFile " ++
+ BaseDir ++ "/ht/groups.file\nAuthName"
+ " Test\nAuthType"
+ " Basic\n<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_htaccess_dirs(Path)->
+ ok = file:make_dir(filename:join([Path,"ht"])),
+ ok = file:make_dir(filename:join([Path,"ht/open"])),
+ ok = file:make_dir(filename:join([Path,"ht/blocknet"])),
+ ok = file:make_dir(filename:join([Path,"ht/secret"])),
+ ok = file:make_dir(filename:join([Path,"ht/secret/top_secret"])).
+
+remove_htaccess_dirs(Path)->
+ file:del_dir(filename:join([Path,"ht/secret/top_secret"])),
+ file:del_dir(filename:join([Path,"ht/secret"])),
+ file:del_dir(filename:join([Path,"ht/blocknet"])),
+ file:del_dir(filename:join([Path,"ht/open"])),
+ file:del_dir(filename:join([Path,"ht"])).
+
+format_ip(IpAddress,Pos)when Pos > 0->
+ case lists:nth(Pos,IpAddress) of
+ $.->
+ case lists:nth(Pos-2,IpAddress) of
+ $.->
+ format_ip(IpAddress,Pos-3);
+ _->
+ lists:sublist(IpAddress,Pos-2) ++ "."
+ end;
+ _ ->
+ format_ip(IpAddress,Pos-1)
+ end;
+
+format_ip(IpAddress, _Pos)->
+ "1" ++ IpAddress.
+
+remove_htaccess(Path)->
+ file:delete(filename:join([Path,"ht/open/dummy.html"])),
+ file:delete(filename:join([Path,"ht/secret/dummy.html"])),
+ file:delete(filename:join([Path,"ht/secret/top_secret/dummy.html"])),
+ file:delete(filename:join([Path,"ht/blocknet/dummy.html"])),
+ file:delete(filename:join([Path,"ht/blocknet/.htaccess"])),
+ file:delete(filename:join([Path,"ht/open/.htaccess"])),
+ file:delete(filename:join([Path,"ht/secret/.htaccess"])),
+ file:delete(filename:join([Path,"ht/secret/top_secret/.htaccess"])),
+ file:delete(filename:join([Path,"ht","users.file"])),
+ file:delete(filename:join([Path,"ht","groups.file"])),
+ remove_htaccess_dirs(Path).
+
+
+dos_hostname_poll(Type, Host, Port, Node, Hosts) ->
+ [dos_hostname_poll1(Type, Host, Port, Node, Host1, Code)
+ || {Host1,Code} <- Hosts].
+
+dos_hostname_poll1(Type, Host, Port, Node, Host1, Code) ->
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ dos_hostname_request(Host1),
+ [{statuscode, Code},
+ {version, "HTTP/1.0"}]).
+
+dos_hostname_request(Host) ->
+ "GET / HTTP/1.0\r\n" ++ Host ++ "\r\n\r\n".
+
+get_nof_clients(Mode, Load) ->
+ get_nof_clients(test_server:os_type(), Mode, Load).
+
+get_nof_clients(_, ip_comm, light) -> 5;
+get_nof_clients(_, ssl, light) -> 2;
+get_nof_clients(_, ip_comm, medium) -> 10;
+get_nof_clients(_, ssl, medium) -> 4;
+get_nof_clients(_, ip_comm, heavy) -> 20;
+get_nof_clients(_, ssl, heavy) -> 6.
+
+%% Make a file 100 bytes long containing 012...9*10
+create_range_data(Path) ->
+ PathAndFileName=filename:join([Path,"range.txt"]),
+ file:write_file(PathAndFileName,list_to_binary(["12345678901234567890",
+ "12345678901234567890",
+ "12345678901234567890",
+ "12345678901234567890",
+ "12345678901234567890"])).
+
+create_ipv6_config(Config, FileName, Ipv6Address) ->
+ ServerRoot = proplists:get_value(server_root, Config),
+ TcTopDir = proplists:get_value(tc_top_dir, Config),
+ Port = proplists:get_value(port, Config),
+ SockType = proplists:get_value(sock_type, Config),
+ Mods = io_lib:format("~p", [httpd_mod]),
+ Funcs = io_lib:format("~p", [ssl_password_cb]),
+ Host = proplists:get_value(ipv6_host, Config),
+
+ MaxHdrSz = io_lib:format("~p", [256]),
+ MaxHdrAct = io_lib:format("~p", [close]),
+
+ Mod_order = "Modules mod_alias mod_auth mod_esi mod_actions mod_cgi"
+ " mod_dir mod_get mod_head"
+ " mod_log mod_disk_log mod_trace",
+
+ SSL =
+ if
+ (SockType =:= ssl) orelse
+ (SockType =:= essl) ->
+ [cline(["SSLCertificateFile ",
+ filename:join(ServerRoot, "ssl/ssl_server.pem")]),
+ cline(["SSLCertificateKeyFile ",
+ filename:join(ServerRoot, "ssl/ssl_server.pem")]),
+ cline(["SSLCACertificateFile ",
+ filename:join(ServerRoot, "ssl/ssl_server.pem")]),
+ cline(["SSLPasswordCallbackModule ", Mods]),
+ cline(["SSLPasswordCallbackFunction ", Funcs]),
+ cline(["SSLVerifyClient 0"]),
+ cline(["SSLVerifyDepth 1"])];
+ true ->
+ []
+ end,
+
+ BindAddress = "[" ++ Ipv6Address ++"]|inet6",
+
+ HttpConfig =
+ [cline(["BindAddress ", BindAddress]),
+ cline(["Port ", integer_to_list(Port)]),
+ cline(["ServerName ", Host]),
+ cline(["SocketType ", atom_to_list(SockType)]),
+ cline([Mod_order]),
+ cline(["ServerRoot ", ServerRoot]),
+ cline(["DocumentRoot ", filename:join(ServerRoot, "htdocs")]),
+ cline(["MaxHeaderSize ",MaxHdrSz]),
+ cline(["MaxHeaderAction ",MaxHdrAct]),
+ cline(["DirectoryIndex ", "index.html "]),
+ cline(["DefaultType ", "text/plain"]),
+ SSL],
+ ConfigFile = filename:join([TcTopDir,FileName]),
+ {ok, Fd} = file:open(ConfigFile, [write]),
+ ok = file:write(Fd, lists:flatten(HttpConfig)),
+ ok = file:close(Fd).
+
+
+tsp(F) ->
+ inets_test_lib:tsp("[~w]" ++ F, [?MODULE]).
+tsp(F, A) ->
+ inets_test_lib:tsp("[~w]" ++ F, [?MODULE|A]).
+
+tsf(Reason) ->
+ inets_test_lib:tsf(Reason).
+
diff --git a/lib/inets/test/old_httpd_SUITE_data/Makefile.src b/lib/inets/test/old_httpd_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..b0fdb43d8d
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/Makefile.src
@@ -0,0 +1,14 @@
+CC = @CC@
+LD = @LD@
+CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@
+CROSSLDFLAGS = @CROSSLDFLAGS@
+
+PROGS = cgi_echo@exe@
+
+all: $(PROGS)
+
+cgi_echo@exe@: cgi_echo@obj@
+ $(LD) $(CROSSLDFLAGS) -o cgi_echo cgi_echo@obj@ @LIBS@
+
+cgi_echo@obj@: cgi_echo.c
+ $(CC) -c -o cgi_echo@obj@ $(CFLAGS) cgi_echo.c
diff --git a/lib/inets/test/old_httpd_SUITE_data/cgi_echo.c b/lib/inets/test/old_httpd_SUITE_data/cgi_echo.c
new file mode 100644
index 0000000000..580f860e96
--- /dev/null
+++ b/lib/inets/test/old_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/Makefile b/lib/inets/test/old_httpd_SUITE_data/server_root/Makefile
index d7a3231068..ed4d63a3bb 100644
--- a/lib/inets/test/httpd_SUITE_data/server_root/Makefile
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. 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/.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# 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.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/auth/group b/lib/inets/test/old_httpd_SUITE_data/server_root/auth/group
new file mode 100644
index 0000000000..b3da0ccbd3
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/auth/group
@@ -0,0 +1,3 @@
+group1: one two
+group2: two three
+group3: three Aladdin
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/auth/passwd b/lib/inets/test/old_httpd_SUITE_data/server_root/auth/passwd
new file mode 100644
index 0000000000..8c980ff547
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/auth/passwd
@@ -0,0 +1,4 @@
+one:onePassword
+two:twoPassword
+three:threePassword
+Aladdin:AladdinPassword
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.bat b/lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.bat
new file mode 100644
index 0000000000..25a49a1536
--- /dev/null
+++ b/lib/inets/test/old_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/old_httpd_SUITE_data/server_root/cgi-bin/printenv.sh b/lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.sh
new file mode 100755
index 0000000000..de81de9bde
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/cgi-bin/printenv.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+echo "Content-type: text/html"
+echo ""
+echo "<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/old_httpd_SUITE_data/server_root/conf/8080.conf
index 48e66f0114..7b1b4a15b2 100644
--- a/lib/inets/test/httpd_SUITE_data/server_root/conf/8080.conf
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8080.conf
@@ -1,7 +1,7 @@
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
+Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log
ServerAdmin [email protected]
ServerRoot /var/tmp/server_root
ErrorLog logs/error_log_8080
diff --git a/lib/inets/test/httpd_SUITE_data/server_root/conf/8888.conf b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8888.conf
index 79bb7fcca4..042779fcd0 100644
--- a/lib/inets/test/httpd_SUITE_data/server_root/conf/8888.conf
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8888.conf
@@ -1,7 +1,7 @@
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
+Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log
ServerAdmin [email protected]
ServerRoot /var/tmp/server_root
ErrorLog logs/error_log_8888
diff --git a/lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/httpd.conf
index ceb94237d2..3f9fde03b5 100644
--- a/lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/httpd.conf
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+# Copyright Ericsson AB 1997-2016. All Rights Reserved.
#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
@@ -63,7 +64,7 @@ SocketType ip_comm
# 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
+Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_responsecontrol mod_trace mod_range mod_head mod_dir mod_get mod_log mod_disk_log
# ServerAdmin: Your address, where problems with the server should be
# e-mailed.
diff --git a/lib/inets/test/httpd_SUITE_data/server_root/conf/mime.types b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/mime.types
index d2f81e4e5e..d2f81e4e5e 100644
--- a/lib/inets/test/httpd_SUITE_data/server_root/conf/mime.types
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/mime.types
diff --git a/lib/inets/test/httpd_SUITE_data/server_root/conf/ssl.conf b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/ssl.conf
index 8b8c57a98b..de49ceafd0 100644
--- a/lib/inets/test/httpd_SUITE_data/server_root/conf/ssl.conf
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/ssl.conf
@@ -1,7 +1,7 @@
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
+Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log
ServerAdmin [email protected]
ServerRoot /var/tmp/server_root
ErrorLog logs/error_log_8088
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/config.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/config.shtml
new file mode 100644
index 0000000000..107e3ff610
--- /dev/null
+++ b/lib/inets/test/old_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/old_httpd_SUITE_data/server_root/htdocs/dets_open/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_open/dummy.html
new file mode 100644
index 0000000000..a6e8a35a04
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_open/dummy.html
@@ -0,0 +1,10 @@
+<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/old_httpd_SUITE_data/server_root/htdocs/dets_secret/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/dummy.html
new file mode 100644
index 0000000000..016b04e540
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/dummy.html
@@ -0,0 +1,10 @@
+<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/old_httpd_SUITE_data/server_root/htdocs/dets_secret/top_secret/index.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/top_secret/index.html
new file mode 100644
index 0000000000..34db3d5d1a
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/dets_secret/top_secret/index.html
@@ -0,0 +1,9 @@
+<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/old_httpd_SUITE_data/server_root/htdocs/echo.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/echo.shtml
new file mode 100644
index 0000000000..141db5be59
--- /dev/null
+++ b/lib/inets/test/old_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/old_httpd_SUITE_data/server_root/htdocs/exec.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/exec.shtml
new file mode 100644
index 0000000000..97333da898
--- /dev/null
+++ b/lib/inets/test/old_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/old_httpd_SUITE_data/server_root/htdocs/flastmod.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/flastmod.shtml
new file mode 100644
index 0000000000..d54c36fe50
--- /dev/null
+++ b/lib/inets/test/old_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/old_httpd_SUITE_data/server_root/htdocs/fsize.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/fsize.shtml
new file mode 100644
index 0000000000..570ee9cf6d
--- /dev/null
+++ b/lib/inets/test/old_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/old_httpd_SUITE_data/server_root/htdocs/include.shtml b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/include.shtml
new file mode 100644
index 0000000000..529aad0437
--- /dev/null
+++ b/lib/inets/test/old_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/old_httpd_SUITE_data/server_root/htdocs/index.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/index.html
new file mode 100644
index 0000000000..cfdc9f9ab7
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/index.html
@@ -0,0 +1,25 @@
+<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/old_httpd_SUITE_data/server_root/htdocs/last_modified.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/last_modified.html
new file mode 100644
index 0000000000..65c1790813
--- /dev/null
+++ b/lib/inets/test/old_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/old_httpd_SUITE_data/server_root/htdocs/misc/friedrich.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/friedrich.html
new file mode 100644
index 0000000000..d7953d5df4
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/friedrich.html
@@ -0,0 +1,7 @@
+<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/old_httpd_SUITE_data/server_root/htdocs/misc/oech.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/oech.html
new file mode 100644
index 0000000000..506064bf04
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/oech.html
@@ -0,0 +1,4 @@
+<P><CITE>
+What excuses stand in your way? How can you eliminate them?<BR>
+-- Roger von Oech
+</CITE>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/welcome.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/welcome.html
new file mode 100644
index 0000000000..8c17451f91
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/misc/welcome.html
@@ -0,0 +1 @@
+<HTML></HTML>
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_open/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_open/dummy.html
new file mode 100644
index 0000000000..a6e8a35a04
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_open/dummy.html
@@ -0,0 +1,10 @@
+<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/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/dummy.html
new file mode 100644
index 0000000000..016b04e540
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/dummy.html
@@ -0,0 +1,10 @@
+<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/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/top_secret/index.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/top_secret/index.html
new file mode 100644
index 0000000000..2d17e8b596
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/mnesia_secret/top_secret/index.html
@@ -0,0 +1,9 @@
+<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/old_httpd_SUITE_data/server_root/htdocs/open/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/open/dummy.html
new file mode 100644
index 0000000000..a6e8a35a04
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/open/dummy.html
@@ -0,0 +1,10 @@
+<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/old_httpd_SUITE_data/server_root/htdocs/secret/dummy.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/dummy.html
new file mode 100644
index 0000000000..016b04e540
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/dummy.html
@@ -0,0 +1,10 @@
+<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/old_httpd_SUITE_data/server_root/htdocs/secret/top_secret/index.html b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/top_secret/index.html
new file mode 100644
index 0000000000..34db3d5d1a
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/htdocs/secret/top_secret/index.html
@@ -0,0 +1,9 @@
+<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/old_httpd_SUITE_data/server_root/icons/README
index a1fc5a5a9c..a1fc5a5a9c 100644
--- a/lib/inets/test/httpd_SUITE_data/server_root/icons/README
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/README
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/a.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/a.gif
new file mode 100644
index 0000000000..bb23d971f4
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/a.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.black.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.black.gif
new file mode 100644
index 0000000000..eaecd2172a
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.black.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.red.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.red.gif
new file mode 100644
index 0000000000..a423894043
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/alert.red.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/apache_pb.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/apache_pb.gif
new file mode 100644
index 0000000000..3a1c139fc4
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/apache_pb.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/back.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/back.gif
new file mode 100644
index 0000000000..a694ae1ec3
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/back.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.gray.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.gray.gif
new file mode 100644
index 0000000000..eb84268c4c
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.gray.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.red.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.red.gif
new file mode 100644
index 0000000000..a8425cb574
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ball.red.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binary.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binary.gif
new file mode 100644
index 0000000000..9a15cbae04
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binary.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binhex.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binhex.gif
new file mode 100644
index 0000000000..62d0363108
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/binhex.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/blank.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/blank.gif
new file mode 100644
index 0000000000..0ccf01e198
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/blank.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/bomb.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/bomb.gif
new file mode 100644
index 0000000000..270fdb1c06
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/bomb.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box1.gif
new file mode 100644
index 0000000000..65dcd002ea
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box1.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box2.gif
new file mode 100644
index 0000000000..c43bc4faec
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/box2.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/broken.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/broken.gif
new file mode 100644
index 0000000000..9f8cbe9f76
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/broken.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/burst.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/burst.gif
new file mode 100644
index 0000000000..fbdcf575f7
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/burst.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button1.gif
new file mode 100644
index 0000000000..eb97cb7333
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button1.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button10.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button10.gif
new file mode 100644
index 0000000000..fe0c97998c
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button10.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button2.gif
new file mode 100644
index 0000000000..7698455bf9
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button2.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button3.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button3.gif
new file mode 100644
index 0000000000..a8b8319232
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button3.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button4.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button4.gif
new file mode 100644
index 0000000000..0fd15a0d7f
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button4.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button5.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button5.gif
new file mode 100644
index 0000000000..64241e5c5d
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button5.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button6.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button6.gif
new file mode 100644
index 0000000000..867cfd1212
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button6.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button7.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button7.gif
new file mode 100644
index 0000000000..b3f5fb248f
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button7.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button8.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button8.gif
new file mode 100644
index 0000000000..7a308be8f6
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button8.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button9.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button9.gif
new file mode 100644
index 0000000000..9acba576c0
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/button9.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonl.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonl.gif
new file mode 100644
index 0000000000..3883088e7a
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonl.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonr.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonr.gif
new file mode 100644
index 0000000000..c4dc3887db
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/buttonr.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/c.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/c.gif
new file mode 100644
index 0000000000..7555b6c164
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/c.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.blue.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.blue.gif
new file mode 100644
index 0000000000..f8d76a8c23
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.blue.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.gray.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.gray.gif
new file mode 100644
index 0000000000..7664cd0364
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/comp.gray.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/compressed.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/compressed.gif
new file mode 100644
index 0000000000..39e732739f
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/compressed.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/continued.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/continued.gif
new file mode 100644
index 0000000000..b0ffb7e0cc
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/continued.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dir.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dir.gif
new file mode 100644
index 0000000000..48264601ae
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dir.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/down.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/down.gif
new file mode 100644
index 0000000000..a354c871cd
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/down.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dvi.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dvi.gif
new file mode 100644
index 0000000000..791be33105
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/dvi.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/f.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/f.gif
new file mode 100644
index 0000000000..fbe353c282
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/f.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.gif
new file mode 100644
index 0000000000..48264601ae
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.open.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.open.gif
new file mode 100644
index 0000000000..30979cb528
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.open.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.sec.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.sec.gif
new file mode 100644
index 0000000000..75332d9e59
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/folder.sec.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/forward.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/forward.gif
new file mode 100644
index 0000000000..b2959b4c85
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/forward.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.gif
new file mode 100644
index 0000000000..de60b2940f
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.red.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.red.gif
new file mode 100644
index 0000000000..94743981d9
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.red.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.sec.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.sec.gif
new file mode 100644
index 0000000000..88d5240c3c
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/generic.sec.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.right.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.right.gif
new file mode 100644
index 0000000000..5cdbc7206d
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.right.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.up.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.up.gif
new file mode 100644
index 0000000000..85a5d68317
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/hand.up.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/htdig.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/htdig.gif
new file mode 100644
index 0000000000..35443fb63a
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/htdig.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/icon.sheet.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/icon.sheet.gif
new file mode 100644
index 0000000000..ad1686e448
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/icon.sheet.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image1.gif
new file mode 100644
index 0000000000..01e442bfa9
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image1.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image2.gif
new file mode 100644
index 0000000000..751faeea36
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image2.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image3.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image3.gif
new file mode 100644
index 0000000000..4f30484ff6
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/image3.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/index.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/index.gif
new file mode 100644
index 0000000000..162478fb3a
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/index.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/layout.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/layout.gif
new file mode 100644
index 0000000000..c96338a152
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/layout.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/left.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/left.gif
new file mode 100644
index 0000000000..279e6710d4
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/left.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/link.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/link.gif
new file mode 100644
index 0000000000..c5b6889a76
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/link.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/movie.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/movie.gif
new file mode 100644
index 0000000000..0035183774
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/movie.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/p.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/p.gif
new file mode 100644
index 0000000000..7b917b4e91
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/p.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/patch.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/patch.gif
new file mode 100644
index 0000000000..39bc90e795
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/patch.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pdf.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pdf.gif
new file mode 100644
index 0000000000..c88fd777c4
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pdf.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie0.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie0.gif
new file mode 100644
index 0000000000..6f7a0ae7a7
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie0.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie1.gif
new file mode 100644
index 0000000000..03aa6be71e
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie1.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie2.gif
new file mode 100644
index 0000000000..b04c5e0908
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie2.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie3.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie3.gif
new file mode 100644
index 0000000000..4db9d023ed
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie3.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie4.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie4.gif
new file mode 100644
index 0000000000..93471fdd88
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie4.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie5.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie5.gif
new file mode 100644
index 0000000000..57aee93f07
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie5.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie6.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie6.gif
new file mode 100644
index 0000000000..0dc327b569
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie6.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie7.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie7.gif
new file mode 100644
index 0000000000..8661337f06
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie7.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie8.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie8.gif
new file mode 100644
index 0000000000..59ddb34ce0
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/pie8.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/portal.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/portal.gif
new file mode 100644
index 0000000000..0e6e506e00
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/portal.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/poweredby.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/poweredby.gif
new file mode 100644
index 0000000000..d324ab80ea
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/poweredby.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ps.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ps.gif
new file mode 100644
index 0000000000..0f565bc1db
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/ps.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/quill.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/quill.gif
new file mode 100644
index 0000000000..818a5cdc7e
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/quill.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/right.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/right.gif
new file mode 100644
index 0000000000..b256e5f75f
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/right.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw1.gif
new file mode 100644
index 0000000000..af6ba2b097
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw1.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw2.gif
new file mode 100644
index 0000000000..06dccb3e44
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/screw2.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/script.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/script.gif
new file mode 100644
index 0000000000..d8a853bc58
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/script.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound1.gif
new file mode 100644
index 0000000000..8efb49f55d
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound1.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound2.gif
new file mode 100644
index 0000000000..48e6a7fb2f
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sound2.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere1.gif
new file mode 100644
index 0000000000..7067070da2
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere1.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere2.gif
new file mode 100644
index 0000000000..a9e462a377
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/sphere2.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star.gif
new file mode 100644
index 0000000000..4cfe0a5e0f
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star_blank.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star_blank.gif
new file mode 100644
index 0000000000..a0c83cb85b
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/star_blank.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tar.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tar.gif
new file mode 100644
index 0000000000..617e779efa
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tar.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tex.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tex.gif
new file mode 100644
index 0000000000..45e43233b8
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/tex.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/text.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/text.gif
new file mode 100644
index 0000000000..4c623909fb
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/text.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/transfer.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/transfer.gif
new file mode 100644
index 0000000000..33697dbb66
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/transfer.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/unknown.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/unknown.gif
new file mode 100644
index 0000000000..32b1ea23fb
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/unknown.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/up.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/up.gif
new file mode 100644
index 0000000000..6d6d6d1ebf
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/up.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uu.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uu.gif
new file mode 100644
index 0000000000..4387d529f6
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uu.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uuencoded.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uuencoded.gif
new file mode 100644
index 0000000000..4387d529f6
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/uuencoded.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/world1.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/world1.gif
new file mode 100644
index 0000000000..05b4ec2058
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/world1.gif
Binary files differ
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/icons/world2.gif b/lib/inets/test/old_httpd_SUITE_data/server_root/icons/world2.gif
new file mode 100644
index 0000000000..e3203f7a88
--- /dev/null
+++ b/lib/inets/test/old_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/old_httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip
index 8d1c8b69c3..8d1c8b69c3 100644
--- a/lib/inets/test/httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/logs/Dummy_File_Needed_By_WinZip
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_client.pem b/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_client.pem
new file mode 100644
index 0000000000..427447958d
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_client.pem
@@ -0,0 +1,31 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSVwC+n
+0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53h2Zr
+3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwIDAQAB
+AoGACdIVYe/LTeydUihtInC8lZ2QuPgJmoBNocRjqJFipEihoL4scHAx25n1bBvB
+I0HZphffzBkGp28oBAtl2LRPWXqu527unc/RWRfLMqSK1xNSq1DxD1a30zkrZPna
+QiV65vEJuNSJTtlDy/Zqc/BVZXCpxWlzYQedZgkmf0Qse8ECQQCmaz02Yur8zC9f
+eSQKU5OSzGw3bSIumEzziCfHdTheK6MEoccf5TCAyLXhZwA7QlKja4tFXfeyVxws
+/LlnUJN9AkEA4j+xnOeYUyGKXL5i+BAbnqpI4MzPiq+IoCYkaRlD/wAws24r5HNI
+ZQmEHWqD/NNzOf/A2XuyLtMiTGJPW/DftwJBAKKpJP6Ytuh6xz8BUCnLwO12Y7vV
+LtjuQiCzD3aUa5EYA9HOMqxJPxxRkf0LyR0i2VUkE8+sZiPpov+R0cJa7p0CQQCj
+40GUiArGRSiF7/+e84QeVfl+pb29F1QftiFv5DZmFEwy3Z572KpbTh5edJbxYHY6
+UDHxGHJFCvnwXNJhpkVXAkBJqfEfiMJ3Q/E5Gpf3sQizacouW92iiN8ojlF1oB80
+t34RysJH7SgI3gdMhTribCo2UUaV0StjR6yodPN+TB2J
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIChzCCAfCgAwIBAgIGAIsapa8BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT
+BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE
+BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD
+VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw
+MDAwWjB7MQ8wDQYDVQQDEwZjbGllbnQxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl
+cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD
+VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSV
+wC+n0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53
+h2Zr3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwID
+AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAG8t6f1A
+PF7xayGxtUpG2r6W5ETylC3ZIKPS2kfJk9aYi7AZNTp7/xTU6SgqvFBN8aBPzxCD
+4jHrSNC8DSb4X1x9uimarb6qdZDHEdij+DRAd2eygJHZxEf7+8B4Fx34thQeU9hZ
+S1Izke5AlsyFMkvB7h0anE4k9BfuU70vl6v5
+-----END CERTIFICATE-----
diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_server.pem b/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_server.pem
new file mode 100644
index 0000000000..4aac86db49
--- /dev/null
+++ b/lib/inets/test/old_httpd_SUITE_data/server_root/ssl/ssl_server.pem
@@ -0,0 +1,31 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9Adq6
+7k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ4UAt
+NHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQIDAQAB
+AoGAQIlma0r6W6bcRj4+Wd4fXCFvHuq5Psu1fYEeC5Yvz8761xVjjSfbrDHJZ9pm
+FjOEgedK+s5lbDXqYVyjbdyZSugStBRocSmbG8SQHcAsxR2ZIkNzX2hYzB+lslWo
+T3YJojDyB134O7XJznCu+ZFXP86jyJ1JT6k6a+OIHcwnJ+ECQQDYn57dY4Px3mEd
+VBLStN3YkRF5oFyT+xk7IaKeLLB6n4gCnoVbBoHut7PFbPYPzoNzEwPk3MQKDIHb
+Kig3S5CpAkEAvPA1VmoJWAlN6kUi+F2L8HXEArzE8x7vwdsslrwMKUe4dFS+ZC/7
+5iDOaxcZ7TYkCgwzBt341++DCgP6j3fY1QJBALB6AcOcwi52m6l4B8mu3ZkEPjdX
+BHTuONTqhv/TqoaLlxODL2NDvvDKqeMp7KBd/srt79swW2lQXS4+fvrlTdkCQQCm
+zxj4O1QWkthkfje6ubSkTwUIOatUzrp1F9GNH2dJRtX2dx9FCwxGCC7WY6XzRXqa
+GF0wsedSllbGD+82nWQlAkAicMGqCqRq4hKR/cVmFatOqKVWCVkx6OFF2FhuiI5Z
+h5eIOPGCt8dVRs1P9DNSld/D98Sfm65m85z8BtXovvYV
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIChzCCAfCgAwIBAgIGANUxXM9BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT
+BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE
+BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD
+VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw
+MDAwWjB7MQ8wDQYDVQQDEwZzZXJ2ZXIxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl
+cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD
+VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9
+Adq67k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ
+4UAtNHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQID
+AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAGF5Pfwk
+QDdwJup/mVITPxbBls4Yl7anDooUQsq8066lA1g54H/PRfXscGkyCFGh1ifXvf1L
+psMRoBAdDHL/wSJplk3rRavkC94eBgnTFZmfKL6844g1j53yameiYL8IEVExYMBg
+/XGyc0qwq57WT8B/K4aElrvlBlQ0wF3wN54M
+-----END CERTIFICATE-----
diff --git a/lib/inets/test/property_test/README b/lib/inets/test/property_test/README
new file mode 100644
index 0000000000..57602bf719
--- /dev/null
+++ b/lib/inets/test/property_test/README
@@ -0,0 +1,12 @@
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% %%%
+%%% WARNING %%%
+%%% %%%
+%%% This is experimental code which may be changed or removed %%%
+%%% anytime without any warning. %%%
+%%% %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+The test in this directory are written assuming that the user has a QuickCheck license. They are to be run manually. Some may be possible to be run with other tools, e.g. PropEr.
+
diff --git a/lib/inets/test/property_test/ftp_simple_client_server.erl b/lib/inets/test/property_test/ftp_simple_client_server.erl
new file mode 100644
index 0000000000..c98d87b514
--- /dev/null
+++ b/lib/inets/test/property_test/ftp_simple_client_server.erl
@@ -0,0 +1,307 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(ftp_simple_client_server).
+
+-compile(export_all).
+
+-ifndef(EQC).
+-ifndef(PROPER).
+-define(EQC,true).
+%%-define(PROPER,true).
+-endif.
+-endif.
+
+
+-ifdef(EQC).
+
+-include_lib("eqc/include/eqc.hrl").
+-include_lib("eqc/include/eqc_statem.hrl").
+-define(MOD_eqc, eqc).
+-define(MOD_eqc_gen, eqc_gen).
+-define(MOD_eqc_statem, eqc_statem).
+
+-else.
+-ifdef(PROPER).
+
+-include_lib("proper/include/proper.hrl").
+-define(MOD_eqc, proper).
+-define(MOD_eqc_gen, proper_gen).
+-define(MOD_eqc_statem, proper_statem).
+
+-endif.
+-endif.
+
+-record(state, {
+ initialized = false,
+ priv_dir,
+ data_dir,
+ servers = [], % [ {IP,Port,Userid,Pwd} ]
+ clients = [], % [ client_ref() ]
+ store = [] % [ {Name,Contents} ]
+ }).
+
+-define(fmt(F,A), io:format(F,A)).
+%%-define(fmt(F,A), ok).
+
+-define(v(K,L), proplists:get_value(K,L)).
+
+%%%================================================================
+%%%
+%%% Properties
+%%%
+
+%% This function is for normal eqc calls:
+prop_ftp() ->
+ {ok,PWD} = file:get_cwd(),
+ prop_ftp(filename:join([PWD,?MODULE_STRING++"_data"]),
+ filename:join([PWD,?MODULE_STRING,"_files"])).
+
+%% This function is for calls from common_test test cases:
+prop_ftp(Config) ->
+ prop_ftp(filename:join([?v(property_dir,Config), ?MODULE_STRING++"_data"]),
+ ?v(priv_dir,Config) ).
+
+
+prop_ftp(DataDir, PrivDir) ->
+ S0 = #state{data_dir = DataDir,
+ priv_dir = PrivDir},
+ ?FORALL(Cmds, more_commands(10,commands(?MODULE,S0)),
+ aggregate(command_names(Cmds),
+ begin {_H,S,Result} = run_commands(?MODULE,Cmds),
+ % io:format('**** Result=~p~n',[Result]),
+ % io:format('**** S=~p~n',[S]),
+ % io:format('**** _H=~p~n',[_H]),
+ % io:format('**** Cmds=~p~n',[Cmds]),
+ [cmnd_stop_server(X) || X <- S#state.servers],
+ [inets:stop(ftpc,X) || {ok,X} <- S#state.clients],
+ Result==ok
+ end)
+ ).
+
+%%%================================================================
+%%%
+%%% State model
+%%%
+
+%% @doc Returns the state in which each test case starts. (Unless a different
+%% initial state is supplied explicitly to, e.g. commands/2.)
+-spec initial_state() ->?MOD_eqc_statem:symbolic_state().
+initial_state() ->
+ ?fmt("Initial_state()~n",[]),
+ #state{}.
+
+%% @doc Command generator, S is the current state
+-spec command(S :: ?MOD_eqc_statem:symbolic_state()) -> ?MOD_eqc_gen:gen(eqc_statem:call()).
+
+command(#state{initialized=false,
+ priv_dir=PrivDir}) ->
+ {call,?MODULE,cmnd_init,[PrivDir]};
+
+command(#state{servers=[],
+ priv_dir=PrivDir,
+ data_dir=DataDir}) ->
+ {call,?MODULE,cmnd_start_server,[PrivDir,DataDir]};
+
+command(#state{servers=Ss=[_|_],
+ clients=[]}) ->
+ {call,?MODULE,cmnd_start_client,[oneof(Ss)]};
+
+command(#state{servers=Ss=[_|_],
+ clients=Cs=[_|_],
+ store=Store=[_|_]
+ }) ->
+ frequency([
+ { 5, {call,?MODULE,cmnd_start_client,[oneof(Ss)]}},
+ { 5, {call,?MODULE,cmnd_stop_client,[oneof(Cs)]}},
+ {10, {call,?MODULE,cmnd_put,[oneof(Cs),file_path(),file_contents()]}},
+ {20, {call,?MODULE,cmnd_get,[oneof(Cs),oneof(Store)]}},
+ {10, {call,?MODULE,cmnd_delete,[oneof(Cs),oneof(Store)]}}
+ ]);
+
+command(#state{servers=Ss=[_|_],
+ clients=Cs=[_|_],
+ store=[]
+ }) ->
+ frequency([
+ {5, {call,?MODULE,cmnd_start_client,[oneof(Ss)]}},
+ {5, {call,?MODULE,cmnd_stop_client,[oneof(Cs)]}},
+ {10, {call,?MODULE,cmnd_put,[oneof(Cs),file_path(),file_contents()]}}
+ ]).
+
+%% @doc Precondition, checked before command is added to the command sequence.
+-spec precondition(S :: ?MOD_eqc_statem:symbolic_state(), C :: ?MOD_eqc_statem:call()) -> boolean().
+
+precondition(#state{clients=Cs}, {call, _, cmnd_put, [C,_,_]}) -> lists:member(C,Cs);
+
+precondition(#state{clients=Cs, store=Store},
+ {call, _, cmnd_get, [C,X]}) -> lists:member(C,Cs) andalso lists:member(X,Store);
+
+precondition(#state{clients=Cs, store=Store},
+ {call, _, cmnd_delete, [C,X]}) -> lists:member(C,Cs) andalso lists:member(X,Store);
+
+precondition(#state{servers=Ss}, {call, _, cmnd_start_client, _}) -> Ss =/= [];
+
+precondition(#state{clients=Cs}, {call, _, cmnd_stop_client, [C]}) -> lists:member(C,Cs);
+
+precondition(#state{initialized=IsInit}, {call, _, cmnd_init, _}) -> IsInit==false;
+
+precondition(_S, {call, _, _, _}) -> true.
+
+
+%% @doc Postcondition, checked after command has been evaluated
+%% Note: S is the state before next_state(S,_,C)
+-spec postcondition(S :: ?MOD_eqc_statem:dynamic_state(), C :: ?MOD_eqc_statem:call(),
+ Res :: term()) -> boolean().
+
+postcondition(_S, {call, _, cmnd_get, [_,{_Name,Expected}]}, {ok,Value}) ->
+ Value == Expected;
+
+postcondition(S, {call, _, cmnd_delete, [_,{Name,_Expected}]}, ok) ->
+ ?fmt("file:read_file(..) = ~p~n",[file:read_file(filename:join(S#state.priv_dir,Name))]),
+ {error,enoent} == file:read_file(filename:join(S#state.priv_dir,Name));
+
+postcondition(S, {call, _, cmnd_put, [_,Name,Value]}, ok) ->
+ {ok,Bin} = file:read_file(filename:join(S#state.priv_dir,Name)),
+ Bin == unicode:characters_to_binary(Value);
+
+postcondition(_S, {call, _, cmnd_stop_client, _}, ok) -> true;
+
+postcondition(_S, {call, _, cmnd_start_client, _}, {ok,_}) -> true;
+
+postcondition(_S, {call, _, cmnd_init, _}, ok) -> true;
+
+postcondition(_S, {call, _, cmnd_start_server, _}, {ok,_}) -> true.
+
+
+%% @doc Next state transformation, S is the current state. Returns next state.
+-spec next_state(S :: ?MOD_eqc_statem:symbolic_state(),
+ V :: ?MOD_eqc_statem:var(),
+ C :: ?MOD_eqc_statem:call()) -> ?MOD_eqc_statem:symbolic_state().
+
+next_state(S, _V, {call, _, cmnd_put, [_,Name,Val]}) ->
+ S#state{store = [{Name,Val} | lists:keydelete(Name,1,S#state.store)]};
+
+next_state(S, _V, {call, _, cmnd_delete, [_,{Name,_Val}]}) ->
+ S#state{store = lists:keydelete(Name,1,S#state.store)};
+
+next_state(S, V, {call, _, cmnd_start_client, _}) ->
+ S#state{clients = [V | S#state.clients]};
+
+next_state(S, V, {call, _, cmnd_start_server, _}) ->
+ S#state{servers = [V | S#state.servers]};
+
+next_state(S, _V, {call, _, cmnd_stop_client, [C]}) ->
+ S#state{clients = S#state.clients -- [C]};
+
+next_state(S, _V, {call, _, cmnd_init, _}) ->
+ S#state{initialized=true};
+
+next_state(S, _V, {call, _, _, _}) ->
+ S.
+
+%%%================================================================
+%%%
+%%% Data model
+%%%
+
+file_path() -> non_empty(list(alphanum_char())).
+%%file_path() -> non_empty( list(oneof([alphanum_char(), utf8_char()])) ).
+
+%%file_contents() -> list(alphanum_char()).
+file_contents() -> list(oneof([alphanum_char(), utf8_char()])).
+
+alphanum_char() -> oneof(lists:seq($a,$z) ++ lists:seq($A,$Z) ++ lists:seq($0,$9)).
+
+utf8_char() -> oneof("åäöÅÄÖ話话カタカナひらがな").
+
+%%%================================================================
+%%%
+%%% Commands doing something with the System Under Test
+%%%
+
+cmnd_init(PrivDir) ->
+ ?fmt('Call cmnd_init(~p)~n',[PrivDir]),
+ os:cmd("killall vsftpd"),
+ clear_files(PrivDir),
+ ok.
+
+cmnd_start_server(PrivDir, DataDir) ->
+ ?fmt('Call cmnd_start_server(~p, ~p)~n',[PrivDir,DataDir]),
+ Cmnd = ["vsftpd ", filename:join(DataDir,"vsftpd.conf"),
+ " -oftpd_banner=erlang_otp_testing"
+ " -oanon_root=",PrivDir
+ ],
+ ?fmt("Cmnd=~s~n",[Cmnd]),
+ case os:cmd(Cmnd) of
+ [] ->
+ {ok,{"localhost",9999,"ftp","[email protected]"}};
+ Other ->
+ {error,Other}
+ end.
+
+cmnd_stop_server({ok,{_Host,Port,_Usr,_Pwd}}) ->
+ os:cmd("kill `netstat -tpln | grep "++integer_to_list(Port)++" | awk '{print $7}' | awk -F/ '{print $1}'`").
+
+cmnd_start_client({ok,{Host,Port,Usr,Pwd}}) ->
+ ?fmt('Call cmnd_start_client(~p)...',[{Host,Port,Usr,Pwd}]),
+ case inets:start(ftpc, [{host,Host},{port,Port}]) of
+ {ok,Client} ->
+ ?fmt("~p...",[{ok,Client}]),
+ case ftp:user(Client, Usr, Pwd) of
+ ok ->
+ ?fmt("OK!~n",[]),
+ {ok,Client};
+ Other ->
+ ?fmt("Other1=~p~n",[Other]),
+ inets:stop(ftpc,Client), Other
+ end;
+ Other ->
+ ?fmt("Other2=~p~n",[Other]),
+ Other
+ end.
+
+cmnd_stop_client({ok,Client}) ->
+ ?fmt('Call cmnd_stop_client(~p)~n',[Client]),
+ inets:stop(ftpc, Client). %% -> ok | Other
+
+cmnd_delete({ok,Client}, {Name,_ExpectedValue}) ->
+ ?fmt('Call cmnd_delete(~p, ~p)~n',[Client,Name]),
+ R=ftp:delete(Client, Name),
+ ?fmt("R=~p~n",[R]),
+ R.
+
+cmnd_put({ok,Client}, Name, Value) ->
+ ?fmt('Call cmnd_put(~p, ~p, ~p)...',[Client, Name, Value]),
+ R = ftp:send_bin(Client, unicode:characters_to_binary(Value), Name), % ok | {error,Error}
+ ?fmt('~p~n',[R]),
+ R.
+
+cmnd_get({ok,Client}, {Name,_ExpectedValue}) ->
+ ?fmt('Call cmnd_get(~p, ~p)~n',[Client,Name]),
+ case ftp:recv_bin(Client, Name) of
+ {ok,Bin} -> {ok, unicode:characters_to_list(Bin)};
+ Other -> Other
+ end.
+
+
+clear_files(Dir) ->
+ os:cmd(["rm -fr ",filename:join(Dir,"*")]).
diff --git a/lib/inets/test/property_test/ftp_simple_client_server_data/vsftpd.conf b/lib/inets/test/property_test/ftp_simple_client_server_data/vsftpd.conf
new file mode 100644
index 0000000000..fd48e2abf0
--- /dev/null
+++ b/lib/inets/test/property_test/ftp_simple_client_server_data/vsftpd.conf
@@ -0,0 +1,26 @@
+
+###
+### Some parameters are given in the vsftpd start command.
+###
+### Typical command-line paramters are such that has a file path
+### component like cert files.
+###
+
+
+listen=YES
+listen_port=9999
+run_as_launching_user=YES
+ssl_enable=NO
+#allow_anon_ssl=YES
+
+background=YES
+
+write_enable=YES
+anonymous_enable=YES
+anon_upload_enable=YES
+anon_mkdir_write_enable=YES
+anon_other_write_enable=YES
+anon_world_readable_only=NO
+
+### Shouldn't be necessary....
+require_ssl_reuse=NO
diff --git a/lib/inets/test/tftp_SUITE.erl b/lib/inets/test/tftp_SUITE.erl
index 59fb644667..09049e36af 100644
--- a/lib/inets/test/tftp_SUITE.erl
+++ b/lib/inets/test/tftp_SUITE.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -75,7 +76,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[simple, extra, reuse_connection, resend_client,
- resend_server].
+ resend_server, large_file].
groups() ->
[].
@@ -901,6 +902,41 @@ reuse_connection(Config) when is_list(Config) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Large file: transfer > 65535 blocks
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+large_file(doc) ->
+ ["Start the daemon and test transfer of files greater than 32M."];
+large_file(suite) ->
+ [];
+large_file(Config) when is_list(Config) ->
+ ?VERIFY(ok, application:start(inets)),
+
+ {Port, DaemonPid} = ?IGNORE(?START_DAEMON(0, [{debug, brief}])),
+
+ %% Read fail
+ RemoteFilename = "tftp_temporary_large_file_remote_test_file.txt",
+ LocalFilename = "tftp_temporary_large_file_local_test_file.txt",
+
+ {ok, FH} = file:open(LocalFilename, [write,exclusive]),
+ {ok, Size} = file:position(FH, {eof, 2*512*65535}),
+ ok = file:truncate(FH),
+ ?IGNORE(file:close(FH)),
+
+ %% Write and read
+ ?VERIFY({ok, Size}, tftp:write_file(RemoteFilename, LocalFilename, [{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.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Goodies
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/inets/test/tftp_test_lib.erl b/lib/inets/test/tftp_test_lib.erl
index e9b691828f..f07795324f 100644
--- a/lib/inets/test/tftp_test_lib.erl
+++ b/lib/inets/test/tftp_test_lib.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/test/tftp_test_lib.hrl b/lib/inets/test/tftp_test_lib.hrl
index bef024720a..e7a5a37d2c 100644
--- a/lib/inets/test/tftp_test_lib.hrl
+++ b/lib/inets/test/tftp_test_lib.hrl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/test/uri_SUITE.erl b/lib/inets/test/uri_SUITE.erl
index 9ba09e1474..b26c645821 100644
--- a/lib/inets/test/uri_SUITE.erl
+++ b/lib/inets/test/uri_SUITE.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. 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/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% 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.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -46,8 +47,10 @@ all() ->
userinfo,
scheme,
queries,
+ fragments,
escaped,
- hexed_query
+ hexed_query,
+ scheme_validation
].
%%--------------------------------------------------------------------
@@ -105,6 +108,42 @@ queries(Config) when is_list(Config) ->
{ok, {http,[],"localhost",8888,"/foobar.html","?foo=bar&foobar=42"}} =
http_uri:parse("http://localhost:8888/foobar.html?foo=bar&foobar=42").
+fragments(Config) when is_list(Config) ->
+ {ok, {http,[],"localhost",80,"/",""}} =
+ http_uri:parse("http://localhost#fragment"),
+ {ok, {http,[],"localhost",80,"/path",""}} =
+ http_uri:parse("http://localhost/path#fragment"),
+ {ok, {http,[],"localhost",80,"/","?query"}} =
+ http_uri:parse("http://localhost?query#fragment"),
+ {ok, {http,[],"localhost",80,"/path","?query"}} =
+ http_uri:parse("http://localhost/path?query#fragment"),
+ {ok, {http,[],"localhost",80,"/","","#fragment"}} =
+ http_uri:parse("http://localhost#fragment", [{fragment,true}]),
+ {ok, {http,[],"localhost",80,"/path","","#fragment"}} =
+ http_uri:parse("http://localhost/path#fragment", [{fragment,true}]),
+ {ok, {http,[],"localhost",80,"/","?query","#fragment"}} =
+ http_uri:parse("http://localhost?query#fragment", [{fragment,true}]),
+ {ok, {http,[],"localhost",80,"/path","?query","#fragment"}} =
+ http_uri:parse("http://localhost/path?query#fragment",
+ [{fragment,true}]),
+ {ok, {http,[],"localhost",80,"/","",""}} =
+ http_uri:parse("http://localhost", [{fragment,true}]),
+ {ok, {http,[],"localhost",80,"/path","",""}} =
+ http_uri:parse("http://localhost/path", [{fragment,true}]),
+ {ok, {http,[],"localhost",80,"/","?query",""}} =
+ http_uri:parse("http://localhost?query", [{fragment,true}]),
+ {ok, {http,[],"localhost",80,"/path","?query",""}} =
+ http_uri:parse("http://localhost/path?query", [{fragment,true}]),
+ {ok, {http,[],"localhost",80,"/","","#"}} =
+ http_uri:parse("http://localhost#", [{fragment,true}]),
+ {ok, {http,[],"localhost",80,"/path","","#"}} =
+ http_uri:parse("http://localhost/path#", [{fragment,true}]),
+ {ok, {http,[],"localhost",80,"/","?query","#"}} =
+ http_uri:parse("http://localhost?query#", [{fragment,true}]),
+ {ok, {http,[],"localhost",80,"/path","?query","#"}} =
+ http_uri:parse("http://localhost/path?query#", [{fragment,true}]),
+ ok.
+
escaped(Config) when is_list(Config) ->
{ok, {http,[],"www.somedomain.com",80,"/%2Eabc",[]}} =
http_uri:parse("http://www.somedomain.com/%2Eabc"),
@@ -137,6 +176,26 @@ hexed_query(Config) when is_list(Config) ->
verify_uri(URI2, Verify2),
verify_uri(URI3, Verify3).
+scheme_validation(Config) when is_list(Config) ->
+ {ok, {http,[],"localhost",80,"/",""}} =
+ http_uri:parse("http://localhost#fragment"),
+
+ ValidationFun =
+ fun("http") -> valid;
+ (_) -> {error, bad_scheme}
+ end,
+
+ {ok, {http,[],"localhost",80,"/",""}} =
+ http_uri:parse("http://localhost#fragment",
+ [{scheme_validation_fun, ValidationFun}]),
+ {error, bad_scheme} =
+ http_uri:parse("https://localhost#fragment",
+ [{scheme_validation_fun, ValidationFun}]),
+ %% non-fun scheme_validation_fun works as no option passed
+ {ok, {https,[],"localhost",443,"/",""}} =
+ http_uri:parse("https://localhost#fragment",
+ [{scheme_validation_fun, none}]).
+
%%--------------------------------------------------------------------
%% Internal Functions ------------------------------------------------
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 3f464c8684..543e0d44fd 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -2,23 +2,23 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2013. All Rights Reserved.
+# Copyright Ericsson AB 2001-2016. All Rights Reserved.
#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 5.9.5
+INETS_VSN = 6.3
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
-