Version in base suite: 2.4.68-1~deb13u1 Version in overlay suite: 2.4.69-1~deb13u1 Base version: apache2_2.4.69-1~deb13u1 Target version: apache2_2.4.71-1~deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/a/apache2/apache2_2.4.69-1~deb13u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/a/apache2/apache2_2.4.71-1~deb13u1.dsc .github/workflows/linux.yml | 280 +-- .github/workflows/sanity.yml | 55 .github/workflows/windows.yml | 68 .gitignore | 5 CHANGES | 251 ++ CMakeLists.txt | 2 Makefile.in | 185 +- ROADMAP | 2 VERSIONING | 2 acinclude.m4 | 2 changes-entries/template.txt | 3 configure | 24 configure.in | 2 debian/changelog | 8 debian/patches/build_suexec-custom.patch | 2 docs/man/dbmmanage.1 | 6 docs/man/htdbm.1 | 8 docs/man/htpasswd.1 | 6 docs/manual/bind.html.de | 3 docs/manual/bind.html.ja.utf8 | 4 docs/manual/bind.html.ko.euc-kr | 2 docs/manual/bind.html.tr.utf8 | 1 docs/manual/caching.html.en | 2 docs/manual/caching.html.tr.utf8 | 1 docs/manual/configuring.html.de | 3 docs/manual/configuring.html.ja.utf8 | 4 docs/manual/configuring.html.ko.euc-kr | 2 docs/manual/configuring.html.tr.utf8 | 1 docs/manual/content-negotiation.html.ja.utf8 | 4 docs/manual/content-negotiation.html.ko.euc-kr | 2 docs/manual/content-negotiation.html.tr.utf8 | 1 docs/manual/convenience.map | 1 docs/manual/custom-error.html.es | 4 docs/manual/custom-error.html.ja.utf8 | 4 docs/manual/custom-error.html.ko.euc-kr | 2 docs/manual/custom-error.html.tr.utf8 | 1 docs/manual/developer/API.html.en | 2 docs/manual/developer/documenting.html.zh-cn.utf8 | 1 docs/manual/developer/hooks.html.en | 2 docs/manual/developer/index.html.zh-cn.utf8 | 1 docs/manual/developer/modules.html.ja.utf8 | 4 docs/manual/developer/output-filters.html.en | 2 docs/manual/dns-caveats.html.ja.utf8 | 4 docs/manual/dns-caveats.html.ko.euc-kr | 2 docs/manual/dso.html.ja.utf8 | 4 docs/manual/dso.html.ko.euc-kr | 2 docs/manual/dso.html.tr.utf8 | 1 docs/manual/env.html.ja.utf8 | 4 docs/manual/env.html.ko.euc-kr | 2 docs/manual/env.html.tr.utf8 | 1 docs/manual/faq/index.html.es | 4 docs/manual/faq/index.html.tr.utf8 | 1 docs/manual/faq/index.html.zh-cn.utf8 | 1 docs/manual/filter.html.es | 4 docs/manual/filter.html.ja.utf8 | 4 docs/manual/filter.html.ko.euc-kr | 2 docs/manual/glossary.html.de | 3 docs/manual/glossary.html.es | 4 docs/manual/glossary.html.ja.utf8 | 4 docs/manual/glossary.html.ko.euc-kr | 2 docs/manual/glossary.html.tr.utf8 | 1 docs/manual/handler.html.ja.utf8 | 4 docs/manual/handler.html.ko.euc-kr | 2 docs/manual/handler.html.zh-cn.utf8 | 1 docs/manual/howto/access.html.es | 4 docs/manual/howto/auth.html.es | 4 docs/manual/howto/auth.html.ja.utf8 | 4 docs/manual/howto/auth.html.ko.euc-kr | 2 docs/manual/howto/auth.html.tr.utf8 | 1 docs/manual/howto/cgi.html.es | 4 docs/manual/howto/cgi.html.ja.utf8 | 4 docs/manual/howto/cgi.html.ko.euc-kr | 2 docs/manual/howto/htaccess.html.es | 4 docs/manual/howto/htaccess.html.ja.utf8 | 4 docs/manual/howto/htaccess.html.ko.euc-kr | 2 docs/manual/howto/htaccess.html.pt-br.utf8 | 2 docs/manual/howto/http2.html.es | 4 docs/manual/howto/index.html.ja.utf8 | 4 docs/manual/howto/index.html.ko.euc-kr | 2 docs/manual/howto/index.html.zh-cn.utf8 | 1 docs/manual/howto/public_html.html.es | 4 docs/manual/howto/public_html.html.ja.utf8 | 4 docs/manual/howto/public_html.html.ko.euc-kr | 2 docs/manual/howto/public_html.html.tr.utf8 | 1 docs/manual/howto/ssi.html.es | 4 docs/manual/howto/ssi.html.ja.utf8 | 4 docs/manual/howto/ssi.html.ko.euc-kr | 2 docs/manual/index.html.da | 1 docs/manual/index.html.de | 3 docs/manual/index.html.en | 2 docs/manual/index.html.es | 4 docs/manual/index.html.ja.utf8 | 4 docs/manual/index.html.ko.euc-kr | 2 docs/manual/index.html.tr.utf8 | 1 docs/manual/install.html.de | 3 docs/manual/install.html.es | 4 docs/manual/install.html.ja.utf8 | 4 docs/manual/install.html.ko.euc-kr | 2 docs/manual/install.html.pt-br.utf8 | 2 docs/manual/install.html.tr.utf8 | 1 docs/manual/invoking.html.de | 3 docs/manual/invoking.html.es | 4 docs/manual/invoking.html.ja.utf8 | 4 docs/manual/invoking.html.ko.euc-kr | 2 docs/manual/invoking.html.tr.utf8 | 1 docs/manual/logs.html.ja.utf8 | 4 docs/manual/logs.html.ko.euc-kr | 2 docs/manual/logs.html.tr.utf8 | 1 docs/manual/misc/index.html.ko.euc-kr | 2 docs/manual/misc/password_encryptions.html.en | 2 docs/manual/misc/perf-tuning.html.ko.euc-kr | 2 docs/manual/misc/perf-tuning.html.tr.utf8 | 1 docs/manual/misc/security_tips.html.en | 6 docs/manual/misc/security_tips.html.ko.euc-kr | 2 docs/manual/misc/security_tips.html.tr.utf8 | 1 docs/manual/mod/core.html.de | 3 docs/manual/mod/core.html.en | 6 docs/manual/mod/core.html.es | 4 docs/manual/mod/core.html.ja.utf8 | 4 docs/manual/mod/core.html.tr.utf8 | 1 docs/manual/mod/directive-dict.html.es | 4 docs/manual/mod/directive-dict.html.ja.utf8 | 4 docs/manual/mod/directive-dict.html.ko.euc-kr | 2 docs/manual/mod/directive-dict.html.tr.utf8 | 1 docs/manual/mod/directives.html.de | 1 docs/manual/mod/directives.html.en | 1 docs/manual/mod/directives.html.es | 1 docs/manual/mod/directives.html.fr.utf8 | 1 docs/manual/mod/directives.html.ja.utf8 | 1 docs/manual/mod/directives.html.ko.euc-kr | 1 docs/manual/mod/directives.html.tr.utf8 | 1 docs/manual/mod/directives.html.zh-cn.utf8 | 1 docs/manual/mod/event.html.en | 2 docs/manual/mod/index.html.de | 2 docs/manual/mod/index.html.en | 2 docs/manual/mod/index.html.es | 6 docs/manual/mod/index.html.ja.utf8 | 2 docs/manual/mod/index.html.ko.euc-kr | 4 docs/manual/mod/index.html.tr.utf8 | 2 docs/manual/mod/index.html.zh-cn.utf8 | 2 docs/manual/mod/mod_access_compat.html.ja.utf8 | 4 docs/manual/mod/mod_actions.html.de | 3 docs/manual/mod/mod_actions.html.ja.utf8 | 4 docs/manual/mod/mod_actions.html.ko.euc-kr | 2 docs/manual/mod/mod_alias.html.ja.utf8 | 4 docs/manual/mod/mod_alias.html.ko.euc-kr | 2 docs/manual/mod/mod_alias.html.tr.utf8 | 1 docs/manual/mod/mod_asis.html.ja.utf8 | 4 docs/manual/mod/mod_asis.html.ko.euc-kr | 2 docs/manual/mod/mod_auth_basic.html.ja.utf8 | 4 docs/manual/mod/mod_auth_basic.html.ko.euc-kr | 2 docs/manual/mod/mod_auth_digest.html.ko.euc-kr | 2 docs/manual/mod/mod_auth_form.html.en | 2 docs/manual/mod/mod_authn_anon.html.ja.utf8 | 4 docs/manual/mod/mod_authn_anon.html.ko.euc-kr | 2 docs/manual/mod/mod_authn_dbm.html.ja.utf8 | 4 docs/manual/mod/mod_authn_dbm.html.ko.euc-kr | 2 docs/manual/mod/mod_authn_file.html.ja.utf8 | 4 docs/manual/mod/mod_authn_file.html.ko.euc-kr | 2 docs/manual/mod/mod_authnz_fcgi.html.en | 2 docs/manual/mod/mod_authz_dbm.html.ko.euc-kr | 2 docs/manual/mod/mod_authz_groupfile.html.ja.utf8 | 4 docs/manual/mod/mod_authz_groupfile.html.ko.euc-kr | 2 docs/manual/mod/mod_authz_owner.html.ja.utf8 | 4 docs/manual/mod/mod_authz_owner.html.ko.euc-kr | 2 docs/manual/mod/mod_authz_user.html.ja.utf8 | 4 docs/manual/mod/mod_authz_user.html.ko.euc-kr | 2 docs/manual/mod/mod_autoindex.html.ja.utf8 | 4 docs/manual/mod/mod_autoindex.html.ko.euc-kr | 2 docs/manual/mod/mod_autoindex.html.tr.utf8 | 1 docs/manual/mod/mod_cache.html.en | 8 docs/manual/mod/mod_cache.html.ja.utf8 | 4 docs/manual/mod/mod_cache.html.ko.euc-kr | 2 docs/manual/mod/mod_cache_disk.html.ja.utf8 | 4 docs/manual/mod/mod_cache_disk.html.ko.euc-kr | 2 docs/manual/mod/mod_cern_meta.html.ko.euc-kr | 2 docs/manual/mod/mod_cgi.html.en | 2 docs/manual/mod/mod_cgi.html.ja.utf8 | 4 docs/manual/mod/mod_cgi.html.ko.euc-kr | 2 docs/manual/mod/mod_cgid.html.ja.utf8 | 4 docs/manual/mod/mod_cgid.html.ko.euc-kr | 2 docs/manual/mod/mod_charset_lite.html.ko.euc-kr | 2 docs/manual/mod/mod_dav.html.ja.utf8 | 4 docs/manual/mod/mod_dav.html.ko.euc-kr | 2 docs/manual/mod/mod_dav_fs.html.ja.utf8 | 4 docs/manual/mod/mod_dav_fs.html.ko.euc-kr | 2 docs/manual/mod/mod_dbd.html.en | 4 docs/manual/mod/mod_deflate.html.ja.utf8 | 4 docs/manual/mod/mod_deflate.html.ko.euc-kr | 2 docs/manual/mod/mod_dir.html.ja.utf8 | 4 docs/manual/mod/mod_dir.html.ko.euc-kr | 2 docs/manual/mod/mod_dir.html.tr.utf8 | 1 docs/manual/mod/mod_dumpio.html.ja.utf8 | 4 docs/manual/mod/mod_echo.html.ko.euc-kr | 2 docs/manual/mod/mod_env.html.ja.utf8 | 4 docs/manual/mod/mod_env.html.ko.euc-kr | 2 docs/manual/mod/mod_example_hooks.html.ko.euc-kr | 2 docs/manual/mod/mod_expires.html.ja.utf8 | 4 docs/manual/mod/mod_expires.html.ko.euc-kr | 2 docs/manual/mod/mod_ext_filter.html.ja.utf8 | 4 docs/manual/mod/mod_ext_filter.html.ko.euc-kr | 2 docs/manual/mod/mod_file_cache.html.ko.euc-kr | 2 docs/manual/mod/mod_filter.html.en | 4 docs/manual/mod/mod_headers.html.ja.utf8 | 4 docs/manual/mod/mod_headers.html.ko.euc-kr | 2 docs/manual/mod/mod_http2.html.en | 6 docs/manual/mod/mod_ident.html.ja.utf8 | 4 docs/manual/mod/mod_ident.html.ko.euc-kr | 2 docs/manual/mod/mod_imagemap.html.ko.euc-kr | 2 docs/manual/mod/mod_include.html.en | 2 docs/manual/mod/mod_include.html.ja.utf8 | 4 docs/manual/mod/mod_info.html.ja.utf8 | 4 docs/manual/mod/mod_info.html.ko.euc-kr | 2 docs/manual/mod/mod_isapi.html.ko.euc-kr | 2 docs/manual/mod/mod_log_config.html.ja.utf8 | 4 docs/manual/mod/mod_log_config.html.ko.euc-kr | 2 docs/manual/mod/mod_log_config.html.tr.utf8 | 1 docs/manual/mod/mod_log_forensic.html.ja.utf8 | 4 docs/manual/mod/mod_logio.html.ja.utf8 | 4 docs/manual/mod/mod_logio.html.ko.euc-kr | 2 docs/manual/mod/mod_logio.html.tr.utf8 | 1 docs/manual/mod/mod_lua.html.en | 2 docs/manual/mod/mod_md.html.en | 41 docs/manual/mod/mod_md.html.fr.utf8 | 41 docs/manual/mod/mod_mime.html.en | 2 docs/manual/mod/mod_mime.html.ja.utf8 | 4 docs/manual/mod/mod_negotiation.html.ja.utf8 | 4 docs/manual/mod/mod_proxy.html.en | 6 docs/manual/mod/mod_proxy.html.ja.utf8 | 4 docs/manual/mod/mod_proxy_ajp.html.ja.utf8 | 4 docs/manual/mod/mod_proxy_balancer.html.ja.utf8 | 4 docs/manual/mod/mod_proxy_connect.html.ja.utf8 | 4 docs/manual/mod/mod_proxy_hcheck.html.en | 2 docs/manual/mod/mod_proxy_html.html.en | 4 docs/manual/mod/mod_ratelimit.html.en | 11 docs/manual/mod/mod_ratelimit.html.fr.utf8 | 14 docs/manual/mod/mod_request.html.tr.utf8 | 1 docs/manual/mod/mod_setenvif.html.ja.utf8 | 4 docs/manual/mod/mod_setenvif.html.ko.euc-kr | 2 docs/manual/mod/mod_setenvif.html.tr.utf8 | 1 docs/manual/mod/mod_so.html.ja.utf8 | 4 docs/manual/mod/mod_so.html.ko.euc-kr | 2 docs/manual/mod/mod_speling.html.ja.utf8 | 4 docs/manual/mod/mod_speling.html.ko.euc-kr | 2 docs/manual/mod/mod_status.html.ja.utf8 | 4 docs/manual/mod/mod_status.html.ko.euc-kr | 2 docs/manual/mod/mod_suexec.html.ja.utf8 | 4 docs/manual/mod/mod_suexec.html.ko.euc-kr | 2 docs/manual/mod/mod_unique_id.html.ja.utf8 | 4 docs/manual/mod/mod_unique_id.html.ko.euc-kr | 2 docs/manual/mod/mod_userdir.html.ja.utf8 | 4 docs/manual/mod/mod_userdir.html.ko.euc-kr | 2 docs/manual/mod/mod_version.html.ko.euc-kr | 2 docs/manual/mod/mod_vhost_alias.html.tr.utf8 | 1 docs/manual/mod/mod_xml2enc.html.en | 4 docs/manual/mod/module-dict.html.ja.utf8 | 4 docs/manual/mod/module-dict.html.ko.euc-kr | 2 docs/manual/mod/module-dict.html.tr.utf8 | 1 docs/manual/mod/mpm_common.html.de | 3 docs/manual/mod/mpm_common.html.en | 4 docs/manual/mod/mpm_common.html.ja.utf8 | 4 docs/manual/mod/mpm_common.html.tr.utf8 | 1 docs/manual/mod/mpm_winnt.html.de | 3 docs/manual/mod/mpm_winnt.html.ja.utf8 | 4 docs/manual/mod/prefork.html.de | 3 docs/manual/mod/prefork.html.ja.utf8 | 4 docs/manual/mod/prefork.html.tr.utf8 | 1 docs/manual/mod/quickreference.html.de | 723 +++---- docs/manual/mod/quickreference.html.en | 721 +++---- docs/manual/mod/quickreference.html.es | 721 +++---- docs/manual/mod/quickreference.html.fr.utf8 | 730 ++++---- docs/manual/mod/quickreference.html.ja.utf8 | 709 +++---- docs/manual/mod/quickreference.html.ko.euc-kr | 719 +++---- docs/manual/mod/quickreference.html.tr.utf8 | 731 ++++---- docs/manual/mod/quickreference.html.zh-cn.utf8 | 722 +++---- docs/manual/mod/worker.html.de | 3 docs/manual/mod/worker.html.ja.utf8 | 4 docs/manual/mod/worker.html.tr.utf8 | 1 docs/manual/mpm.html.de | 3 docs/manual/mpm.html.en | 2 docs/manual/mpm.html.es | 4 docs/manual/mpm.html.ja.utf8 | 4 docs/manual/mpm.html.ko.euc-kr | 2 docs/manual/mpm.html.tr.utf8 | 1 docs/manual/mpm.html.zh-cn.utf8 | 1 docs/manual/new_features_2_0.html.ja.utf8 | 4 docs/manual/new_features_2_0.html.ko.euc-kr | 2 docs/manual/new_features_2_2.html.en | 2 docs/manual/new_features_2_2.html.ko.euc-kr | 2 docs/manual/new_features_2_2.html.tr.utf8 | 1 docs/manual/new_features_2_4.html.en | 8 docs/manual/new_features_2_4.html.tr.utf8 | 1 docs/manual/platform/ebcdic.html.ko.euc-kr | 2 docs/manual/platform/index.html.en | 2 docs/manual/platform/index.html.ko.euc-kr | 2 docs/manual/platform/index.html.zh-cn.utf8 | 1 docs/manual/platform/netware.html.ko.euc-kr | 2 docs/manual/platform/rpm.html.en | 4 docs/manual/platform/win_compiling.html.en | 6 docs/manual/platform/win_compiling.html.ko.euc-kr | 2 docs/manual/platform/windows.html.en | 6 docs/manual/platform/windows.html.fr.utf8 | 8 docs/manual/platform/windows.html.ko.euc-kr | 2 docs/manual/programs/ab.html.ko.euc-kr | 2 docs/manual/programs/ab.html.tr.utf8 | 1 docs/manual/programs/apachectl.html.ko.euc-kr | 2 docs/manual/programs/apachectl.html.tr.utf8 | 1 docs/manual/programs/apxs.html.ko.euc-kr | 2 docs/manual/programs/apxs.html.tr.utf8 | 1 docs/manual/programs/configure.html.ko.euc-kr | 2 docs/manual/programs/configure.html.tr.utf8 | 1 docs/manual/programs/dbmmanage.html.en | 4 docs/manual/programs/dbmmanage.html.ko.euc-kr | 2 docs/manual/programs/dbmmanage.html.tr.utf8 | 1 docs/manual/programs/htcacheclean.html.ko.euc-kr | 2 docs/manual/programs/htcacheclean.html.tr.utf8 | 1 docs/manual/programs/htdbm.html.en | 8 docs/manual/programs/htdbm.html.tr.utf8 | 1 docs/manual/programs/htdigest.html.ko.euc-kr | 2 docs/manual/programs/htdigest.html.tr.utf8 | 1 docs/manual/programs/htpasswd.html.en | 4 docs/manual/programs/htpasswd.html.ko.euc-kr | 2 docs/manual/programs/htpasswd.html.tr.utf8 | 1 docs/manual/programs/httpd.html.ko.euc-kr | 2 docs/manual/programs/httpd.html.tr.utf8 | 1 docs/manual/programs/httxt2dbm.html.tr.utf8 | 1 docs/manual/programs/index.html.ko.euc-kr | 2 docs/manual/programs/index.html.zh-cn.utf8 | 1 docs/manual/programs/logresolve.html.ko.euc-kr | 2 docs/manual/programs/logresolve.html.tr.utf8 | 1 docs/manual/programs/other.html.ko.euc-kr | 2 docs/manual/programs/rotatelogs.html.ko.euc-kr | 2 docs/manual/programs/rotatelogs.html.tr.utf8 | 1 docs/manual/programs/suexec.html.ko.euc-kr | 2 docs/manual/programs/suexec.html.tr.utf8 | 1 docs/manual/rewrite/avoid.html.en | 2 docs/manual/rewrite/flags.html.en | 2 docs/manual/rewrite/index.html.tr.utf8 | 1 docs/manual/rewrite/index.html.zh-cn.utf8 | 1 docs/manual/rewrite/remapping.html.en | 2 docs/manual/sections.html.ja.utf8 | 4 docs/manual/sections.html.ko.euc-kr | 2 docs/manual/sections.html.tr.utf8 | 1 docs/manual/server-wide.html.ja.utf8 | 4 docs/manual/server-wide.html.ko.euc-kr | 2 docs/manual/sitemap.html.de | 3 docs/manual/sitemap.html.es | 4 docs/manual/sitemap.html.ja.utf8 | 4 docs/manual/sitemap.html.ko.euc-kr | 2 docs/manual/sitemap.html.tr.utf8 | 1 docs/manual/sitemap.html.zh-cn.utf8 | 1 docs/manual/ssl/index.html.zh-cn.utf8 | 1 docs/manual/ssl/ssl_intro.html.ja.utf8 | 4 docs/manual/stopping.html.de | 3 docs/manual/stopping.html.en | 2 docs/manual/stopping.html.es | 4 docs/manual/stopping.html.ja.utf8 | 4 docs/manual/stopping.html.ko.euc-kr | 2 docs/manual/stopping.html.tr.utf8 | 1 docs/manual/style/version.ent | 2 docs/manual/suexec.html.ja.utf8 | 4 docs/manual/suexec.html.ko.euc-kr | 2 docs/manual/suexec.html.tr.utf8 | 1 docs/manual/urlmapping.html.ja.utf8 | 4 docs/manual/urlmapping.html.ko.euc-kr | 2 docs/manual/urlmapping.html.tr.utf8 | 1 docs/manual/vhosts/details.html.ko.euc-kr | 2 docs/manual/vhosts/details.html.tr.utf8 | 1 docs/manual/vhosts/examples.html.ja.utf8 | 4 docs/manual/vhosts/examples.html.ko.euc-kr | 2 docs/manual/vhosts/fd-limits.html.ja.utf8 | 4 docs/manual/vhosts/fd-limits.html.ko.euc-kr | 2 docs/manual/vhosts/index.html.de | 3 docs/manual/vhosts/index.html.ja.utf8 | 4 docs/manual/vhosts/index.html.ko.euc-kr | 2 docs/manual/vhosts/index.html.zh-cn.utf8 | 1 docs/manual/vhosts/ip-based.html.ja.utf8 | 4 docs/manual/vhosts/ip-based.html.ko.euc-kr | 2 docs/manual/vhosts/mass.html.ko.euc-kr | 2 docs/manual/vhosts/mass.html.tr.utf8 | 1 docs/manual/vhosts/name-based.html.de | 3 docs/manual/vhosts/name-based.html.ja.utf8 | 4 docs/manual/vhosts/name-based.html.ko.euc-kr | 2 httpd.spec | 2 include/ap_release.h | 2 modules/aaa/mod_authnz_ldap.c | 5 modules/cache/cache_storage.c | 13 modules/cache/mod_cache.c | 7 modules/dav/lock/locks.c | 4 modules/dav/main/util.c | 50 modules/filters/mod_deflate.c | 4 modules/filters/mod_substitute.c | 2 modules/generators/mod_cgi.c | 3 modules/generators/mod_cgid.c | 33 modules/http2/h2.h | 2 modules/http2/h2_protocol.c | 6 modules/http2/h2_protocol.h | 4 modules/http2/h2_push.c | 176 - modules/http2/h2_push.h | 15 modules/http2/h2_request.c | 2 modules/http2/h2_session.c | 4 modules/http2/h2_stream.c | 15 modules/http2/h2_switch.c | 6 modules/ldap/util_ldap.c | 7 modules/mappers/mod_vhost_alias.c | 2 modules/md/md.h | 9 modules/md/md_acme.c | 9 modules/md/md_acme.h | 6 modules/md/md_acme_authz.c | 2 modules/md/md_acme_drive.c | 12 modules/md/md_core.c | 12 modules/md/md_crypt.c | 12 modules/md/md_curl.c | 14 modules/md/md_http.c | 12 modules/md/md_http.h | 10 modules/md/md_reg.c | 21 modules/md/md_reg.h | 15 modules/md/mod_md.c | 16 modules/md/mod_md_config.c | 66 modules/md/mod_md_config.h | 8 modules/md/mod_md_status.c | 1 modules/metadata/mod_remoteip.c | 101 - support/ab.c | 4 test/.gitignore | 2 test/GitHub-CI-Emu.md | 296 +++ test/README | 152 - test/README.ci | 256 ++ test/README.pytest | 167 + test/check-test-sync.sh | 28 test/clients/h2ws.c | 39 test/conftest.py | 201 +- test/gha-docs-check.sh | 46 test/gha-resolve-deps.sh | 93 + test/gha-sanity-check.sh | 34 test/httpdunit.c | 53 test/httpdunit.h | 120 + test/modules/aaa/__init__.py | 1 test/modules/aaa/conftest.py | 128 + test/modules/aaa/digest_client.py | 134 + test/modules/aaa/env.py | 79 test/modules/aaa/htdocs/digest/default/secret.txt | 1 test/modules/aaa/htdocs/digest/domain/nested/secret.txt | 1 test/modules/aaa/htdocs/digest/domain/secret.txt | 1 test/modules/aaa/htdocs/digest/nccheck-shortlife/secret.txt | 1 test/modules/aaa/htdocs/digest/nccheck/secret.txt | 1 test/modules/aaa/htdocs/digest/neverexpire/secret.txt | 1 test/modules/aaa/htdocs/digest/noprovider/secret.txt | 1 test/modules/aaa/htdocs/digest/onetime-nccheck/secret.txt | 1 test/modules/aaa/htdocs/digest/onetime/secret.txt | 1 test/modules/aaa/htdocs/digest/shortlife/secret.txt | 1 test/modules/aaa/test_001_challenge_response.py | 199 ++ test/modules/aaa/test_002_nonce.py | 134 + test/modules/aaa/test_003_nccheck.py | 180 + test/modules/aaa/test_004_domain.py | 56 test/modules/aaa/test_005_provider.py | 37 test/modules/aaa/test_006_config_errors.py | 107 + test/modules/aaa/test_007_replay.py | 237 ++ test/modules/aaa/test_008_onetime_nccheck.py | 194 ++ test/modules/aaa/test_009_restart.py | 230 ++ test/modules/aaa/test_010_eviction.py | 134 + test/modules/aaa/test_011_authinfo_errors.py | 93 + test/modules/aaa/test_012_errordoc.py | 40 test/modules/aaa/test_012_truncated_nonce.py | 29 test/modules/aaa/test_013_proxy_domain.py | 28 test/modules/arch/linux/README | 109 + test/modules/arch/linux/conftest.py | 41 test/modules/arch/linux/env.py | 638 +++++++ test/modules/arch/linux/test_001_notify.py | 155 + test/modules/arch/linux/test_002_monitor.py | 110 + test/modules/arch/linux/test_003_extended_status.py | 69 test/modules/arch/linux/test_004_socket_activation.py | 92 + test/modules/arch/linux/test_005_service.py | 95 + test/modules/arch/linux/test_006_watchdog.py | 217 ++ test/modules/arch/linux/test_007_notify_reload.py | 146 + test/modules/conftest.py | 70 test/modules/core/env.py | 2 test/modules/core/htdocs/cgi/delay.py | 11 test/modules/core/htdocs/cgi/env_parameters.py | 14 test/modules/core/htdocs/cgiredir/redir.cgi | 13 test/modules/core/htdocs/cgiredir/target | 9 test/modules/core/htdocs/ssi/exec.shtml | 1 test/modules/core/htdocs/userdir/testuser/public_html/cgi-bin/.htaccess | 1 test/modules/core/htdocs/userdir/testuser/public_html/cgi-bin/test.cgi | 4 test/modules/core/test_001_encoding.py | 8 test/modules/core/test_002_restarts.py | 4 test/modules/core/test_003_cgi_env_vars.py | 33 test/modules/core/test_004_ssi.py | 35 test/modules/core/test_005_userdir_headers.py | 43 test/modules/core/test_006_merge_lang.py | 39 test/modules/core/test_007_cgi_redirect.py | 43 test/modules/core/test_008_restart.py | 59 test/modules/core/test_009_allow_methods.py | 49 test/modules/core/test_010_limit_xml_request_body.py | 48 test/modules/core/test_012_config_off_t.py | 117 + test/modules/dav/test_002_options_trace.py | 77 test/modules/dav/test_003_config_quota.py | 38 test/modules/dav/test_004_lock_timeout_overflow.py | 86 test/modules/filters/conftest.py | 30 test/modules/filters/env.py | 26 test/modules/filters/htdocs/test1/cgi/echo.py | 12 test/modules/filters/htdocs/test1/cgi/nobody.py | 5 test/modules/filters/test_001_substitute_maxlen.py | 83 test/modules/filters/test_002_sed_ycomp.py | 62 test/modules/filters/test_003_sed.py | 565 ++++++ test/modules/filters/test_004_buffer_size.py | 38 test/modules/generators/conftest.py | 35 test/modules/generators/env.py | 39 test/modules/generators/htdocs/autoindex/index.html | 1 test/modules/generators/htdocs/autoindex/listing/att.html | 1 test/modules/generators/htdocs/cgi/arglen.py | 5 test/modules/generators/htdocs/cgi/argv.py | 6 test/modules/generators/htdocs/cgi/envcount.py | 5 test/modules/generators/htdocs/cgi/noread.py | 3 test/modules/generators/htdocs/cgi/nph-hello.py | 4 test/modules/generators/htdocs/cgi/redirect.py | 2 test/modules/generators/htdocs/cgi/sleep.py | 6 test/modules/generators/htdocs/cgi/sleep_trap.py | 8 test/modules/generators/htdocs/cgi/ssi/exec_bogus.shtml | 1 test/modules/generators/htdocs/cgi/ssi/exec_cgi.shtml | 1 test/modules/generators/htdocs/cgi/ssi/exec_cgi_missing.shtml | 1 test/modules/generators/htdocs/cgi/ssi/exec_cgi_nph.shtml | 1 test/modules/generators/htdocs/cgi/ssi/exec_cgi_query.shtml | 1 test/modules/generators/htdocs/cgi/ssi/exec_cgi_redirect.shtml | 1 test/modules/generators/htdocs/cgi/ssi/exec_noexec.shtml | 1 test/modules/generators/htdocs/cgi/ssi/exec_path_info.shtml | 1 test/modules/generators/htdocs/cgi/stderr_fail.py | 5 test/modules/generators/test_001_cgid.py | 239 ++ test/modules/generators/test_002_autoindex.py | 34 test/modules/http1/env.py | 22 test/modules/http1/htdocs/cgi/requestparser.py | 5 test/modules/http1/test_004_post.py | 3 test/modules/http1/test_005_trailers.py | 6 test/modules/http2/Makefile.in | 20 test/modules/http2/conftest.py | 10 test/modules/http2/env.py | 30 test/modules/http2/htdocs/cgi/echo.py | 1 test/modules/http2/htdocs/cgi/requestparser.py | 5 test/modules/http2/test_003_get.py | 1 test/modules/http2/test_004_post.py | 2 test/modules/http2/test_006_assets.py | 1 test/modules/http2/test_008_ranges.py | 3 test/modules/http2/test_009_timing.py | 2 test/modules/http2/test_101_ssl_reneg.py | 22 test/modules/http2/test_103_upgrade.py | 1 test/modules/http2/test_104_padding.py | 1 test/modules/http2/test_105_timeout.py | 8 test/modules/http2/test_106_shutdown.py | 8 test/modules/http2/test_107_frame_lengths.py | 1 test/modules/http2/test_108_goaway.py | 105 + test/modules/http2/test_111_header_limit_log.py | 53 test/modules/http2/test_200_header_invalid.py | 21 test/modules/http2/test_202_trailer.py | 1 test/modules/http2/test_203_rfc9113.py | 25 test/modules/http2/test_400_push.py | 18 test/modules/http2/test_401_early_hints.py | 1 test/modules/http2/test_500_proxy.py | 1 test/modules/http2/test_503_proxy_fwd.py | 12 test/modules/http2/test_600_h2proxy.py | 28 test/modules/http2/test_601_h2proxy_twisted.py | 14 test/modules/http2/test_700_load_get.py | 3 test/modules/http2/test_710_load_post_static.py | 1 test/modules/http2/test_711_load_post_cgi.py | 1 test/modules/http2/test_712_buffering.py | 5 test/modules/http2/test_800_websockets.py | 23 test/modules/md/conftest.py | 30 test/modules/md/dns01.py | 18 test/modules/md/dns01_record.py | 35 test/modules/md/dns01_v2.py | 18 test/modules/md/md_cert_util.py | 143 - test/modules/md/md_conf.py | 11 test/modules/md/md_env.py | 74 test/modules/md/test_001_store.py | 6 test/modules/md/test_010_store_migrate.py | 4 test/modules/md/test_100_reg_add.py | 9 test/modules/md/test_110_reg_update.py | 9 test/modules/md/test_120_reg_list.py | 9 test/modules/md/test_202_acmev2_regs.py | 9 test/modules/md/test_300_conf_validate.py | 28 test/modules/md/test_310_conf_store.py | 24 test/modules/md/test_502_acmev2_drive.py | 23 test/modules/md/test_602_roundtrip.py | 12 test/modules/md/test_702_auto.py | 123 + test/modules/md/test_710_profiles.py | 7 test/modules/md/test_720_wildcard.py | 7 test/modules/md/test_721_dns01.py | 99 + test/modules/md/test_730_static.py | 7 test/modules/md/test_740_acme_errors.py | 7 test/modules/md/test_741_setup_errors.py | 7 test/modules/md/test_790_failover.py | 7 test/modules/md/test_800_must_staple.py | 20 test/modules/md/test_801_stapling.py | 43 test/modules/md/test_810_ec.py | 13 test/modules/md/test_820_locks.py | 7 test/modules/md/test_900_notify.py | 7 test/modules/md/test_901_message.py | 11 test/modules/md/test_910_cleanups.py | 7 test/modules/md/test_920_status.py | 8 test/modules/metadata/conftest.py | 35 test/modules/metadata/env.py | 40 test/modules/metadata/samples.py | 49 test/modules/metadata/test_001_mime_magic.py | 136 + test/modules/metadata/test_002_mime_libmagic.py | 91 + test/modules/metadata/test_002_setenvfromfile.py | 206 ++ test/modules/metadata/test_003_compare.py | 86 test/modules/metadata/test_004_remoteip.py | 334 +++ test/modules/proxy/env.py | 23 test/modules/proxy/test_01_http.py | 6 test/modules/proxy/test_02_unix.py | 6 test/modules/proxy/test_03_response.py | 30 test/modules/proxy/test_03_uwsgi.py | 91 - test/modules/proxy/test_04_ajp.py | 127 + test/modules/proxy/test_05_uwsgi.py | 73 test/modules/proxy/test_07_cache_control.py | 173 + test/modules/proxy/test_08_cache_vary_star.py | 133 + test/modules/proxy/uwsgi_faker.py | 146 - test/modules/ssl/conftest.py | 45 test/modules/ssl/env.py | 26 test/modules/ssl/test_001_sni.py | 38 test/modules/ssl/test_003_verify_client_perdir.py | 144 + test/modules/ssl/test_004_socache_redis.py | 91 + test/modules/ssl/test_005_vhost_compat.py | 309 +++ test/modules/ssl/test_006_ct_shutdown.py | 30 test/pyhttpd/backport.py | 90 test/pyhttpd/build-win.bat | 293 +++ test/pyhttpd/build.py | 514 +++++ test/pyhttpd/classic_errorlog.py | 243 ++ test/pyhttpd/cmodules.py | 154 + test/pyhttpd/conf/httpd.conf.template | 14 test/pyhttpd/conf/mime.types | 1 test/pyhttpd/conf/stop.conf.template | 5 test/pyhttpd/depends.py | 255 ++ test/pyhttpd/env.py | 739 ++++++-- test/pyhttpd/gating.py | 164 + test/pyhttpd/install_tools.bat | 277 +++ test/pyhttpd/instance.py | 430 ++++ test/pyhttpd/log.py | 30 test/pyhttpd/mod_aptest/mod_aptest.c | 81 test/pyhttpd/plugin.py | 93 + test/pyhttpd/ports.py | 60 test/pyhttpd/pyproject.toml | 16 test/pyhttpd/runtests.sh | 87 test/pyhttpd/sslca.py | 331 +++ test/pyhttpd/uv.lock | 679 ------- test/pyproject.toml | 41 test/pytest_suite/.gitignore | 1 test/pytest_suite/README.md | 89 test/pytest_suite/apache_pytest/__init__.py | 2 test/pytest_suite/apache_pytest/cmodules.py | 48 test/pytest_suite/apache_pytest/private_server.py | 155 + test/pytest_suite/apache_pytest/probe.py | 257 -- test/pytest_suite/apache_pytest/server.py | 232 -- test/pytest_suite/apache_pytest/sslca.py | 270 -- test/pytest_suite/apache_pytest/testapi.py | 13 test/pytest_suite/conftest.py | 211 -- test/pytest_suite/gen_test_header.py | 4 test/pytest_suite/pyproject.toml | 29 test/pytest_suite/ruff.toml | 2 test/pytest_suite/runtests.bat | 164 - test/pytest_suite/runtests.sh | 104 - test/pytest_suite/t/conf/extra.conf.in | 52 test/pytest_suite/t/conf/proxy_beacon.conf.in | 121 + test/pytest_suite/t/conf/proxy_hcheck.conf.in | 107 + test/pytest_suite/t/htdocs/modules/negotiation/qs/after.qsafter | 1 test/pytest_suite/t/htdocs/modules/negotiation/qs/before.qsbefore | 1 test/pytest_suite/t/htdocs/modules/negotiation/qs/doc.html | 1 test/pytest_suite/t/htdocs/modules/negotiation/qs/doc.xhtml | 1 test/pytest_suite/t/htdocs/modules/negotiation/qs/quoted.quoted | 1 test/pytest_suite/t/htdocs/modules/negotiation/qs/tm1.htm | 1 test/pytest_suite/t/htdocs/modules/negotiation/qs/tm2.xht | 1 test/pytest_suite/t/htdocs/modules/negotiation/qs/typemap.var | 5 test/pytest_suite/tests/t/apache/test_motorz_connections.py | 134 + test/pytest_suite/tests/t/apache/test_motorz_graceful.py | 175 + test/pytest_suite/tests/t/apache/test_motorz_hooks.py | 149 + test/pytest_suite/tests/t/apache/test_motorz_limits.py | 43 test/pytest_suite/tests/t/modules/test_dav.py | 41 test/pytest_suite/tests/t/modules/test_ldap.py | 17 test/pytest_suite/tests/t/modules/test_negotiation.py | 49 test/pytest_suite/tests/t/modules/test_proxy_beacon.py | 3 test/pytest_suite/tests/t/modules/test_proxy_beacon_input.py | 908 ++++++++++ test/pytest_suite/tests/t/modules/test_proxy_hcheck.py | 424 ++++ test/pytest_suite/tests/t/modules/test_speling.py | 14 test/pytest_suite/tests/test_backport_marks.py | 27 test/pytest_suite/uv.lock | 156 - test/run-all-tests.sh | 196 -- test/run-one-test.sh | 17 test/runtests.bat | 179 + test/runtests.sh | 149 + test/test_backport_marks.py | 122 + test/test_build.py | 72 test/test_classic_errorlog.py | 100 + test/test_cmodule_header.py | 17 test/test_config_off_t_marks.py | 20 test/test_instance.py | 180 + test/test_md_renewal.py | 115 + test/test_strict_optional.py | 308 +++ test/test_travis_conditions.sh | 1 test/travis_Dockerfile_slapd.centos | 2 test/travis_before_linux.sh | 171 + test/travis_run_linux.sh | 160 + test/unit/authn.c | 155 + test/unit/base64.c | 175 + test/unit/util.c | 226 ++ test/uv.lock | 536 +++++ 703 files changed, 23386 insertions(+), 7547 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpnfo5cz2c/apache2_2.4.69-1~deb13u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpnfo5cz2c/apache2_2.4.71-1~deb13u1.dsc: no acceptable signature found diff -Nru apache2-2.4.69/.github/workflows/linux.yml apache2-2.4.71/.github/workflows/linux.yml --- apache2-2.4.69/.github/workflows/linux.yml 2026-08-10 13:52:29.000000000 +0000 +++ apache2-2.4.71/.github/workflows/linux.yml 2026-11-09 03:07:05.000000000 +0000 @@ -10,6 +10,7 @@ - README* - '**.md' - changes-entries/* + - .asf.yaml tags: - 2.* pull_request: @@ -22,11 +23,11 @@ - '**.md' - changes-entries/* +permissions: + contents: read + env: - MARGS: "-j2" CFLAGS: "-g" - # This will need updating as the ubuntu-latest image changes: - PHP_FPM: "/usr/sbin/php-fpm8.3" concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -36,13 +37,73 @@ build: strategy: fail-fast: false + # https://infra.apache.org/github-actions-policy.html requires a + # job concurrency level of 20 or less. + max-parallel: 20 matrix: include: # ------------------------------------------------------------------------- - - name: Empty APLOGNO() test + - name: Shared MPMs (event), pool-debug, SSL/TLS variants + config: --enable-mods-shared=reallyall --enable-mpms-shared=all --with-mpm=event env: | - SKIP_TESTING=1 - TEST_LOGNO=1 + APR_VERSION=1.7.x + APR_CONFIG="--enable-pool-debug" + APU_VERSION=1.7.x + APU_CONFIG="--with-crypto --with-ldap" + TEST_MALLOC=1 + TEST_SSL=1 + # ------------------------------------------------------------------------- + - name: Shared MPMs (worker), pool-debug, SSL/TLS variants + config: --enable-mods-shared=reallyall --enable-mpms-shared=all --with-mpm=worker + env: | + APR_VERSION=1.7.x + APR_CONFIG="--enable-pool-debug" + APU_VERSION=1.7.x + APU_CONFIG="--with-crypto --with-ldap" + TEST_MALLOC=1 + TEST_SSL=1 + # ------------------------------------------------------------------------- + - name: Shared MPMs (prefork), pool-debug, SSL/TLS variants + config: --enable-mods-shared=reallyall --enable-mpms-shared=all --with-mpm=prefork + env: | + APR_VERSION=1.7.x + APR_CONFIG="--enable-pool-debug" + APU_VERSION=1.7.x + APU_CONFIG="--with-crypto --with-ldap" + TEST_MALLOC=1 + TEST_SSL=1 + # ------------------------------------------------------------------------- + - name: ASan + notest-cflags: -ggdb -fsanitize=address -fno-sanitize-recover=address -fno-omit-frame-pointer + config: --enable-mods-shared=reallyall + env: | + APR_VERSION=1.7.x + APU_VERSION=1.7.x + APU_CONFIG="--with-crypto --with-ldap" + TEST_ASAN=1 + # ------------------------------------------------------------------------- + - name: ASan, pool-debug + notest-cflags: -ggdb -fsanitize=address -fno-sanitize-recover=address -fno-omit-frame-pointer -O2 -Wno-deprecated-declarations + config: --enable-mods-shared=reallyall --enable-maintainer-mode + env: | + APR_VERSION=1.7.x + APR_CONFIG="--enable-pool-debug" + APU_VERSION=1.7.x + APU_CONFIG="--with-crypto --with-ldap" + TEST_ASAN=1 + # ------------------------------------------------------------------------- + # Runs every pytest-based test suite via `make check-all-pytest`. + # See TEST_PYTEST/TEST_MD in test/travis_run_linux.sh. + - name: Python pytest test suites + config: --enable-mods-shared=reallyall --with-mpm=event --enable-mpms-shared=all + pkgs: nghttp2-client + env: | + APR_VERSION=1.7.6 + APU_VERSION=1.6.5 + APU_CONFIG="--with-crypto" + NO_TEST_FRAMEWORK=1 + TEST_PYTEST=1 + TEST_MD=1 # ------------------------------------------------------------------------- - name: Default # ------------------------------------------------------------------------- @@ -65,9 +126,6 @@ config: --enable-mods-shared=reallyall --enable-mpms-shared=all os: ubuntu-24.04-arm # ------------------------------------------------------------------------- - - name: Event MPM, all-modules, mod_cgid w/o fdpassing - config: --enable-mods-shared=reallyall --with-mpm=event --disable-cgi - # ------------------------------------------------------------------------- - name: Default, all-modules + install config: --enable-mods-shared=reallyall env: | @@ -79,11 +137,13 @@ env: | TEST_ARGS=-order=random # ------------------------------------------------------------------------- - - name: GCC 12 maintainer-mode w/-Werror, install + VPATH + - name: GCC 14 maintainer-mode w/-Werror, install + VPATH + os: ubuntu-24.04 config: --enable-mods-shared=reallyall --enable-maintainer-mode notest-cflags: -Werror -O2 + pkgs: gcc-14 env: | - CC=gcc-12 + CC=gcc-14 TEST_VPATH=1 TEST_INSTALL=1 SKIP_TESTING=1 @@ -102,16 +162,24 @@ APU_VERSION=1.6.5 APU_CONFIG="--without-crypto --without-ldap" # ------------------------------------------------------------------------- + - name: APR 1.7.x, APR-util 1.6.x + config: --enable-mods-shared=reallyall + env: | + APR_VERSION=1.7.x + APU_VERSION=1.6.x + APU_CONFIG="--with-crypto --with-ldap" + # ------------------------------------------------------------------------- - name: APR 1.8.x, APR-util 1.7.x config: --enable-mods-shared=reallyall env: | APR_VERSION=1.8.x APU_VERSION=1.7.x APU_CONFIG="--with-crypto --with-ldap" - CLEAR_CACHE=1 # ------------------------------------------------------------------------- - # Oldest supported APR/APR-util branches: + # Oldest supported APR/APR-util branches. Their configure + # tests fail to compile with GCC 14 or later. - name: APR 1.4.8, APR-util 1.3.12 + os: ubuntu-24.04 config: --enable-mods-shared=reallyall env: | TEST_APR_TARBALL=yes @@ -127,40 +195,6 @@ APU_VERSION=1.7.x APU_CONFIG="--with-crypto --with-ldap" TEST_MALLOC=1 - CLEAR_CACHE=1 - # ------------------------------------------------------------------------- - - name: Shared MPMs (event), pool-debug, SSL/TLS variants - config: --enable-mods-shared=reallyall --enable-mpms-shared=all --with-mpm=event - env: | - APR_VERSION=1.7.x - APR_CONFIG="--enable-pool-debug" - APU_VERSION=1.7.x - APU_CONFIG="--with-crypto --with-ldap" - TEST_MALLOC=1 - TEST_SSL=1 - CLEAR_CACHE=1 - # ------------------------------------------------------------------------- - - name: Shared MPMs (worker), pool-debug, SSL/TLS variants - config: --enable-mods-shared=reallyall --enable-mpms-shared=all --with-mpm=worker - env: | - APR_VERSION=1.7.x - APR_CONFIG="--enable-pool-debug" - APU_VERSION=1.7.x - APU_CONFIG="--with-crypto --with-ldap" - TEST_MALLOC=1 - TEST_SSL=1 - CLEAR_CACHE=1 - # ------------------------------------------------------------------------- - - name: Shared MPMs (prefork), pool-debug, SSL/TLS variants - config: --enable-mods-shared=reallyall --enable-mpms-shared=all --with-mpm=prefork - env: | - APR_VERSION=1.7.x - APR_CONFIG="--enable-pool-debug" - APU_VERSION=1.7.x - APU_CONFIG="--with-crypto --with-ldap" - TEST_MALLOC=1 - TEST_SSL=1 - CLEAR_CACHE=1 # ------------------------------------------------------------------------- - name: litmus WebDAV tests config: --enable-dav --enable-dav-fs @@ -192,7 +226,6 @@ TEST_LDAP=1 TEST_ARGS="-defines LDAP" TESTS="t/modules/" - CLEAR_CACHE=1 # ------------------------------------------------------------------------- ### TODO: if: *condition_not_24x - name: APR trunk thread debugging @@ -201,40 +234,6 @@ APR_VERSION=trunk APR_CONFIG="--with-crypto --enable-thread-debug" # ------------------------------------------------------------------------- - - name: ASan - notest-cflags: -ggdb -fsanitize=address -fno-sanitize-recover=address -fno-omit-frame-pointer - config: --enable-mods-shared=reallyall - env: | - APR_VERSION=1.7.x - APU_VERSION=1.7.x - APU_CONFIG="--with-crypto --with-ldap" - TEST_ASAN=1 - CLEAR_CACHE=1 - # ------------------------------------------------------------------------- - - name: ASan, pool-debug - notest-cflags: -ggdb -fsanitize=address -fno-sanitize-recover=address -fno-omit-frame-pointer - config: --enable-mods-shared=reallyall - env: | - APR_VERSION=1.7.x - APR_CONFIG="--enable-pool-debug" - APU_VERSION=1.7.x - APU_CONFIG="--with-crypto --with-ldap" - TEST_ASAN=1 - CLEAR_CACHE=1 - # ------------------------------------------------------------------------- - - name: HTTP/2 test suite - config: --enable-mods-shared=reallyall --with-mpm=event --enable-mpms-shared=all - pkgs: curl python3-pytest nghttp2-client python3-cryptography python3-requests python3-multipart python3-filelock python3-websockets - env: | - APR_VERSION=1.7.6 - APU_VERSION=1.6.5 - APU_CONFIG="--with-crypto" - NO_TEST_FRAMEWORK=1 - TEST_INSTALL=1 - TEST_H2=1 - TEST_CORE=1 - TEST_PROXY=1 - # ------------------------------------------------------------------------- ### TODO: if: *condition_not_24x ### TODO: pebble install is broken. # - name: ACME test suite @@ -272,53 +271,58 @@ - name: OpenSSL 3.0 LTS config: --enable-mods-shared=most --enable-maintainer-mode --disable-md --disable-http2 --disable-ldap --disable-crypto env: | - TEST_OPENSSL3=3.0.21 + TEST_OPENSSL3=3.0.22 APR_VERSION=1.7.6 APU_VERSION=1.6.5 APU_CONFIG="--without-crypto" - pkgs: subversion + TEST_PYTEST=1 + PYTEST_ARGS=modules/ssl # ------------------------------------------------------------------------- - name: OpenSSL 3.4 -Werror config: --enable-mods-shared=most --enable-maintainer-mode --disable-md --disable-http2 --disable-ldap --disable-crypto notest-cflags: -Werror -O2 env: | - TEST_OPENSSL3=3.4.6 + TEST_OPENSSL3=3.4.8 APR_VERSION=1.7.6 APU_VERSION=1.6.5 APU_CONFIG="--without-crypto" - pkgs: subversion + TEST_PYTEST=1 + PYTEST_ARGS=modules/ssl # ------------------------------------------------------------------------- - name: OpenSSL 3.4 no-engine config: --enable-mods-shared=most --enable-maintainer-mode --disable-md --disable-http2 --disable-ldap --disable-crypto env: | - TEST_OPENSSL3=3.4.6 + TEST_OPENSSL3=3.4.8 OPENSSL_CONFIG=no-engine APR_VERSION=1.7.6 APU_VERSION=1.6.5 APU_CONFIG="--without-crypto" - pkgs: subversion + TEST_PYTEST=1 + PYTEST_ARGS=modules/ssl # ------------------------------------------------------------------------- - name: OpenSSL 3.5 no-engine -Werror config: --enable-mods-shared=most --enable-maintainer-mode --disable-md --disable-http2 --disable-ldap --disable-crypto notest-cflags: -Werror -O2 env: | - TEST_OPENSSL3=3.5.7 + TEST_OPENSSL3=3.5.9 OPENSSL_CONFIG=no-engine APR_VERSION=1.7.6 APU_VERSION=1.6.5 APU_CONFIG="--without-crypto" - pkgs: subversion + TEST_PYTEST=1 + PYTEST_ARGS=modules/ssl # ------------------------------------------------------------------------- - name: OpenSSL 4.0 config: --enable-mods-shared=most --enable-maintainer-mode --disable-md --disable-http2 --disable-ldap --disable-crypto notest-cflags: -Werror -O2 env: | - TEST_OPENSSL3=4.0.1 + TEST_OPENSSL3=4.0.3 OPENSSL_CONFIG= APR_VERSION=1.7.6 APU_VERSION=1.6.5 APU_CONFIG="--without-crypto" - pkgs: subversion + TEST_PYTEST=1 + PYTEST_ARGS=modules/ssl # ------------------------------------------------------------------------- runs-on: ${{ matrix.os == '' && 'ubuntu-latest' || matrix.os }} timeout-minutes: 30 @@ -327,48 +331,107 @@ CONFIG: ${{ matrix.config }} name: ${{ matrix.name }} steps: - # JOBID is used in the cache keys, created here as a hash of all - # properties of the environment, including the image OS version, - # compiler flags and any job-specific properties. + # JOBID names the artifacts uploaded on failure; see "Artifacts" in + # README.ci. IMAGEOS is exported so that it is visible to the env + # context, which does not see the runner's own $ImageOS. - name: Set environment variables + env: + MATRIX_NAME: ${{ matrix.name }} run: | echo "${{ matrix.env }}" >> $GITHUB_ENV - echo JOBID=`echo "OS=$ImageOS ${{ matrix.notest-cflags }} ${{ matrix.env }} ${{ matrix.config }}" \ - | md5sum - | sed 's/ .*//'` >> $GITHUB_ENV + echo "IMAGEOS=$ImageOS" >> $GITHUB_ENV + # The scripts run "make $MFLAGS"; default it to the number of + # CPUs the runner has, unless the matrix entry set it already. + grep -q '^MFLAGS=' $GITHUB_ENV || echo "MFLAGS=-j`nproc`" >> $GITHUB_ENV + # Use whichever php-fpm the runner image ships, so this doesn't + # break when the image moves to a new PHP version, unless the + # matrix entry set PHP_FPM already. + if ! grep -q '^PHP_FPM=' $GITHUB_ENV; then + PHP_FPM=`ls -1 /usr/sbin/php-fpm* 2>/dev/null | sort -V | tail -n1` + if test -z "$PHP_FPM"; then + echo "::error::No php-fpm found in /usr/sbin" + exit 1 + fi + echo "PHP_FPM=$PHP_FPM" >> $GITHUB_ENV + fi + echo JOBID=`printf '%s-%s' "${{ strategy.job-index }}" "$MATRIX_NAME" \ + | tr -cs '[:alnum:]._-' '-'` >> $GITHUB_ENV - name: apt refresh run: sudo apt-get -o Acquire::Retries=5 update + # cpanminus and perl-doc are only used to build the CPAN modules for + # the Perl test framework, and perl-doc drags in around a hundred + # packages. gdb is only used to extract a backtrace from any core + # dumped during testing, which includes the pytest suites. - name: Install prerequisites run: sudo apt-get install -o Acquire::Retries=5 - cpanminus libtool-bin libapr1-dev libaprutil1-dev - liblua5.3-dev libbrotli-dev libcurl4-openssl-dev - libnghttp2-dev libjansson-dev libpcre2-dev gdb - perl-doc libsasl2-dev ${{ matrix.pkgs }} - - uses: actions/checkout@v4 + libtool-bin libapr1-dev libaprutil1-dev + liblua5.3-dev libbrotli-dev libcurl4-openssl-dev + libnghttp2-dev libjansson-dev libpcre2-dev + ${{ ! ( env.SKIP_TESTING || env.NO_TEST_FRAMEWORK ) && 'cpanminus perl-doc' || '' }} + ${{ ! env.SKIP_TESTING && 'gdb' || '' }} + libsasl2-dev ${{ matrix.pkgs }} + - uses: actions/checkout@v6 - uses: actions/checkout@v6 with: repository: apache/httpd-tests path: test/perl-framework submodules: true if: ${{ ! ( env.SKIP_TESTING || env.NO_TEST_FRAMEWORK ) }} - - name: Cache installed libraries - uses: actions/cache@v4 + # Sets $ROOTID, identifying the contents of ~/root - left unset for + # a job which builds no dependencies from source - and $PERLID, + # identifying the perl which the ~/perl5 modules are built against. + - name: Resolve dependency versions + run: ./test/gha-resolve-deps.sh + # Restored here but saved only once the dependencies have been built + # successfully, below: a job which fails part-way through building + # them would otherwise save a half-built tree under a key which can + # never be updated. Bump the serial in the key to discard the cached + # builds and start afresh. + - name: Restore installed libraries + uses: actions/cache/restore@v5 + id: cache-libs + if: env.ROOTID != '' with: path: ~/root - key: cache-libs-${{ env.JOBID }} + key: cache-libs-1-${{ env.IMAGEOS }}-${{ runner.arch }}-${{ env.ROOTID }} + # ~/perl5 does not vary by job: the module list is fixed and the + # modules are always built with CC=gcc. It varies only by the perl + # ABI, which $PERLID identifies, and by the module list itself, so + # one cache is shared by every job. Bump the serial in the key to + # force a rebuild against current CPAN. - name: Cache CPAN modules - uses: actions/cache@v4 + uses: actions/cache@v5 + if: ${{ ! ( env.SKIP_TESTING || env.NO_TEST_FRAMEWORK ) }} with: path: ~/perl5 - key: cache-cpan-${{ env.JOBID }} + key: cache-cpan-1-${{ env.PERLID }}-${{ runner.arch }}-${{ hashFiles('test/travis_before_linux.sh') }} - name: Configure environment run: ./test/travis_before_linux.sh timeout-minutes: 15 + - name: Save installed libraries + uses: actions/cache/save@v5 + if: success() && env.ROOTID != '' && steps.cache-libs.outputs.cache-hit != 'true' + with: + path: ~/root + key: ${{ steps.cache-libs.outputs.cache-primary-key }} - uses: actions/upload-artifact@v7 if: failure() with: name: config.log-${{ env.JOBID }} path: | /home/runner/build/**/config.log + - name: Install uv + if: env.TEST_PYTEST == '1' + run: | + pipx install uv + echo "$HOME/.local/bin" >> $GITHUB_PATH + - name: Install pebble + if: env.TEST_MD == '1' + run: | + go install github.com/letsencrypt/pebble/v2/cmd/pebble@latest \ + && go install github.com/letsencrypt/pebble/v2/cmd/pebble-challtestsrv@latest \ + && echo "$(go env GOPATH)/bin" >> $GITHUB_PATH \ + || echo "::warning::pebble install failed; modules/md tests will be skipped" - name: Build and test run: ./test/travis_run_linux.sh - uses: actions/upload-artifact@v7 @@ -378,3 +441,6 @@ path: | **/config.log test/perl-framework/t/logs/error_log + test/pytest_suite/t/logs/error_log + test/gen/apache/logs/error_log + test/gen/failed-tests/** diff -Nru apache2-2.4.69/.github/workflows/sanity.yml apache2-2.4.71/.github/workflows/sanity.yml --- apache2-2.4.69/.github/workflows/sanity.yml 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/.github/workflows/sanity.yml 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,55 @@ +# Quick checks against the source which need nothing built, and so do +# not belong in the build matrix: there they would spend twenty seconds +# installing packages to run a one second check. Everything here is +# sequential and quick, so the 1-vCPU ubuntu-slim runner (15 minute +# job limit) is enough. + +name: Sanity + +on: + push: + branches: [ "*" ] + paths-ignore: + - STATUS + - CHANGES + - README* + - '**.md' + - changes-entries/* + - .asf.yaml + pull_request: + branches: [ "trunk", "2.4.x" ] + paths-ignore: + - STATUS + - CHANGES + - README* + - '**.md' + - changes-entries/* + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + aplogno: + runs-on: ubuntu-slim + timeout-minutes: 5 + steps: + - uses: actions/checkout@v6 + - name: Check APLOGNO() macros + run: ./test/travis_run_linux.sh + env: + TEST_LOGNO: 1 + + docs: + runs-on: ubuntu-slim + timeout-minutes: 5 + steps: + - uses: actions/checkout@v6 + - name: Install prerequisites + run: sudo apt-get -o Acquire::Retries=5 update && + sudo apt-get install -o Acquire::Retries=5 libxml2-utils w3c-sgml-lib + - name: Check docs XML + run: ./test/gha-docs-check.sh diff -Nru apache2-2.4.69/.github/workflows/windows.yml apache2-2.4.71/.github/workflows/windows.yml --- apache2-2.4.69/.github/workflows/windows.yml 2026-07-17 08:23:35.000000000 +0000 +++ apache2-2.4.71/.github/workflows/windows.yml 2026-11-09 03:07:05.000000000 +0000 @@ -10,6 +10,13 @@ - README* - '**.md' - changes-entries/* + - 'modules/arch/unix/**' + - 'os/unix/**' + - server/mpm_unix.c + - 'test/modules/arch/linux/**' + - 'test/travis_*.sh' + - 'test/gha-*.sh' + - .asf.yaml tags: - 2.* pull_request: @@ -21,6 +28,15 @@ - README* - '**.md' - changes-entries/* + - 'modules/arch/unix/**' + - 'os/unix/**' + - server/mpm_unix.c + - 'test/modules/arch/linux/**' + - 'test/travis_*.sh' + - 'test/gha-*.sh' + +permissions: + contents: read concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -33,13 +49,13 @@ matrix: include: - name: Default - triplet: x64-windows + triplet: x64-windows-release arch: x64 - build-type: Debug + build-type: Release generator: "Ninja" runs-on: windows-latest - timeout-minutes: 30 + timeout-minutes: 40 name: ${{ matrix.name }} env: VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" @@ -60,19 +76,57 @@ core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - name: Install dependencies - run: vcpkg install --triplet ${{ matrix.triplet }} apr[private-headers] apr-util pcre2 openssl + run: vcpkg install --triplet ${{ matrix.triplet }} apr[private-headers] apr-util pcre2 openssl nghttp2 curl libxml2 jansson - uses: actions/checkout@v6 - name: Configure CMake run: | cmake --version - cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} ` + cmake -B ${{github.workspace}}/build ` -G "${{ matrix.generator }}" ` + -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} ` -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake ` + -DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} ` -DAPR_INCLUDE_DIR=C:/vcpkg/installed/${{ matrix.triplet }}/include ` - "-DAPR_LIBRARIES=C:/vcpkg/installed/${{ matrix.triplet }}/lib/libapr-1.lib;C:/vcpkg/installed/${{ matrix.triplet }}/lib/libaprutil-1.lib" + "-DAPR_LIBRARIES=C:/vcpkg/installed/${{ matrix.triplet }}/lib/libapr-1.lib;C:/vcpkg/installed/${{ matrix.triplet }}/lib/libaprutil-1.lib" ` + -DNGHTTP2_INCLUDE_DIR=C:/vcpkg/installed/${{ matrix.triplet }}/include ` + -DNGHTTP2_LIBRARIES=C:/vcpkg/installed/${{ matrix.triplet }}/lib/nghttp2.lib ` + -DJANSSON_INCLUDE_DIR=C:/vcpkg/installed/${{ matrix.triplet }}/include ` + -DJANSSON_LIBRARIES=C:/vcpkg/installed/${{ matrix.triplet }}/lib/jansson.lib ` + -DENABLE_MIME_MAGIC=A ` + -DBUILD_TEST_MODULES=true - name: Build run: | - cmake --build ${{github.workspace}}/build --config ${{ matrix.build-type }} + cmake --build ${{github.workspace}}/build --config ${{ matrix.build-type }} -- -j 1 + + - name: Install + run: | + cmake --install ${{github.workspace}}/build --config ${{ matrix.build-type }} + + - name: Install Tools for tests + env: + VCPKG_DIR: C:\vcpkg + VCPKG_TRIPLET: ${{ matrix.triplet }} + run: | + ${{github.workspace}}\test\pyhttpd\install_tools.bat "C:\Program Files (x86)\HTTPD" + + - name: Check httpd + run: | + $env:PATH = "C:\Program Files (x86)\HTTPD\bin;C:\vcpkg\installed\x64-windows\bin;$env:PATH" + & "C:\Program Files (x86)\HTTPD\bin\httpd.exe" -V + & "C:\Program Files (x86)\HTTPD\bin\httpd.exe" -t -f "C:\Program Files (x86)\HTTPD\conf\httpd.conf" + + - name: Test httpd + run: | + ${{github.workspace}}\test\runtests.bat --httpd "C:\Program Files (x86)\HTTPD\bin\httpd.exe" --conf "C:\Program Files (x86)\HTTPD\conf\httpd.conf" modules -vvvv -x + + - name: Upload test logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-logs + path: | + C:\Program Files (x86)\HTTPD\logs\ + ${{github.workspace}}\test\gen\apache\logs\ diff -Nru apache2-2.4.69/.gitignore apache2-2.4.71/.gitignore --- apache2-2.4.69/.gitignore 2023-01-09 07:35:18.000000000 +0000 +++ apache2-2.4.71/.gitignore 2026-11-09 03:07:05.000000000 +0000 @@ -7,6 +7,7 @@ *.lo *.la *.slo +*.gcda *.so *.vcproj *.vcproj.* @@ -57,6 +58,7 @@ /.kdev_include_paths /.cproject /.project +/dist/ # /built/ /built/ @@ -368,3 +370,6 @@ test/pyhttpd/config.ini __pycache__ +# make check +check +build/config_vars.out diff -Nru apache2-2.4.69/CHANGES apache2-2.4.71/CHANGES --- apache2-2.4.69/CHANGES 2026-09-28 13:14:11.000000000 +0000 +++ apache2-2.4.71/CHANGES 2026-11-09 03:07:05.000000000 +0000 @@ -1,6 +1,257 @@ -*- coding: utf-8 -*- +Changes with Apache 2.4.71 + + *) mod_md: MDHttpProxy and MDCACertificateFile can now be set per MDomain. + New directive MDHttpProxyCACertificateFile sets the CA certificates + used to verify an HTTPS proxy. + [Michael Kaufmann ] + + *) mod_deflate: Read the gzip extra-field (FEXTRA) length as unsigned so a + length byte with the high bit set is no longer sign-extended, fixing + inflation of gzip streams that carry an extra field. + [arshiya tabasum ] + + *) mod_remoteip: Apply RemoteIP{Trusted,Internal}ProxyList to the virtual host + they are configured in, and merge the proxy entries of the main server + and of the virtual host, logging a warning when both have some. + PR 70207. [Arturo Bernal] + + *) mod_dav: Fix handling of invalid or overflowing "Timeout:" + request headers, which could silently grant a lock with an + already-expired timeout instead of the RFC 4918 maximum. + PR 65788 [Arturo Bernal] + + *) mod_http2: Correctly handle the asterisk-form of OPTIONS requests + instead of rejecting them as malformed. PR 70195. + [Arturo Bernal ] + + *) mod_http2: Fix duplicate logging of header limit violations. + [Michael Kaufmann ] + + *) mod_http2: Fix a crash creating an HTTP/2 session with + "H2PushDiarySize 0" and debug logging enabled. [Joe Orton] + + *) mod_dav_lock: Fix dav_generic_load_lock_record() reading the + indirect-lock key length with the wrong width, which could read + back a wrong length on big-endian platforms. PR 70299 + [Arturo Bernal] + + *) mod_remoteip: Log a connection closed before a complete PROXY protocol + header was received at info level rather than as an error. PR 63893. + [Joe Orton] + +Changes with Apache 2.4.70 + + *) mod_authnz_ldap: Fix crashes in 2.4.69 reported on Windows. + PR 70261. [Eric Covener] + + *) mod_cgid: Increase limits for number and length of + environment variables, URI and arguments. PR 70270. + [Emmanuel Dreyfus , Joe Orton] + + *) mod_substitute: Fix an integer overflow that can happen in very special + setups. PR 66034 [Ruediger Pluem] + + *) mod_cache: Do not store a response whose "Vary: *" was set via + "Header always". PR 70289 [Arturo Bernal] + + *) mod_dav_lock: Fix a heap buffer overflow in dav_size_indirect(), which + under-reserved space for the stored key length field, written as + apr_size_t but sized as int. PR 70298 [Arturo Bernal] + Changes with Apache 2.4.69 + *) SECURITY: CVE-2026-93546: mod_dav_fs namespace overflow + (cve.mitre.org) + Integer overflow in mod_dav_fs in Apache HTTP Server through + 2.4.68 allows an authenticated WebDAV client with write access + to crash worker processes and persistently corrupt a directory's + property database via PROPPATCH requests declaring many XML + namespaces. + Credits: Zhen Kong + + *) SECURITY: CVE-2026-79768: mod_userdir information disclosure + (cve.mitre.org) + Path equivalence: '/./' (single dot directory) vulnerability in + Apache HTTP Server's mod_userdir module when configured + with absolute non-wildcard UserDir directive (the 2nd form + in https://httpd.apache.org/docs/2.4/mod/mod_userdir.html#userdir) + This issue affects Apache HTTP Server: from 2.4.0 through 2.4.68. + Credits: Vlatko Kosturjak, Marlink Cyber + + *) SECURITY: CVE-2026-73637: mod_auth_digest DoS attack + (cve.mitre.org) + Use after free in mod_auth_digest in Apache Software Foundation + Apache HTTP Server before 2.4.69 on all platforms allows an + unauthenticated remote client to cause authentication state + corruption via concurrent Digest authentication requests when + AuthDigestNcCheck is enabled or AuthDigestNonceLifetime is set + to 0. + Users are recommended to upgrade to version 2.4.69, which fixes + this issue. + Credits: Zhen Kong + + *) SECURITY: CVE-2026-73636: mod_auth_digest one-time-nonce replay + attack (cve.mitre.org) + Authentication bypass by capture-replay in mod_auth_digest in + Apache Software Foundation Apache HTTP Server 2.4.x on all + platforms allows a man-in-the-middle (MITM) attacker to replay + captured digest authentication credentials via crafted requests + that trigger garbage collection of the client's shared memory + entry when AuthDigestNonceLifetime is set to 0. + Users are recommended to upgrade to version 2.4.69, which fixes + this issue. + Credits: Hyojae Lee + + *) SECURITY: CVE-2026-63718: mod_proxy_uwsgi Transfer-Encoding + response smuggling (cve.mitre.org) + Inconsistent Interpretation of HTTP Requests ('HTTP + Request/Response Smuggling') response smuggling vulnerability in + Apache HTTP Server via mod_proxy_uwsgi and a crafted uwsgi + response with Transfer-Encoding. + This issue affects Apache HTTP Server: from 2.4.30 through + 2.4.68. + Credits: Qing Xu + + *) SECURITY: CVE-2026-63686: mod_xml2enc crash on charset + conversion failure (cve.mitre.org) + A NULL pointer dereference in mod_xml2enc in Apache Software + Foundation Apache HTTP Server before 2.4.69 on all platforms + allows an untrusted backend server to cause a denial of service + via a proxied response with a charset whose conversion partially + succeeds then fails. + Users are recommended to upgrade to version 2.4.69, which fixes + this issue. + Credits: Lucian Nitescu + + *) SECURITY: CVE-2026-63292: mod_vhost_alias stack overflow + (cve.mitre.org) + Stack-based buffer overflow in mod_vhost_alias in Apache + Software Foundation Apache HTTP Server through 2.4.68 on all + platforms allows a remote client to cause a denial of service or + potentially execute arbitrary code via an HTTP request with a + Host header exceeding 8192 bytes when VirtualDocumentRoot uses a + hostname format specifier and LimitRequestFieldSize is raised + above the default. + Users are recommended to upgrade to version 2.4.69, which fixes + this issue. + Credits: Hyojae Lee + + *) SECURITY: CVE-2026-63045: mod_proxy_ftp PASV address handling + (cve.mitre.org) + Improper validation of FTP PASV reply address in mod_proxy_ftp + in Apache Software Foundation Apache HTTP Server through 2.4.68 + on all platforms allows, in forward proxy configurations, an + untrusted FTP server to cause the proxy to open a data + connection to an arbitrary third-party host via a crafted PASV + response. + Users are recommended to upgrade to version 2.4.69, which fixes + this issue. + Credits: Zhen Kong + + *) SECURITY: CVE-2026-59797: mod_ssl SSLRequire allows .htaccess + ap_expr file-function (cve.mitre.org) + Improper Privilege Management vulnerability in Apache HTTP + Server's mod_ssl via SSLRequire and file-related expressions. + This issue affects Apache HTTP Server: from 2.4.0 through 2.4.68. + Credits: kimchunbok + + *) SECURITY: CVE-2026-59685: Out-of-Bounds Write in + ap_directory_walk() Canonical-Name Rewrite on + CASE_BLIND_FILESYSTEM (cve.mitre.org) + Out-of-bounds Write vulnerability in Apache HTTP Server on + Windows while processing paths with 8.3 names that may grow when + expanded. + This issue affects Apache HTTP Server: from 2.4.0 through 2.4.68. + Credits: Dhiraj Mishra + + *) SECURITY: CVE-2026-58415: mod_dav_fs property database read + access (cve.mitre.org) + Internal state files accessible to external parties in + mod_dav_fs in Apache Software Foundation Apache HTTP Server + before 2.4.69 on all platforms allows a remote client to read + WebDAV dead properties of resources it cannot author via a GET + request for the .DAV state directory + This issue affects Apache HTTP Server: from 2.4.0 through 2.4.68. + Credits: ì�´ì§€ì›… (kimchunbok) + + *) SECURITY: CVE-2026-57941: mod_http2 use-after-free / wild write + via shared session->bbtmp re-entrancy (cve.mitre.org) + Use After Free vulnerability in Apache HTTP Server's + mod_http2 via shared session->bbtmp re-entrancy + This issue affects Apache HTTP Server: from 2.4.0 through 2.4.68. + Credits: Lucian Nitescu + + *) SECURITY: CVE-2026-56449: mod_proxy_html: crash in dump_content + (cve.mitre.org) + Out-of-bounds Write vulnerability in Apache HTTP Server's + mod_proxy_html with crafted HTTP response bodies. + This issue affects Apache HTTP Server: from 2.4.0 through 2.4.68. + Credits: Lucian Nitescu + + *) SECURITY: CVE-2026-56154: mod_rewrite use-after-free via + %{LA-U:HTTP:...} (cve.mitre.org) + Use After Free vulnerability in Apache HTTP Server's mod_rewrite + when using lookahead (%{LA-U:HTTP:...}) + This issue affects Apache HTTP Server: from 2.4.0 through 2.4.68. + Credits: Nebula Security (@nebusecurity) + + *) SECURITY: CVE-2026-56153: mod_charset_lite: Heap overflow in + finish_partial_char (cve.mitre.org) + Out-of-bounds Write vulnerability in Apache HTTP Server's + mod_charset_lite. + This issue affects Apache HTTP Server: from 2.4.0 through 2.4.68. + Credits: Bartlomiej Dmitruk at striga.ai + + *) SECURITY: CVE-2026-48005: mod_auth_digest reauthentication + attack (cve.mitre.org) + Missing authentication checks in mod_auth_digest in Apache + Software Foundation Apache HTTP Server before 2.4.69 on all + platforms allows an unauthenticated remote client to cause a + denial of service (forced re-authentication) via forged + Authorization headers when Digest authentication is enabled with + AuthDigestNcCheck . + Users are recommended to upgrade to version 2.4.69, which fixes + this issue. + Credits: lokerxx + + *) SECURITY: CVE-2026-47360: mod_session: Session cookie not + removed during internal redirect (cve.mitre.org) + Exposure of Sensitive Information to an Unauthorized Actor + vulnerability in Apache HTTP Server's mod_session_cookie module. + + When SessionCookieRemove changes across internal redirects, the + session cookie may still be passed to a backend server. + This issue affects Apache HTTP Server: from 2.4.0 through 2.4.68. + Credits: lokerxx + + *) SECURITY: CVE-2026-46729: mod_heartmonitor denial of service + (cve.mitre.org) + NULL Pointer Dereference vulnerability in Apache HTTP Servers + mod_heartmonitor over unicast listener. + This issue affects Apache HTTP Server: from 2.4.0 through 2.4.68. + Credits: Zhang San + + *) SECURITY: CVE-2026-42528: mod_dav shared lock overflow + (cve.mitre.org) + A memory calculation bug in mod_dav in Apache httpd 2.4.67 and + earlier allows an attacker with permission to create WebDAV + locks to crash server child processes. + Users are recommended to upgrade to version 2.4.69, which fixes + this issue + Credits: Zhenpeng (Leo) Lin at depthfirst + + *) SECURITY: CVE-2026-42356: limited RCE for some internal + redirects to non-CGI files in CGI directories (cve.mitre.org) + Deployment of wrong handler vulnerability in Apache HTTP Server + allows the target of some internal redirects from CGI programs + to also be treated as CGI and executed. The target must already + be in a directory enabled for CGI and have no other extension + understood by mod_mime. + This issue affects Apache HTTP Server: from 2.4.60 through + 2.4.68. + Credits: Feliks Penconek + *) Fix the tar icon in the documentation so that its background is transparent. PR 70238. [Jeffery To ] diff -Nru apache2-2.4.69/CMakeLists.txt apache2-2.4.71/CMakeLists.txt --- apache2-2.4.69/CMakeLists.txt 2026-09-28 16:36:07.000000000 +0000 +++ apache2-2.4.71/CMakeLists.txt 2026-11-09 03:07:05.000000000 +0000 @@ -392,6 +392,7 @@ "modules/proxy/mod_proxy_http+I+Apache proxy HTTP module. Requires and is enabled by --enable-proxy." "modules/proxy/mod_proxy_hcheck+I+Apache proxy health check module. Requires and is enabled by --enable-proxy." "modules/proxy/mod_proxy_scgi+I+Apache proxy SCGI module. Requires and is enabled by --enable-proxy." + "modules/proxy/mod_proxy_uwsgi+I+Apache proxy uWSGI module. Requires and is enabled by --enable-proxy." "modules/proxy/mod_proxy_wstunnel+I+Apache proxy Websocket Tunnel module. Requires and is enabled by --enable-proxy." "modules/http2/mod_proxy_http2+i+Apache proxy HTTP/2 module. Requires --enable-proxy." "modules/session/mod_session+I+session module" @@ -588,6 +589,7 @@ SET(mod_proxy_html_extra_libs "${LIBXML2_LIBRARIES};${LIBXML2_ICONV_LIBRARIES}") ENDIF() SET(mod_proxy_scgi_extra_libs mod_proxy) +SET(mod_proxy_uwsgi_extra_libs mod_proxy) SET(mod_proxy_wstunnel_extra_libs mod_proxy) SET(mod_proxy_http2_requires NGHTTP2_FOUND) SET(mod_proxy_http2_extra_defines ssize_t=long) diff -Nru apache2-2.4.69/Makefile.in apache2-2.4.71/Makefile.in --- apache2-2.4.69/Makefile.in 2025-07-07 10:18:22.000000000 +0000 +++ apache2-2.4.71/Makefile.in 2026-11-09 03:07:05.000000000 +0000 @@ -18,6 +18,9 @@ install-other install-cgi install-include install-suexec install-build \ install-man +CLEAN_TARGETS = check/bin/* check/build/config_vars.mk \ + check/conf/$(PROGRAM_NAME).conf check/conf/magic check/conf/mime.types \ + check/conf/extra/* check/include/* DISTCLEAN_TARGETS = include/ap_config_auto.h include/ap_config_layout.h \ include/apache_probes.h \ modules.c config.cache config.log config.status build/config_vars.mk \ @@ -25,6 +28,7 @@ build/pkg/pkginfo build/config_vars.sh EXTRACLEAN_TARGETS = configure include/ap_config_auto.h.in generated_lists \ httpd.spec +PHONY_TARGETS := check-conf check-dirs check-include check-all-pytest include $(top_builddir)/build/rules.mk include $(top_srcdir)/build/program.mk @@ -156,23 +160,23 @@ fi update-changes: - @find changes-entries -type f | while read -r i ; do \ + @find changes-entries -type f ! -name template.txt | while read -r i ; do \ cp CHANGES CHANGES.tmp ; \ awk -v fname="$$i" 'BEGIN{done = 0; active = 0} \ done == 0 && active == 0 && /^Changes with Apache /{ active = 1; print; next}; \ /^( *\*|Changes with Apache )/ && active == 1 && done == 0{rec=$$0; while((getline 0){if (! ($$0 ~ /^ *$$/)){print}}printf "\n";print rec; active = 0; done = 1; next} //;' \ CHANGES.tmp > CHANGES ; \ rm CHANGES.tmp ; \ - if [ -n "$(SVN)" ] ; then \ - if ! $(SVN) rm "$$i" 2>/dev/null ; then \ + if [ -n "$(GIT)" ] ; then \ + if ! $(GIT) rm "$$i" 2>/dev/null ; then \ $(RM) "$$i" ; \ fi ; \ else \ $(RM) "$$i" ; \ fi ; \ done ; \ - if [ -n "$(SVN)" ] ; then \ - $(SVN) diff CHANGES ; \ + if [ -n "$(GIT)" ] ; then \ + $(GIT) diff -- CHANGES ; \ fi validate-xml: @@ -316,3 +320,174 @@ # XXX: This looks awfully platform-specific [read: bad form and style] include $(top_srcdir)/os/os2/core.mk + +# +# In-tree Testing +# + +# Installs the include/ header files into the ./check/ directory so we can use +# them during `make check`. +check-include: override includedir:=$(top_builddir)/check/include +check-include: install-include + +# This is basically the same as install-conf, but we only care about the files +# necessary to run the test suite, and the LoadModule lines point back to the +# build tree. Configuration files will be installed to ./check/conf and pointed +# to by ./check/apxs. +check-conf: + @mkdir -p check/conf/extra; \ + cd $(top_srcdir)/docs/conf; \ + for i in mime.types magic; do \ + $(INSTALL_DATA) $$i $(top_builddir)/check/conf; \ + done; \ + for j in $(top_srcdir)/docs/conf $(top_builddir)/docs/conf ; do \ + cd $$j ; \ + for i in httpd.conf extra/*.conf; do \ + if [ -f $$i ] ; then \ + ( \ + n_lm=`awk 'BEGIN {n=0} /@@LoadModule@@/ {n+=1} END {print n}' < $$i`; \ + if test $$n_lm -eq 0 -o "x$(MPM_MODULES)$(DSO_MODULES)" = "x"; then \ + sed -e 's#@@ServerRoot@@#$(top_builddir)/check#g' \ + -e 's#@@Port@@#$(PORT)#g' \ + -e 's#@@SSLPort@@#$(SSLPORT)#g' \ + -e 's#$(prefix)#$(top_builddir)/check#g' \ + -e 's/^#ServerName /ServerName /' \ + -e '/@@LoadModule@@/d' \ + < $$i; \ + else \ + sed -n -e '/@@LoadModule@@/q' \ + -e 's#@@ServerRoot@@#$(top_builddir)/check#g' \ + -e 's#@@Port@@#$(PORT)#g' \ + -e 's#@@SSLPort@@#$(SSLPORT)#g' \ + -e 's#$(prefix)#$(top_builddir)/check#g' \ + -e 's/^#ServerName /ServerName /' \ + -e 'p' \ + < $$i; \ + if echo " $(DSO_MODULES) "|$(EGREP) " cgi " > /dev/null ; then \ + have_cgi="1"; \ + else \ + have_cgi="0"; \ + fi; \ + if echo " $(DSO_MODULES) "|$(EGREP) " cgid " > /dev/null ; then \ + have_cgid="1"; \ + else \ + have_cgid="0"; \ + fi; \ + for j in $(MPM_MODULES) "^EOL^"; do \ + path=$$(find "$(top_builddir)/server" -name "mod_$$j.so"); \ + if test $$j != "^EOL^"; then \ + if echo ",$(ENABLED_MPM_MODULE),"|$(EGREP) ",$$j," > /dev/null ; then \ + loading_disabled=""; \ + else \ + loading_disabled="#"; \ + fi; \ + echo "$${loading_disabled}LoadModule $${j}_module $${path}"; \ + fi; \ + done; \ + for j in $(DSO_MODULES) "^EOL^"; do \ + path=$$(find "$(top_builddir)/modules" -name "mod_$$j.so"); \ + if test $$j != "^EOL^"; then \ + if test $$j = "cgid" -a "$$have_cgi" = "1"; then \ + echo ""; \ + echo " LoadModule $${j}_module $${path}"; \ + echo ""; \ + elif test $$j = "cgi" -a "$$have_cgid" = "1"; then \ + echo ""; \ + echo " LoadModule $${j}_module $${path}"; \ + echo ""; \ + else \ + echo "LoadModule $${j}_module $${path}"; \ + fi; \ + fi; \ + done; \ + sed -e '1,/@@LoadModule@@/d' \ + -e '/@@LoadModule@@/d' \ + -e 's#@@ServerRoot@@#$(top_builddir)/check#g' \ + -e 's#@@Port@@#$(PORT)#g' \ + -e 's#@@SSLPort@@#$(SSLPORT)#g' \ + -e 's#$(prefix)#$(top_builddir)/check#g' \ + -e 's/^#ServerName /ServerName /' \ + < $$i; \ + fi \ + ) > $(top_builddir)/check/conf/$$i; \ + chmod 0644 $(top_builddir)/check/conf/$$i; \ + if [ "$$i" = "httpd.conf" ]; then \ + mv "$(top_builddir)/check/conf/$$i" "$(top_builddir)/check/conf/$(PROGRAM_NAME).conf"; \ + fi; \ + fi; \ + done ; \ + done ; + +# Sometimes (e.g. when MPM DSOs are enabled) the test suite runs a full +# configuration check on our in-tree build. To pass, we have to have all of the +# correct directories, even though they won't be used. +check-dirs: + @mkdir -p check/htdocs check/logs check/bin + +# EXTRA_CHECK_BINARIES is set by configure for optional support programs +# (e.g. check/bin/a2md when a2md is enabled). +check-binaries: check/bin/apxs check/bin/httpd check/bin/ab \ + check/bin/htcacheclean check/bin/httxt2dbm check/bin/htpasswd \ + check/bin/htdigest check/bin/htdbm check/bin/logresolve \ + $(EXTRA_CHECK_BINARIES) + +check/bin/httpd: + ln -s $(top_builddir)/$(PROGRAM_NAME) check/bin/httpd + +check/bin/ab: + ln -s $(top_builddir)/support/ab check/bin/ab + +check/bin/htpasswd: + ln -s $(top_builddir)/support/htpasswd check/bin/htpasswd + +check/bin/htdigest: + ln -s $(top_builddir)/support/htdigest check/bin/htdigest + +check/bin/htdbm: + ln -s $(top_builddir)/support/htdbm check/bin/htdbm + +check/bin/logresolve: + ln -s $(top_builddir)/support/logresolve check/bin/logresolve + +check/bin/htcacheclean: + ln -s $(top_builddir)/support/htcacheclean check/bin/htcacheclean + +check/bin/httxt2dbm: + ln -s $(top_builddir)/support/httxt2dbm check/bin/httxt2dbm + +# A version of apxs with the PREFIX overridden to point inside the install root +check/bin/apxs: + mkdir -p check + sed -e 's#$(prefix)#$(top_builddir)/check#g' support/apxs > $@ + chmod +x $@ + +# A version of apachectl with the PREFIX overridden to point inside the install root +check/bin/apachectl: + mkdir -p check + sed -e 's#$(prefix)#$(top_builddir)/check#g' support/apachectl > $@ + chmod +x $@ + +# A version of config_vars.mk with PREFIX and SBINDIR and BINDIR overridden. +check/build/config_vars.mk: build/config_vars.out + mkdir -p check/build + cp $< $@ + echo "prefix = $(top_builddir)/check" >> $@ + echo "sbindir = $(top_builddir)/check/bin" >> $@ + echo "bindir = $(top_builddir)/check/bin" >> $@ + +check-tree: check-include check-dirs check-conf check/build/config_vars.mk \ + check-binaries + +# Generate config.ini suitable for running against the check/ install prefix +check/config.ini: test/pyhttpd/config.ini + sed -e 's#$(prefix)#$(top_builddir)/check#g' test/pyhttpd/config.ini > $@ + +check/modules/stamp: build/config_vars.mk + mkdir -p check/modules + rm -f check/modules/*.so + find modules -name \*.so -exec ln -s $(top_builddir)/{} check/modules/ \; + find server/mpm -name \*.so -exec ln -s $(top_builddir)/{} check/modules/ \; + date > $@ + +check-all-pytest: check-tree check/bin/apachectl check/config.ini check/modules/stamp + PYHTTPD_CONFIG=$(top_builddir)/check/config.ini $(SHELL) $(top_srcdir)/test/runtests.sh --apxs="$(top_builddir)/check/bin/apxs" $(PYTEST_ARGS) diff -Nru apache2-2.4.69/ROADMAP apache2-2.4.71/ROADMAP --- apache2-2.4.69/ROADMAP 2020-02-21 00:33:40.000000000 +0000 +++ apache2-2.4.71/ROADMAP 2026-11-09 03:07:05.000000000 +0000 @@ -1,6 +1,6 @@ APACHE 2.x ROADMAP ================== -Last modified at [$Date: 2020-02-20 19:33:40 -0500 (Thu, 20 Feb 2020) $] +Last modified at [$Date$] WORKS IN PROGRESS diff -Nru apache2-2.4.69/VERSIONING apache2-2.4.71/VERSIONING --- apache2-2.4.69/VERSIONING 2020-02-21 00:33:40.000000000 +0000 +++ apache2-2.4.71/VERSIONING 2026-11-09 03:07:05.000000000 +0000 @@ -1,6 +1,6 @@ APACHE 2.x VERSIONING ===================== -[$LastChangedDate: 2020-02-20 19:33:40 -0500 (Thu, 20 Feb 2020) $] +[$LastChangedDate$] INTRODUCTION diff -Nru apache2-2.4.69/acinclude.m4 apache2-2.4.71/acinclude.m4 --- apache2-2.4.69/acinclude.m4 2021-12-13 19:30:46.000000000 +0000 +++ apache2-2.4.71/acinclude.m4 2026-11-09 03:07:05.000000000 +0000 @@ -79,7 +79,7 @@ APACHE_SUBST(LIBTOOL) APACHE_SUBST(SHELL) APACHE_SUBST(RSYNC) - APACHE_SUBST(SVN) + APACHE_SUBST(GIT) APACHE_SUBST(MODULE_DIRS) APACHE_SUBST(MODULE_CLEANDIRS) APACHE_SUBST(PORT) diff -Nru apache2-2.4.69/changes-entries/template.txt apache2-2.4.71/changes-entries/template.txt --- apache2-2.4.69/changes-entries/template.txt 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/changes-entries/template.txt 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,3 @@ + *) module: ... + ... + PR ... [author] diff -Nru apache2-2.4.69/configure apache2-2.4.71/configure --- apache2-2.4.69/configure 2026-09-29 12:44:25.000000000 +0000 +++ apache2-2.4.71/configure 2026-11-09 03:18:44.000000000 +0000 @@ -865,7 +865,7 @@ RANLIB LN_S AWK -SVN +GIT RSYNC PKGCONFIG RM @@ -7489,17 +7489,17 @@ fi -# Extract the first word of "svn", so it can be a program name with args. -set dummy svn; ac_word=$2 +# Extract the first word of "git", so it can be a program name with args. +set dummy git; ac_word=$2 { printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_path_SVN+y} +if test ${ac_cv_path_GIT+y} then : printf %s "(cached) " >&6 else case e in #( - e) case $SVN in + e) case $GIT in [\\/]* | ?:[\\/]*) - ac_cv_path_SVN="$SVN" # Let the user override the test with a path. + ac_cv_path_GIT="$GIT" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -7513,7 +7513,7 @@ esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_path_SVN="$as_dir$ac_word$ac_exec_ext" + ac_cv_path_GIT="$as_dir$ac_word$ac_exec_ext" printf '%s\n' "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi @@ -7525,10 +7525,10 @@ esac ;; esac fi -SVN=$ac_cv_path_SVN -if test -n "$SVN"; then - { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $SVN" >&5 -printf '%s\n' "$SVN" >&6; } +GIT=$ac_cv_path_GIT +if test -n "$GIT"; then + { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $GIT" >&5 +printf '%s\n' "$GIT" >&6; } else { printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf '%s\n' "no" >&6; } @@ -42352,7 +42352,7 @@ - APACHE_VAR_SUBST="$APACHE_VAR_SUBST SVN" + APACHE_VAR_SUBST="$APACHE_VAR_SUBST GIT" diff -Nru apache2-2.4.69/configure.in apache2-2.4.71/configure.in --- apache2-2.4.69/configure.in 2026-06-01 13:02:06.000000000 +0000 +++ apache2-2.4.71/configure.in 2026-11-09 03:07:05.000000000 +0000 @@ -395,7 +395,7 @@ AC_PATH_PROG(RM, rm) AC_PATH_PROG(PKGCONFIG, pkg-config) AC_PATH_PROG(RSYNC, rsync) -AC_PATH_PROG(SVN, svn) +AC_PATH_PROG(GIT, git) AC_PROG_AWK AC_PROG_LN_S AC_CHECK_TOOL(RANLIB, ranlib, true) diff -Nru apache2-2.4.69/debian/changelog apache2-2.4.71/debian/changelog --- apache2-2.4.69/debian/changelog 2026-10-01 16:54:08.000000000 +0000 +++ apache2-2.4.71/debian/changelog 2026-11-11 12:54:19.000000000 +0000 @@ -1,3 +1,11 @@ +apache2 (2.4.71-1~deb13u1) trixie-security; urgency=medium + + * New upstream release + (Closes: #1153732, CVE-2026-119618, CVE-2026-106023, CVE-2026-109163) + * Unfuzz patches + + -- Xavier Guimard Wed, 11 Nov 2026 13:54:19 +0100 + apache2 (2.4.69-1~deb13u1) trixie-security; urgency=medium * New upstream version (Closes: CVE-2026-42356, CVE-2026-42528, diff -Nru apache2-2.4.69/debian/patches/build_suexec-custom.patch apache2-2.4.71/debian/patches/build_suexec-custom.patch --- apache2-2.4.69/debian/patches/build_suexec-custom.patch 2025-12-05 05:18:23.000000000 +0000 +++ apache2-2.4.71/debian/patches/build_suexec-custom.patch 2026-11-11 12:53:58.000000000 +0000 @@ -4,7 +4,7 @@ Last-Update: 2012-02-25 --- a/Makefile.in +++ b/Makefile.in -@@ -293,23 +293,26 @@ +@@ -297,23 +297,26 @@ install-suexec: install-suexec-$(INSTALL_SUEXEC) install-suexec-binary: diff -Nru apache2-2.4.69/docs/man/dbmmanage.1 apache2-2.4.71/docs/man/dbmmanage.1 --- apache2-2.4.69/docs/man/dbmmanage.1 2026-04-30 12:18:13.000000000 +0000 +++ apache2-2.4.71/docs/man/dbmmanage.1 2026-11-09 03:07:05.000000000 +0000 @@ -19,7 +19,7 @@ .el .ne 3 .IP "\\$1" \\$2 .. -.TH "DBMMANAGE" 1 "2026-04-30" "Apache HTTP Server" "dbmmanage" +.TH "DBMMANAGE" 1 "2026-11-09" "Apache HTTP Server" "dbmmanage" .SH NAME dbmmanage \- Manage user authentication files in DBM format @@ -72,10 +72,10 @@ .TP \fB-d\fR -crypt hashing (default, except on Win32, Netware) +crypt hashing (default, except on Win32, NetWare) .TP \fB-m\fR -MD5 hashing (default on Win32, Netware) +MD5 hashing (default on Win32, NetWare) .TP \fB-s\fR SHA1 hashing diff -Nru apache2-2.4.69/docs/man/htdbm.1 apache2-2.4.71/docs/man/htdbm.1 --- apache2-2.4.69/docs/man/htdbm.1 2026-05-08 15:07:00.000000000 +0000 +++ apache2-2.4.71/docs/man/htdbm.1 2026-11-09 03:07:05.000000000 +0000 @@ -19,7 +19,7 @@ .el .ne 3 .IP "\\$1" \\$2 .. -.TH "HTDBM" 1 "2026-05-08" "Apache HTTP Server" "htdbm" +.TH "HTDBM" 1 "2026-11-09" "Apache HTTP Server" "htdbm" .SH NAME htdbm \- Manipulate DBM password databases @@ -74,7 +74,7 @@ Display the results on standard output rather than updating a database\&. This option changes the syntax of the command line, since the \fIpasswdfile\fR argument (usually the first one) is omitted\&. It cannot be combined with the \fB-c\fR option\&. .TP \fB-m\fR -Use MD5 hashing for passwords\&. On Windows and Netware, this is the default\&. +Use MD5 hashing for passwords\&. On Windows and NetWare, this is the default\&. .TP \fB-B\fR Use bcrypt hashing for passwords\&. This is currently considered to be very secure\&. @@ -83,13 +83,13 @@ This flag is only allowed in combination with \fB-B\fR (bcrypt hashing)\&. It sets the computing time used for the bcrypt algorithm (higher is more secure but slower, default: 5, valid: 4 to 31)\&. .TP \fB-d\fR -Use \fBcrypt()\fR hashing for passwords\&. The default on all platforms but Windows and Netware\&. Though possibly supported by \fBhtdbm\fR on all platforms, it is not supported by the \fBhttpd\fR server on Windows and Netware\&. This algorithm is \fBinsecure\fR by today's standards\&. +Use \fBcrypt()\fR hashing for passwords\&. The default on all platforms but Windows and NetWare\&. Though possibly supported by \fBhtdbm\fR on all platforms, it is not supported by the \fBhttpd\fR server on Windows and NetWare\&. This algorithm is \fBinsecure\fR by today's standards\&. .TP \fB-s\fR Use SHA hashing for passwords\&. Facilitates migration from/to Netscape servers using the LDAP Directory Interchange Format (ldif)\&. This algorithm is \fBinsecure\fR by today's standards\&. .TP \fB-p\fR -Use plaintext passwords\&. Though \fBhtdbm\fR will support creation on all platforms, the \fBhttpd\fR daemon will only accept plain text passwords on Windows and Netware\&. +Use plaintext passwords\&. Though \fBhtdbm\fR will support creation on all platforms, the \fBhttpd\fR daemon will only accept plain text passwords on Windows and NetWare\&. .TP \fB-l\fR Print each of the usernames and comments from the database on stdout\&. diff -Nru apache2-2.4.69/docs/man/htpasswd.1 apache2-2.4.71/docs/man/htpasswd.1 --- apache2-2.4.69/docs/man/htpasswd.1 2026-05-08 15:07:00.000000000 +0000 +++ apache2-2.4.71/docs/man/htpasswd.1 2026-11-09 03:07:05.000000000 +0000 @@ -19,7 +19,7 @@ .el .ne 3 .IP "\\$1" \\$2 .. -.TH "HTPASSWD" 1 "2026-05-08" "Apache HTTP Server" "htpasswd" +.TH "HTPASSWD" 1 "2026-11-09" "Apache HTTP Server" "htpasswd" .SH NAME htpasswd \- Manage user files for basic authentication @@ -89,13 +89,13 @@ This flag is only allowed in combination with \fB-2\fR or \fB-5\fR\&. It sets the number of hash rounds used for the SHA-2 algorithms (higher is more secure but slower; the default is 5,000)\&. .TP \fB-d\fR -Use \fBcrypt()\fR hashing for passwords\&. This is not supported by the \fBhttpd\fR server on Windows and Netware\&. This algorithm limits the password length to 8 characters\&. This algorithm is \fBinsecure\fR by today's standards\&. It used to be the default algorithm until version 2\&.2\&.17\&. +Use \fBcrypt()\fR hashing for passwords\&. This is not supported by the \fBhttpd\fR server on Windows and NetWare\&. This algorithm limits the password length to 8 characters\&. This algorithm is \fBinsecure\fR by today's standards\&. It used to be the default algorithm until version 2\&.2\&.17\&. .TP \fB-s\fR Use SHA-1 (160-bit) hashing for passwords\&. Facilitates migration from/to Netscape servers using the LDAP Directory Interchange Format (ldif)\&. This algorithm is \fBinsecure\fR by today's standards\&. .TP \fB-p\fR -Use plaintext passwords\&. Though \fBhtpasswd\fR will support creation on all platforms, the \fBhttpd\fR daemon will only accept plain text passwords on Windows and Netware\&. +Use plaintext passwords\&. Though \fBhtpasswd\fR will support creation on all platforms, the \fBhttpd\fR daemon will only accept plain text passwords on Windows and NetWare\&. .TP \fB-D\fR Delete user\&. If the username exists in the specified htpasswd file, it will be deleted\&. diff -Nru apache2-2.4.69/docs/manual/bind.html.de apache2-2.4.71/docs/manual/bind.html.de --- apache2-2.4.69/docs/manual/bind.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/bind.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -31,9 +31,6 @@  pt-br  |  tr 

-
Diese Übersetzung ist möglicherweise - nicht mehr aktuell. Bitte prüfen Sie die englische Version auf - die neuesten Änderungen.

Konfiguration der vom Apache HTTP Server verwendeten Adressen und Ports.

diff -Nru apache2-2.4.69/docs/manual/bind.html.ja.utf8 apache2-2.4.71/docs/manual/bind.html.ja.utf8 --- apache2-2.4.69/docs/manual/bind.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/bind.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  pt-br  |  tr 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

Apache �使用�るアドレス��ート�設定を���。

diff -Nru apache2-2.4.69/docs/manual/bind.html.ko.euc-kr apache2-2.4.71/docs/manual/bind.html.ko.euc-kr --- apache2-2.4.69/docs/manual/bind.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/bind.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  pt-br  |  tr 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

¾ÆÆÄÄ¡°¡ ƯÁ¤ ÁÖ¼Ò¿Í Æ÷Æ®¿¡¼­ ¼­ºñ½ºÇϵµ·Ï ¼³Á¤Çϱâ.

diff -Nru apache2-2.4.69/docs/manual/bind.html.tr.utf8 apache2-2.4.71/docs/manual/bind.html.tr.utf8 --- apache2-2.4.69/docs/manual/bind.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/bind.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,7 +31,6 @@  pt-br  |  tr 

-
Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

Apache HTTPD sunucusunun belli adresleri ve portları dinlemek üzere yapılandırılması.

diff -Nru apache2-2.4.69/docs/manual/caching.html.en apache2-2.4.71/docs/manual/caching.html.en --- apache2-2.4.69/docs/manual/caching.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/caching.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -544,7 +544,7 @@

Using the mod_cache_socache module, mod_cache can cache data from a variety of implementations (aka: "providers"). Using the mod_socache_memcache module, for example, one can specify that - memcached is to be used as the + memcached is to be used as the backend storage mechanism.

Typically the module will be configured as so:

diff -Nru apache2-2.4.69/docs/manual/caching.html.tr.utf8 apache2-2.4.71/docs/manual/caching.html.tr.utf8 --- apache2-2.4.69/docs/manual/caching.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/caching.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -27,7 +27,6 @@  fr  |  tr 

-
Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

Bu belge mod_cache, mod_cache_disk, mod_file_cache diff -Nru apache2-2.4.69/docs/manual/configuring.html.de apache2-2.4.71/docs/manual/configuring.html.de --- apache2-2.4.69/docs/manual/configuring.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/configuring.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -30,9 +30,6 @@  ko  |  tr 

-
Diese Übersetzung ist möglicherweise - nicht mehr aktuell. Bitte prüfen Sie die englische Version auf - die neuesten Änderungen.

Dieses Dokument beschreibt die Dateien, die zur Konfiguration des Apache HTTP Servers verwendet werden.

diff -Nru apache2-2.4.69/docs/manual/configuring.html.ja.utf8 apache2-2.4.71/docs/manual/configuring.html.ja.utf8 --- apache2-2.4.69/docs/manual/configuring.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/configuring.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  ko  |  tr 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

��文書���Apache HTTP サー�を設定�る��使用�るファイル���� 記述�����。

diff -Nru apache2-2.4.69/docs/manual/configuring.html.ko.euc-kr apache2-2.4.71/docs/manual/configuring.html.ko.euc-kr --- apache2-2.4.69/docs/manual/configuring.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/configuring.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  ko  |  tr 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

ÀÌ ¹®¼­´Â ¾ÆÆÄÄ¡ À¥¼­¹ö¸¦ ¼³Á¤ÇÏ´Â ÆÄÀϵéÀ» ¼³¸íÇÑ´Ù.

diff -Nru apache2-2.4.69/docs/manual/configuring.html.tr.utf8 apache2-2.4.71/docs/manual/configuring.html.tr.utf8 --- apache2-2.4.69/docs/manual/configuring.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/configuring.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,7 +30,6 @@  ko  |  tr 

-
Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

Bu belgede Apache HTTP Sunucusunu yapılandırmakta kullanılan dosyalar açıklanmıştır.

diff -Nru apache2-2.4.69/docs/manual/content-negotiation.html.ja.utf8 apache2-2.4.71/docs/manual/content-negotiation.html.ja.utf8 --- apache2-2.4.69/docs/manual/content-negotiation.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/content-negotiation.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,10 +29,6 @@  ko  |  tr 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

Apache ã�¯ HTTP/1.1 ã�®è¦�æ ¼ã�«è¨˜è¿°ã�•れã�¦ã�„るコンテントãƒ�ゴシエーションを diff -Nru apache2-2.4.69/docs/manual/content-negotiation.html.ko.euc-kr apache2-2.4.71/docs/manual/content-negotiation.html.ko.euc-kr --- apache2-2.4.69/docs/manual/content-negotiation.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/content-negotiation.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -29,8 +29,6 @@  ko  |  tr 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

¾ÆÆÄÄ¡´Â HTTP/1.1 ±Ô¾à¿¡ ±â¼úµÈ ³»¿ëÇù»ó(content diff -Nru apache2-2.4.69/docs/manual/content-negotiation.html.tr.utf8 apache2-2.4.71/docs/manual/content-negotiation.html.tr.utf8 --- apache2-2.4.69/docs/manual/content-negotiation.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/content-negotiation.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,7 +29,6 @@  ko  |  tr 

-
Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

Apache HTTPD, içerik uzlaşımını HTTP/1.1 belirtiminde bahsedildiÄŸi ÅŸekliyle diff -Nru apache2-2.4.69/docs/manual/convenience.map apache2-2.4.71/docs/manual/convenience.map --- apache2-2.4.69/docs/manual/convenience.map 2026-06-08 12:25:13.000000000 +0000 +++ apache2-2.4.71/docs/manual/convenience.map 2026-11-09 03:07:05.000000000 +0000 @@ -414,6 +414,7 @@ mddrivemode mod/mod_md.html#mddrivemode mdexternalaccountbinding mod/mod_md.html#mdexternalaccountbinding mdhttpproxy mod/mod_md.html#mdhttpproxy +mdhttpproxycacertificatefile mod/mod_md.html#mdhttpproxycacertificatefile mdinitialdelay mod/mod_md.html#mdinitialdelay mdmatchnames mod/mod_md.html#mdmatchnames mdmember mod/mod_md.html#mdmember diff -Nru apache2-2.4.69/docs/manual/custom-error.html.es apache2-2.4.71/docs/manual/custom-error.html.es --- apache2-2.4.69/docs/manual/custom-error.html.es 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/custom-error.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  ko  |  tr 

-
Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

Apache ofrece la posibilidad de que los webmasters puedan configurar las respuestas que muestra el servidor Apache cuando se diff -Nru apache2-2.4.69/docs/manual/custom-error.html.ja.utf8 apache2-2.4.71/docs/manual/custom-error.html.ja.utf8 --- apache2-2.4.69/docs/manual/custom-error.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/custom-error.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  ko  |  tr 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

ウェブマスター�何ら��エラーや�題�対�る Apache ��応を設定��るよ���る追加機能を�供���。

diff -Nru apache2-2.4.69/docs/manual/custom-error.html.ko.euc-kr apache2-2.4.71/docs/manual/custom-error.html.ko.euc-kr --- apache2-2.4.69/docs/manual/custom-error.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/custom-error.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  ko  |  tr 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

À¥¸¶½ºÅÍ´Â ¿À·ù³ª ¹®Á¦°¡ ¹ß»ýÇßÀ»¶§ ¾ÆÆÄÄ¡ÀÇ ÀÀ´äÀ» ¼³Á¤ÇÒ ¼ö ÀÖ´Ù.

diff -Nru apache2-2.4.69/docs/manual/custom-error.html.tr.utf8 apache2-2.4.71/docs/manual/custom-error.html.tr.utf8 --- apache2-2.4.69/docs/manual/custom-error.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/custom-error.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,7 +30,6 @@  ko  |  tr 

-
Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

Apache HTTP Sunucusu 4xx veya 5xx HTTP durum kodları ile ilgili diff -Nru apache2-2.4.69/docs/manual/developer/API.html.en apache2-2.4.71/docs/manual/developer/API.html.en --- apache2-2.4.69/docs/manual/developer/API.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/developer/API.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -766,7 +766,7 @@ some modules... such as modules that need to open a database connection once per child, and wish to clean it up when the child dies.

-

You can also see how some bugs have manifested themself, such as +

You can also see how some bugs have manifested themselves, such as setting connection->user to a value from r->pool -- in this case connection exists for the lifetime of ptrans, which is longer than diff -Nru apache2-2.4.69/docs/manual/developer/documenting.html.zh-cn.utf8 apache2-2.4.71/docs/manual/developer/documenting.html.zh-cn.utf8 --- apache2-2.4.69/docs/manual/developer/documenting.html.zh-cn.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/developer/documenting.html.zh-cn.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -26,7 +26,6 @@

å�¯ç”¨è¯­è¨€:  en  |  zh-cn 

-
此翻译�能过期。�了解最近的更改,请阅读英文版。

Apache 2.0 使用 Doxygen 从代�中 生� API 和全局��的文档。下�是对使用 Doxygen 生�文档的简介。

diff -Nru apache2-2.4.69/docs/manual/developer/hooks.html.en apache2-2.4.71/docs/manual/developer/hooks.html.en --- apache2-2.4.69/docs/manual/developer/hooks.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/developer/hooks.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -43,7 +43,7 @@
top

Core Hooks

-

The httpd's core modules offer a predefinined list of hooks +

The httpd's core modules offer a predefined list of hooks used during the standard request processing phase. Creating a new hook will expose a function that implements it (see sections below) but it is essential to understand that you will not diff -Nru apache2-2.4.69/docs/manual/developer/index.html.zh-cn.utf8 apache2-2.4.71/docs/manual/developer/index.html.zh-cn.utf8 --- apache2-2.4.69/docs/manual/developer/index.html.zh-cn.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/developer/index.html.zh-cn.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -26,7 +26,6 @@

å�¯ç”¨è¯­è¨€:  en  |  zh-cn 

-
此翻译�能过期。�了解最近的更改,请阅读英文版。

开�者页�的许多文档都�自于 Apache 1.3。当更新到 Apache 2 时,它们�能�于��的阶段。请�心等待,或者直接� diff -Nru apache2-2.4.69/docs/manual/developer/modules.html.ja.utf8 apache2-2.4.71/docs/manual/developer/modules.html.ja.utf8 --- apache2-2.4.69/docs/manual/developer/modules.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/developer/modules.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -26,10 +26,6 @@

翻訳済ã�¿è¨€èªž:  en  |  ja 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

��文書� mod_mmap_static モジュールを Apache 2.0 用�移���時� 学ん�経験をも��書���最��手引�書��。����完全�ゃ���� diff -Nru apache2-2.4.69/docs/manual/developer/output-filters.html.en apache2-2.4.71/docs/manual/developer/output-filters.html.en --- apache2-2.4.69/docs/manual/developer/output-filters.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/developer/output-filters.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -87,7 +87,7 @@

Filters can create FLUSH buckets and pass these down the filter chain if desired. Generating FLUSH buckets unnecessarily, or too frequently, can harm network - utilisation since it may force large numbers of small packets to + utilization since it may force large numbers of small packets to be sent, rather than a small number of larger packets. The section on Non-blocking bucket reads covers a case where filters are encouraged to generate diff -Nru apache2-2.4.69/docs/manual/dns-caveats.html.ja.utf8 apache2-2.4.71/docs/manual/dns-caveats.html.ja.utf8 --- apache2-2.4.69/docs/manual/dns-caveats.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/dns-caveats.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,10 +29,6 @@  ko  |  tr 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

本文書ã�®å†…容ã�¯æ¬¡ã�®ä¸€è¨€ã�«å°½ã��ã�¾ã�™ã€‚「Apache ã�Œè¨­å®šãƒ•ァイルを読ã�¿è¾¼ã‚€ã�¨ã��ã�« DNS を使用ã�™ã‚‹å¿…è¦�ã�Œã�ªã�„よã�†ã�«ã�—ã�¦ä¸‹ã�•ã�„ã€�。Apache ã�Œè¨­å®šãƒ•ァイルを diff -Nru apache2-2.4.69/docs/manual/dns-caveats.html.ko.euc-kr apache2-2.4.71/docs/manual/dns-caveats.html.ko.euc-kr --- apache2-2.4.69/docs/manual/dns-caveats.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/dns-caveats.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -29,8 +29,6 @@  ko  |  tr 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

ÀÌ ¹®¼­¸¦ ÇÑ ¹®ÀåÀ¸·Î ¿ä¾àÇÒ ¼ö ÀÖ´Ù. ¾ÆÆÄÄ¡°¡ ¼³Á¤ÆÄÀÏÀ» ÀÐÀ»¶§ DNS¸¦ ÀÇÁ¸ÇÏÁö¾Êµµ·Ï Ç϶ó. ¾ÆÆÄÄ¡°¡ ¼³Á¤ÆÄÀÏÀ» Àдµ¥ diff -Nru apache2-2.4.69/docs/manual/dso.html.ja.utf8 apache2-2.4.71/docs/manual/dso.html.ja.utf8 --- apache2-2.4.69/docs/manual/dso.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/dso.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,10 +29,6 @@  ko  |  tr 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

Apache HTTP サーãƒ�ã�¯ãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«åŒ–ã�•れã�Ÿãƒ—ログラムã�§ã€� 管ç�†è€…ã�Œãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«ã‚’é�¸æŠžã�™ã‚‹ã�“ã�¨ã�§ã‚µãƒ¼ãƒ�ã�«çµ„ã�¿è¾¼ã‚€æ©Ÿèƒ½ã‚’é�¸ã�¶ã�“ã�¨ã�Œã�§ã��ã�¾ã�™ã€‚ diff -Nru apache2-2.4.69/docs/manual/dso.html.ko.euc-kr apache2-2.4.71/docs/manual/dso.html.ko.euc-kr --- apache2-2.4.69/docs/manual/dso.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/dso.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -29,8 +29,6 @@  ko  |  tr 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

¾ÆÆÄÄ¡ À¥¼­¹ö´Â °ü¸®ÀÚ°¡ ¸ðµâµéÀ» ¼±ÅÃÇÏ¿© ¼­¹ö¿¡ Æ÷ÇÔÇÒ ±â´ÉÀ» °áÁ¤ÇÒ ¼ö ÀÖ´Â ¸ðµâÈ­µÈ ÇÁ·Î±×·¥ÀÌ´Ù. ¼­¹ö¸¦ ÄÄÆÄÇÒ¶§ diff -Nru apache2-2.4.69/docs/manual/dso.html.tr.utf8 apache2-2.4.71/docs/manual/dso.html.tr.utf8 --- apache2-2.4.69/docs/manual/dso.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/dso.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,7 +29,6 @@  ko  |  tr 

-
Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

Apache HTTP Sunucusu modüler bir program olup, yönetici sadece bir grup modül seçerek sunucuya iÅŸlevsellik ekleyebilir. Modüller, Devingen diff -Nru apache2-2.4.69/docs/manual/env.html.ja.utf8 apache2-2.4.71/docs/manual/env.html.ja.utf8 --- apache2-2.4.69/docs/manual/env.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/env.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,10 +29,6 @@  ko  |  tr 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

Apache HTTP サーãƒ�ã�¯ç’°å¢ƒå¤‰æ•°ã�¨å‘¼ã�°ã‚Œã‚‹ã€�å��å‰�ã�®ã�¤ã�„ã�Ÿ 変数ã�«æƒ…報を記憶ã�™ã‚‹ä»•組ã�¿ã‚’æ��ä¾›ã�—ã�¦ã�„ã�¾ã�™ã€‚ã�“ã�®æƒ…å ±ã�¯ãƒ­ã‚°å�Žé›†ã‚„ diff -Nru apache2-2.4.69/docs/manual/env.html.ko.euc-kr apache2-2.4.71/docs/manual/env.html.ko.euc-kr --- apache2-2.4.69/docs/manual/env.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/env.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -29,8 +29,6 @@  ko  |  tr 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

¾ÆÆÄÄ¡ À¥¼­¹ö´Â ȯ°æº¯¼ö(environment variable)¶ó´Â º¯¼ö¿¡ Á¤º¸¸¦ ÀúÀåÇÒ ¼ö ÀÖ´Ù. ÀÌ Á¤º¸¸¦ »ç¿ëÇÏ¿© ·Î±×³ª diff -Nru apache2-2.4.69/docs/manual/env.html.tr.utf8 apache2-2.4.71/docs/manual/env.html.tr.utf8 --- apache2-2.4.69/docs/manual/env.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/env.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,7 +29,6 @@  ko  |  tr 

-
Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

Apache HTTP Sunucusunu etkileyen ortam deÄŸiÅŸkenleri iki çeÅŸittir.

diff -Nru apache2-2.4.69/docs/manual/faq/index.html.es apache2-2.4.71/docs/manual/faq/index.html.es --- apache2-2.4.69/docs/manual/faq/index.html.es 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/faq/index.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -29,10 +29,6 @@  tr  |  zh-cn 

-
Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

Las preguntas frecuentes se han movido a la Wiki de HTTP Server (en Inglés).

diff -Nru apache2-2.4.69/docs/manual/faq/index.html.tr.utf8 apache2-2.4.71/docs/manual/faq/index.html.tr.utf8 --- apache2-2.4.69/docs/manual/faq/index.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/faq/index.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,7 +29,6 @@  tr  |  zh-cn 

-
Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

SSS belgesi HTTP Server Wiki'ye taşındı.

diff -Nru apache2-2.4.69/docs/manual/faq/index.html.zh-cn.utf8 apache2-2.4.71/docs/manual/faq/index.html.zh-cn.utf8 --- apache2-2.4.69/docs/manual/faq/index.html.zh-cn.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/faq/index.html.zh-cn.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,7 +29,6 @@  tr  |  zh-cn 

-
此翻译�能过期。�了解最近的更改,请阅读英文版。

常�问题已�移到 HTTP �务器维基。

diff -Nru apache2-2.4.69/docs/manual/filter.html.es apache2-2.4.71/docs/manual/filter.html.es --- apache2-2.4.69/docs/manual/filter.html.es 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/filter.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  pt-br  |  tr 

-
Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

Este documento describe cómo usar filtros en Apache.

diff -Nru apache2-2.4.69/docs/manual/filter.html.ja.utf8 apache2-2.4.71/docs/manual/filter.html.ja.utf8 --- apache2-2.4.69/docs/manual/filter.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/filter.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  pt-br  |  tr 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

Apache ��フィルタ�使�方����記述�����。

diff -Nru apache2-2.4.69/docs/manual/filter.html.ko.euc-kr apache2-2.4.71/docs/manual/filter.html.ko.euc-kr --- apache2-2.4.69/docs/manual/filter.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/filter.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  pt-br  |  tr 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

ÀÌ ¹®¼­´Â ¾ÆÆÄÄ¡¿¡¼­ ÇÊÅ͸¦ »ç¿ëÇÏ´Â ¹æ¹ýÀ» ¼³¸íÇÑ´Ù.

diff -Nru apache2-2.4.69/docs/manual/glossary.html.de apache2-2.4.71/docs/manual/glossary.html.de --- apache2-2.4.69/docs/manual/glossary.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/glossary.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -31,9 +31,6 @@  ko  |  tr 

-
Diese Übersetzung ist möglicherweise - nicht mehr aktuell. Bitte prüfen Sie die englische Version auf - die neuesten Änderungen.

Dieses Glossar erläutert einige gebräuchliche Fachbegriffe im Zusammenhang mit dem Apache im Speziellen und Web-Diensten im diff -Nru apache2-2.4.69/docs/manual/glossary.html.es apache2-2.4.71/docs/manual/glossary.html.es --- apache2-2.4.69/docs/manual/glossary.html.es 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/glossary.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ko  |  tr 

-
Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

Éste glosario define las terminologías más comunes relacionada con Apache en particular, y con los servidores web en diff -Nru apache2-2.4.69/docs/manual/glossary.html.ja.utf8 apache2-2.4.71/docs/manual/glossary.html.ja.utf8 --- apache2-2.4.69/docs/manual/glossary.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/glossary.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ko  |  tr 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

ã�“ã�®ç”¨èªžé›†ã�§ã�¯ Apacheã�«ç‰¹åŒ–ã�—ã�Ÿç”¨èªžã�¨ã€� ウェブサーãƒ�全般ã�§ä¸€èˆ¬çš„ã�ªç”¨èªžã‚’ã�„ã��ã�¤ã�‹å®šç¾©ã�—ã�¦ã�„ã�¾ã�™ã€‚ diff -Nru apache2-2.4.69/docs/manual/glossary.html.ko.euc-kr apache2-2.4.71/docs/manual/glossary.html.ko.euc-kr --- apache2-2.4.69/docs/manual/glossary.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/glossary.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ko  |  tr 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

ÀÌ ¹®¼­´Â À¥ ¼­ºñ½º ÀϹݿ¡ ´ëÇÑ, ƯÈ÷ ¾ÆÆÄÄ¡¿Í °ü·ÃµÈ, ¿ë¾îµéÀ» Á¤ÀÇÇÑ´Ù. °¢ °³³ä¿¡ ´ëÇÑ ÀÚ¼¼ÇÑ Á¤º¸´Â ¸µÅ©¸¦ Âü°íÇ϶ó. diff -Nru apache2-2.4.69/docs/manual/glossary.html.tr.utf8 apache2-2.4.71/docs/manual/glossary.html.tr.utf8 --- apache2-2.4.69/docs/manual/glossary.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/glossary.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,7 +31,6 @@  ko  |  tr 

-
Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

Bu sözlük, genelinde HTML sayfa sunumuna, özelinde Apache HTTP Sunucusuna özgü ortak terminolojinin bir kısmını içerir. Her kavram ile ilgili daha diff -Nru apache2-2.4.69/docs/manual/handler.html.ja.utf8 apache2-2.4.71/docs/manual/handler.html.ja.utf8 --- apache2-2.4.69/docs/manual/handler.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/handler.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  tr  |  zh-cn 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

Apache ��ンドラ�使用�関��記述�����。

diff -Nru apache2-2.4.69/docs/manual/handler.html.ko.euc-kr apache2-2.4.71/docs/manual/handler.html.ko.euc-kr --- apache2-2.4.69/docs/manual/handler.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/handler.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  tr  |  zh-cn 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

ÀÌ ¹®¼­´Â ¾ÆÆÄÄ¡¿¡¼­ Çڵ鷯¸¦ »ç¿ëÇÏ´Â ¹æ¹ýÀ» ¼³¸íÇÑ´Ù.

diff -Nru apache2-2.4.69/docs/manual/handler.html.zh-cn.utf8 apache2-2.4.71/docs/manual/handler.html.zh-cn.utf8 --- apache2-2.4.69/docs/manual/handler.html.zh-cn.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/handler.html.zh-cn.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,7 +31,6 @@  tr  |  zh-cn 

-
此翻译�能过期。�了解最近的更改,请阅读英文版。

本页�述 Apache 处�器的用法。

diff -Nru apache2-2.4.69/docs/manual/howto/access.html.es apache2-2.4.71/docs/manual/howto/access.html.es --- apache2-2.4.69/docs/manual/howto/access.html.es 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/access.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -27,10 +27,6 @@  es  |  fr 

-
Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

El control de acceso, hace referencia a todos los medios que proporcionan una forma de controlar el acceso a cualquier recurso. Esta parte está diff -Nru apache2-2.4.69/docs/manual/howto/auth.html.es apache2-2.4.71/docs/manual/howto/auth.html.es --- apache2-2.4.69/docs/manual/howto/auth.html.es 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/auth.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  ko  |  tr 

-
Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

Autenticación es cualquier proceso por el cuál se verifica que uno es quien dice ser. Autorización es cualquier proceso en el cuál cualquiera diff -Nru apache2-2.4.69/docs/manual/howto/auth.html.ja.utf8 apache2-2.4.71/docs/manual/howto/auth.html.ja.utf8 --- apache2-2.4.69/docs/manual/howto/auth.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/auth.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  ko  |  tr 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

「èª�証ã€�ã�¨ã�¯ã€�誰ã�‹ã�Œè‡ªåˆ†ã�¯èª°ã�§ã�‚ã‚‹ã�‹ã‚’主張ã�—ã�Ÿå ´å�ˆã�«ã€� ã��れを確èª�ã�™ã‚‹ã�Ÿã‚�ã�®å…¨é�Žç¨‹ã‚’指ã�—ã�¾ã�™ã€‚「承èª�ã€�ã�¨ã�¯ã€� diff -Nru apache2-2.4.69/docs/manual/howto/auth.html.ko.euc-kr apache2-2.4.71/docs/manual/howto/auth.html.ko.euc-kr --- apache2-2.4.69/docs/manual/howto/auth.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/auth.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  ko  |  tr 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

ÀÎÁõ(authentication)Àº ÀÚ½ÅÀÌ ´©±¸¶ó°í ÁÖÀåÇÏ´Â »ç¶÷À» È®ÀÎÇÏ´Â ÀýÂ÷ÀÌ´Ù. ±ÇÇѺο©(authorization)´Â °¡°í ½ÍÀº °÷À¸·Î diff -Nru apache2-2.4.69/docs/manual/howto/auth.html.tr.utf8 apache2-2.4.71/docs/manual/howto/auth.html.tr.utf8 --- apache2-2.4.69/docs/manual/howto/auth.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/auth.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,7 +30,6 @@  ko  |  tr 

-
Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

Kimlik DoÄŸrulama istediÄŸiniz kiÅŸileri teyid etme iÅŸlemidir. Yetkilendirme ise kiÅŸilerin nereye gireceklerine ve hangi bilgiye diff -Nru apache2-2.4.69/docs/manual/howto/cgi.html.es apache2-2.4.71/docs/manual/howto/cgi.html.es --- apache2-2.4.69/docs/manual/howto/cgi.html.es 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/cgi.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -29,10 +29,6 @@  ja  |  ko 

-
Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.
  • Introducción
  • Configurando Apache para permitir CGI
  • diff -Nru apache2-2.4.69/docs/manual/howto/cgi.html.ja.utf8 apache2-2.4.71/docs/manual/howto/cgi.html.ja.utf8 --- apache2-2.4.69/docs/manual/howto/cgi.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/cgi.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,10 +29,6 @@  ja  |  ko 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
-
Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

Los ficheros .htaccess facilitan una forma de realizar cambios en la configuración en contexto directorio.

diff -Nru apache2-2.4.69/docs/manual/howto/htaccess.html.ja.utf8 apache2-2.4.71/docs/manual/howto/htaccess.html.ja.utf8 --- apache2-2.4.69/docs/manual/howto/htaccess.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/htaccess.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  ko  |  pt-br 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

.htaccess ファイル�ディレクトリ毎�設定を変更�る方法を �供���。

diff -Nru apache2-2.4.69/docs/manual/howto/htaccess.html.ko.euc-kr apache2-2.4.71/docs/manual/howto/htaccess.html.ko.euc-kr --- apache2-2.4.69/docs/manual/howto/htaccess.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/htaccess.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  ko  |  pt-br 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

.htaccess ÆÄÀÏÀ» »ç¿ëÇÏ¿© µð·ºÅ丮º°·Î ¼³Á¤À» º¯°æÇÒ ¼ö ÀÖ´Ù.

diff -Nru apache2-2.4.69/docs/manual/howto/htaccess.html.pt-br.utf8 apache2-2.4.71/docs/manual/howto/htaccess.html.pt-br.utf8 --- apache2-2.4.69/docs/manual/howto/htaccess.html.pt-br.utf8 2026-06-19 14:27:29.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/htaccess.html.pt-br.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  ko  |  pt-br 

-
Esta tradução pode estar desatualizada. - Verifique a versão em inglês para alterações recentes.

Arquivos .htaccess oferecem um meio de fazer alterações nas configurações por diretório, sem modificar diretamente os arquivos diff -Nru apache2-2.4.69/docs/manual/howto/http2.html.es apache2-2.4.71/docs/manual/howto/http2.html.es --- apache2-2.4.69/docs/manual/howto/http2.html.es 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/http2.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -27,10 +27,6 @@  es  |  fr 

-
Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

Esta es la guía para configurar HTTP/2 en Apache httpd. Ésta característica diff -Nru apache2-2.4.69/docs/manual/howto/index.html.ja.utf8 apache2-2.4.71/docs/manual/howto/index.html.ja.utf8 --- apache2-2.4.69/docs/manual/howto/index.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/index.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  ko  |  zh-cn 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
top
diff -Nru apache2-2.4.69/docs/manual/howto/index.html.ko.euc-kr apache2-2.4.71/docs/manual/howto/index.html.ko.euc-kr --- apache2-2.4.69/docs/manual/howto/index.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/index.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  ko  |  zh-cn 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
top
diff -Nru apache2-2.4.69/docs/manual/howto/index.html.zh-cn.utf8 apache2-2.4.71/docs/manual/howto/index.html.zh-cn.utf8 --- apache2-2.4.69/docs/manual/howto/index.html.zh-cn.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/index.html.zh-cn.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,7 +30,6 @@  ko  |  zh-cn 

-
此翻译�能过期。�了解最近的更改,请阅读英文版。
top
diff -Nru apache2-2.4.69/docs/manual/howto/public_html.html.es apache2-2.4.71/docs/manual/howto/public_html.html.es --- apache2-2.4.69/docs/manual/howto/public_html.html.es 2026-06-19 14:27:29.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/public_html.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  ko  |  tr 

-
Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

En sistemas con múltiples usuarios, cada usuario puede tener un website en su directorio home usando la directiva UserDir. Los visitantes de una URL diff -Nru apache2-2.4.69/docs/manual/howto/public_html.html.ja.utf8 apache2-2.4.71/docs/manual/howto/public_html.html.ja.utf8 --- apache2-2.4.69/docs/manual/howto/public_html.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/public_html.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  ko  |  tr 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

複数ã�®ãƒ¦ãƒ¼ã‚¶ã�®ã�„るシステムã�§ã�¯ã€�UserDir ディレクティブを使ã�£ã�¦ å�„ユーザã�Œãƒ›ãƒ¼ãƒ ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã�«ã‚¦ã‚§ãƒ–サイトを構築ã�§ã��るよã�†ã�«è¨­å®šã�™ã‚‹ã�“ã�¨ã�Œ diff -Nru apache2-2.4.69/docs/manual/howto/public_html.html.ko.euc-kr apache2-2.4.71/docs/manual/howto/public_html.html.ko.euc-kr --- apache2-2.4.69/docs/manual/howto/public_html.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/public_html.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  ko  |  tr 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

¿©·¯ »ç¿ëÀÚ°¡ ÀÖ´Â ½Ã½ºÅÛ¿¡¼­ UserDir Áö½Ã¾î¸¦ »ç¿ëÇÏ¸é °¢ »ç¿ëÀÚ´Â ÀÚ½ÅÀÇ È¨µð·ºÅ丮 ¾È¿¡ À¥»çÀÌÆ®¸¦ ¸¸µé ¼ö ÀÖ´Ù. diff -Nru apache2-2.4.69/docs/manual/howto/public_html.html.tr.utf8 apache2-2.4.71/docs/manual/howto/public_html.html.tr.utf8 --- apache2-2.4.69/docs/manual/howto/public_html.html.tr.utf8 2026-06-19 14:27:29.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/public_html.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,7 +30,6 @@  ko  |  tr 

-
Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

Çok kullanıcılı sistemlerde, UserDir yönergesi ile her kullanıcının kendi ev dizininde bir sitesi olması saÄŸlanabilir. diff -Nru apache2-2.4.69/docs/manual/howto/ssi.html.es apache2-2.4.71/docs/manual/howto/ssi.html.es --- apache2-2.4.69/docs/manual/howto/ssi.html.es 2026-06-19 14:27:29.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/ssi.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

-
Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

Los Server Side Includes (Inclusiones en la parte Servidor) facilitan un método para añadir contenido dinámico a documentos HTML existentes.

diff -Nru apache2-2.4.69/docs/manual/howto/ssi.html.ja.utf8 apache2-2.4.71/docs/manual/howto/ssi.html.ja.utf8 --- apache2-2.4.69/docs/manual/howto/ssi.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/ssi.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,10 +29,6 @@  ja  |  ko 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

サー�サイドインクルード�よ���既存� HTML ドキュメント�動的�コンテンツを追加�る�������。

diff -Nru apache2-2.4.69/docs/manual/howto/ssi.html.ko.euc-kr apache2-2.4.71/docs/manual/howto/ssi.html.ko.euc-kr --- apache2-2.4.69/docs/manual/howto/ssi.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/howto/ssi.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -29,8 +29,6 @@  ja  |  ko 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

Server-side includes¸¦ »ç¿ëÇÏ¿© HTML ¹®¼­¿¡ µ¿ÀûÀÎ ³»¿ëÀ» Ãß°¡ÇÒ ¼ö ÀÖ´Ù.

diff -Nru apache2-2.4.69/docs/manual/index.html.da apache2-2.4.71/docs/manual/index.html.da --- apache2-2.4.69/docs/manual/index.html.da 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/index.html.da 2026-11-09 03:07:05.000000000 +0000 @@ -39,7 +39,6 @@  tr  |  zh-cn 

-
Denne oversættelse kan være forældet. Se den Engelske version for de seneste opdateringer.

- + @@ -693,464 +693,465 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - + - - - + - - - + + - - - - - - - - + + + + + - - - + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - + + + + + + + - - - - - + + + + - - + - - + - - - - - - - - - - + + + + + + + + + - - - - - - - - + + + + + - - - - - - + + + + - - + - - - - + + + - - - - + - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - + + + - - + - - - - - - + - - + - - - + + + - - - - + - - - - - - - - - - + + + + + + - - + - - - - + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - + - - - - - + + + + - - + - - - - - - - + + + + + + + - - - - - + + + + - - - - - + - - - - - + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - + - - - + - - - - - - - - - - - - + + + + + + - - - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - + + + - - - - + + + - - - - - - + + + + + - - + - - - - - - - - - - - + + + + + + + + - - - + - - - - + + - - + - - - - - - + - - + - +

Udgivelses Noter

  • Nye funktioner i Apache 2.3/2.4
  • diff -Nru apache2-2.4.69/docs/manual/index.html.de apache2-2.4.71/docs/manual/index.html.de --- apache2-2.4.69/docs/manual/index.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/index.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -39,9 +39,6 @@  tr  |  zh-cn 

-
Diese Übersetzung ist möglicherweise - nicht mehr aktuell. Bitte prüfen Sie die englische Version auf - die neuesten Änderungen.

- - - - + + + + - - + - - + - - - - - - - - - + + + + + + - - - + - - - + - - - - + - - + - - - - + - - - - - - + + - - - - - - + - - - - - - - - - - + + + - - - + - - - - - - - - - + + + + - - - - - - - - - - - + + - - - + - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - + - - + - - - - + - - + - - + - - + - - - - - + - - - - - - - - - - - + - - - - - - - - - - - + - - - - - - - - - - - - - - - - + - - - - - - - - - + - - - - + - - + - - - - - - - + - - - - - - - - - + - - + - - - - - + - - + - - - - + - - - - - - + - - - - - - - + - - - + + - - - - - + - - + - - - + + - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - + - - - - - - + - - - - + + - - - - + - - - - - + + + - - - - - - - - + + - - - - - + + + + - - - - - - - - + - - - - - - - - + - - + -

Hinweise zur Version

  • Neue Funktionen in Version 2.3/2.4
  • diff -Nru apache2-2.4.69/docs/manual/index.html.en apache2-2.4.71/docs/manual/index.html.en --- apache2-2.4.69/docs/manual/index.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/index.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -92,7 +92,7 @@

Platform Specific Notes

diff -Nru apache2-2.4.69/docs/manual/index.html.es apache2-2.4.71/docs/manual/index.html.es --- apache2-2.4.69/docs/manual/index.html.es 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/index.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -39,10 +39,6 @@  tr  |  zh-cn 

-
Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

- + @@ -730,501 +730,502 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - + - - - + - - - + + - - - - - - - + + - - - - + + - - - - - - - - - + + + + + + + - - - - - - - - + + + + + + - - - - - - - + + + + + + + - - - - - + + + + - - + - - + - - - - + + + - - - - - - + + + + - - - - - - + - - + - - - - - - + + + + - - + - - + - - + - - - - + - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - + - - - - - - + - - + - - - + + + - - - - - - - - - - + - - + - - + - - + - - - + - - - - + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - + - - - - - + + + + - - + - - - - - + - - + - - - + + - - + - - - - - + - - - - - + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - + - - - + - - - - - - - - - - - - + + + + + + - - - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - + + + - - - - + + + - - - - - - + + + + + - - + - - - - - + + + - - - - - + + + + - - - - + - - - - + + - - + - - - - - - + - - + - +

Notas de la versión

  • Nuevas funcionalidades en Apache 2.3/2.4
  • diff -Nru apache2-2.4.69/docs/manual/index.html.ja.utf8 apache2-2.4.71/docs/manual/index.html.ja.utf8 --- apache2-2.4.69/docs/manual/index.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/index.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -39,10 +39,6 @@  tr  |  zh-cn 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

-

リリースノート

  • Apache 2.3/2.4 ã�®æ–°æ©Ÿèƒ½
  • diff -Nru apache2-2.4.69/docs/manual/index.html.ko.euc-kr apache2-2.4.71/docs/manual/index.html.ko.euc-kr --- apache2-2.4.69/docs/manual/index.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/index.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -37,8 +37,6 @@  tr  |  zh-cn 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

-

¹ßÇ¥¹®

  • ¹öÀü 2.0ÀÇ »õ·Î¿î ±â´É
  • diff -Nru apache2-2.4.69/docs/manual/index.html.tr.utf8 apache2-2.4.71/docs/manual/index.html.tr.utf8 --- apache2-2.4.69/docs/manual/index.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/index.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -39,7 +39,6 @@  tr  |  zh-cn 

-
Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

+

Sürümlerin Dağıtım Bilgileri

  • 2.3/2.4'deki yeni özellikler
  • diff -Nru apache2-2.4.69/docs/manual/install.html.de apache2-2.4.71/docs/manual/install.html.de --- apache2-2.4.69/docs/manual/install.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/install.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -32,9 +32,6 @@  pt-br  |  tr 

-
Diese Übersetzung ist möglicherweise - nicht mehr aktuell. Bitte prüfen Sie die englische Version auf - die neuesten Änderungen.

Dieses Dokument umfaßt nur die Kompilierung und Installation des Apache auf Unix und Unix-ähnlichen Systemen. Für die diff -Nru apache2-2.4.69/docs/manual/install.html.es apache2-2.4.71/docs/manual/install.html.es --- apache2-2.4.69/docs/manual/install.html.es 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/install.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  pt-br  |  tr 

-
Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

Éste documento hace referencia a la compilación y la instalación del Apache diff -Nru apache2-2.4.69/docs/manual/install.html.ja.utf8 apache2-2.4.71/docs/manual/install.html.ja.utf8 --- apache2-2.4.69/docs/manual/install.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/install.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  pt-br  |  tr 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

ã�“ã�®æ–‡æ›¸ã�§æ‰±ã�†ç¯„囲ã�¯ã€�Unix ã‚„ Unix ã�«é¡žä¼¼ã�—ã�Ÿã‚·ã‚¹ãƒ†ãƒ ã�§ã�® diff -Nru apache2-2.4.69/docs/manual/install.html.ko.euc-kr apache2-2.4.71/docs/manual/install.html.ko.euc-kr --- apache2-2.4.69/docs/manual/install.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/install.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  pt-br  |  tr 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

ÀÌ ¹®¼­´Â À¯´Ð½º¿Í À¯´Ð½º·ù ½Ã½ºÅÛ¿¡¼­ ¾ÆÆÄÄ¡¸¦ ÄÄÆÄÀÏÇϰí diff -Nru apache2-2.4.69/docs/manual/install.html.pt-br.utf8 apache2-2.4.71/docs/manual/install.html.pt-br.utf8 --- apache2-2.4.69/docs/manual/install.html.pt-br.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/install.html.pt-br.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  pt-br  |  tr 

-
Esta tradução pode estar desatualizada. - Verifique a versão em inglês para alterações recentes.

Este documento cobre a compilação e a instalação do Servidor HTTP Apache diff -Nru apache2-2.4.69/docs/manual/install.html.tr.utf8 apache2-2.4.71/docs/manual/install.html.tr.utf8 --- apache2-2.4.69/docs/manual/install.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/install.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,7 +32,6 @@  pt-br  |  tr 

-
Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

Bu belge Apache HTTP Sunucusunun sadece Unix ve Unix benzeri diff -Nru apache2-2.4.69/docs/manual/invoking.html.de apache2-2.4.71/docs/manual/invoking.html.de --- apache2-2.4.69/docs/manual/invoking.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/invoking.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -32,9 +32,6 @@  pt-br  |  tr 

-
Diese Übersetzung ist möglicherweise - nicht mehr aktuell. Bitte prüfen Sie die englische Version auf - die neuesten Änderungen.

Unter Windows läuft der Apache üblicherweise als Dienst (Windows NT, 2000 und XP) oder als Konsolenanwendung (Windows 9x und diff -Nru apache2-2.4.69/docs/manual/invoking.html.es apache2-2.4.71/docs/manual/invoking.html.es --- apache2-2.4.69/docs/manual/invoking.html.es 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/invoking.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  pt-br  |  tr 

-
Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

En Windows, Apache se ejecuta normalmente como un servicio. Para obtener más información, consulte diff -Nru apache2-2.4.69/docs/manual/invoking.html.ja.utf8 apache2-2.4.71/docs/manual/invoking.html.ja.utf8 --- apache2-2.4.69/docs/manual/invoking.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/invoking.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  pt-br  |  tr 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

Windows 上ã�§ã�¯ã€�Apache ã�¯é€šå¸¸ã�¯ã‚µãƒ¼ãƒ“スã�¨ã�—ã�¦å®Ÿè¡Œã�•れã�¾ã�™ã€‚ 詳細ã�«é–¢ã�—ã�¦ã�¯ã€�「 diff -Nru apache2-2.4.69/docs/manual/invoking.html.ko.euc-kr apache2-2.4.71/docs/manual/invoking.html.ko.euc-kr --- apache2-2.4.69/docs/manual/invoking.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/invoking.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  pt-br  |  tr 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

º¸Åë ¾ÆÆÄÄ¡´Â Windows NT, 2000, XP¿¡¼­´Â ¼­ºñ½º·Î, Windows 95°ú ME¿¡¼­´Â ÄÜ¼Ö ÇÁ·Î±×·¥À¸·Î ½ÇÇàµÈ´Ù. ÀÚ¼¼ÇÑ diff -Nru apache2-2.4.69/docs/manual/invoking.html.tr.utf8 apache2-2.4.71/docs/manual/invoking.html.tr.utf8 --- apache2-2.4.69/docs/manual/invoking.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/invoking.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,7 +32,6 @@  pt-br  |  tr 

-
Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

Apache normal olarak, Windows'ta bir hizmet olarak çalışır. Ayrıntılı bilgi için Apache HTTPD'nin bir diff -Nru apache2-2.4.69/docs/manual/logs.html.ja.utf8 apache2-2.4.71/docs/manual/logs.html.ja.utf8 --- apache2-2.4.69/docs/manual/logs.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/logs.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,10 +29,6 @@  ko  |  tr 

-
��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

ウェブサーãƒ�を効果的ã�«ç®¡ç�†ã�™ã‚‹ã�Ÿã‚�ã�«ã�¯ã€�サーãƒ�ã�®æ´»å‹•やパフォーマンスã€� 今発生ã�—ã�¦ã�„ã‚‹ã�‹ã‚‚ã�—れã�ªã�„å•�題ã�«é–¢ã�™ã‚‹ãƒ•ィードãƒ�ックを得るã�“ã�¨ã�Œå¿…è¦�ã�§ã�™ã€‚ diff -Nru apache2-2.4.69/docs/manual/logs.html.ko.euc-kr apache2-2.4.71/docs/manual/logs.html.ko.euc-kr --- apache2-2.4.69/docs/manual/logs.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/logs.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -29,8 +29,6 @@  ko  |  tr 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

È¿À²ÀûÀ¸·Î À¥¼­¹ö¸¦ °ü¸®ÇÏ·Á¸é ¹ß»ýÇÏ´Â ¹®Á¦¿Í ÇÔ²² ¼­¹öÀÇ È°µ¿°ú ¼º´É¿¡ ´ëÇØ ¾Ë¾Æ¾ß ÇÑ´Ù. ¾ÆÆÄÄ¡ À¥¼­¹ö´Â ¸Å¿ì Á¾ÇÕÀûÀ̰í diff -Nru apache2-2.4.69/docs/manual/logs.html.tr.utf8 apache2-2.4.71/docs/manual/logs.html.tr.utf8 --- apache2-2.4.69/docs/manual/logs.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/logs.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,7 +29,6 @@  ko  |  tr 

-
Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

Bir HTTP sunucusunu verimli ÅŸekilde yönetebilmek için oluÅŸabilecek sorunlardan baÅŸka sunucunun baÅŸarımı ve etkinliÄŸi hakkında da bazı geri diff -Nru apache2-2.4.69/docs/manual/misc/index.html.ko.euc-kr apache2-2.4.71/docs/manual/misc/index.html.ko.euc-kr --- apache2-2.4.69/docs/manual/misc/index.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/misc/index.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  tr  |  zh-cn 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

¾Æ·¡´Â ¾ÆÆÄÄ¡ À¥¼­¹ö °³¹ß ÇÁ·ÎÁ§Æ®¿¡ ´ëÇÑ Ãß°¡ ¹®¼­ diff -Nru apache2-2.4.69/docs/manual/misc/password_encryptions.html.en apache2-2.4.71/docs/manual/misc/password_encryptions.html.en --- apache2-2.4.69/docs/manual/misc/password_encryptions.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/misc/password_encryptions.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -63,7 +63,7 @@ characters of the password. Insecure.

PLAIN TEXT (i.e. unencrypted)
-
Windows & Netware only. Insecure.
+
Windows & NetWare only. Insecure.

Generating values with htpasswd

diff -Nru apache2-2.4.69/docs/manual/misc/perf-tuning.html.ko.euc-kr apache2-2.4.71/docs/manual/misc/perf-tuning.html.ko.euc-kr --- apache2-2.4.69/docs/manual/misc/perf-tuning.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/misc/perf-tuning.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -28,8 +28,6 @@  ko  |  tr 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

¾ÆÆÄÄ¡ 2.0Àº ±â´É°ú Æ÷ÆÃ°¡´É¼º°ú ¼º´ÉÀÇ ±ÕÇüÀÌ ¸Âµµ·Ï diff -Nru apache2-2.4.69/docs/manual/misc/perf-tuning.html.tr.utf8 apache2-2.4.71/docs/manual/misc/perf-tuning.html.tr.utf8 --- apache2-2.4.69/docs/manual/misc/perf-tuning.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/misc/perf-tuning.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  ko  |  tr 

-
Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

Apache 2.x, esneklik, taşınabilirlik ve başarım arasında bir denge diff -Nru apache2-2.4.69/docs/manual/misc/security_tips.html.en apache2-2.4.71/docs/manual/misc/security_tips.html.en --- apache2-2.4.69/docs/manual/misc/security_tips.html.en 2026-09-29 11:33:23.000000000 +0000 +++ apache2-2.4.71/docs/manual/misc/security_tips.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -155,7 +155,7 @@ switches to the user defined by the User directive to serve hits. As is the case with any command that root executes, you must take care that it is protected from modification by non-root users. Not only must the files - themselves be writeable only by root, but so must the directories, and + themselves be writable only by root, but so must the directories, and parents of all directories. For example, if you choose to place ServerRoot in /usr/local/apache then it is suggested that you create that directory as root, with commands like these:

@@ -189,10 +189,10 @@ executes or writes on then you open your system to root compromises. For example, someone could replace the httpd binary so that the next time you start it, it will execute some arbitrary code. If - the logs directory is writeable (by a non-root user), someone could replace + the logs directory is writable (by a non-root user), someone could replace a log file with a symlink to some other system file, and then root might overwrite that file with arbitrary data. If the log files - themselves are writeable (by a non-root user), then someone may be + themselves are writable (by a non-root user), then someone may be able to overwrite the log itself with bogus data.

top
diff -Nru apache2-2.4.69/docs/manual/misc/security_tips.html.ko.euc-kr apache2-2.4.71/docs/manual/misc/security_tips.html.ko.euc-kr --- apache2-2.4.69/docs/manual/misc/security_tips.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/misc/security_tips.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -28,8 +28,6 @@  ko  |  tr 

-
ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

À¥¼­¹ö¸¦ ¿î¿µÇÒ¶§ µµ¿òÀÌ µÉ º¸¾È °ü·Ã ÈùÆ®¿Í ÆÁÀÌ´Ù. ¾î¶² °ÍÀº ÀϹÝÀûÀ̰í, ¾î¶² °ÍÀº ¾ÆÆÄÄ¡¿¡¸¸ ÇØ´çÇÏ´Â °ÍÀÌ´Ù.

diff -Nru apache2-2.4.69/docs/manual/misc/security_tips.html.tr.utf8 apache2-2.4.71/docs/manual/misc/security_tips.html.tr.utf8 --- apache2-2.4.69/docs/manual/misc/security_tips.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/misc/security_tips.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  ko  |  tr 

-
Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

Bir HTTP Sunucusunu ayarlarken dikkat edilmesi gerekenler ve bazı ipuçları. Öneriler kısmen Apache'ye özel kısmen de genel olacaktır.

diff -Nru apache2-2.4.69/docs/manual/mod/core.html.de apache2-2.4.71/docs/manual/mod/core.html.de --- apache2-2.4.69/docs/manual/mod/core.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/core.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -33,9 +33,6 @@  ja  |  tr 

-
Diese Übersetzung ist möglicherweise - nicht mehr aktuell. Bitte prüfen Sie die englische Version auf - die neuesten Änderungen.
Beschreibung:Ständig verfügbare Kernfunktionen des Apache HTTP Servers
Status:Core
diff -Nru apache2-2.4.69/docs/manual/mod/core.html.en apache2-2.4.71/docs/manual/mod/core.html.en --- apache2-2.4.69/docs/manual/mod/core.html.en 2026-06-08 12:25:13.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/core.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -3762,7 +3762,7 @@ module documentation for more information.

  • posixsem -

    This is a mutex variant based on a Posix semaphore.

    +

    This is a mutex variant based on a POSIX semaphore.

    Warning

    The semaphore ownership is not recovered if a thread in the process @@ -3788,10 +3788,10 @@

  • sem

    This selects the "best" available semaphore implementation, choosing - between Posix and SystemV IPC semaphores, in that order.

  • + between POSIX and SystemV IPC semaphores, in that order.

  • pthread -

    This is a mutex variant based on cross-process Posix thread +

    This is a mutex variant based on cross-process POSIX thread mutexes.

    Warning

    diff -Nru apache2-2.4.69/docs/manual/mod/core.html.es apache2-2.4.71/docs/manual/mod/core.html.es --- apache2-2.4.69/docs/manual/mod/core.html.es 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/core.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -33,10 +33,6 @@  ja  |  tr 

    -
    Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.
    Descripción:Funcionalides básicas del Servidor HTTP Apache que siempre están presentes.
    Estado:Core
    diff -Nru apache2-2.4.69/docs/manual/mod/core.html.ja.utf8 apache2-2.4.71/docs/manual/mod/core.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/core.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/core.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -33,10 +33,6 @@  ja  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    説明:常�使用�能� Apache HTTP サー��コア機能
    ステータス:Core
    diff -Nru apache2-2.4.69/docs/manual/mod/core.html.tr.utf8 apache2-2.4.71/docs/manual/mod/core.html.tr.utf8 --- apache2-2.4.69/docs/manual/mod/core.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/core.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -33,7 +33,6 @@  ja  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.
    Açıklama:Apache HTTP Sunucusunda daima mevcut olan çekirdek özellikler
    Durum:Çekirdek
    diff -Nru apache2-2.4.69/docs/manual/mod/directive-dict.html.es apache2-2.4.71/docs/manual/mod/directive-dict.html.es --- apache2-2.4.69/docs/manual/mod/directive-dict.html.es 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/directive-dict.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  ko  |  tr 

    -
    Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

    Este documento describe los términos que se usan para describir cada directiva de configuración de diff -Nru apache2-2.4.69/docs/manual/mod/directive-dict.html.ja.utf8 apache2-2.4.71/docs/manual/mod/directive-dict.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/directive-dict.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/directive-dict.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  ko  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

    ��文書�� Apache 設定ディレクティブ を説明�る���使�れ��る用語を説明���。

    diff -Nru apache2-2.4.69/docs/manual/mod/directive-dict.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/directive-dict.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/directive-dict.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/directive-dict.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    ÀÌ ¹®¼­´Â ¾ÆÆÄÄ¡ ¼³Á¤ Áö½Ã¾î¸¦ ¼³¸íÇϴµ¥ »ç¿ëÇÑ ¿ë¾îµéÀ» ¼³¸íÇÑ´Ù.

    diff -Nru apache2-2.4.69/docs/manual/mod/directive-dict.html.tr.utf8 apache2-2.4.71/docs/manual/mod/directive-dict.html.tr.utf8 --- apache2-2.4.69/docs/manual/mod/directive-dict.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/directive-dict.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,7 +30,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    Bu belgede Apache yapılandırma yönergelerini tanımlamakta kullanılan terimler açıklanmıştır.

    diff -Nru apache2-2.4.69/docs/manual/mod/directives.html.de apache2-2.4.71/docs/manual/mod/directives.html.de --- apache2-2.4.69/docs/manual/mod/directives.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/directives.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -456,6 +456,7 @@
  • MDDriveMode
  • MDExternalAccountBinding
  • MDHttpProxy
  • +
  • MDHttpProxyCACertificateFile
  • MDInitialDelay
  • MDMatchNames
  • MDMember
  • diff -Nru apache2-2.4.69/docs/manual/mod/directives.html.en apache2-2.4.71/docs/manual/mod/directives.html.en --- apache2-2.4.69/docs/manual/mod/directives.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/directives.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -457,6 +457,7 @@
  • MDDriveMode
  • MDExternalAccountBinding
  • MDHttpProxy
  • +
  • MDHttpProxyCACertificateFile
  • MDInitialDelay
  • MDMatchNames
  • MDMember
  • diff -Nru apache2-2.4.69/docs/manual/mod/directives.html.es apache2-2.4.71/docs/manual/mod/directives.html.es --- apache2-2.4.69/docs/manual/mod/directives.html.es 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/directives.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -459,6 +459,7 @@
  • MDDriveMode
  • MDExternalAccountBinding
  • MDHttpProxy
  • +
  • MDHttpProxyCACertificateFile
  • MDInitialDelay
  • MDMatchNames
  • MDMember
  • diff -Nru apache2-2.4.69/docs/manual/mod/directives.html.fr.utf8 apache2-2.4.71/docs/manual/mod/directives.html.fr.utf8 --- apache2-2.4.69/docs/manual/mod/directives.html.fr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/directives.html.fr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -457,6 +457,7 @@
  • MDDriveMode
  • MDExternalAccountBinding
  • MDHttpProxy
  • +
  • MDHttpProxyCACertificateFile
  • MDInitialDelay
  • MDMatchNames
  • MDMember
  • diff -Nru apache2-2.4.69/docs/manual/mod/directives.html.ja.utf8 apache2-2.4.71/docs/manual/mod/directives.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/directives.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/directives.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -454,6 +454,7 @@
  • MDDriveMode
  • MDExternalAccountBinding
  • MDHttpProxy
  • +
  • MDHttpProxyCACertificateFile
  • MDInitialDelay
  • MDMatchNames
  • MDMember
  • diff -Nru apache2-2.4.69/docs/manual/mod/directives.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/directives.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/directives.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/directives.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -454,6 +454,7 @@
  • MDDriveMode
  • MDExternalAccountBinding
  • MDHttpProxy
  • +
  • MDHttpProxyCACertificateFile
  • MDInitialDelay
  • MDMatchNames
  • MDMember
  • diff -Nru apache2-2.4.69/docs/manual/mod/directives.html.tr.utf8 apache2-2.4.71/docs/manual/mod/directives.html.tr.utf8 --- apache2-2.4.69/docs/manual/mod/directives.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/directives.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -453,6 +453,7 @@
  • MDDriveMode
  • MDExternalAccountBinding
  • MDHttpProxy
  • +
  • MDHttpProxyCACertificateFile
  • MDInitialDelay
  • MDMatchNames
  • MDMember
  • diff -Nru apache2-2.4.69/docs/manual/mod/directives.html.zh-cn.utf8 apache2-2.4.71/docs/manual/mod/directives.html.zh-cn.utf8 --- apache2-2.4.69/docs/manual/mod/directives.html.zh-cn.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/directives.html.zh-cn.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -452,6 +452,7 @@
  • MDDriveMode
  • MDExternalAccountBinding
  • MDHttpProxy
  • +
  • MDHttpProxyCACertificateFile
  • MDInitialDelay
  • MDMatchNames
  • MDMember
  • diff -Nru apache2-2.4.69/docs/manual/mod/event.html.en apache2-2.4.71/docs/manual/mod/event.html.en --- apache2-2.4.69/docs/manual/mod/event.html.en 2026-06-16 16:46:03.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/event.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -367,7 +367,7 @@

    When all the worker threads are idle, then absolute maximum numbers of concurrent - connections can be calculared in a simpler way:

    + connections can be calculated in a simpler way:

    (AsyncRequestWorkerFactor + 1) * diff -Nru apache2-2.4.69/docs/manual/mod/index.html.de apache2-2.4.71/docs/manual/mod/index.html.de --- apache2-2.4.69/docs/manual/mod/index.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/index.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -258,7 +258,7 @@ hosting

    mod_watchdog
    provides infrastructure for other modules to periodically run tasks
    -
    mod_xml2enc
    Enhanced charset/internationalisation support for libxml2-based +
    mod_xml2enc
    Enhanced charset/internationalization support for libxml2-based filter modules
    diff -Nru apache2-2.4.69/docs/manual/mod/index.html.en apache2-2.4.71/docs/manual/mod/index.html.en --- apache2-2.4.69/docs/manual/mod/index.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/index.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -254,7 +254,7 @@ hosting
    mod_watchdog
    provides infrastructure for other modules to periodically run tasks
    -
    mod_xml2enc
    Enhanced charset/internationalisation support for libxml2-based +
    mod_xml2enc
    Enhanced charset/internationalization support for libxml2-based filter modules
    diff -Nru apache2-2.4.69/docs/manual/mod/index.html.es apache2-2.4.71/docs/manual/mod/index.html.es --- apache2-2.4.69/docs/manual/mod/index.html.es 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/index.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  tr  |  zh-cn 

    -
    Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

    Abajo se muestra una lista con todos los módulos que forman @@ -259,7 +255,7 @@ hosting

    mod_watchdog
    provides infrastructure for other modules to periodically run tasks
    -
    mod_xml2enc
    Enhanced charset/internationalisation support for libxml2-based +
    mod_xml2enc
    Enhanced charset/internationalization support for libxml2-based filter modules
    diff -Nru apache2-2.4.69/docs/manual/mod/index.html.ja.utf8 apache2-2.4.71/docs/manual/mod/index.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/index.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/index.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -244,7 +244,7 @@ hosting
    mod_watchdog
    provides infrastructure for other modules to periodically run tasks
    -
    mod_xml2enc
    Enhanced charset/internationalisation support for libxml2-based +
    mod_xml2enc
    Enhanced charset/internationalization support for libxml2-based filter modules
    diff -Nru apache2-2.4.69/docs/manual/mod/index.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/index.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/index.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/index.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  tr  |  zh-cn 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    ´ÙÀ½Àº ¾ÆÆÄÄ¡ ¹èÆ÷º»¿¡ Æ÷ÇÔµÈ ¸ðµç ¸ðµâ ¸ñ·ÏÀÌ´Ù. ¸ðµç ¾ÆÆÄÄ¡ Áö½Ã¾î ¸ñ·Ïµµ @@ -240,7 +238,7 @@ hosting

    mod_watchdog
    provides infrastructure for other modules to periodically run tasks
    -
    mod_xml2enc
    Enhanced charset/internationalisation support for libxml2-based +
    mod_xml2enc
    Enhanced charset/internationalization support for libxml2-based filter modules
    diff -Nru apache2-2.4.69/docs/manual/mod/index.html.tr.utf8 apache2-2.4.71/docs/manual/mod/index.html.tr.utf8 --- apache2-2.4.69/docs/manual/mod/index.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/index.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -247,7 +247,7 @@
    mod_vhost_alias
    Kitlesel sanal konakların devingen olarak yapılandırılmasını sağlar
    mod_watchdog
    provides infrastructure for other modules to periodically run tasks
    -
    mod_xml2enc
    Enhanced charset/internationalisation support for libxml2-based +
    mod_xml2enc
    Enhanced charset/internationalization support for libxml2-based filter modules
    diff -Nru apache2-2.4.69/docs/manual/mod/index.html.zh-cn.utf8 apache2-2.4.71/docs/manual/mod/index.html.zh-cn.utf8 --- apache2-2.4.69/docs/manual/mod/index.html.zh-cn.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/index.html.zh-cn.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -249,7 +249,7 @@ hosting
    mod_watchdog
    provides infrastructure for other modules to periodically run tasks
    -
    mod_xml2enc
    Enhanced charset/internationalisation support for libxml2-based +
    mod_xml2enc
    Enhanced charset/internationalization support for libxml2-based filter modules
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_access_compat.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_access_compat.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_access_compat.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_access_compat.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  fr  |  ja 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_actions.html.de apache2-2.4.71/docs/manual/mod/mod_actions.html.de --- apache2-2.4.69/docs/manual/mod/mod_actions.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_actions.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -32,9 +32,6 @@  ja  |  ko 

    -
    Diese Übersetzung ist möglicherweise - nicht mehr aktuell. Bitte prüfen Sie die englische Version auf - die neuesten Änderungen.
    説明:ホスト (��も��� IP アドレス) �基���グループ承�
    ステータス:Extension
    モジュール識別�:access_compat_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_actions.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_actions.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_actions.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_actions.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    Beschreibung:Dieses Modul ermöglicht die Ausführung von CGI-Skripten in Abhängigkeit von Medientypen und Anfragemethoden.
    Status:Basis
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_actions.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_actions.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_actions.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_actions.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:メディアタイプやリクエストメソッド�応�� CGI スクリプトを実行�る機能を�供
    ステータス:Base
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_alias.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_alias.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_alias.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_alias.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  ko  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:ÀÌ ¸ðµâÀº ¹Ìµð¾îÁ¾·ù³ª ¿äû¸Þ¼­µå¿¡ µû¶ó CGI ½ºÅ©¸³Æ®¸¦ ½ÇÇàÇÑ´Ù.
    »óÅÂ:Base
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_alias.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_alias.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_alias.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_alias.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:ホストファイルシステム上��������場所を ドキュメントツリー�マップ�る機能�� URL �リダイレクトを行��機能を�供�る
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_alias.html.tr.utf8 apache2-2.4.71/docs/manual/mod/mod_alias.html.tr.utf8 --- apache2-2.4.69/docs/manual/mod/mod_alias.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_alias.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,7 +32,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.
    ¼³¸í:ÆÄÀϽýºÅÛÀÇ ´Ù¸¥ ºÎºÐµéÀ» ¹®¼­ °èÃþ±¸Á¶¿¡ Æ÷ÇÔÇϰí, URL ¸®´ÙÀÌ·º¼ÇÀ» Á¦°øÇÑ´Ù
    »óÅÂ:Base
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_asis.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_asis.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_asis.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_asis.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    Açıklama:Belge aÄŸacının parçalarının dosya sisteminin parçalarıyla eÅŸlenmesini saÄŸlar ve URL yönlendirmesi yapar.
    Durum:Temel
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_asis.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_asis.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_asis.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_asis.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:自分用� HTTP ヘッダ�書�れ��るファイルを�信�る
    ステータス:Base
    モジュール識別�:asis_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_auth_basic.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_auth_basic.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_auth_basic.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_auth_basic.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:HTTP Çì´õ¸¦ Æ÷ÇÔÇÑ ÆÄÀÏÀ» º¸³½´Ù
    »óÅÂ:Base
    ¸ðµâ¸í:asis_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_auth_basic.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_auth_basic.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_auth_basic.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_auth_basic.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:基本�証
    ステータス:Base
    モジュール識別�:auth_basic_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_auth_digest.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_auth_digest.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_auth_digest.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_auth_digest.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  fr  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    ¼³¸í:Basic authentication
    »óÅÂ:Base
    ¸ðµâ¸í:auth_basic_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_auth_form.html.en apache2-2.4.71/docs/manual/mod/mod_auth_form.html.en --- apache2-2.4.69/docs/manual/mod/mod_auth_form.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_auth_form.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -156,7 +156,7 @@ attempts should be redirected to a login form created by the website for this purpose, using the AuthFormLoginRequiredLocation directive. Typically this login page will contain an HTML form, asking the user to - provide their usename and password.

    + provide their username and password.

    Example login form

    <form method="POST" action="/dologin.html">
       Username: <input type="text" name="httpd_username" value="" />
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_authn_anon.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_authn_anon.html.ja.utf8
    --- apache2-2.4.69/docs/manual/mod/mod_authn_anon.html.ja.utf8	2026-05-31 13:57:20.000000000 +0000
    +++ apache2-2.4.71/docs/manual/mod/mod_authn_anon.html.ja.utf8	2026-11-09 03:07:05.000000000 +0000
    @@ -31,10 +31,6 @@
      ja  |
      ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:MD5 Digest AuthenticationÀ» »ç¿ëÇÑ »ç¿ëÀÚÀÎÁõ.
    »óÅÂ:Experimental
    ¸ðµâ¸í:auth_digest_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_authn_anon.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_authn_anon.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_authn_anon.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_authn_anon.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:�証�必��領域�� "anonymous" ユーザ�アクセスを許��る
    ステータス:Extension
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_authn_dbm.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_authn_dbm.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_authn_dbm.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_authn_dbm.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:ÀÎÁõ¿µ¿ª¿¡ "À͸í(anonymous)" »ç¿ëÀÚÀÇ Á¢±ÙÀ» Çã¿ëÇÑ´Ù
    »óÅÂ:Extension
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_authn_dbm.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_authn_dbm.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_authn_dbm.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_authn_dbm.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:DBM ファイルを用��ユーザ�証
    ステータス:Extension
    モジュール識別�:authn_dbm_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_authn_file.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_authn_file.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_authn_file.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_authn_file.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:DBM ÆÄÀÏÀ» »ç¿ëÇÑ »ç¿ëÀÚ ÀÎÁõ
    »óÅÂ:Extension
    ¸ðµâ¸í:authn_dbm_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_authn_file.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_authn_file.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_authn_file.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_authn_file.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:テキストファイルを用��ユーザ�証
    ステータス:Base
    モジュール識別�:authn_file_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_authnz_fcgi.html.en apache2-2.4.71/docs/manual/mod/mod_authnz_fcgi.html.en --- apache2-2.4.69/docs/manual/mod/mod_authnz_fcgi.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_authnz_fcgi.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -41,7 +41,7 @@ authenticate users and authorize access to resources. It supports generic FastCGI authorizers which participate in a single phase for authentication and authorization as well as Apache httpd-specific - authenticators and authorizors which participate in one or both + authenticators and authorizers which participate in one or both phases.

    FastCGI authorizers can authenticate using user id and password, diff -Nru apache2-2.4.69/docs/manual/mod/mod_authz_dbm.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_authz_dbm.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_authz_dbm.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_authz_dbm.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  fr  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    ¼³¸í:¹®ÀÚÆÄÀÏÀ» ÀÌ¿ëÇÑ »ç¿ëÀÚ ÀÎÁõ
    »óÅÂ:Base
    ¸ðµâ¸í:authn_file_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_authz_groupfile.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_authz_groupfile.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_authz_groupfile.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_authz_groupfile.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:DBM ÆÄÀÏÀ» »ç¿ëÇÑ ±×·ì ÀÎÁõ
    »óÅÂ:Extension
    ¸ðµâ¸í:authz_dbm_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_authz_groupfile.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_authz_groupfile.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_authz_groupfile.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_authz_groupfile.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:プレーンテキストファイルを用��グループ承�
    ステータス:Base
    モジュール識別�:authz_groupfile_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_authz_owner.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_authz_owner.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_authz_owner.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_authz_owner.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:ÀÏ¹Ý ¹®ÀÚÆÄÀÏÀ» ÀÌ¿ëÇÑ ±×·ì ±ÇÇѺο©
    »óÅÂ:Base
    ¸ðµâ¸í:authz_groupfile_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_authz_owner.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_authz_owner.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_authz_owner.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_authz_owner.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:ファイル�所有者�基���承�
    ステータス:Extension
    モジュール識別�:authz_owner_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_authz_user.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_authz_user.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_authz_user.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_authz_user.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:ÆÄÀÏ ¼ÒÀ¯ÀÚ¸¦ ÀÌ¿ëÇÑ ±ÇÇѺο©
    »óÅÂ:Extension
    ¸ðµâ¸í:authz_owner_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_authz_user.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_authz_user.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_authz_user.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_authz_user.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:ユーザ承�
    ステータス:Base
    モジュール識別�:authz_user_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_autoindex.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_autoindex.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_autoindex.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_autoindex.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  ko  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:»ç¿ëÀÚ ±ÇÇѺο©
    »óÅÂ:Base
    ¸ðµâ¸í:authz_user_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_autoindex.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_autoindex.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_autoindex.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_autoindex.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:Unix � ls コマンドや Win32 � dir シェルコマンド�似� ディレクトリインデックスを生��る
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_autoindex.html.tr.utf8 apache2-2.4.71/docs/manual/mod/mod_autoindex.html.tr.utf8 --- apache2-2.4.69/docs/manual/mod/mod_autoindex.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_autoindex.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,7 +32,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.
    ¼³¸í:ÀÚµ¿À¸·Î À¯´Ð½ºÀÇ ls ¸í·É¾î³ª Win32ÀÇ dir ½©¸í·É¾î¿Í À¯»çÇÑ µð·ºÅ丮 ¸ñ·ÏÀ» ¸¸µç´Ù
    »óÅÂ:Base
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_cache.html.en apache2-2.4.71/docs/manual/mod/mod_cache.html.en --- apache2-2.4.69/docs/manual/mod/mod_cache.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_cache.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -65,7 +65,7 @@

    In the default configuration, mod_cache inserts the caching filter as far forward as possible within the filter stack, - utilising the quick handler to bypass all per request + utilizing the quick handler to bypass all per request processing when returning content to the client. In this mode of operation, mod_cache may be thought of as a caching proxy server bolted to the front of the webserver, while running within @@ -75,7 +75,7 @@ CacheQuickHandler directive, it becomes possible to insert the CACHE filter at a point in the filter stack chosen by the administrator. This provides the - opportunity to cache content before that content is personalised by the + opportunity to cache content before that content is personalized by the mod_include filter, or optionally compressed by the mod_deflate filter.

    @@ -262,7 +262,7 @@

    While this mode offers the best performance, the administrator may find that under certain circumstances they may want to perform further processing on the - request after the request is cached, such as to inject personalisation into the + request after the request is cached, such as to inject personalization into the cached page, or to apply authorization restrictions to the content. Under these circumstances, an administrator is often forced to place independent reverse proxy servers either behind or in front of the caching server to achieve this.

    @@ -285,7 +285,7 @@ AddOutputFilterByType CACHE;DEFLATE text/plain -

    Another option is to have content cached before personalisation is applied +

    Another option is to have content cached before personalization is applied by mod_include (or another content processing filter). In this example templates containing tags understood by mod_include are cached before being parsed:

    diff -Nru apache2-2.4.69/docs/manual/mod/mod_cache.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_cache.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_cache.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_cache.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    Açıklama:Unix ls veya Win32 dir kabuk komutunun yaptığı gibi dizin içeriÄŸini listeler.
    Durum:Temel
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_cache.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_cache.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_cache.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_cache.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:URI をキー���コンテンツ�キャッシュ
    ステータス:Extension
    モジュール識別�:cache_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_cache_disk.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_cache_disk.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_cache_disk.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_cache_disk.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:URI¸¦ Ű·Î »ç¿ëÇÏ¿© ³»¿ëÀ» ij½¬ÇÑ´Ù.
    »óÅÂ:Experimental
    ¸ðµâ¸í:cache_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_cache_disk.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_cache_disk.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_cache_disk.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_cache_disk.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:URI をキー���コンテンツキャッシュストレージ管�
    ステータス:Extension
    モジュール識別�:cache_disk_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_cern_meta.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_cern_meta.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_cern_meta.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_cern_meta.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  fr  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    ¼³¸í:Content cache storage manager keyed to URIs
    »óÅÂ:Experimental
    ¸ðµâ¸í:cache_disk_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_cgi.html.en apache2-2.4.71/docs/manual/mod/mod_cgi.html.en --- apache2-2.4.69/docs/manual/mod/mod_cgi.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_cgi.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -50,7 +50,7 @@ our tutorial on Dynamic Content With CGI.

    -

    When using a multi-threaded MPM under unix, the module +

    When using a multi-threaded MPM under UNIX, the module mod_cgid should be used in place of this module. At the user level, the two modules are essentially identical.

    diff -Nru apache2-2.4.69/docs/manual/mod/mod_cgi.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_cgi.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_cgi.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_cgi.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:CERN À¥¼­¹ö ¸ÞŸÆÄÀÏ Áö¿ø
    »óÅÂ:Extension
    ¸ðµâ¸í:cern_meta_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_cgi.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_cgi.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_cgi.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_cgi.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:CGI スクリプト�実行
    ステータス:Base
    モジュール識別�:cgi_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_cgid.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_cgid.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_cgid.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_cgid.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:CGI ½ºÅ©¸³Æ® ½ÇÇà
    »óÅÂ:Base
    ¸ðµâ¸í:cgi_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_cgid.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_cgid.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_cgid.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_cgid.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:外部 CGI デーモンを使�� CGI スクリプト�実行
    ステータス:Base
    モジュール識別�:cgid_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_charset_lite.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_charset_lite.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_charset_lite.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_charset_lite.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  fr  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    ¼³¸í:¿ÜºÎ CGI µ¥¸óÀ» »ç¿ëÇÏ¿© CGI ½ºÅ©¸³Æ®¸¦ ½ÇÇà
    »óÅÂ:Base
    ¸ðµâ¸í:cgid_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_dav.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_dav.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_dav.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_dav.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:¹®ÀÚÁýÇÕ º¯È¯À» ÁöÁ¤
    »óÅÂ:Experimental
    ¸ðµâ¸í:charset_lite_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_dav.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_dav.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_dav.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_dav.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:分散オーサリング��ージョン管� (WebDAV) 機能
    ステータス:Extension
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_dav_fs.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_dav_fs.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_dav_fs.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_dav_fs.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:Distributed Authoring and Versioning (WebDAV) ±â´É
    »óÅÂ:Extension
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_dav_fs.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_dav_fs.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_dav_fs.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_dav_fs.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:mod_dav ����ファイルシステムプロ�イダ
    ステータス:Extension
    モジュール識別�:dav_fs_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_dbd.html.en apache2-2.4.71/docs/manual/mod/mod_dbd.html.en --- apache2-2.4.69/docs/manual/mod/mod_dbd.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_dbd.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -72,7 +72,7 @@

    This module manages database connections, in a manner optimized for the platform. On non-threaded platforms, it provides a persistent connection in the manner of - classic LAMP (Linux, Apache, Mysql, Perl/PHP/Python). + classic LAMP (Linux, Apache, MySQL, Perl/PHP/Python). On threaded platform, it provides an altogether more scalable and efficient connection pool, as described in this @@ -290,7 +290,7 @@ password, database name, hostname and port number for connection.

    Connection string parameters for current drivers include:

    -
    FreeTDS (for MSSQL and SyBase)
    +
    FreeTDS (for MSSQL and Sybase)
    username, password, appname, dbname, host, charset, lang, server
    MySQL
    host, port, user, pass, dbname, sock, flags, fldsz, group, reconnect
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_deflate.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_deflate.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_deflate.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_deflate.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@
     ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:mod_davÀ» À§ÇÑ ÆÄÀϽýºÅÛ Á¦°øÀÚ
    »óÅÂ:Extension
    ¸ðµâ¸í:dav_fs_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_deflate.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_deflate.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_deflate.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_deflate.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:クライアント��られる��コンテンツを圧縮�る
    ステータス:Extension
    モジュール識別�:deflate_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_dir.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_dir.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_dir.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_dir.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  ko  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:³»¿ëÀ» Ŭ¶óÀÌ¾ðÆ®·Î º¸³»±â Àü¿¡ ¾ÐÃàÇÑ´Ù
    »óÅÂ:Extension
    ¸ðµâ¸í:deflate_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_dir.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_dir.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_dir.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_dir.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:「最後�スラッシュ��リダイレクト��ディレクトリ� インデックスファイルを扱�機能を�供�る
    ステータス:Base
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_dir.html.tr.utf8 apache2-2.4.71/docs/manual/mod/mod_dir.html.tr.utf8 --- apache2-2.4.69/docs/manual/mod/mod_dir.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_dir.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,7 +32,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.
    ¼³¸í:"¸¶Áö¸· ½½·¡½¬" ¸®´ÙÀÌ·º¼ÇÀ» Á¦°øÇÏ°í µð·ºÅ丮 index ÆÄÀÏÀ» ¼­ºñ½ºÇÑ´Ù
    »óÅÂ:Base
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_dumpio.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_dumpio.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_dumpio.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_dumpio.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  fr  |  ja 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    Açıklama:Bölü çizgisiyle biten yönlendirmeleri yapar ve dizin içeriÄŸi dosyalarını sunar.
    Durum:Temel
    Modül Betimleyici:dir_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_echo.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_echo.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_echo.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_echo.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:望むよ������ I/O をエラーログ�ダンプ�る
    ステータス:Extension
    モジュール識別�:dumpio_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_env.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_env.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_env.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_env.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  ko  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:ÇÁ·ÎÅäÄÝ ¸ðµâÀ» ¼³¸íÇϱâÀ§ÇÑ °£´ÜÇÑ echo ¼­¹ö
    »óÅÂ:Experimental
    ¸ðµâ¸í:echo_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_env.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_env.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_env.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_env.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:CGI スクリプト�� SSI ページ�渡�れる環境変数を変更�る機能を�供�る
    ステータス:Base
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_example_hooks.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_example_hooks.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_example_hooks.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_example_hooks.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  fr  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    ¼³¸í:CGI ½ºÅ©¸³Æ®³ª SSI ÆäÀÌÁö¿¡ Àü´ÞÇÒ È¯°æº¯¼ö¸¦ ¼öÁ¤ÇÑ´Ù
    »óÅÂ:Base
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_expires.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_expires.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_expires.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_expires.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:¾ÆÆÄÄ¡ ¸ðµâ API¸¦ ¼³¸íÇÑ´Ù
    »óÅÂ:Experimental
    ¸ðµâ¸í:example_hooks_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_expires.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_expires.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_expires.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_expires.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:ユーザ�指定��基準�基��� Expires � Cache-Control HTTP ヘッダ�生�
    ステータス:Extension
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_ext_filter.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_ext_filter.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_ext_filter.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_ext_filter.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:»ç¿ëÀÚ°¡ ÁöÁ¤ÇÑ ±âÁØ¿¡ µû¶ó Expires¿Í Cache-Control HTTP Çì´õ¸¦ »ý¼ºÇÑ´Ù
    »óÅÂ:Extension
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_ext_filter.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_ext_filter.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_ext_filter.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_ext_filter.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:レス�ンス�ボディをクライアント��る��外部プログラム�処��る
    ステータス:Extension
    モジュール識別�:ext_filter_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_file_cache.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_file_cache.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_file_cache.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_file_cache.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  fr  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    ¼³¸í:ÀÀ´ä ³»¿ëÀ» ¿ÜºÎ ÇÁ·Î±×·¥À¸·Î ó¸®ÇÑ ÈÄ Å¬¶óÀÌ¾ðÆ®·Î º¸³½´Ù
    »óÅÂ:Extension
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_filter.html.en apache2-2.4.71/docs/manual/mod/mod_filter.html.en --- apache2-2.4.69/docs/manual/mod/mod_filter.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_filter.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -79,7 +79,7 @@

    mod_filter by contrast gives server administrators a great deal of flexibility in configuring the filter chain. In fact, filters can be inserted based on complex boolean - expressions This generalises the limited + expressions This generalizes the limited flexibility offered by AddOutputFilterByType.

    top
    @@ -453,7 +453,7 @@ the HTTP Cache-Control: no-transform header.
    cache=no
    -
    The filter renders the output uncacheable (eg by introducing randomised +
    The filter renders the output uncacheable (eg by introducing randomized content changes)
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_headers.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_headers.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_headers.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_headers.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:¸Þ¸ð¸®¿¡ Á¤Àû ÆÄÀϵéÀ» ij½¬
    »óÅÂ:Experimental
    ¸ðµâ¸í:file_cache_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_headers.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_headers.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_headers.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_headers.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:HTTP リクエスト�ヘッダ�応答�ヘッダ�カスタマイズ
    ステータス:Extension
    モジュール識別�:headers_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_http2.html.en apache2-2.4.71/docs/manual/mod/mod_http2.html.en --- apache2-2.4.69/docs/manual/mod/mod_http2.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_http2.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -233,7 +233,7 @@

    This directive influences how file content is handled in responses. When off, which is the default, file handles - are passed from the requestion processing down to the main + are passed from the request processing down to the main connection, using the usual Apache setaside handling for managing the lifetime of the file.

    @@ -512,7 +512,7 @@ <VirtualHost>s.

    - The security checks require that the TSL protocol is at least + The security checks require that the TLS protocol is at least TLSv1.2 and that none of the ciphers listed in RFC 7540, Appendix A is used. These checks will be extended once new security requirements come into place. @@ -553,7 +553,7 @@ The directive H2OutputBuffering controls the buffering of stream output. The default is on, which is the behavior of previous versions. When off, all bytes are made available immediately to the main connection for sending them - out to the client. This fixes interop issues with certain flavours of gRPC. + out to the client. This fixes interop issues with certain flavors of gRPC.

    diff -Nru apache2-2.4.69/docs/manual/mod/mod_ident.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_ident.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_ident.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_ident.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:HTTP ¿äû Çì´õ¿Í ÀÀ´ä Çì´õ ¼öÁ¤
    »óÅÂ:Extension
    ¸ðµâ¸í:headers_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_ident.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_ident.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_ident.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_ident.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:RFC 1413 ident lookups
    ステータス:Extension
    モジュール識別�:ident_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_imagemap.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_imagemap.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_imagemap.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_imagemap.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  fr  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    ¼³¸í:RFC 1413 ident °Ë»ö
    »óÅÂ:Extension
    ¸ðµâ¸í:ident_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_include.html.en apache2-2.4.71/docs/manual/mod/mod_include.html.en --- apache2-2.4.69/docs/manual/mod/mod_include.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_include.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -132,7 +132,7 @@ Note that the comment terminator (-->) should be preceded by whitespace to ensure that it isn't considered part of an SSI token. Note that the leading <!--# is one - token and may not contain any whitespaces.

    + token and may not contain any whitespace.

    The allowed elements are listed in the following table:

    diff -Nru apache2-2.4.69/docs/manual/mod/mod_include.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_include.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_include.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_include.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  fr  |  ja 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:¼­¹öÃø À̹ÌÁö¸Ê(imagemap) ó¸®
    »óÅÂ:Base
    ¸ðµâ¸í:imagemap_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_info.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_info.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_info.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_info.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    説明:サー��パース�る html ドキュメント (Server Side Includes)
    ステータス:Base
    モジュール識別�:include_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_info.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_info.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_info.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_info.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:サー��設定�包括的�概観を�供�る
    ステータス:Extension
    モジュール識別�:info_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_isapi.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_isapi.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_isapi.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_isapi.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  fr  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    ¼³¸í:¼­¹ö ¼³Á¤¿¡ ´ëÇÑ Á¾ÇÕÀûÀÎ Á¤º¸¸¦ º¸¿©ÁØ´Ù
    »óÅÂ:Extension
    ¸ðµâ¸í:info_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_log_config.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_log_config.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_log_config.html.ja.utf8 2026-06-08 12:25:13.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_log_config.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  ko  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:Windows¿ë ¾ÆÆÄÄ¡¿¡¼­ ISAPI Extension »ç¿ë
    »óÅÂ:Base
    ¸ðµâ¸í:isapi_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_log_config.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_log_config.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_log_config.html.ko.euc-kr 2026-06-08 12:25:13.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_log_config.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:サー���リクエスト�ロギング
    ステータス:Base
    モジュール識別�:log_config_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_log_config.html.tr.utf8 apache2-2.4.71/docs/manual/mod/mod_log_config.html.tr.utf8 --- apache2-2.4.69/docs/manual/mod/mod_log_config.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_log_config.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,7 +32,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.
    ¼³¸í:¼­¹ö·ÎÀÇ ¿äûÀ» ·Î±×¿¡ ±â·ÏÇÑ´Ù
    »óÅÂ:Base
    ¸ðµâ¸í:log_config_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_log_forensic.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_log_forensic.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_log_forensic.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_log_forensic.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    Açıklama:Sunucuya yapılan isteklerin günlük kayıtlarının tutulması
    Durum:Temel
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_logio.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_logio.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_logio.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_logio.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  ko  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    説明:サー���られ�リクエスト� forensic ロギング
    ステータス:Extension
    モジュール識別�:log_forensic_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_logio.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_logio.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_logio.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_logio.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:リクエスト毎�入力�イト数�出力�イト数�をロギング
    ステータス:Extension
    モジュール識別�:logio_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_logio.html.tr.utf8 apache2-2.4.71/docs/manual/mod/mod_logio.html.tr.utf8 --- apache2-2.4.69/docs/manual/mod/mod_logio.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_logio.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,7 +32,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.
    ¼³¸í:¿äû´ç ÀÔÃâ·Â ¹ÙÀÌÆ®¼ö¸¦ ±â·Ï
    »óÅÂ:Extension
    ¸ðµâ¸í:logio_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_lua.html.en apache2-2.4.71/docs/manual/mod/mod_lua.html.en --- apache2-2.4.69/docs/manual/mod/mod_lua.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_lua.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -1089,7 +1089,7 @@

    Mod_lua implements a simple database feature for querying and running commands - on the most popular database engines (mySQL, PostgreSQL, FreeTDS, ODBC, SQLite, Oracle) + on the most popular database engines (MySQL, PostgreSQL, FreeTDS, ODBC, SQLite, Oracle) as well as mod_dbd.

    The dbType to use as the first parameter of dbacquire diff -Nru apache2-2.4.69/docs/manual/mod/mod_md.html.en apache2-2.4.71/docs/manual/mod/mod_md.html.en --- apache2-2.4.69/docs/manual/mod/mod_md.html.en 2026-07-25 13:10:55.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_md.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -129,7 +129,7 @@

    And the `tls-alpn-01` challenge type is available. -

    +

    +
    Açıklama:Her isteÄŸin girdi ve çıktı uzunluklarının günlüklenmesi.
    Durum:Eklenti
    Context:server config
    Status:Experimental
    Module:mod_md
    Compatibility:Since version 2.4.70, this can be configured separately for each MDomain.

    This is mainly used in test setups where the module needs to @@ -532,7 +534,7 @@

    top

    MDCertificateKeyFile Directive

    - + @@ -775,9 +777,39 @@ +
    Description:Specify a static private key for for the static cerrtificate.
    Description:Specify a static private key for the static certificate.
    Syntax:MDCertificateKeyFile path-to-file
    Context:server config
    Status:Experimental
    Context:server config
    Status:Experimental
    Module:mod_md
    Compatibility:Since version 2.4.70, a proxy can be configured separately for each MDomain.
    -

    Use a http proxy to connect to the MDCertificateAuthority. Define this - if your webserver can only reach the internet with a forward proxy. +

    + Use the given http forward proxy URL to connect to the MDCertificateAuthority. + Define this if your webserver can only reach the internet with a forward proxy. +

    + +
    +
    top
    +

    MDHttpProxyCACertificateFile Directive

    + + + + + + + + +
    Description:Sets the root (CA) certificates to use for TLS connections to the http-proxy.
    Syntax:MDHttpProxyCACertificateFile path-to-pem-file
    Default:MDHttpProxyCACertificateFile none
    Context:server config
    Status:Experimental
    Module:mod_md
    Compatibility:Available in version 2.4.70 and later
    +

    + This is used for connections to the HTTPS forward proxy (MDHttpProxy). + It is needed if the certificate of the HTTPS proxy cannot be verified using the general CA root store. + This is sometimes the case in test setups or enterprise environments. +

    +

    + The certificate of the ACME server is verified with the root certificates set by + MDCACertificateFile, so you might need to use both settings. +

    +

    + Use "none" as path to disable explicitly. +

    +

    + This can be configured separately for each MDomain.

    @@ -1431,6 +1463,7 @@
    Context:server config
    Status:Experimental
    Module:mod_md
    Compatibility:The default was changed to 'off' in 2.4.69

    If enabled, adds a section to the diff -Nru apache2-2.4.69/docs/manual/mod/mod_md.html.fr.utf8 apache2-2.4.71/docs/manual/mod/mod_md.html.fr.utf8 --- apache2-2.4.69/docs/manual/mod/mod_md.html.fr.utf8 2026-07-18 14:52:34.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_md.html.fr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -355,6 +355,7 @@

  • MDDriveMode
  • MDExternalAccountBinding
  • MDHttpProxy
  • +
  • MDHttpProxyCACertificateFile
  • MDInitialDelay
  • MDMatchNames
  • MDMember
  • @@ -432,7 +433,7 @@
    Contexte:configuration globale
    Statut:Expérimental
    Module:mod_md
    Compatibilité:Depuis la version 2.4.69, cette directive peut être +
    Compatibilité:Depuis la version 2.4.70, cette directive peut être définie séparément pour chaque MDomain.

    @@ -896,7 +897,7 @@

    Contexte:configuration globale
    Statut:Expérimental
    Module:mod_md
    Compatibilité:À partir de la version 2.4.69, un mandataire peut être +
    Compatibilité:À partir de la version 2.4.70, un mandataire peut être configuré séparément pour chaque MDomain.

    Utiliser l’URL du serveur http mandataire direct donné @@ -908,6 +909,40 @@

    top
    +

    Directive MDHttpProxyCACertificateFile

    + + + + + + + + +
    Description:Définition des certificats racine (CA) à utiliser pour les + connexions TLS avec le mandataire http.
    Syntaxe:MDHttpProxyCACertificateFile path-to-pem-file
    Défaut:MDHttpProxyCACertificateFile none
    Contexte:configuration globale
    Statut:Expérimental
    Module:mod_md
    Compatibilité:Disponible à partir de la version 2.4.70 du serveur HTTP + Apache
    +

    + Cette directive est utilisée pour les connexions avec le + mandataire HTTPS direct (directive MDHttpProxy). Elle est requise si le + certificat du mandataire HTTPS ne peut pas être vérifié en + utilisant le magasin général de la racine du CA. Cela se produit + parfois dans les environnements de test ou au sein d’une + entreprise. +

    +

    + Le certificat du serveur ACME est vérifié avec les certificats + racine définis via la directive MDCACertificateFile ; vous serez + donc amené à utiliser les deux définitions. +

    +

    + Utilisez « none » comme chemin pour désactiver cette + fonctionnalité explicitement. +

    +

    Cette directive peut être configurée séparément pour chaque + MDomain.

    + +
    +
    top

    Directive MDInitialDelay

    @@ -1654,6 +1689,8 @@ +
    Description:Le délai avant la première vérification de certificat.
    Contexte:configuration globale
    Statut:Expérimental
    Module:mod_md
    Compatibilité:La valeur par défaut est « off » à partir de la version + 2.4.69 du serveur HTTP Apache

    Si cette directive est activée, une section est ajoutée au gestionnaire « server-status » de diff -Nru apache2-2.4.69/docs/manual/mod/mod_mime.html.en apache2-2.4.71/docs/manual/mod/mod_mime.html.en --- apache2-2.4.69/docs/manual/mod/mod_mime.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_mime.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -761,7 +761,7 @@ never expected to be served.

    For example, the following configuration will allow handlers - and filters to participate in Multviews, but will exclude unknown + and filters to participate in MultiViews, but will exclude unknown files:

    MultiviewsMatch Handlers Filters
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_mime.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_mime.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_mime.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_mime.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  fr  |  ja 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_negotiation.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_negotiation.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_negotiation.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_negotiation.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  fr  |  ja 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    説明:リクエスト�れ�ファイル�拡張��ファイル�振る舞� (�ンドラ�フィルタ)�内容 (MIME タイプ�言語�文字セット�エンコーディング) �を関連付�る
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_proxy.html.en apache2-2.4.71/docs/manual/mod/mod_proxy.html.en --- apache2-2.4.69/docs/manual/mod/mod_proxy.html.en 2026-09-03 12:30:26.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_proxy.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -65,7 +65,7 @@
    説明:コンテント�ゴシエーション 機能を�供�る
    ステータス:Base
    - @@ -764,7 +764,7 @@

    Differences from the Location configuration section

    -

    A backend URL matches the configuration section if it begins with the +

    A backend URL matches the configuration section if it begins with the wildcard-url string, even if the last path segment in the directive only matches a prefix of the backend URL. For example, <Proxy "http://example.com/foo"> matches all of @@ -1627,7 +1627,7 @@

    Additional ProxyPass Keywords

    -

    Normally, mod_proxy will canonicalise ProxyPassed URLs. +

    Normally, mod_proxy will canonicalize ProxyPassed URLs. But this may be incompatible with some backends, particularly those that make use of PATH_INFO. The optional nocanon keyword suppresses this and passes the URL path "raw" to the diff -Nru apache2-2.4.69/docs/manual/mod/mod_proxy.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_proxy.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_proxy.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_proxy.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  fr  |  ja 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ProtocolModule
    AJP13 (Apache JServe Protocol version +
    AJP13 (Apache JServ Protocol version 1.3)mod_proxy_ajp
    CONNECT (for SSL)mod_proxy_connect
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_proxy_ajp.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_proxy_ajp.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_proxy_ajp.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_proxy_ajp.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  fr  |  ja 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    説明:HTTP/1.1 プロキシ/ゲートウェイサー�
    ステータス:Extension
    モジュール識別�:proxy_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_proxy_balancer.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_proxy_balancer.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_proxy_balancer.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_proxy_balancer.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  fr  |  ja 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    説明:mod_proxy � AJP をサ�ート�る���モジュール
    ステータス:Extension
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_proxy_connect.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_proxy_connect.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_proxy_connect.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_proxy_connect.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  fr  |  ja 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    説明:負�分散���� mod_proxy 拡張
    ステータス:Extension
    モジュール識別�:proxy_balancer_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_proxy_hcheck.html.en apache2-2.4.71/docs/manual/mod/mod_proxy_hcheck.html.en --- apache2-2.4.69/docs/manual/mod/mod_proxy_hcheck.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_proxy_hcheck.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -183,7 +183,7 @@
    The expression can use curly-parens ("{}") as - quoting deliminators in addition to normal quotes. + quoting delimiters in addition to normal quotes.

    If using a health check method (eg: GET) which results in a response diff -Nru apache2-2.4.69/docs/manual/mod/mod_proxy_html.html.en apache2-2.4.71/docs/manual/mod/mod_proxy_html.html.en --- apache2-2.4.69/docs/manual/mod/mod_proxy_html.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_proxy_html.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -164,7 +164,7 @@

    説明:CONNECT リクエストを扱� mod_proxy 用�拡張
    ステータス:Extension

    A simple switch to enable or disable the proxy_html filter. If mod_xml2enc is loaded it will also automatically - set up internationalisation support.

    + set up internationalization support.

    Note that the proxy_html filter will only act on HTML data (Content-Type text/html or application/xhtml+xml) and when the data are proxied. You can override this (at your own risk) by @@ -326,7 +326,7 @@ <head> sections.

    If not required, turning ProxyHTMLMeta Off will give a small performance boost by skipping this parse step. However, it - is sometimes necessary for internationalisation to work correctly.

    + is sometimes necessary for internationalization to work correctly.

    ProxyHTMLMeta has two effects. Firstly and most importantly it enables detection of character encodings declared in the form

    <meta http-equiv="Content-Type" content="text/html;charset=foo">
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_ratelimit.html.en apache2-2.4.71/docs/manual/mod/mod_ratelimit.html.en --- apache2-2.4.69/docs/manual/mod/mod_ratelimit.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_ratelimit.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -51,6 +51,17 @@ specified rate limit. This value is optional, and is set using the environment variable rate-initial-burst.

    +

    Resource consumption

    +

    Rate limiting is implemented by sleeping between writes to the client, +so each rate-limited response occupies a server thread (or process) for +the entire, deliberately prolonged, duration of the transfer. This applies +to all MPMs, including event. A modest number of slow, +rate-limited downloads can exhaust the server's available workers and +prevent other requests from being served. Use this module with caution, +limit it to the resources that need it, and size +MaxRequestWorkers accordingly.

    +
    +

    Example Configuration

    <Location "/downloads">
         SetOutputFilter RATE_LIMIT
         SetEnv rate-limit 400 
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_ratelimit.html.fr.utf8 apache2-2.4.71/docs/manual/mod/mod_ratelimit.html.fr.utf8
    --- apache2-2.4.69/docs/manual/mod/mod_ratelimit.html.fr.utf8	2026-05-31 13:57:20.000000000 +0000
    +++ apache2-2.4.71/docs/manual/mod/mod_ratelimit.html.fr.utf8	2026-11-09 03:07:05.000000000 +0000
    @@ -53,6 +53,20 @@
     kOctets à transmettre à pleine vitesse avant de limiter la bande passante à la
     valeur voulue.

    +

    Consommation de ressources

    +

    La limitation de bande passante est implémentée en insérant des pauses entre +les écritures vers le client ; ainsi, chaque réponse faisant l’objet d’une +limitation de bande passante mobilise un thread du serveur (ou un processus) +pour la durée entière du transfert délibérément prolongé. Cela concerne tous les +MPMs, y compris event. Un certain nombre, même modeste, de +téléchargements lents limités en bande passante peut entraîner le dépassement du +nombre de threads ou processus de travail du serveur disponibles et empêcher le +traitement des autres requêtes. Il est donc recommandé d’utiliser ce module avec +prudence, de limiter son utilisation aux ressources qui en ont vraiment besoin +et de définir MaxRequestWorkers en +conséquence.

    +
    +

    Exemple de configuration

    <Location "/downloads">
         SetOutputFilter RATE_LIMIT
         SetEnv rate-limit 400
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_request.html.tr.utf8 apache2-2.4.71/docs/manual/mod/mod_request.html.tr.utf8
    --- apache2-2.4.69/docs/manual/mod/mod_request.html.tr.utf8	2026-05-31 13:57:20.000000000 +0000
    +++ apache2-2.4.71/docs/manual/mod/mod_request.html.tr.utf8	2026-11-09 03:07:05.000000000 +0000
    @@ -30,7 +30,6 @@
      fr  |
      tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_setenvif.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_setenvif.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_setenvif.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_setenvif.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  ko  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    Açıklama:HTTP istek gövdelerini iÅŸleme sokup kullanılabilir kılan süzgeçler
    Durum:Temel
    Modül Betimleyici:request_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_setenvif.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_setenvif.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_setenvif.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_setenvif.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:リクエスト�特徴�基���環境変数�設定を�能��る
    ステータス:Base
    モジュール識別�:setenvif_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_setenvif.html.tr.utf8 apache2-2.4.71/docs/manual/mod/mod_setenvif.html.tr.utf8 --- apache2-2.4.69/docs/manual/mod/mod_setenvif.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_setenvif.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,7 +32,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.
    ¼³¸í:¿äûÀÇ ¼º°Ý¿¡ µû¶ó ȯ°æº¯¼ö ¼³Á¤À» º¯°æÇÑ´Ù
    »óÅÂ:Base
    ¸ðµâ¸í:setenvif_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_so.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_so.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_so.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_so.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  ko  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    Açıklama:Ortam deÄŸiÅŸkenlerinin isteÄŸin özelliklerine uygun olarak atanmasını saÄŸlar
    Durum:Temel
    Modül Betimleyici:setenvif_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_so.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_so.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_so.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_so.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:起動時や�起動時�実行コード�モジュールをサー��ロード�る
    ステータス:Extension
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_speling.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_speling.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_speling.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_speling.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:½ÃÀÛÇÒ¶§ ȤÀº Àç½ÃÀÛÇÒ¶§ ½ÇÇà°¡´ÉÇÑ ÄÚµå¿Í ¸ðµâÀ» ¼­¹ö·Î ÀоîµéÀδÙ
    »óÅÂ:Extension
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_speling.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_speling.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_speling.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_speling.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:ユーザ�入力������間��� URL を� 大文字�文字�区別を無視�る���一�以下�綴り間��を許容�る��� 修正を試�る
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_status.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_status.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_status.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_status.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  ko  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:»ç¿ëÀÚ°¡ ´ë¼Ò¹®ÀÚ¸¦ À߸ø »ç¿ëÇϰųª ¸ÂÃã¹ýÀÌ Æ²¸®´Â °ÍÀ» Çѹø±îÁö Çã¿ëÇÏ¿© À߸øµÈ URLÀ» °íÄ¡·Á°í ½ÃµµÇÑ´Ù
    »óÅÂ:Extension
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_status.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_status.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_status.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_status.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:サー��活動状��性能�関�る情報を�供�る
    ステータス:Base
    モジュール識別�:status_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_suexec.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_suexec.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_suexec.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_suexec.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  ko  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:¼­¹ö Ȱµ¿°ú ¼º´É¿¡ ´ëÇÑ Á¤º¸¸¦ Á¦°øÇÑ´Ù
    »óÅÂ:Base
    ¸ðµâ¸í:status_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_suexec.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_suexec.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_suexec.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_suexec.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:指定�れ�ユーザ�グループ� CGI スクリプトを実行�る
    ステータス:Extension
    モジュール識別�:suexec_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_unique_id.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_unique_id.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_unique_id.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_unique_id.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ja  |  ko 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:CGI ½ºÅ©¸³Æ®¸¦ ƯÁ¤ »ç¿ëÀÚ¿Í ±×·ì ±ÇÇÑÀ¸·Î ½ÇÇàÇÑ´Ù
    »óÅÂ:Extension
    ¸ðµâ¸í:suexec_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_unique_id.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_unique_id.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_unique_id.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_unique_id.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:�れ�れ�リクエスト�対�る一��識別��入��環境変数を �供�る
    ステータス:Extension
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_userdir.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mod_userdir.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mod_userdir.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_userdir.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  ko  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    ¼³¸í:°¢ ¿äû¸¶´Ù À¯ÀÏÇÑ ½Äº°ÀÚ¸¦ °¡Áö´Â ȯ°æº¯¼ö¸¦ Á¦°øÇÑ´Ù
    »óÅÂ:Extension
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_userdir.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_userdir.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_userdir.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_userdir.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    説明:ユーザ専用�ディレクトリを�供
    ステータス:Base
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_version.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/mod_version.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/mod_version.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_version.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ja  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    ¼³¸í:»ç¿ëÀÚº° µð·ºÅ丮
    »óÅÂ:Base
    ¸ðµâ¸í:userdir_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_vhost_alias.html.tr.utf8 apache2-2.4.71/docs/manual/mod/mod_vhost_alias.html.tr.utf8 --- apache2-2.4.69/docs/manual/mod/mod_vhost_alias.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_vhost_alias.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,7 +30,6 @@  fr  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.
    ¼³¸í:¹öÀüº° ¼³Á¤
    »óÅÂ:Extension
    ¸ðµâ¸í:version_module
    diff -Nru apache2-2.4.69/docs/manual/mod/mod_xml2enc.html.en apache2-2.4.71/docs/manual/mod/mod_xml2enc.html.en --- apache2-2.4.69/docs/manual/mod/mod_xml2enc.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mod_xml2enc.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -29,7 +29,7 @@

    Available Languages:  en  |  fr 

    -
    Açıklama:Kitlesel sanal konakların devingen olarak yapılandırılmasını saÄŸlar
    Durum:Eklenti
    Modül Betimleyici:vhost_alias_module
    - + @@ -727,500 +727,501 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - + - - - + - - - + + - - - - - - - + + - - - - + + - - - - - - - - - + + + + + + + - - - - - - - - + + + + + + - - - - - - - + + + + + + + - - - - - + + + + - - + - - + - - - - + + + - - - - - - + + + + - - - - - - + - - + - - - - - - + + + + - - + - - + - - + - - - - + - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - + - - - - - - + - - + - - - + + + - - - - - - - - - - + - - + - - + - - + - - - + - - - - + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - + - - - - - + + + + - - + - - - - - + - - + - - - + + - - + - - - - - + - - - - - + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - + - - - + - - - - - - - - - - - - + + + + + + - - - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - + + + - - - - + + + - - - - - - + + + + + - - + - - - - - + + + - - - - - + + + + - - + - - + - - - - + + - - + - - - - - - + - - + - +
    Description:Enhanced charset/internationalisation support for libxml2-based + @@ -38,7 +38,7 @@ for 2.2.x versions
    Description:Enhanced charset/internationalization support for libxml2-based filter modules
    Status:Base
    Module Identifier:xml2enc_module

    Summary

    -

    This module provides enhanced internationalisation support for +

    This module provides enhanced internationalization support for markup-aware filter modules such as mod_proxy_html. It can automatically detect the encoding of input data and ensure they are correctly processed by the libxml2 parser, including converting to Unicode (UTF-8) where diff -Nru apache2-2.4.69/docs/manual/mod/module-dict.html.ja.utf8 apache2-2.4.71/docs/manual/mod/module-dict.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/module-dict.html.ja.utf8 2026-09-29 11:57:11.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/module-dict.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,10 +29,6 @@  ko  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

    ��文書� Apache �� モジュール を説明�る��� 使�れ��る用語を説明���。

    diff -Nru apache2-2.4.69/docs/manual/mod/module-dict.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/module-dict.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/module-dict.html.ko.euc-kr 2026-09-29 11:57:11.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/module-dict.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -29,8 +29,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    ÀÌ ¹®¼­´Â ¾ÆÆÄÄ¡ ¸ðµâÀ» ¼³¸íÇϱâÀ§ÇØ »ç¿ëÇÑ ¿ë¾î¸¦ ¼³¸íÇÑ´Ù.

    diff -Nru apache2-2.4.69/docs/manual/mod/module-dict.html.tr.utf8 apache2-2.4.71/docs/manual/mod/module-dict.html.tr.utf8 --- apache2-2.4.69/docs/manual/mod/module-dict.html.tr.utf8 2026-09-29 11:57:11.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/module-dict.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,7 +29,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    Bu belgede Apache modüllerini tanımlarken kullanılan terimler açıklanmıştır.

    diff -Nru apache2-2.4.69/docs/manual/mod/mpm_common.html.de apache2-2.4.71/docs/manual/mod/mpm_common.html.de --- apache2-2.4.69/docs/manual/mod/mpm_common.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mpm_common.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -32,9 +32,6 @@  ja  |  tr 

    -
    Diese Übersetzung ist möglicherweise - nicht mehr aktuell. Bitte prüfen Sie die englische Version auf - die neuesten Änderungen.
    Beschreibung:Eine Sammlung von Direktiven, die in mehr als einem Multi-Processing-Modul (MPM) implementiert sind.
    Status:MPM
    diff -Nru apache2-2.4.69/docs/manual/mod/mpm_common.html.en apache2-2.4.71/docs/manual/mod/mpm_common.html.en --- apache2-2.4.69/docs/manual/mod/mpm_common.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mpm_common.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -113,7 +113,7 @@

    Specific signals

    CoreDumpDirectory processing only occurs for - a select set of fatal signals: SIGFPE, SIGILL, SIGABORT, + a select set of fatal signals: SIGFPE, SIGILL, SIGABRT, SIGSEGV, and SIGBUS.

    On some operating systems, SIGQUIT also results in a core dump but does not go through CoreDumpDirectory or @@ -321,7 +321,7 @@ the same IP address and port

    Setting the SO_REUSEPORT option on the listening socket(s) consequently allows multiple processes (sharing the same EUID, - e.g. root) to bind to the the same IP address and port, + e.g. root) to bind to the same IP address and port, without the binding error raised by the system in the usual case.

    This also means that multiple instances of Apache httpd configured on a same IP:port and with a positive ListenCoresBucketsRatio diff -Nru apache2-2.4.69/docs/manual/mod/mpm_common.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mpm_common.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mpm_common.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mpm_common.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  ja  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    説明:二�以上�マル�プロセッシングモジュール (MPM) �実装�れ��るディレクティブ�コレクション
    ステータス:MPM
    diff -Nru apache2-2.4.69/docs/manual/mod/mpm_common.html.tr.utf8 apache2-2.4.71/docs/manual/mod/mpm_common.html.tr.utf8 --- apache2-2.4.69/docs/manual/mod/mpm_common.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mpm_common.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,7 +32,6 @@  ja  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.
    Açıklama:Birden fazla Çok Süreçlilik Modülü (MPM) tarafından gerçeklenmiÅŸ yönergeler bütünü.
    Durum:MPM
    diff -Nru apache2-2.4.69/docs/manual/mod/mpm_winnt.html.de apache2-2.4.71/docs/manual/mod/mpm_winnt.html.de --- apache2-2.4.69/docs/manual/mod/mpm_winnt.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mpm_winnt.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -31,9 +31,6 @@  fr  |  ja 

    -
    Diese Übersetzung ist möglicherweise - nicht mehr aktuell. Bitte prüfen Sie die englische Version auf - die neuesten Änderungen.
    diff -Nru apache2-2.4.69/docs/manual/mod/mpm_winnt.html.ja.utf8 apache2-2.4.71/docs/manual/mod/mpm_winnt.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/mpm_winnt.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/mpm_winnt.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  fr  |  ja 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    Beschreibung: Das Multi-Processing-Modul ist optimiert für Windows NT.
    Status:MPM
    diff -Nru apache2-2.4.69/docs/manual/mod/prefork.html.de apache2-2.4.71/docs/manual/mod/prefork.html.de --- apache2-2.4.69/docs/manual/mod/prefork.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/prefork.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -32,9 +32,6 @@  ja  |  tr 

    -
    Diese Übersetzung ist möglicherweise - nicht mehr aktuell. Bitte prüfen Sie die englische Version auf - die neuesten Änderungen.
    説明:Windows NT ���最�化�れ�マル�プロセッシングモジュール
    ステータス:MPM
    diff -Nru apache2-2.4.69/docs/manual/mod/prefork.html.ja.utf8 apache2-2.4.71/docs/manual/mod/prefork.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/prefork.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/prefork.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  ja  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    Beschreibung:Implementiert einen im Voraus forkenden Webserver ohne Thread-Unterstützung
    Status:MPM
    diff -Nru apache2-2.4.69/docs/manual/mod/prefork.html.tr.utf8 apache2-2.4.71/docs/manual/mod/prefork.html.tr.utf8 --- apache2-2.4.69/docs/manual/mod/prefork.html.tr.utf8 2026-06-16 16:46:03.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/prefork.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,7 +32,6 @@  ja  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.
    説明:スレッドを使���先行�� fork を行��ウェブサー�を実装
    ステータス:MPM
    diff -Nru apache2-2.4.69/docs/manual/mod/quickreference.html.de apache2-2.4.71/docs/manual/mod/quickreference.html.de --- apache2-2.4.69/docs/manual/mod/quickreference.html.de 2026-09-29 11:57:11.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/quickreference.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -723,7 +723,7 @@ - + @@ -734,508 +734,509 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - + - - - - - - + + - - - - - - - + + - - - - + + - - - - - - - - - + + + + + + + - - - - - - - - + + + + + + - - - - - - - + + + + + + + - - - - - + + + + - - + - - + - - - - + + + - - - - - - + + + + - - - - - - + - - + - - - - - - + + + + - - + - - + - - + - - - - + - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - + - - - - - - + - - + - - - + + + - - - - - - - - - - + - - + - - + - - + - - - - - - + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - + - - - - - + + + + - - + - - - - - + + - - + - - - + + - - + - - - - - + - - - - - + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - + - - - + - - - - - - - - - - - - + + + + + + - - - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - + + + - - + - - + - - - - - - + + + + + - - - - - - - + + + - - - - - + + + + - - - - + - - - - + + - - + - - - - - - + - - + - +
    Açıklama:Evresiz ön çatallamalı HTTP sunucusu oluÅŸturur
    Durum:MPM
    Modül Betimleyici:mpm_prefork_module
    MDCertificateAuthority url letsencrypt sX
    The URL(s) of the ACME Certificate Authority to use.
    MDCertificateCheck name urlsX
    Set name and URL pattern for a certificate monitoring site.
    MDCertificateFile path-to-pem-filesX
    Specify a static certificate file for the MD.
    MDCertificateKeyFile path-to-filesX
    Specify a static private key for for the static cerrtificate.
    MDCertificateKeyFile path-to-filesX
    Specify a static private key for the static certificate.
    MDCertificateMonitor name url crt.sh https://crt. +sX
    The URL of a certificate log monitor.
    MDCertificateProtocol protocol ACME sX
    The protocol to use with the Certificate Authority.
    MDCertificateStatus on|off on sX
    Exposes public certificate information in JSON.
    MDDriveMode always|auto|manual auto sX
    former name of MDRenewMode.
    MDExternalAccountBinding key-id hmac-64 | none | file none sX
    Set the external account binding keyid and hmac values to use at CA
    MDHttpProxy urlsX
    Define a proxy for outgoing connections.
    MDInitialDelay duration 0s sX
    How long to delay the first certificate check.
    MDMatchNames all|servernames all sX
    Determines how DNS names are matched to vhosts
    MDMember hostnamesX
    Additional hostname for the managed domain.
    MDMembers auto|manual auto sX
    Control if the alias domain names are automatically added.
    MDMessageCmd path-to-cmd optional-argssX
    Handle events for Manage Domains
    MDMustStaple on|off off sX
    Control if new certificates carry the OCSP Must Staple flag.
    MDNotifyCmd path [ args ]sX
    Run a program when a Managed Domain is ready.
    MDomain dns-name [ other-dns-name... ] [auto|manual]sX
    Define list of domain names that belong to one group.
    <MDomainSet dns-name [ other-dns-name... ]>...</MDomainSet>sX
    Container for directives applied to the same managed domains.
    MDPortMap map1 [ map2 ] http:80 https:443 sX
    Map external to internal ports for domain ownership verification.
    MDPrivateKeys type [ params... ] RSA 2048 sX
    Set type and size of the private keys generated.
    MDProfile namesX
    Use a specific ACME profile from the CA
    MDProfileMandatory on|off off sX
    Control if an MDProfile is mandatory.
    MDRenewMode always|auto|manual auto sX
    Controls if certificates shall be renewed.
    MDRenewViaARI on|off on sX
    usage of the ACME ARI extension (rfc9773).
    MDRenewWindow duration 33% sX
    Control when a certificate will be renewed.
    MDRequireHttps off|temporary|permanent off sX
    Redirects http: traffic to https: for Managed Domains.
    MDRetryDelay duration 30s sX
    Time length for first retry, doubled on every consecutive error.
    MDRetryFailover number 13 sX
    The number of errors before a failover to another CA is triggered
    MDServerStatus on|off off sX
    Control if Managed Domain information is added to server-status.
    MDStapleOthers on|off on sX
    Enable stapling for certificates not managed by mod_md.
    MDStapling on|off off sX
    Enable stapling for all or a particular MDomain.
    MDStaplingKeepResponse duration 7d sX
    Controls when old responses should be removed.
    MDStaplingRenewWindow duration 33% sX
    Control when the stapling responses will be renewed.
    MDStoreDir path md sX
    Path on the local file system to store the Managed Domains data.
    MDStoreLocks on|off|duration off sX
    Configure locking of store for updates
    MDWarnWindow duration 10% sX
    Define the time window when you want to be warned about an expiring certificate.
    MemcacheConnTTL num[units] 15s svE
    Keepalive time for idle connections
    MergeSlashes ON|OFF ON svC
    Controls whether the server merges consecutive slashes in URLs. +
    MDHttpProxyCACertificateFile path-to-pem-file none sX
    Sets the root (CA) certificates to use for TLS connections to the http-proxy.
    MDInitialDelay duration 0s sX
    How long to delay the first certificate check.
    MDMatchNames all|servernames all sX
    Determines how DNS names are matched to vhosts
    MDMember hostnamesX
    Additional hostname for the managed domain.
    MDMembers auto|manual auto sX
    Control if the alias domain names are automatically added.
    MDMessageCmd path-to-cmd optional-argssX
    Handle events for Manage Domains
    MDMustStaple on|off off sX
    Control if new certificates carry the OCSP Must Staple flag.
    MDNotifyCmd path [ args ]sX
    Run a program when a Managed Domain is ready.
    MDomain dns-name [ other-dns-name... ] [auto|manual]sX
    Define list of domain names that belong to one group.
    <MDomainSet dns-name [ other-dns-name... ]>...</MDomainSet>sX
    Container for directives applied to the same managed domains.
    MDPortMap map1 [ map2 ] http:80 https:443 sX
    Map external to internal ports for domain ownership verification.
    MDPrivateKeys type [ params... ] RSA 2048 sX
    Set type and size of the private keys generated.
    MDProfile namesX
    Use a specific ACME profile from the CA
    MDProfileMandatory on|off off sX
    Control if an MDProfile is mandatory.
    MDRenewMode always|auto|manual auto sX
    Controls if certificates shall be renewed.
    MDRenewViaARI on|off on sX
    usage of the ACME ARI extension (rfc9773).
    MDRenewWindow duration 33% sX
    Control when a certificate will be renewed.
    MDRequireHttps off|temporary|permanent off sX
    Redirects http: traffic to https: for Managed Domains.
    MDRetryDelay duration 30s sX
    Time length for first retry, doubled on every consecutive error.
    MDRetryFailover number 13 sX
    The number of errors before a failover to another CA is triggered
    MDServerStatus on|off off sX
    Control if Managed Domain information is added to server-status.
    MDStapleOthers on|off on sX
    Enable stapling for certificates not managed by mod_md.
    MDStapling on|off off sX
    Enable stapling for all or a particular MDomain.
    MDStaplingKeepResponse duration 7d sX
    Controls when old responses should be removed.
    MDStaplingRenewWindow duration 33% sX
    Control when the stapling responses will be renewed.
    MDStoreDir path md sX
    Path on the local file system to store the Managed Domains data.
    MDStoreLocks on|off|duration off sX
    Configure locking of store for updates
    MDWarnWindow duration 10% sX
    Define the time window when you want to be warned about an expiring certificate.
    MemcacheConnTTL num[units] 15s svE
    Keepalive time for idle connections
    MergeSlashes ON|OFF ON svC
    Controls whether the server merges consecutive slashes in URLs.
    MergeTrailers [on|off] off svC
    Determines whether trailers are merged into headers
    MetaDir directory .web svdhD
    Name of the directory to find CERN-style meta information +
    MergeTrailers [on|off] off svC
    Determines whether trailers are merged into headers
    MetaDir directory .web svdhD
    Name of the directory to find CERN-style meta information files
    MetaFiles on|off off svdhD
    Activates CERN meta-file processing
    MetaSuffix suffix .meta svdhD
    File name suffix for the file containing CERN-style +
    MetaFiles on|off off svdhD
    Activates CERN meta-file processing
    MetaSuffix suffix .meta svdhD
    File name suffix for the file containing CERN-style meta information
    MimeMagicFile file-pathsvE
    Enable MIME-type determination based on file contents +
    MimeMagicFile file-pathsvE
    Enable MIME-type determination based on file contents using the specified magic file
    MinSpareServers Anzahl 5 sM
    Minimale Anzahl der unbeschäftigten Kindprozesse des +
    MinSpareServers Anzahl 5 sM
    Minimale Anzahl der unbeschäftigten Kindprozesse des Servers
    MinSpareThreads AnzahlsM
    Minimale Anzahl unbeschäftigter Threads, die zur +
    MinSpareThreads AnzahlsM
    Minimale Anzahl unbeschäftigter Threads, die zur Bedienung von Anfragespitzen zur Verfügung stehen
    MMapFile file-path [file-path] ...sX
    Map a list of files into memory at startup time
    ModemStandard V.21|V.26bis|V.32|V.34|V.92dX
    Modem standard to simulate
    ModMimeUsePathInfo On|Off Off dB
    Tells mod_mime to treat path_info +
    MMapFile file-path [file-path] ...sX
    Map a list of files into memory at startup time
    ModemStandard V.21|V.26bis|V.32|V.34|V.92dX
    Modem standard to simulate
    ModMimeUsePathInfo On|Off Off dB
    Tells mod_mime to treat path_info components as part of the filename
    MultiviewsMatch Any|NegotiatedOnly|Filters|Handlers -[Handlers|Filters] NegotiatedOnly svdhB
    The types of files that will be included when searching for +
    MultiviewsMatch Any|NegotiatedOnly|Filters|Handlers +[Handlers|Filters] NegotiatedOnly svdhB
    The types of files that will be included when searching for a matching file with MultiViews
    Mutex mechanism [default|mutex-name] ... [OmitPID] default sC
    Configures mutex mechanism and lock file directory for all +
    Mutex mechanism [default|mutex-name] ... [OmitPID] default sC
    Configures mutex mechanism and lock file directory for all or specified mutexes
    NameVirtualHost Adresse[:Port]sC
    Bestimmt eine IP-Adresse für den Betrieb namensbasierter +
    NameVirtualHost Adresse[:Port]sC
    Bestimmt eine IP-Adresse für den Betrieb namensbasierter virtueller Hosts
    NoProxy host [host] ...svE
    Hosts, domains, or networks that will be connected to +
    NoProxy host [host] ...svE
    Hosts, domains, or networks that will be connected to directly
    NWSSLTrustedCerts filename [filename] ...sB
    List of additional client certificates
    NWSSLUpgradeable [IP-address:]portnumbersB
    Allows a connection to be upgraded to an SSL connection upon request
    Options - [+|-]Option [[+|-]Option] ... All svdhC
    Definiert, welche Eigenschaften oder Funktionen in einem +
    NWSSLTrustedCerts filename [filename] ...sB
    List of additional client certificates
    NWSSLUpgradeable [IP-address:]portnumbersB
    Allows a connection to be upgraded to an SSL connection upon request
    Options + [+|-]Option [[+|-]Option] ... All svdhC
    Definiert, welche Eigenschaften oder Funktionen in einem bestimmten Verzeichnis verfügbar sind
    Order ordering Deny,Allow dhD
    Controls the default access state and the order in which +
    Order ordering Deny,Allow dhD
    Controls the default access state and the order in which Allow and Deny are evaluated.
    OutputSed sed-commanddhX
    Sed command for filtering response content
    PassEnv env-variable [env-variable] -...svdhB
    Passes environment variables from the shell
    PidFile Dateiname logs/httpd.pid sM
    Datei, in welcher der Server die Prozess-ID des Daemons +
    OutputSed sed-commanddhX
    Sed command for filtering response content
    PassEnv env-variable [env-variable] +...svdhB
    Passes environment variables from the shell
    PidFile Dateiname logs/httpd.pid sM
    Datei, in welcher der Server die Prozess-ID des Daemons ablegt
    PrivilegesMode FAST|SECURE|SELECTIVE FAST svdD
    Trade off processing speed and efficiency vs security against +
    PrivilegesMode FAST|SECURE|SELECTIVE FAST svdD
    Trade off processing speed and efficiency vs security against malicious privileges-aware code.
    Protocol protocolsvC
    Protocol for a listening socket
    ProtocolEcho On|Off Off svX
    Turn the echo server on or off
    Protocols protocol ... http/1.1 svC
    Protocols available for a server/virtual host
    ProtocolsHonorOrder On|Off On svC
    Determines if order of Protocols determines precedence during negotiation
    <Proxy wildcard-url> ...</Proxy>svE
    Container for directives applied to proxied resources
    Proxy100Continue Off|On On svdE
    Forward 100-continue expectation to the origin server
    ProxyAddHeaders Off|On On svdE
    Add proxy information in X-Forwarded-* headers
    ProxyBadHeader IsError|Ignore|StartBody IsError svE
    Determines how to handle bad header lines in a +
    Protocol protocolsvC
    Protocol for a listening socket
    ProtocolEcho On|Off Off svX
    Turn the echo server on or off
    Protocols protocol ... http/1.1 svC
    Protocols available for a server/virtual host
    ProtocolsHonorOrder On|Off On svC
    Determines if order of Protocols determines precedence during negotiation
    <Proxy wildcard-url> ...</Proxy>svE
    Container for directives applied to proxied resources
    Proxy100Continue Off|On On svdE
    Forward 100-continue expectation to the origin server
    ProxyAddHeaders Off|On On svdE
    Add proxy information in X-Forwarded-* headers
    ProxyBadHeader IsError|Ignore|StartBody IsError svE
    Determines how to handle bad header lines in a response
    ProxyBlock *|word|host|domain -[word|host|domain] ...svE
    Words, hosts, or domains that are banned from being +
    ProxyBlock *|word|host|domain +[word|host|domain] ...svE
    Words, hosts, or domains that are banned from being proxied
    ProxyDomain DomainsvE
    Default domain name for proxied requests
    ProxyErrorOverride Off|On [code ...] Off svdE
    Override error pages for proxied content
    ProxyExpressDBMFile pathnamesvE
    Pathname to DBM file.
    ProxyExpressDBMType type default svE
    DBM type of file.
    ProxyExpressEnable on|off off svE
    Enable the module functionality.
    ProxyFCGIBackendType FPM|GENERIC FPM svdhE
    Specify the type of backend FastCGI application
    ProxyFCGISetEnvIf conditional-expression +
    ProxyDomain DomainsvE
    Default domain name for proxied requests
    ProxyErrorOverride Off|On [code ...] Off svdE
    Override error pages for proxied content
    ProxyExpressDBMFile pathnamesvE
    Pathname to DBM file.
    ProxyExpressDBMType type default svE
    DBM type of file.
    ProxyExpressEnable on|off off svE
    Enable the module functionality.
    ProxyFCGIBackendType FPM|GENERIC FPM svdhE
    Specify the type of backend FastCGI application
    ProxyFCGISetEnvIf conditional-expression [!]environment-variable-name - [value-expression]svdhE
    Allow variables sent to FastCGI servers to be fixed up
    ProxyFtpDirCharset character_set ISO-8859-1 svdE
    Define the character set for proxied FTP listings
    ProxyFtpEscapeWildcards on|off on svdE
    Whether wildcards in requested filenames are escaped when sent to the FTP server
    ProxyFtpListOnWildcard on|off on svdE
    Whether wildcards in requested filenames trigger a file listing
    ProxyHCExpr name {ap_expr expression}svE
    Creates a named condition expression to use to determine health of the backend based on its response
    ProxyHCTemplate name parameter=setting [...]svE
    Creates a named template for setting various health check parameters
    ProxyHCTPsize size 16 sE
    Sets the total server-wide size of the threadpool used for the health check workers
    ProxyHTMLBufSize bytes 8192 svdB
    Sets the buffer size increment for buffering inline scripts and + [value-expression]svdhE
    Allow variables sent to FastCGI servers to be fixed up
    ProxyFtpDirCharset character_set ISO-8859-1 svdE
    Define the character set for proxied FTP listings
    ProxyFtpEscapeWildcards on|off on svdE
    Whether wildcards in requested filenames are escaped when sent to the FTP server
    ProxyFtpListOnWildcard on|off on svdE
    Whether wildcards in requested filenames trigger a file listing
    ProxyHCExpr name {ap_expr expression}svE
    Creates a named condition expression to use to determine health of the backend based on its response
    ProxyHCTemplate name parameter=setting [...]svE
    Creates a named template for setting various health check parameters
    ProxyHCTPsize size 16 sE
    Sets the total server-wide size of the threadpool used for the health check workers
    ProxyHTMLBufSize bytes 8192 svdB
    Sets the buffer size increment for buffering inline scripts and stylesheets.
    ProxyHTMLCharsetOut Charset | * UTF-8 svdB
    Specify a charset for mod_proxy_html output.
    ProxyHTMLDocType HTML|XHTML [Legacy]
    OR -
    ProxyHTMLDocType fpi [SGML|XML]
    svdB
    Sets an HTML or XHTML document type declaration.
    ProxyHTMLEnable On|Off Off svdB
    Turns the proxy_html filter on or off.
    ProxyHTMLEvents attribute [attribute ...]svdB
    Specify attributes to treat as scripting events.
    ProxyHTMLExtended On|Off Off svdB
    Determines whether to fix links in inline scripts, stylesheets, +
    ProxyHTMLCharsetOut Charset | * UTF-8 svdB
    Specify a charset for mod_proxy_html output.
    ProxyHTMLDocType HTML|XHTML [Legacy]
    OR +
    ProxyHTMLDocType fpi [SGML|XML]
    svdB
    Sets an HTML or XHTML document type declaration.
    ProxyHTMLEnable On|Off Off svdB
    Turns the proxy_html filter on or off.
    ProxyHTMLEvents attribute [attribute ...]svdB
    Specify attributes to treat as scripting events.
    ProxyHTMLExtended On|Off Off svdB
    Determines whether to fix links in inline scripts, stylesheets, and scripting events.
    ProxyHTMLFixups [lowercase] [dospath] [reset] none svdB
    Fixes for simple HTML errors.
    ProxyHTMLInterp On|Off Off svdB
    Enables per-request interpolation of +
    ProxyHTMLFixups [lowercase] [dospath] [reset] none svdB
    Fixes for simple HTML errors.
    ProxyHTMLInterp On|Off Off svdB
    Enables per-request interpolation of ProxyHTMLURLMap rules.
    ProxyHTMLLinks element attribute [attribute2 ...]svdB
    Specify HTML elements that have URL attributes to be rewritten.
    ProxyHTMLMeta On|Off Off svdB
    Turns on or off extra pre-parsing of metadata in HTML +
    ProxyHTMLLinks element attribute [attribute2 ...]svdB
    Specify HTML elements that have URL attributes to be rewritten.
    ProxyHTMLMeta On|Off Off svdB
    Turns on or off extra pre-parsing of metadata in HTML <head> sections.
    ProxyHTMLStripComments On|Off Off svdB
    Determines whether to strip HTML comments.
    ProxyHTMLURLMap from-pattern to-pattern [flags] [cond]svdB
    Defines a rule to rewrite HTML links
    ProxyIOBufferSize bytes 8192 svE
    Determine size of internal data throughput buffer
    <ProxyMatch regex> ...</ProxyMatch>svE
    Container for directives applied to regular-expression-matched +
    ProxyHTMLStripComments On|Off Off svdB
    Determines whether to strip HTML comments.
    ProxyHTMLURLMap from-pattern to-pattern [flags] [cond]svdB
    Defines a rule to rewrite HTML links
    ProxyIOBufferSize bytes 8192 svE
    Determine size of internal data throughput buffer
    <ProxyMatch regex> ...</ProxyMatch>svE
    Container for directives applied to regular-expression-matched proxied resources
    ProxyMaxForwards number -1 svE
    Maximum number of proxies that a request can be forwarded +
    ProxyMaxForwards number -1 svE
    Maximum number of proxies that a request can be forwarded through
    ProxyPass [path] !|url [key=value - [key=value ...]] [nocanon] [interpolate] [noquery]svdE
    Maps remote servers into the local server URL-space
    ProxyPassInherit On|Off On svE
    Inherit ProxyPass directives defined from the main server
    ProxyPassInterpolateEnv On|Off Off svdE
    Enable Environment Variable interpolation in Reverse Proxy configurations
    ProxyPassMatch [regex] !|url [key=value - [key=value ...]]svdE
    Maps remote servers into the local server URL-space using regular expressions
    ProxyPassReverse [path] url -[interpolate]svdE
    Adjusts the URL in HTTP response headers sent from a reverse +
    ProxyPass [path] !|url [key=value + [key=value ...]] [nocanon] [interpolate] [noquery]svdE
    Maps remote servers into the local server URL-space
    ProxyPassInherit On|Off On svE
    Inherit ProxyPass directives defined from the main server
    ProxyPassInterpolateEnv On|Off Off svdE
    Enable Environment Variable interpolation in Reverse Proxy configurations
    ProxyPassMatch [regex] !|url [key=value + [key=value ...]]svdE
    Maps remote servers into the local server URL-space using regular expressions
    ProxyPassReverse [path] url +[interpolate]svdE
    Adjusts the URL in HTTP response headers sent from a reverse proxied server
    ProxyPassReverseCookieDomain internal-domain -public-domain [interpolate]svdE
    Adjusts the Domain string in Set-Cookie headers from a reverse- +
    ProxyPassReverseCookieDomain internal-domain +public-domain [interpolate]svdE
    Adjusts the Domain string in Set-Cookie headers from a reverse- proxied server
    ProxyPassReverseCookiePath internal-path -public-path [interpolate]svdE
    Adjusts the Path string in Set-Cookie headers from a reverse- +
    ProxyPassReverseCookiePath internal-path +public-path [interpolate]svdE
    Adjusts the Path string in Set-Cookie headers from a reverse- proxied server
    ProxyPreserveHost On|Off Off svdE
    Use incoming Host HTTP request header for proxy +
    ProxyPreserveHost On|Off Off svdE
    Use incoming Host HTTP request header for proxy request
    ProxyReceiveBufferSize bytes 0 svE
    Network buffer size for proxied HTTP and FTP +
    ProxyReceiveBufferSize bytes 0 svE
    Network buffer size for proxied HTTP and FTP connections
    ProxyRemote match remote-server [username:password]svE
    Remote proxy used to handle certain requests
    ProxyRemoteMatch regex remote-server [username:password]svE
    Remote proxy used to handle requests matched by regular +
    ProxyRemote match remote-server [username:password]svE
    Remote proxy used to handle certain requests
    ProxyRemoteMatch regex remote-server [username:password]svE
    Remote proxy used to handle requests matched by regular expressions
    ProxyRequests On|Off Off svE
    Enables forward (standard) proxy requests
    ProxySCGIInternalRedirect On|Off|Headername On svdE
    Enable or disable internal redirect responses from the +
    ProxyRequests On|Off Off svE
    Enables forward (standard) proxy requests
    ProxySCGIInternalRedirect On|Off|Headername On svdE
    Enable or disable internal redirect responses from the backend
    ProxySCGISendfile On|Off|Headername Off svdE
    Enable evaluation of X-Sendfile pseudo response +
    ProxySCGISendfile On|Off|Headername Off svdE
    Enable evaluation of X-Sendfile pseudo response header
    ProxySet url key=value [key=value ...]svdE
    Set various Proxy balancer or member parameters
    ProxySourceAddress addresssvE
    Set local IP address for outgoing proxy connections
    ProxyStatus Off|On|Full Off svE
    Show Proxy LoadBalancer status in mod_status
    ProxyTimeout secondssvE
    Network timeout for proxied requests
    ProxyVia On|Off|Full|Block Off svE
    Information provided in the Via HTTP response +
    ProxySet url key=value [key=value ...]svdE
    Set various Proxy balancer or member parameters
    ProxySourceAddress addresssvE
    Set local IP address for outgoing proxy connections
    ProxyStatus Off|On|Full Off svE
    Show Proxy LoadBalancer status in mod_status
    ProxyTimeout secondssvE
    Network timeout for proxied requests
    ProxyVia On|Off|Full|Block Off svE
    Information provided in the Via HTTP response header for proxied requests
    ProxyWebsocketFallbackToProxyHttp On|Off On svD
    Instructs this module to let mod_proxy_http handle the request
    QualifyRedirectURL On|Off Off svdC
    Controls whether the REDIRECT_URL environment variable is +
    ProxyWebsocketFallbackToProxyHttp On|Off On svD
    Instructs this module to let mod_proxy_http handle the request
    QualifyRedirectURL On|Off Off svdC
    Controls whether the REDIRECT_URL environment variable is fully qualified
    ReadBufferSize bytes 8192 svdC
    Size of the buffers used to read data
    ReadmeName filenamesvdhB
    Name of the file that will be inserted at the end +
    ReadBufferSize bytes 8192 svdC
    Size of the buffers used to read data
    ReadmeName filenamesvdhB
    Name of the file that will be inserted at the end of the index listing
    ReceiveBufferSize bytes 0 sM
    TCP receive buffer size
    Redirect [status] [URL-path] -URLsvdhB
    Sends an external redirect asking the client to fetch +
    ReceiveBufferSize bytes 0 sM
    TCP receive buffer size
    Redirect [status] [URL-path] +URLsvdhB
    Sends an external redirect asking the client to fetch a different URL
    RedirectMatch [status] regex -URLsvdhB
    Sends an external redirect based on a regular expression match +
    RedirectMatch [status] regex +URLsvdhB
    Sends an external redirect based on a regular expression match of the current URL
    RedirectPermanent URL-path URLsvdhB
    Sends an external permanent redirect asking the client to fetch +
    RedirectPermanent URL-path URLsvdhB
    Sends an external permanent redirect asking the client to fetch a different URL
    RedirectRelative On|Off Off svdB
    Allows relative redirect targets.
    RedirectTemp URL-path URLsvdhB
    Sends an external temporary redirect asking the client to fetch +
    RedirectRelative On|Off Off svdB
    Allows relative redirect targets.
    RedirectTemp URL-path URLsvdhB
    Sends an external temporary redirect asking the client to fetch a different URL
    RedisConnPoolTTL num[units] 15s svE
    TTL used for the connection pool with the Redis server(s)
    RedisTimeout num[units] 5s svE
    R/W timeout used for the connection with the Redis server(s)
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    RegexDefaultOptions [none] [+|-]option [[+|-]option] ... DOTALL DOLLAR_ENDON +sC
    Allow to configure global/default options for regexes
    RegisterHttpMethod method [method [...]]sC
    Register non-standard HTTP methods
    RemoteIPHeader header-fieldsvB
    Declare the header field which should be parsed for useragent IP addresses
    RemoteIPInternalProxy proxy-ip|proxy-ip/subnet|hostname ...svB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPInternalProxyList filenamesvB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPProxiesHeader HeaderFieldNamesvB
    Declare the header field which will record all intermediate IP addresses
    RemoteIPProxyProtocol On|OffsvB
    Enable or disable PROXY protocol handling
    RemoteIPProxyProtocolExceptions host|range [host|range] [host|range]svB
    Disable processing of PROXY header for certain hosts or networks
    RemoteIPTrustedProxy proxy-ip|proxy-ip/subnet|hostname ...svB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPTrustedProxyList filenamesvB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoveCharset extension [extension] -...vdhB
    Removes any character set associations for a set of file +
    RedisConnPoolTTL num[units] 15s svE
    TTL used for the connection pool with the Redis server(s)
    RedisTimeout num[units] 5s svE
    R/W timeout used for the connection with the Redis server(s)
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    RegexDefaultOptions [none] [+|-]option [[+|-]option] ... DOTALL DOLLAR_ENDON +sC
    Allow to configure global/default options for regexes
    RegisterHttpMethod method [method [...]]sC
    Register non-standard HTTP methods
    RemoteIPHeader header-fieldsvB
    Declare the header field which should be parsed for useragent IP addresses
    RemoteIPInternalProxy proxy-ip|proxy-ip/subnet|hostname ...svB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPInternalProxyList filenamesvB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPProxiesHeader HeaderFieldNamesvB
    Declare the header field which will record all intermediate IP addresses
    RemoteIPProxyProtocol On|OffsvB
    Enable or disable PROXY protocol handling
    RemoteIPProxyProtocolExceptions host|range [host|range] [host|range]svB
    Disable processing of PROXY header for certain hosts or networks
    RemoteIPTrustedProxy proxy-ip|proxy-ip/subnet|hostname ...svB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPTrustedProxyList filenamesvB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoveCharset extension [extension] +...vdhB
    Removes any character set associations for a set of file extensions
    RemoveEncoding extension [extension] -...vdhB
    Removes any content encoding associations for a set of file +
    RemoveEncoding extension [extension] +...vdhB
    Removes any content encoding associations for a set of file extensions
    RemoveHandler extension [extension] -...vdhB
    Removes any handler associations for a set of file +
    RemoveHandler extension [extension] +...vdhB
    Removes any handler associations for a set of file extensions
    RemoveInputFilter extension [extension] -...vdhB
    Removes any input filter associations for a set of file +
    RemoveInputFilter extension [extension] +...vdhB
    Removes any input filter associations for a set of file extensions
    RemoveLanguage extension [extension] -...vdhB
    Removes any language associations for a set of file +
    RemoveLanguage extension [extension] +...vdhB
    Removes any language associations for a set of file extensions
    RemoveOutputFilter extension [extension] -...vdhB
    Removes any output filter associations for a set of file +
    RemoveOutputFilter extension [extension] +...vdhB
    Removes any output filter associations for a set of file extensions
    RemoveType extension [extension] -...vdhB
    Removes any content type associations for a set of file +
    RemoveType extension [extension] +...vdhB
    Removes any content type associations for a set of file extensions
    RequestHeader add|append|edit|edit*|merge|set|setifempty|unset +
    RequestHeader add|append|edit|edit*|merge|set|setifempty|unset header [[expr=]value [replacement] [early|env=[!]varname|expr=expression]] -svdhE
    Configure HTTP request headers
    RequestReadTimeout +svdhE
    Configure HTTP request headers
    RequestReadTimeout [handshake=timeout[-maxtimeout][,MinRate=rate] [header=timeout[-maxtimeout][,MinRate=rate] [body=timeout[-maxtimeout][,MinRate=rate] - handshake=0 header= +svE
    Set timeout values for completing the TLS handshake, receiving + handshake=0 header= +svE
    Set timeout values for completing the TLS handshake, receiving the request headers and/or body from client.
    Require [not] entity-name - [entity-name] ...dhB
    Tests whether an authenticated user is authorized by +
    Require [not] entity-name + [entity-name] ...dhB
    Tests whether an authenticated user is authorized by an authorization provider.
    <RequireAll> ... </RequireAll>dhB
    Enclose a group of authorization directives of which none +
    <RequireAll> ... </RequireAll>dhB
    Enclose a group of authorization directives of which none must fail and at least one must succeed for the enclosing directive to succeed.
    <RequireAny> ... </RequireAny>dhB
    Enclose a group of authorization directives of which one +
    <RequireAny> ... </RequireAny>dhB
    Enclose a group of authorization directives of which one must succeed for the enclosing directive to succeed.
    <RequireNone> ... </RequireNone>dhB
    Enclose a group of authorization directives of which none +
    <RequireNone> ... </RequireNone>dhB
    Enclose a group of authorization directives of which none must succeed for the enclosing directive to not fail.
    RewriteBase URL-pathdhE
    Sets the base URL for per-directory rewrites
    RewriteCond - TestString [!]CondPattern [flags]svdhE
    Defines a condition under which rewriting will take place +
    RewriteBase URL-pathdhE
    Sets the base URL for per-directory rewrites
    RewriteCond + TestString [!]CondPattern [flags]svdhE
    Defines a condition under which rewriting will take place
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource +
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource [MapTypeOptions] -svE
    Defines a mapping function for key-lookup
    RewriteOptions OptionssvdhE
    Sets some special options for the rewrite engine
    RewriteRule - [!]Pattern Substitution [flags]svdhE
    Defines rules for the rewriting engine
    RLimitCPU Sekunden|max [Sekunden|max]svdhC
    Begrenzt den CPU-Verbrauch von Prozessen, die von +svE
    Defines a mapping function for key-lookup
    RewriteOptions OptionssvdhE
    Sets some special options for the rewrite engine
    RewriteRule + [!]Pattern Substitution [flags]svdhE
    Defines rules for the rewriting engine
    RLimitCPU Sekunden|max [Sekunden|max]svdhC
    Begrenzt den CPU-Verbrauch von Prozessen, die von Apache-Kindprozessen gestartet wurden
    RLimitMEM Bytes|max [Bytes|max]svdhC
    Begrenzt den Speicherverbrauch von Prozessen, die von +
    RLimitMEM Bytes|max [Bytes|max]svdhC
    Begrenzt den Speicherverbrauch von Prozessen, die von Apache-Kindprozessen gestartet wurden
    RLimitNPROC Zahl|max [Zahl|max]svdhC
    Begrenzt die Anzahl der Prozesse, die von Prozessen gestartet +
    RLimitNPROC Zahl|max [Zahl|max]svdhC
    Begrenzt die Anzahl der Prozesse, die von Prozessen gestartet werden können, der ihrerseits von Apache-Kinprozessen gestartet wurden
    Satisfy Any|All All dhD
    Interaction between host-level access control and +
    Satisfy Any|All All dhD
    Interaction between host-level access control and user authentication
    ScoreBoardFile Dateipfad logs/apache_status sM
    Ablageort der Datei, die zur Speicherung von Daten zur +
    ScoreBoardFile Dateipfad logs/apache_status sM
    Ablageort der Datei, die zur Speicherung von Daten zur Koordinierung der Kindprozesse verwendet wird
    Script Methode CGI-SkriptsvdB
    Aktiviert ein CGI-Skript für eine bestimmte +
    Script Methode CGI-SkriptsvdB
    Aktiviert ein CGI-Skript für eine bestimmte Anfragemethode.
    ScriptAlias [URL-path] -file-path|directory-pathsvdB
    Maps a URL to a filesystem location and designates the +
    ScriptAlias [URL-path] +file-path|directory-pathsvdB
    Maps a URL to a filesystem location and designates the target as a CGI script
    ScriptAliasMatch regex -file-path|directory-pathsvB
    Maps a URL to a filesystem location using a regular expression +
    ScriptAliasMatch regex +file-path|directory-pathsvB
    Maps a URL to a filesystem location using a regular expression and designates the target as a CGI script
    ScriptInterpreterSource Registry|Registry-Strict|Script Script svdhC
    Methode zur Ermittlung des Interpreters von +
    ScriptInterpreterSource Registry|Registry-Strict|Script Script svdhC
    Methode zur Ermittlung des Interpreters von CGI-Skripten
    ScriptLog file-pathsvB
    Location of the CGI script error logfile
    ScriptLogBuffer bytes 1024 svB
    Maximum amount of PUT or POST requests that will be recorded +
    ScriptLog file-pathsvB
    Location of the CGI script error logfile
    ScriptLogBuffer bytes 1024 svB
    Maximum amount of PUT or POST requests that will be recorded in the scriptlog
    ScriptLogLength bytes 10385760 svB
    Size limit of the CGI script logfile
    ScriptSock file-path cgisock sB
    The filename prefix of the socket to use for communication with +
    ScriptLogLength bytes 10385760 svB
    Size limit of the CGI script logfile
    ScriptSock file-path cgisock sB
    The filename prefix of the socket to use for communication with the cgi daemon
    SecureListen [IP-address:]portnumber -Certificate-Name [MUTUAL]sB
    Enables SSL encryption for the specified port
    SeeRequestTail On|Off Off sC
    Determine if mod_status displays the first 63 characters +
    SecureListen [IP-address:]portnumber +Certificate-Name [MUTUAL]sB
    Enables SSL encryption for the specified port
    SeeRequestTail On|Off Off sC
    Determine if mod_status displays the first 63 characters of a request or the last 63, assuming the request itself is greater than 63 chars.
    SendBufferSize Bytes 0 sM
    Größe des TCP-Puffers
    ServerAdmin E-Mail-Adresse|URLsvC
    E-Mail-Adresse, die der Server in Fehlermeldungen einfügt, +
    SendBufferSize Bytes 0 sM
    Größe des TCP-Puffers
    ServerAdmin E-Mail-Adresse|URLsvC
    E-Mail-Adresse, die der Server in Fehlermeldungen einfügt, welche an den Client gesendet werden
    ServerAlias Hostname [Hostname] ...vC
    Alternativer Name für einen Host, der verwendet wird, wenn +
    ServerAlias Hostname [Hostname] ...vC
    Alternativer Name für einen Host, der verwendet wird, wenn Anfragen einem namensbasierten virtuellen Host zugeordnet werden
    ServerLimit AnzahlsM
    Obergrenze für die konfigurierbare Anzahl von +
    ServerLimit AnzahlsM
    Obergrenze für die konfigurierbare Anzahl von Prozessen
    ServerName -voll-qualifizierter-Domainname[:port]svC
    Rechnername und Port, die der Server dazu verwendet, sich +
    ServerName +voll-qualifizierter-Domainname[:port]svC
    Rechnername und Port, die der Server dazu verwendet, sich selbst zu identifizieren
    ServerPath URL-PfadvC
    Veralteter URL-Pfad für einen namensbasierten +
    ServerPath URL-PfadvC
    Veralteter URL-Pfad für einen namensbasierten virtuellen Host, auf den von einem inkompatiblen Browser zugegriffen wird
    ServerRoot Verzeichnis /usr/local/apache sC
    Basisverzeichnis der Serverinstallation
    ServerSignature On|Off|EMail Off svdhC
    Konfiguriert die Fußzeile von servergenerierten +
    ServerRoot Verzeichnis /usr/local/apache sC
    Basisverzeichnis der Serverinstallation
    ServerSignature On|Off|EMail Off svdhC
    Konfiguriert die Fußzeile von servergenerierten Dokumenten
    ServerTokens Major|Minor|Min[imal]|Prod[uctOnly]|OS|Full Full sC
    Konfiguriert den HTTP-Response-Header +
    ServerTokens Major|Minor|Min[imal]|Prod[uctOnly]|OS|Full Full sC
    Konfiguriert den HTTP-Response-Header Server
    Session On|Off Off svdhE
    Enables a session for the current directory or location
    SessionCookieName name attributessvdhE
    Name and attributes for the RFC2109 cookie storing the session
    SessionCookieName2 name attributessvdhE
    Name and attributes for the RFC2965 cookie storing the session
    SessionCookieRemove On|Off Off svdhE
    Control for whether session cookies should be removed from incoming HTTP headers
    SessionCryptoCipher name aes256 svdhX
    The crypto cipher to be used to encrypt the session
    SessionCryptoDriver name [param[=value]]sX
    The crypto driver to be used to encrypt the session
    SessionCryptoPassphrase secret [ secret ... ] svdhX
    The key used to encrypt the session
    SessionCryptoPassphraseFile filenamesvdX
    File containing keys used to encrypt the session
    SessionDBDCookieName name attributessvdhE
    Name and attributes for the RFC2109 cookie storing the session ID
    SessionDBDCookieName2 name attributessvdhE
    Name and attributes for the RFC2965 cookie storing the session ID
    SessionDBDCookieRemove On|Off On svdhE
    Control for whether session ID cookies should be removed from incoming HTTP headers
    SessionDBDDeleteLabel label deletesession svdhE
    The SQL query to use to remove sessions from the database
    SessionDBDInsertLabel label insertsession svdhE
    The SQL query to use to insert sessions into the database
    SessionDBDPerUser On|Off Off svdhE
    Enable a per user session
    SessionDBDSelectLabel label selectsession svdhE
    The SQL query to use to select sessions from the database
    SessionDBDUpdateLabel label updatesession svdhE
    The SQL query to use to update existing sessions in the database
    SessionEnv On|Off Off svdhE
    Control whether the contents of the session are written to the +
    Session On|Off Off svdhE
    Enables a session for the current directory or location
    SessionCookieName name attributessvdhE
    Name and attributes for the RFC2109 cookie storing the session
    SessionCookieName2 name attributessvdhE
    Name and attributes for the RFC2965 cookie storing the session
    SessionCookieRemove On|Off Off svdhE
    Control for whether session cookies should be removed from incoming HTTP headers
    SessionCryptoCipher name aes256 svdhX
    The crypto cipher to be used to encrypt the session
    SessionCryptoDriver name [param[=value]]sX
    The crypto driver to be used to encrypt the session
    SessionCryptoPassphrase secret [ secret ... ] svdhX
    The key used to encrypt the session
    SessionCryptoPassphraseFile filenamesvdX
    File containing keys used to encrypt the session
    SessionDBDCookieName name attributessvdhE
    Name and attributes for the RFC2109 cookie storing the session ID
    SessionDBDCookieName2 name attributessvdhE
    Name and attributes for the RFC2965 cookie storing the session ID
    SessionDBDCookieRemove On|Off On svdhE
    Control for whether session ID cookies should be removed from incoming HTTP headers
    SessionDBDDeleteLabel label deletesession svdhE
    The SQL query to use to remove sessions from the database
    SessionDBDInsertLabel label insertsession svdhE
    The SQL query to use to insert sessions into the database
    SessionDBDPerUser On|Off Off svdhE
    Enable a per user session
    SessionDBDSelectLabel label selectsession svdhE
    The SQL query to use to select sessions from the database
    SessionDBDUpdateLabel label updatesession svdhE
    The SQL query to use to update existing sessions in the database
    SessionEnv On|Off Off svdhE
    Control whether the contents of the session are written to the HTTP_SESSION environment variable
    SessionExclude pathsvdhE
    Define URL prefixes for which a session is ignored
    SessionExpiryUpdateInterval interval 0 (always update) svdhE
    Define the number of seconds a session's expiry may change without +
    SessionExclude pathsvdhE
    Define URL prefixes for which a session is ignored
    SessionExpiryUpdateInterval interval 0 (always update) svdhE
    Define the number of seconds a session's expiry may change without the session being updated
    SessionHeader headersvdhE
    Import session updates from a given HTTP response header
    SessionInclude pathsvdhE
    Define URL prefixes for which a session is valid
    SessionMaxAge maxage 0 svdhE
    Define a maximum age in seconds for a session
    SetEnv env-variable [value]svdhB
    Sets environment variables
    SetEnvIf attribute +
    SessionHeader headersvdhE
    Import session updates from a given HTTP response header
    SessionInclude pathsvdhE
    Define URL prefixes for which a session is valid
    SessionMaxAge maxage 0 svdhE
    Define a maximum age in seconds for a session
    SetEnv env-variable [value]svdhB
    Sets environment variables
    SetEnvIf attribute regex [!]env-variable[=value] - [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on attributes of the request + [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on attributes of the request
    SetEnvIfExpr expr +
    SetEnvIfExpr expr [!]env-variable[=value] - [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on an ap_expr expression
    SetEnvIfNoCase attribute regex + [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on an ap_expr expression
    SetEnvIfNoCase attribute regex [!]env-variable[=value] - [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on attributes of the request + [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on attributes of the request without respect to case
    SetHandler Handlername|NonesvdhC
    Erzwingt die Verarbeitung aller passenden Dateien durch +
    SetHandler Handlername|NonesvdhC
    Erzwingt die Verarbeitung aller passenden Dateien durch einen Handler
    SetInputFilter Filter[;Filter...]svdhC
    Bestimmt die Filter, die Client-Anfragen und POST-Eingaben +
    SetInputFilter Filter[;Filter...]svdhC
    Bestimmt die Filter, die Client-Anfragen und POST-Eingaben verarbeiten
    SetOutputFilter Filter[;Filter...]svdhC
    Bestimmt die Filter, die Antworten des Servers verarbeiten
    SSIEndTag tag "-->" svB
    String that ends an include element
    SSIErrorMsg message "[an error occurred +svdhB
    Error message displayed when there is an SSI +
    SetOutputFilter Filter[;Filter...]svdhC
    Bestimmt die Filter, die Antworten des Servers verarbeiten
    SSIEndTag tag "-->" svB
    String that ends an include element
    SSIErrorMsg message "[an error occurred +svdhB
    Error message displayed when there is an SSI error
    SSIETag on|off off dhB
    Controls whether ETags are generated by the server.
    SSILastModified on|off off dhB
    Controls whether Last-Modified headers are generated by the +
    SSIETag on|off off dhB
    Controls whether ETags are generated by the server.
    SSILastModified on|off off dhB
    Controls whether Last-Modified headers are generated by the server.
    SSILegacyExprParser on|off off dhB
    Enable compatibility mode for conditional expressions.
    SSIStartTag tag "<!--#" svB
    String that starts an include element
    SSITimeFormat formatstring "%A, %d-%b-%Y %H:%M +svdhB
    Configures the format in which date strings are +
    SSILegacyExprParser on|off off dhB
    Enable compatibility mode for conditional expressions.
    SSIStartTag tag "<!--#" svB
    String that starts an include element
    SSITimeFormat formatstring "%A, %d-%b-%Y %H:%M +svdhB
    Configures the format in which date strings are displayed
    SSIUndefinedEcho string "(none)" svdhB
    String displayed when an unset variable is echoed
    SSLCACertificateFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates +
    SSIUndefinedEcho string "(none)" svdhB
    String displayed when an unset variable is echoed
    SSLCACertificateFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates for Client Auth
    SSLCACertificatePath directory-pathsvE
    Directory of PEM-encoded CA Certificates for +
    SSLCACertificatePath directory-pathsvE
    Directory of PEM-encoded CA Certificates for Client Auth
    SSLCADNRequestFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates +
    SSLCADNRequestFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates for defining acceptable CA names
    SSLCADNRequestPath directory-pathsvE
    Directory of PEM-encoded CA Certificates for +
    SSLCADNRequestPath directory-pathsvE
    Directory of PEM-encoded CA Certificates for defining acceptable CA names
    SSLCARevocationCheck chain|leaf|none [flags ...] none svE
    Enable CRL-based revocation checking
    SSLCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for +
    SSLCARevocationCheck chain|leaf|none [flags ...] none svE
    Enable CRL-based revocation checking
    SSLCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for Client Auth
    SSLCARevocationPath directory-pathsvE
    Directory of PEM-encoded CA CRLs for +
    SSLCARevocationPath directory-pathsvE
    Directory of PEM-encoded CA CRLs for Client Auth
    SSLCertificateChainFile file-pathsvE
    File of PEM-encoded Server CA Certificates
    SSLCertificateFile file-path|certidsvE
    Server PEM-encoded X.509 certificate data file or token identifier
    SSLCertificateKeyFile file-path|keyidsvE
    Server PEM-encoded private key file
    SSLCipherSuite [protocol] cipher-spec DEFAULT (depends on +svdhE
    Cipher Suite available for negotiation in SSL +
    SSLCertificateChainFile file-pathsvE
    File of PEM-encoded Server CA Certificates
    SSLCertificateFile file-path|certidsvE
    Server PEM-encoded X.509 certificate data file or token identifier
    SSLCertificateKeyFile file-path|keyidsvE
    Server PEM-encoded private key file
    SSLCipherSuite [protocol] cipher-spec DEFAULT (depends on +svdhE
    Cipher Suite available for negotiation in SSL handshake
    SSLCompression on|off off svE
    Enable compression on the SSL level
    SSLCryptoDevice engine builtin sE
    Enable use of a cryptographic hardware accelerator
    SSLEngine on|off off svE
    SSL Engine Operation Switch
    SSLFIPS on|off off sE
    SSL FIPS mode Switch
    SSLHonorCipherOrder on|off off svE
    Option to prefer the server's cipher preference order
    SSLInsecureRenegotiation on|off off svE
    Option to enable support for insecure renegotiation
    SSLOCSPDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|leaf|off [flags] off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPNoverify on|off off svE
    skip the OCSP responder certificates verification
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    SSLOCSPProxyURL urlsvE
    Proxy URL to use for OCSP requests
    SSLOCSPResponderCertificateFile filesvE
    Set of trusted PEM encoded OCSP responder certificates
    SSLOCSPResponderTimeout seconds 10 svE
    Timeout for OCSP queries
    SSLOCSPResponseMaxAge seconds -1 svE
    Maximum allowable age for OCSP responses
    SSLOCSPResponseTimeSkew seconds 300 svE
    Maximum allowable time skew for OCSP response validation
    SSLOCSPUseRequestNonce on|off on svE
    Use a nonce within OCSP queries
    SSLOpenSSLConfCmd command-name command-valuesvE
    Configure OpenSSL parameters through its SSL_CONF API
    SSLOptions [+|-]option ...svdhE
    Configure various SSL engine run-time options
    SSLPassPhraseDialog type builtin sE
    Type of pass phrase dialog for encrypted private +
    SSLCompression on|off off svE
    Enable compression on the SSL level
    SSLCryptoDevice engine builtin sE
    Enable use of a cryptographic hardware accelerator
    SSLEngine on|off off svE
    SSL Engine Operation Switch
    SSLFIPS on|off off sE
    SSL FIPS mode Switch
    SSLHonorCipherOrder on|off off svE
    Option to prefer the server's cipher preference order
    SSLInsecureRenegotiation on|off off svE
    Option to enable support for insecure renegotiation
    SSLOCSPDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|leaf|off [flags] off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPNoverify on|off off svE
    skip the OCSP responder certificates verification
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    SSLOCSPProxyURL urlsvE
    Proxy URL to use for OCSP requests
    SSLOCSPResponderCertificateFile filesvE
    Set of trusted PEM encoded OCSP responder certificates
    SSLOCSPResponderTimeout seconds 10 svE
    Timeout for OCSP queries
    SSLOCSPResponseMaxAge seconds -1 svE
    Maximum allowable age for OCSP responses
    SSLOCSPResponseTimeSkew seconds 300 svE
    Maximum allowable time skew for OCSP response validation
    SSLOCSPUseRequestNonce on|off on svE
    Use a nonce within OCSP queries
    SSLOpenSSLConfCmd command-name command-valuesvE
    Configure OpenSSL parameters through its SSL_CONF API
    SSLOptions [+|-]option ...svdhE
    Configure various SSL engine run-time options
    SSLPassPhraseDialog type builtin sE
    Type of pass phrase dialog for encrypted private keys
    SSLProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svE
    Configure usable SSL/TLS protocol versions
    SSLProxyCACertificateFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates +
    SSLProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svE
    Configure usable SSL/TLS protocol versions
    SSLProxyCACertificateFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates for Remote Server Auth
    SSLProxyCACertificatePath directory-pathsvE
    Directory of PEM-encoded CA Certificates for +
    SSLProxyCACertificatePath directory-pathsvE
    Directory of PEM-encoded CA Certificates for Remote Server Auth
    SSLProxyCARevocationCheck chain|leaf|none none svE
    Enable CRL-based revocation checking for Remote Server Auth
    SSLProxyCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for +
    SSLProxyCARevocationCheck chain|leaf|none none svE
    Enable CRL-based revocation checking for Remote Server Auth
    SSLProxyCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for Remote Server Auth
    SSLProxyCARevocationPath directory-pathsvE
    Directory of PEM-encoded CA CRLs for +
    SSLProxyCARevocationPath directory-pathsvE
    Directory of PEM-encoded CA CRLs for Remote Server Auth
    SSLProxyCheckPeerCN on|off on svE
    Whether to check the remote server certificate's CN field +
    SSLProxyCheckPeerCN on|off on svE
    Whether to check the remote server certificate's CN field
    SSLProxyCheckPeerExpire on|off on svE
    Whether to check if remote server certificate is expired +
    SSLProxyCheckPeerExpire on|off on svE
    Whether to check if remote server certificate is expired
    SSLProxyCheckPeerName on|off on svE
    Configure host name checking for remote server certificates +
    SSLProxyCheckPeerName on|off on svE
    Configure host name checking for remote server certificates
    SSLProxyCipherSuite [protocol] cipher-spec ALL:!ADH:RC4+RSA:+H +svE
    Cipher Suite available for negotiation in SSL +
    SSLProxyCipherSuite [protocol] cipher-spec ALL:!ADH:RC4+RSA:+H +svE
    Cipher Suite available for negotiation in SSL proxy handshake
    SSLProxyEngine on|off off svE
    SSL Proxy Engine Operation Switch
    SSLProxyMachineCertificateChainFile filenamesvE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesvE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysvE
    Directory of PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svE
    Configure usable SSL protocol flavors for proxy usage
    SSLProxyVerify level none svE
    Type of remote server Certificate verification
    SSLProxyVerifyDepth number 1 svE
    Maximum depth of CA Certificates in Remote Server +
    SSLProxyEngine on|off off svE
    SSL Proxy Engine Operation Switch
    SSLProxyMachineCertificateChainFile filenamesvE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesvE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysvE
    Directory of PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svE
    Configure usable SSL protocol flavors for proxy usage
    SSLProxyVerify level none svE
    Type of remote server Certificate verification
    SSLProxyVerifyDepth number 1 svE
    Maximum depth of CA Certificates in Remote Server Certificate verification
    SSLRandomSeed context source -[bytes]sE
    Pseudo Random Number Generator (PRNG) seeding +
    SSLRandomSeed context source +[bytes]sE
    Pseudo Random Number Generator (PRNG) seeding source
    SSLRenegBufferSize bytes 131072 dhE
    Set the size for the SSL renegotiation buffer
    SSLRequire expressiondhE
    Allow access only when an arbitrarily complex +
    SSLRenegBufferSize bytes 131072 dhE
    Set the size for the SSL renegotiation buffer
    SSLRequire expressiondhE
    Allow access only when an arbitrarily complex boolean expression is true
    SSLRequireSSLdhE
    Deny access when SSL is not used for the +
    SSLRequireSSLdhE
    Deny access when SSL is not used for the HTTP request
    SSLSessionCache type none sE
    Type of the global/inter-process SSL Session +
    SSLSessionCache type none sE
    Type of the global/inter-process SSL Session Cache
    SSLSessionCacheTimeout seconds 300 svE
    Number of seconds before an SSL session expires +
    SSLSessionCacheTimeout seconds 300 svE
    Number of seconds before an SSL session expires in the Session Cache
    SSLSessionTicketKeyFile file-pathsvE
    Persistent encryption/decryption key for TLS session tickets
    SSLSessionTickets on|off on svE
    Enable or disable use of TLS session tickets
    SSLSRPUnknownUserSeed secret-stringsvE
    SRP unknown user seed
    SSLSRPVerifierFile file-pathsvE
    Path to SRP verifier file
    SSLStaplingCache typesE
    Configures the OCSP stapling cache
    SSLStaplingErrorCacheTimeout seconds 600 svE
    Number of seconds before expiring invalid responses in the OCSP stapling cache
    SSLStaplingFakeTryLater on|off on svE
    Synthesize "tryLater" responses for failed OCSP stapling queries
    SSLStaplingForceURL urisvE
    Override the OCSP responder URI specified in the certificate's AIA extension
    SSLStaplingResponderTimeout seconds 10 svE
    Timeout for OCSP stapling queries
    SSLStaplingResponseMaxAge seconds -1 svE
    Maximum allowable age for OCSP stapling responses
    SSLStaplingResponseTimeSkew seconds 300 svE
    Maximum allowable time skew for OCSP stapling response validation
    SSLStaplingReturnResponderErrors on|off on svE
    Pass stapling related OCSP errors on to client
    SSLStaplingStandardCacheTimeout seconds 3600 svE
    Number of seconds before expiring responses in the OCSP stapling cache
    SSLStrictSNIVHostCheck on|off off svE
    Whether to allow non-SNI clients to access a name-based virtual +
    SSLSessionTicketKeyFile file-pathsvE
    Persistent encryption/decryption key for TLS session tickets
    SSLSessionTickets on|off on svE
    Enable or disable use of TLS session tickets
    SSLSRPUnknownUserSeed secret-stringsvE
    SRP unknown user seed
    SSLSRPVerifierFile file-pathsvE
    Path to SRP verifier file
    SSLStaplingCache typesE
    Configures the OCSP stapling cache
    SSLStaplingErrorCacheTimeout seconds 600 svE
    Number of seconds before expiring invalid responses in the OCSP stapling cache
    SSLStaplingFakeTryLater on|off on svE
    Synthesize "tryLater" responses for failed OCSP stapling queries
    SSLStaplingForceURL urisvE
    Override the OCSP responder URI specified in the certificate's AIA extension
    SSLStaplingResponderTimeout seconds 10 svE
    Timeout for OCSP stapling queries
    SSLStaplingResponseMaxAge seconds -1 svE
    Maximum allowable age for OCSP stapling responses
    SSLStaplingResponseTimeSkew seconds 300 svE
    Maximum allowable time skew for OCSP stapling response validation
    SSLStaplingReturnResponderErrors on|off on svE
    Pass stapling related OCSP errors on to client
    SSLStaplingStandardCacheTimeout seconds 3600 svE
    Number of seconds before expiring responses in the OCSP stapling cache
    SSLStrictSNIVHostCheck on|off off svE
    Whether to allow non-SNI clients to access a name-based virtual host.
    SSLUserName varnamesdhE
    Variable name to determine user name
    SSLUseStapling on|off off svE
    Enable stapling of OCSP responses in the TLS handshake
    SSLVerifyClient level none svdhE
    Type of Client Certificate verification
    SSLVerifyDepth number 1 svdhE
    Maximum depth of CA Certificates in Client +
    SSLUserName varnamesdhE
    Variable name to determine user name
    SSLUseStapling on|off off svE
    Enable stapling of OCSP responses in the TLS handshake
    SSLVerifyClient level none svdhE
    Type of Client Certificate verification
    SSLVerifyDepth number 1 svdhE
    Maximum depth of CA Certificates in Client Certificate verification
    SSLVHostSNIPolicy strict|secure|authonly|insecure secure sE
    Set compatibility policy for SNI client access to virtual hosts.
    StartServers AnzahlsM
    Anzahl der Kindprozesse des Servers, die beim Start erstellt +
    SSLVHostSNIPolicy strict|secure|authonly|insecure secure sE
    Set compatibility policy for SNI client access to virtual hosts.
    StartServers AnzahlsM
    Anzahl der Kindprozesse des Servers, die beim Start erstellt werden
    StartThreads AnzahlsM
    Anzahl der Threads, die beim Start erstellt werden
    StrictHostCheck ON|OFF OFF svC
    Controls whether the server requires the requested hostname be +
    StartThreads AnzahlsM
    Anzahl der Threads, die beim Start erstellt werden
    StrictHostCheck ON|OFF OFF svC
    Controls whether the server requires the requested hostname be listed enumerated in the virtual host handling the request
    Substitute s/pattern/substitution/[infq]dhE
    Pattern to filter the response content
    SubstituteInheritBefore on|off off dhE
    Change the merge order of inherited patterns
    SubstituteMaxLineLength bytes(b|B|k|K|m|M|g|G) 1m dhE
    Set the maximum line size
    Suexec On|OffsB
    Enable or disable the suEXEC feature
    SuexecUserGroup User GroupsvE
    User and group for CGI programs to run as
    ThreadLimit AnzahlsM
    Bestimmt die Obergrenze der konfigurierbaren Anzahl von Threads +
    Substitute s/pattern/substitution/[infq]dhE
    Pattern to filter the response content
    SubstituteInheritBefore on|off off dhE
    Change the merge order of inherited patterns
    SubstituteMaxLineLength bytes(b|B|k|K|m|M|g|G) 1m dhE
    Set the maximum line size
    Suexec On|OffsB
    Enable or disable the suEXEC feature
    SuexecUserGroup User GroupsvE
    User and group for CGI programs to run as
    ThreadLimit AnzahlsM
    Bestimmt die Obergrenze der konfigurierbaren Anzahl von Threads pro Kindprozess
    ThreadsPerChild AnzahlsM
    Anzahl der Threads, die mit jedem Kindprozess gestartet +
    ThreadsPerChild AnzahlsM
    Anzahl der Threads, die mit jedem Kindprozess gestartet werden
    ThreadStackSize sizesM
    Die Größe des Stacks in Bytes, der von Threads +
    ThreadStackSize sizesM
    Die Größe des Stacks in Bytes, der von Threads verwendet wird, die Client-Verbindungen bearbeiten.
    TimeOut Sekunden 60 svC
    Zeitspanne, die der Server auf verschiedene Ereignisse wartet, +
    TimeOut Sekunden 60 svC
    Zeitspanne, die der Server auf verschiedene Ereignisse wartet, bevor er die Anfrage abbricht
    TraceEnable [on|off|extended] on svC
    Legt das Verhalten von TRACE-Anfragen fest
    TransferLog file|pipesvB
    Specify location of a log file
    TypesConfig file-path conf/mime.types sB
    The location of the mime.types file
    UNCList hostname [hostname...]sC
    Controls what UNC host names can be accessed by the server +
    TraceEnable [on|off|extended] on svC
    Legt das Verhalten von TRACE-Anfragen fest
    TransferLog file|pipesvB
    Specify location of a log file
    TypesConfig file-path conf/mime.types sB
    The location of the mime.types file
    UNCList hostname [hostname...]sC
    Controls what UNC host names can be accessed by the server
    UnDefine parameter-namesC
    Undefine the existence of a variable
    UndefMacro namesvdB
    Undefine a macro
    UnsetEnv env-variable [env-variable] -...svdhB
    Removes variables from the environment
    Use name [value1 ... valueN] -svdB
    Use a macro
    UseCanonicalName On|Off|DNS Off svdC
    Bestimmt, wie der Server seinen eigenen Namen und Port +
    UnDefine parameter-namesC
    Undefine the existence of a variable
    UndefMacro namesvdB
    Undefine a macro
    UnsetEnv env-variable [env-variable] +...svdhB
    Removes variables from the environment
    Use name [value1 ... valueN] +svdB
    Use a macro
    UseCanonicalName On|Off|DNS Off svdC
    Bestimmt, wie der Server seinen eigenen Namen und Port ermittelt
    UseCanonicalPhysicalPort On|Off Off svdC
    Bestimmt, wie der Server seinen eigenen Namen und Port +
    UseCanonicalPhysicalPort On|Off Off svdC
    Bestimmt, wie der Server seinen eigenen Namen und Port ermittelt
    User unix-userid #-1 sB
    The userid under which the server will answer +
    User unix-userid #-1 sB
    The userid under which the server will answer requests
    UserDir directory-filename [directory-filename] ... -svB
    Location of the user-specific directories
    VHostCGIMode On|Off|Secure On vD
    Determines whether the virtualhost can run +
    UserDir directory-filename [directory-filename] ... +svB
    Location of the user-specific directories
    VHostCGIMode On|Off|Secure On vD
    Determines whether the virtualhost can run subprocesses, and the privileges available to subprocesses.
    VHostCGIPrivs [+-]?privilege-name [[+-]?privilege-name] ...vD
    Assign arbitrary privileges to subprocesses created +
    VHostCGIPrivs [+-]?privilege-name [[+-]?privilege-name] ...vD
    Assign arbitrary privileges to subprocesses created by a virtual host.
    VHostGroup unix-groupidvD
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vD
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vD
    Determines whether the server runs with enhanced security +
    VHostGroup unix-groupidvD
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vD
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vD
    Determines whether the server runs with enhanced security for the virtualhost.
    VHostUser unix-useridvD
    Sets the User ID under which a virtual host runs.
    VirtualDocumentRoot interpolated-directory|none none svE
    Dynamically configure the location of the document root +
    VHostUser unix-useridvD
    Sets the User ID under which a virtual host runs.
    VirtualDocumentRoot interpolated-directory|none none svE
    Dynamically configure the location of the document root for a given virtual host
    VirtualDocumentRootIP interpolated-directory|none none svE
    Dynamically configure the location of the document root +
    VirtualDocumentRootIP interpolated-directory|none none svE
    Dynamically configure the location of the document root for a given virtual host
    <VirtualHost +
    <VirtualHost Adresse[:Port] [Adresse[:Port]] - ...> ... </VirtualHost>sC
    Enthält Direktiven, die nur auf bestimmte Hostnamen oder + ...> ... </VirtualHost>sC
    Enthält Direktiven, die nur auf bestimmte Hostnamen oder IP-Adressen angewendet werden
    VirtualScriptAlias interpolated-directory|none none svE
    Dynamically configure the location of the CGI directory for +
    VirtualScriptAlias interpolated-directory|none none svE
    Dynamically configure the location of the CGI directory for a given virtual host
    VirtualScriptAliasIP interpolated-directory|none none svE
    Dynamically configure the location of the CGI directory for +
    VirtualScriptAliasIP interpolated-directory|none none svE
    Dynamically configure the location of the CGI directory for a given virtual host
    WatchdogInterval time-interval[s] 1 sB
    Watchdog interval in seconds
    XBitHack on|off|full off svdhB
    Parse SSI directives in files with the execute bit +
    WatchdogInterval time-interval[s] 1 sB
    Watchdog interval in seconds
    XBitHack on|off|full off svdhB
    Parse SSI directives in files with the execute bit set
    xml2EncAlias charset alias [alias ...]sB
    Recognize Aliases for encoding values
    xml2EncDefault namesvdhB
    Sets a default encoding to assume when absolutely no information +
    xml2EncAlias charset alias [alias ...]sB
    Recognize Aliases for encoding values
    xml2EncDefault namesvdhB
    Sets a default encoding to assume when absolutely no information can be automatically detected
    xml2StartParse element [element ...]svdhB
    Advise the parser to skip leading junk.
    xml2StartParse element [element ...]svdhB
    Advise the parser to skip leading junk.

    Verfügbare Sprachen:  de  | diff -Nru apache2-2.4.69/docs/manual/mod/quickreference.html.en apache2-2.4.71/docs/manual/mod/quickreference.html.en --- apache2-2.4.69/docs/manual/mod/quickreference.html.en 2026-07-25 13:10:55.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/quickreference.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -716,7 +716,7 @@

    MDCertificateAuthority url letsencrypt sX
    The URL(s) of the ACME Certificate Authority to use.
    MDCertificateCheck name urlsX
    Set name and URL pattern for a certificate monitoring site.
    MDCertificateFile path-to-pem-filesX
    Specify a static certificate file for the MD.
    MDCertificateKeyFile path-to-filesX
    Specify a static private key for for the static cerrtificate.
    MDCertificateKeyFile path-to-filesX
    Specify a static private key for the static certificate.
    MDCertificateMonitor name url crt.sh https://crt. +sX
    The URL of a certificate log monitor.
    MDCertificateProtocol protocol ACME sX
    The protocol to use with the Certificate Authority.
    MDCertificateStatus on|off on sX
    Exposes public certificate information in JSON.
    MDDriveMode always|auto|manual auto sX
    former name of MDRenewMode.
    MDExternalAccountBinding key-id hmac-64 | none | file none sX
    Set the external account binding keyid and hmac values to use at CA
    MDHttpProxy urlsX
    Define a proxy for outgoing connections.
    MDInitialDelay duration 0s sX
    How long to delay the first certificate check.
    MDMatchNames all|servernames all sX
    Determines how DNS names are matched to vhosts
    MDMember hostnamesX
    Additional hostname for the managed domain.
    MDMembers auto|manual auto sX
    Control if the alias domain names are automatically added.
    MDMessageCmd path-to-cmd optional-argssX
    Handle events for Manage Domains
    MDMustStaple on|off off sX
    Control if new certificates carry the OCSP Must Staple flag.
    MDNotifyCmd path [ args ]sX
    Run a program when a Managed Domain is ready.
    MDomain dns-name [ other-dns-name... ] [auto|manual]sX
    Define list of domain names that belong to one group.
    <MDomainSet dns-name [ other-dns-name... ]>...</MDomainSet>sX
    Container for directives applied to the same managed domains.
    MDPortMap map1 [ map2 ] http:80 https:443 sX
    Map external to internal ports for domain ownership verification.
    MDPrivateKeys type [ params... ] RSA 2048 sX
    Set type and size of the private keys generated.
    MDProfile namesX
    Use a specific ACME profile from the CA
    MDProfileMandatory on|off off sX
    Control if an MDProfile is mandatory.
    MDRenewMode always|auto|manual auto sX
    Controls if certificates shall be renewed.
    MDRenewViaARI on|off on sX
    usage of the ACME ARI extension (rfc9773).
    MDRenewWindow duration 33% sX
    Control when a certificate will be renewed.
    MDRequireHttps off|temporary|permanent off sX
    Redirects http: traffic to https: for Managed Domains.
    MDRetryDelay duration 30s sX
    Time length for first retry, doubled on every consecutive error.
    MDRetryFailover number 13 sX
    The number of errors before a failover to another CA is triggered
    MDServerStatus on|off off sX
    Control if Managed Domain information is added to server-status.
    MDStapleOthers on|off on sX
    Enable stapling for certificates not managed by mod_md.
    MDStapling on|off off sX
    Enable stapling for all or a particular MDomain.
    MDStaplingKeepResponse duration 7d sX
    Controls when old responses should be removed.
    MDStaplingRenewWindow duration 33% sX
    Control when the stapling responses will be renewed.
    MDStoreDir path md sX
    Path on the local file system to store the Managed Domains data.
    MDStoreLocks on|off|duration off sX
    Configure locking of store for updates
    MDWarnWindow duration 10% sX
    Define the time window when you want to be warned about an expiring certificate.
    MemcacheConnTTL num[units] 15s svE
    Keepalive time for idle connections
    MergeSlashes ON|OFF ON svC
    Controls whether the server merges consecutive slashes in URLs. +
    MDHttpProxyCACertificateFile path-to-pem-file none sX
    Sets the root (CA) certificates to use for TLS connections to the http-proxy.
    MDInitialDelay duration 0s sX
    How long to delay the first certificate check.
    MDMatchNames all|servernames all sX
    Determines how DNS names are matched to vhosts
    MDMember hostnamesX
    Additional hostname for the managed domain.
    MDMembers auto|manual auto sX
    Control if the alias domain names are automatically added.
    MDMessageCmd path-to-cmd optional-argssX
    Handle events for Manage Domains
    MDMustStaple on|off off sX
    Control if new certificates carry the OCSP Must Staple flag.
    MDNotifyCmd path [ args ]sX
    Run a program when a Managed Domain is ready.
    MDomain dns-name [ other-dns-name... ] [auto|manual]sX
    Define list of domain names that belong to one group.
    <MDomainSet dns-name [ other-dns-name... ]>...</MDomainSet>sX
    Container for directives applied to the same managed domains.
    MDPortMap map1 [ map2 ] http:80 https:443 sX
    Map external to internal ports for domain ownership verification.
    MDPrivateKeys type [ params... ] RSA 2048 sX
    Set type and size of the private keys generated.
    MDProfile namesX
    Use a specific ACME profile from the CA
    MDProfileMandatory on|off off sX
    Control if an MDProfile is mandatory.
    MDRenewMode always|auto|manual auto sX
    Controls if certificates shall be renewed.
    MDRenewViaARI on|off on sX
    usage of the ACME ARI extension (rfc9773).
    MDRenewWindow duration 33% sX
    Control when a certificate will be renewed.
    MDRequireHttps off|temporary|permanent off sX
    Redirects http: traffic to https: for Managed Domains.
    MDRetryDelay duration 30s sX
    Time length for first retry, doubled on every consecutive error.
    MDRetryFailover number 13 sX
    The number of errors before a failover to another CA is triggered
    MDServerStatus on|off off sX
    Control if Managed Domain information is added to server-status.
    MDStapleOthers on|off on sX
    Enable stapling for certificates not managed by mod_md.
    MDStapling on|off off sX
    Enable stapling for all or a particular MDomain.
    MDStaplingKeepResponse duration 7d sX
    Controls when old responses should be removed.
    MDStaplingRenewWindow duration 33% sX
    Control when the stapling responses will be renewed.
    MDStoreDir path md sX
    Path on the local file system to store the Managed Domains data.
    MDStoreLocks on|off|duration off sX
    Configure locking of store for updates
    MDWarnWindow duration 10% sX
    Define the time window when you want to be warned about an expiring certificate.
    MemcacheConnTTL num[units] 15s svE
    Keepalive time for idle connections
    MergeSlashes ON|OFF ON svC
    Controls whether the server merges consecutive slashes in URLs.
    MergeTrailers [on|off] off svC
    Determines whether trailers are merged into headers
    MetaDir directory .web svdhD
    Name of the directory to find CERN-style meta information +
    MergeTrailers [on|off] off svC
    Determines whether trailers are merged into headers
    MetaDir directory .web svdhD
    Name of the directory to find CERN-style meta information files
    MetaFiles on|off off svdhD
    Activates CERN meta-file processing
    MetaSuffix suffix .meta svdhD
    File name suffix for the file containing CERN-style +
    MetaFiles on|off off svdhD
    Activates CERN meta-file processing
    MetaSuffix suffix .meta svdhD
    File name suffix for the file containing CERN-style meta information
    MimeMagicFile file-pathsvE
    Enable MIME-type determination based on file contents +
    MimeMagicFile file-pathsvE
    Enable MIME-type determination based on file contents using the specified magic file
    MinSpareServers number 5 sM
    Minimum number of idle child server processes
    MinSpareThreads numbersM
    Minimum number of idle threads available to handle request +
    MinSpareServers number 5 sM
    Minimum number of idle child server processes
    MinSpareThreads numbersM
    Minimum number of idle threads available to handle request spikes
    MMapFile file-path [file-path] ...sX
    Map a list of files into memory at startup time
    ModemStandard V.21|V.26bis|V.32|V.34|V.92dX
    Modem standard to simulate
    ModMimeUsePathInfo On|Off Off dB
    Tells mod_mime to treat path_info +
    MMapFile file-path [file-path] ...sX
    Map a list of files into memory at startup time
    ModemStandard V.21|V.26bis|V.32|V.34|V.92dX
    Modem standard to simulate
    ModMimeUsePathInfo On|Off Off dB
    Tells mod_mime to treat path_info components as part of the filename
    MultiviewsMatch Any|NegotiatedOnly|Filters|Handlers -[Handlers|Filters] NegotiatedOnly svdhB
    The types of files that will be included when searching for +
    MultiviewsMatch Any|NegotiatedOnly|Filters|Handlers +[Handlers|Filters] NegotiatedOnly svdhB
    The types of files that will be included when searching for a matching file with MultiViews
    Mutex mechanism [default|mutex-name] ... [OmitPID] default sC
    Configures mutex mechanism and lock file directory for all +
    Mutex mechanism [default|mutex-name] ... [OmitPID] default sC
    Configures mutex mechanism and lock file directory for all or specified mutexes
    NameVirtualHost addr[:port]sC
    DEPRECATED: Designates an IP address for name-virtual +
    NameVirtualHost addr[:port]sC
    DEPRECATED: Designates an IP address for name-virtual hosting
    NoProxy host [host] ...svE
    Hosts, domains, or networks that will be connected to +
    NoProxy host [host] ...svE
    Hosts, domains, or networks that will be connected to directly
    NWSSLTrustedCerts filename [filename] ...sB
    List of additional client certificates
    NWSSLUpgradeable [IP-address:]portnumbersB
    Allows a connection to be upgraded to an SSL connection upon request
    Options - [+|-]option [[+|-]option] ... FollowSymlinks svdhC
    Configures what features are available in a particular +
    NWSSLTrustedCerts filename [filename] ...sB
    List of additional client certificates
    NWSSLUpgradeable [IP-address:]portnumbersB
    Allows a connection to be upgraded to an SSL connection upon request
    Options + [+|-]option [[+|-]option] ... FollowSymlinks svdhC
    Configures what features are available in a particular directory
    Order ordering Deny,Allow dhD
    Controls the default access state and the order in which +
    Order ordering Deny,Allow dhD
    Controls the default access state and the order in which Allow and Deny are evaluated.
    OutputSed sed-commanddhX
    Sed command for filtering response content
    PassEnv env-variable [env-variable] -...svdhB
    Passes environment variables from the shell
    PidFile filename logs/httpd.pid sM
    File where the server records the process ID +
    OutputSed sed-commanddhX
    Sed command for filtering response content
    PassEnv env-variable [env-variable] +...svdhB
    Passes environment variables from the shell
    PidFile filename logs/httpd.pid sM
    File where the server records the process ID of the daemon
    PrivilegesMode FAST|SECURE|SELECTIVE FAST svdD
    Trade off processing speed and efficiency vs security against +
    PrivilegesMode FAST|SECURE|SELECTIVE FAST svdD
    Trade off processing speed and efficiency vs security against malicious privileges-aware code.
    Protocol protocolsvC
    Protocol for a listening socket
    ProtocolEcho On|Off Off svX
    Turn the echo server on or off
    Protocols protocol ... http/1.1 svC
    Protocols available for a server/virtual host
    ProtocolsHonorOrder On|Off On svC
    Determines if order of Protocols determines precedence during negotiation
    <Proxy wildcard-url> ...</Proxy>svE
    Container for directives applied to proxied resources
    Proxy100Continue Off|On On svdE
    Forward 100-continue expectation to the origin server
    ProxyAddHeaders Off|On On svdE
    Add proxy information in X-Forwarded-* headers
    ProxyBadHeader IsError|Ignore|StartBody IsError svE
    Determines how to handle bad header lines in a +
    Protocol protocolsvC
    Protocol for a listening socket
    ProtocolEcho On|Off Off svX
    Turn the echo server on or off
    Protocols protocol ... http/1.1 svC
    Protocols available for a server/virtual host
    ProtocolsHonorOrder On|Off On svC
    Determines if order of Protocols determines precedence during negotiation
    <Proxy wildcard-url> ...</Proxy>svE
    Container for directives applied to proxied resources
    Proxy100Continue Off|On On svdE
    Forward 100-continue expectation to the origin server
    ProxyAddHeaders Off|On On svdE
    Add proxy information in X-Forwarded-* headers
    ProxyBadHeader IsError|Ignore|StartBody IsError svE
    Determines how to handle bad header lines in a response
    ProxyBlock *|word|host|domain -[word|host|domain] ...svE
    Words, hosts, or domains that are banned from being +
    ProxyBlock *|word|host|domain +[word|host|domain] ...svE
    Words, hosts, or domains that are banned from being proxied
    ProxyDomain DomainsvE
    Default domain name for proxied requests
    ProxyErrorOverride Off|On [code ...] Off svdE
    Override error pages for proxied content
    ProxyExpressDBMFile pathnamesvE
    Pathname to DBM file.
    ProxyExpressDBMType type default svE
    DBM type of file.
    ProxyExpressEnable on|off off svE
    Enable the module functionality.
    ProxyFCGIBackendType FPM|GENERIC FPM svdhE
    Specify the type of backend FastCGI application
    ProxyFCGISetEnvIf conditional-expression +
    ProxyDomain DomainsvE
    Default domain name for proxied requests
    ProxyErrorOverride Off|On [code ...] Off svdE
    Override error pages for proxied content
    ProxyExpressDBMFile pathnamesvE
    Pathname to DBM file.
    ProxyExpressDBMType type default svE
    DBM type of file.
    ProxyExpressEnable on|off off svE
    Enable the module functionality.
    ProxyFCGIBackendType FPM|GENERIC FPM svdhE
    Specify the type of backend FastCGI application
    ProxyFCGISetEnvIf conditional-expression [!]environment-variable-name - [value-expression]svdhE
    Allow variables sent to FastCGI servers to be fixed up
    ProxyFtpDirCharset character_set ISO-8859-1 svdE
    Define the character set for proxied FTP listings
    ProxyFtpEscapeWildcards on|off on svdE
    Whether wildcards in requested filenames are escaped when sent to the FTP server
    ProxyFtpListOnWildcard on|off on svdE
    Whether wildcards in requested filenames trigger a file listing
    ProxyHCExpr name {ap_expr expression}svE
    Creates a named condition expression to use to determine health of the backend based on its response
    ProxyHCTemplate name parameter=setting [...]svE
    Creates a named template for setting various health check parameters
    ProxyHCTPsize size 16 sE
    Sets the total server-wide size of the threadpool used for the health check workers
    ProxyHTMLBufSize bytes 8192 svdB
    Sets the buffer size increment for buffering inline scripts and + [value-expression]svdhE
    Allow variables sent to FastCGI servers to be fixed up
    ProxyFtpDirCharset character_set ISO-8859-1 svdE
    Define the character set for proxied FTP listings
    ProxyFtpEscapeWildcards on|off on svdE
    Whether wildcards in requested filenames are escaped when sent to the FTP server
    ProxyFtpListOnWildcard on|off on svdE
    Whether wildcards in requested filenames trigger a file listing
    ProxyHCExpr name {ap_expr expression}svE
    Creates a named condition expression to use to determine health of the backend based on its response
    ProxyHCTemplate name parameter=setting [...]svE
    Creates a named template for setting various health check parameters
    ProxyHCTPsize size 16 sE
    Sets the total server-wide size of the threadpool used for the health check workers
    ProxyHTMLBufSize bytes 8192 svdB
    Sets the buffer size increment for buffering inline scripts and stylesheets.
    ProxyHTMLCharsetOut Charset | * UTF-8 svdB
    Specify a charset for mod_proxy_html output.
    ProxyHTMLDocType HTML|XHTML [Legacy]
    OR -
    ProxyHTMLDocType fpi [SGML|XML]
    svdB
    Sets an HTML or XHTML document type declaration.
    ProxyHTMLEnable On|Off Off svdB
    Turns the proxy_html filter on or off.
    ProxyHTMLEvents attribute [attribute ...]svdB
    Specify attributes to treat as scripting events.
    ProxyHTMLExtended On|Off Off svdB
    Determines whether to fix links in inline scripts, stylesheets, +
    ProxyHTMLCharsetOut Charset | * UTF-8 svdB
    Specify a charset for mod_proxy_html output.
    ProxyHTMLDocType HTML|XHTML [Legacy]
    OR +
    ProxyHTMLDocType fpi [SGML|XML]
    svdB
    Sets an HTML or XHTML document type declaration.
    ProxyHTMLEnable On|Off Off svdB
    Turns the proxy_html filter on or off.
    ProxyHTMLEvents attribute [attribute ...]svdB
    Specify attributes to treat as scripting events.
    ProxyHTMLExtended On|Off Off svdB
    Determines whether to fix links in inline scripts, stylesheets, and scripting events.
    ProxyHTMLFixups [lowercase] [dospath] [reset] none svdB
    Fixes for simple HTML errors.
    ProxyHTMLInterp On|Off Off svdB
    Enables per-request interpolation of +
    ProxyHTMLFixups [lowercase] [dospath] [reset] none svdB
    Fixes for simple HTML errors.
    ProxyHTMLInterp On|Off Off svdB
    Enables per-request interpolation of ProxyHTMLURLMap rules.
    ProxyHTMLLinks element attribute [attribute2 ...]svdB
    Specify HTML elements that have URL attributes to be rewritten.
    ProxyHTMLMeta On|Off Off svdB
    Turns on or off extra pre-parsing of metadata in HTML +
    ProxyHTMLLinks element attribute [attribute2 ...]svdB
    Specify HTML elements that have URL attributes to be rewritten.
    ProxyHTMLMeta On|Off Off svdB
    Turns on or off extra pre-parsing of metadata in HTML <head> sections.
    ProxyHTMLStripComments On|Off Off svdB
    Determines whether to strip HTML comments.
    ProxyHTMLURLMap from-pattern to-pattern [flags] [cond]svdB
    Defines a rule to rewrite HTML links
    ProxyIOBufferSize bytes 8192 svE
    Determine size of internal data throughput buffer
    <ProxyMatch regex> ...</ProxyMatch>svE
    Container for directives applied to regular-expression-matched +
    ProxyHTMLStripComments On|Off Off svdB
    Determines whether to strip HTML comments.
    ProxyHTMLURLMap from-pattern to-pattern [flags] [cond]svdB
    Defines a rule to rewrite HTML links
    ProxyIOBufferSize bytes 8192 svE
    Determine size of internal data throughput buffer
    <ProxyMatch regex> ...</ProxyMatch>svE
    Container for directives applied to regular-expression-matched proxied resources
    ProxyMaxForwards number -1 svE
    Maximum number of proxies that a request can be forwarded +
    ProxyMaxForwards number -1 svE
    Maximum number of proxies that a request can be forwarded through
    ProxyPass [path] !|url [key=value - [key=value ...]] [nocanon] [interpolate] [noquery]svdE
    Maps remote servers into the local server URL-space
    ProxyPassInherit On|Off On svE
    Inherit ProxyPass directives defined from the main server
    ProxyPassInterpolateEnv On|Off Off svdE
    Enable Environment Variable interpolation in Reverse Proxy configurations
    ProxyPassMatch [regex] !|url [key=value - [key=value ...]]svdE
    Maps remote servers into the local server URL-space using regular expressions
    ProxyPassReverse [path] url -[interpolate]svdE
    Adjusts the URL in HTTP response headers sent from a reverse +
    ProxyPass [path] !|url [key=value + [key=value ...]] [nocanon] [interpolate] [noquery]svdE
    Maps remote servers into the local server URL-space
    ProxyPassInherit On|Off On svE
    Inherit ProxyPass directives defined from the main server
    ProxyPassInterpolateEnv On|Off Off svdE
    Enable Environment Variable interpolation in Reverse Proxy configurations
    ProxyPassMatch [regex] !|url [key=value + [key=value ...]]svdE
    Maps remote servers into the local server URL-space using regular expressions
    ProxyPassReverse [path] url +[interpolate]svdE
    Adjusts the URL in HTTP response headers sent from a reverse proxied server
    ProxyPassReverseCookieDomain internal-domain -public-domain [interpolate]svdE
    Adjusts the Domain string in Set-Cookie headers from a reverse- +
    ProxyPassReverseCookieDomain internal-domain +public-domain [interpolate]svdE
    Adjusts the Domain string in Set-Cookie headers from a reverse- proxied server
    ProxyPassReverseCookiePath internal-path -public-path [interpolate]svdE
    Adjusts the Path string in Set-Cookie headers from a reverse- +
    ProxyPassReverseCookiePath internal-path +public-path [interpolate]svdE
    Adjusts the Path string in Set-Cookie headers from a reverse- proxied server
    ProxyPreserveHost On|Off Off svdE
    Use incoming Host HTTP request header for proxy +
    ProxyPreserveHost On|Off Off svdE
    Use incoming Host HTTP request header for proxy request
    ProxyReceiveBufferSize bytes 0 svE
    Network buffer size for proxied HTTP and FTP +
    ProxyReceiveBufferSize bytes 0 svE
    Network buffer size for proxied HTTP and FTP connections
    ProxyRemote match remote-server [username:password]svE
    Remote proxy used to handle certain requests
    ProxyRemoteMatch regex remote-server [username:password]svE
    Remote proxy used to handle requests matched by regular +
    ProxyRemote match remote-server [username:password]svE
    Remote proxy used to handle certain requests
    ProxyRemoteMatch regex remote-server [username:password]svE
    Remote proxy used to handle requests matched by regular expressions
    ProxyRequests On|Off Off svE
    Enables forward (standard) proxy requests
    ProxySCGIInternalRedirect On|Off|Headername On svdE
    Enable or disable internal redirect responses from the +
    ProxyRequests On|Off Off svE
    Enables forward (standard) proxy requests
    ProxySCGIInternalRedirect On|Off|Headername On svdE
    Enable or disable internal redirect responses from the backend
    ProxySCGISendfile On|Off|Headername Off svdE
    Enable evaluation of X-Sendfile pseudo response +
    ProxySCGISendfile On|Off|Headername Off svdE
    Enable evaluation of X-Sendfile pseudo response header
    ProxySet url key=value [key=value ...]svdE
    Set various Proxy balancer or member parameters
    ProxySourceAddress addresssvE
    Set local IP address for outgoing proxy connections
    ProxyStatus Off|On|Full Off svE
    Show Proxy LoadBalancer status in mod_status
    ProxyTimeout secondssvE
    Network timeout for proxied requests
    ProxyVia On|Off|Full|Block Off svE
    Information provided in the Via HTTP response +
    ProxySet url key=value [key=value ...]svdE
    Set various Proxy balancer or member parameters
    ProxySourceAddress addresssvE
    Set local IP address for outgoing proxy connections
    ProxyStatus Off|On|Full Off svE
    Show Proxy LoadBalancer status in mod_status
    ProxyTimeout secondssvE
    Network timeout for proxied requests
    ProxyVia On|Off|Full|Block Off svE
    Information provided in the Via HTTP response header for proxied requests
    ProxyWebsocketFallbackToProxyHttp On|Off On svD
    Instructs this module to let mod_proxy_http handle the request
    QualifyRedirectURL On|Off Off svdC
    Controls whether the REDIRECT_URL environment variable is +
    ProxyWebsocketFallbackToProxyHttp On|Off On svD
    Instructs this module to let mod_proxy_http handle the request
    QualifyRedirectURL On|Off Off svdC
    Controls whether the REDIRECT_URL environment variable is fully qualified
    ReadBufferSize bytes 8192 svdC
    Size of the buffers used to read data
    ReadmeName filenamesvdhB
    Name of the file that will be inserted at the end +
    ReadBufferSize bytes 8192 svdC
    Size of the buffers used to read data
    ReadmeName filenamesvdhB
    Name of the file that will be inserted at the end of the index listing
    ReceiveBufferSize bytes 0 sM
    TCP receive buffer size
    Redirect [status] [URL-path] -URLsvdhB
    Sends an external redirect asking the client to fetch +
    ReceiveBufferSize bytes 0 sM
    TCP receive buffer size
    Redirect [status] [URL-path] +URLsvdhB
    Sends an external redirect asking the client to fetch a different URL
    RedirectMatch [status] regex -URLsvdhB
    Sends an external redirect based on a regular expression match +
    RedirectMatch [status] regex +URLsvdhB
    Sends an external redirect based on a regular expression match of the current URL
    RedirectPermanent URL-path URLsvdhB
    Sends an external permanent redirect asking the client to fetch +
    RedirectPermanent URL-path URLsvdhB
    Sends an external permanent redirect asking the client to fetch a different URL
    RedirectRelative On|Off Off svdB
    Allows relative redirect targets.
    RedirectTemp URL-path URLsvdhB
    Sends an external temporary redirect asking the client to fetch +
    RedirectRelative On|Off Off svdB
    Allows relative redirect targets.
    RedirectTemp URL-path URLsvdhB
    Sends an external temporary redirect asking the client to fetch a different URL
    RedisConnPoolTTL num[units] 15s svE
    TTL used for the connection pool with the Redis server(s)
    RedisTimeout num[units] 5s svE
    R/W timeout used for the connection with the Redis server(s)
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    RegexDefaultOptions [none] [+|-]option [[+|-]option] ... DOTALL DOLLAR_ENDON +sC
    Allow to configure global/default options for regexes
    RegisterHttpMethod method [method [...]]sC
    Register non-standard HTTP methods
    RemoteIPHeader header-fieldsvB
    Declare the header field which should be parsed for useragent IP addresses
    RemoteIPInternalProxy proxy-ip|proxy-ip/subnet|hostname ...svB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPInternalProxyList filenamesvB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPProxiesHeader HeaderFieldNamesvB
    Declare the header field which will record all intermediate IP addresses
    RemoteIPProxyProtocol On|OffsvB
    Enable or disable PROXY protocol handling
    RemoteIPProxyProtocolExceptions host|range [host|range] [host|range]svB
    Disable processing of PROXY header for certain hosts or networks
    RemoteIPTrustedProxy proxy-ip|proxy-ip/subnet|hostname ...svB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPTrustedProxyList filenamesvB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoveCharset extension [extension] -...vdhB
    Removes any character set associations for a set of file +
    RedisConnPoolTTL num[units] 15s svE
    TTL used for the connection pool with the Redis server(s)
    RedisTimeout num[units] 5s svE
    R/W timeout used for the connection with the Redis server(s)
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    RegexDefaultOptions [none] [+|-]option [[+|-]option] ... DOTALL DOLLAR_ENDON +sC
    Allow to configure global/default options for regexes
    RegisterHttpMethod method [method [...]]sC
    Register non-standard HTTP methods
    RemoteIPHeader header-fieldsvB
    Declare the header field which should be parsed for useragent IP addresses
    RemoteIPInternalProxy proxy-ip|proxy-ip/subnet|hostname ...svB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPInternalProxyList filenamesvB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPProxiesHeader HeaderFieldNamesvB
    Declare the header field which will record all intermediate IP addresses
    RemoteIPProxyProtocol On|OffsvB
    Enable or disable PROXY protocol handling
    RemoteIPProxyProtocolExceptions host|range [host|range] [host|range]svB
    Disable processing of PROXY header for certain hosts or networks
    RemoteIPTrustedProxy proxy-ip|proxy-ip/subnet|hostname ...svB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPTrustedProxyList filenamesvB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoveCharset extension [extension] +...vdhB
    Removes any character set associations for a set of file extensions
    RemoveEncoding extension [extension] -...vdhB
    Removes any content encoding associations for a set of file +
    RemoveEncoding extension [extension] +...vdhB
    Removes any content encoding associations for a set of file extensions
    RemoveHandler extension [extension] -...vdhB
    Removes any handler associations for a set of file +
    RemoveHandler extension [extension] +...vdhB
    Removes any handler associations for a set of file extensions
    RemoveInputFilter extension [extension] -...vdhB
    Removes any input filter associations for a set of file +
    RemoveInputFilter extension [extension] +...vdhB
    Removes any input filter associations for a set of file extensions
    RemoveLanguage extension [extension] -...vdhB
    Removes any language associations for a set of file +
    RemoveLanguage extension [extension] +...vdhB
    Removes any language associations for a set of file extensions
    RemoveOutputFilter extension [extension] -...vdhB
    Removes any output filter associations for a set of file +
    RemoveOutputFilter extension [extension] +...vdhB
    Removes any output filter associations for a set of file extensions
    RemoveType extension [extension] -...vdhB
    Removes any content type associations for a set of file +
    RemoveType extension [extension] +...vdhB
    Removes any content type associations for a set of file extensions
    RequestHeader add|append|edit|edit*|merge|set|setifempty|unset +
    RequestHeader add|append|edit|edit*|merge|set|setifempty|unset header [[expr=]value [replacement] [early|env=[!]varname|expr=expression]] -svdhE
    Configure HTTP request headers
    RequestReadTimeout +svdhE
    Configure HTTP request headers
    RequestReadTimeout [handshake=timeout[-maxtimeout][,MinRate=rate] [header=timeout[-maxtimeout][,MinRate=rate] [body=timeout[-maxtimeout][,MinRate=rate] - handshake=0 header= +svE
    Set timeout values for completing the TLS handshake, receiving + handshake=0 header= +svE
    Set timeout values for completing the TLS handshake, receiving the request headers and/or body from client.
    Require [not] entity-name - [entity-name] ...dhB
    Tests whether an authenticated user is authorized by +
    Require [not] entity-name + [entity-name] ...dhB
    Tests whether an authenticated user is authorized by an authorization provider.
    <RequireAll> ... </RequireAll>dhB
    Enclose a group of authorization directives of which none +
    <RequireAll> ... </RequireAll>dhB
    Enclose a group of authorization directives of which none must fail and at least one must succeed for the enclosing directive to succeed.
    <RequireAny> ... </RequireAny>dhB
    Enclose a group of authorization directives of which one +
    <RequireAny> ... </RequireAny>dhB
    Enclose a group of authorization directives of which one must succeed for the enclosing directive to succeed.
    <RequireNone> ... </RequireNone>dhB
    Enclose a group of authorization directives of which none +
    <RequireNone> ... </RequireNone>dhB
    Enclose a group of authorization directives of which none must succeed for the enclosing directive to not fail.
    RewriteBase URL-pathdhE
    Sets the base URL for per-directory rewrites
    RewriteCond - TestString [!]CondPattern [flags]svdhE
    Defines a condition under which rewriting will take place +
    RewriteBase URL-pathdhE
    Sets the base URL for per-directory rewrites
    RewriteCond + TestString [!]CondPattern [flags]svdhE
    Defines a condition under which rewriting will take place
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource +
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource [MapTypeOptions] -svE
    Defines a mapping function for key-lookup
    RewriteOptions OptionssvdhE
    Sets some special options for the rewrite engine
    RewriteRule - [!]Pattern Substitution [flags]svdhE
    Defines rules for the rewriting engine
    RLimitCPU seconds|max [seconds|max]svdhC
    Limits the CPU consumption of processes launched +svE
    Defines a mapping function for key-lookup
    RewriteOptions OptionssvdhE
    Sets some special options for the rewrite engine
    RewriteRule + [!]Pattern Substitution [flags]svdhE
    Defines rules for the rewriting engine
    RLimitCPU seconds|max [seconds|max]svdhC
    Limits the CPU consumption of processes launched by Apache httpd children
    RLimitMEM bytes|max [bytes|max]svdhC
    Limits the memory consumption of processes launched +
    RLimitMEM bytes|max [bytes|max]svdhC
    Limits the memory consumption of processes launched by Apache httpd children
    RLimitNPROC number|max [number|max]svdhC
    Limits the number of processes that can be launched by +
    RLimitNPROC number|max [number|max]svdhC
    Limits the number of processes that can be launched by processes launched by Apache httpd children
    Satisfy Any|All All dhD
    Interaction between host-level access control and +
    Satisfy Any|All All dhD
    Interaction between host-level access control and user authentication
    ScoreBoardFile file-path logs/apache_runtime +sM
    Location of the file used to store coordination data for +
    ScoreBoardFile file-path logs/apache_runtime +sM
    Location of the file used to store coordination data for the child processes
    Script method cgi-scriptsvdB
    Activates a CGI script for a particular request +
    Script method cgi-scriptsvdB
    Activates a CGI script for a particular request method.
    ScriptAlias [URL-path] -file-path|directory-pathsvdB
    Maps a URL to a filesystem location and designates the +
    ScriptAlias [URL-path] +file-path|directory-pathsvdB
    Maps a URL to a filesystem location and designates the target as a CGI script
    ScriptAliasMatch regex -file-path|directory-pathsvB
    Maps a URL to a filesystem location using a regular expression +
    ScriptAliasMatch regex +file-path|directory-pathsvB
    Maps a URL to a filesystem location using a regular expression and designates the target as a CGI script
    ScriptInterpreterSource Registry|Registry-Strict|Script Script svdhC
    Technique for locating the interpreter for CGI +
    ScriptInterpreterSource Registry|Registry-Strict|Script Script svdhC
    Technique for locating the interpreter for CGI scripts
    ScriptLog file-pathsvB
    Location of the CGI script error logfile
    ScriptLogBuffer bytes 1024 svB
    Maximum amount of PUT or POST requests that will be recorded +
    ScriptLog file-pathsvB
    Location of the CGI script error logfile
    ScriptLogBuffer bytes 1024 svB
    Maximum amount of PUT or POST requests that will be recorded in the scriptlog
    ScriptLogLength bytes 10385760 svB
    Size limit of the CGI script logfile
    ScriptSock file-path cgisock sB
    The filename prefix of the socket to use for communication with +
    ScriptLogLength bytes 10385760 svB
    Size limit of the CGI script logfile
    ScriptSock file-path cgisock sB
    The filename prefix of the socket to use for communication with the cgi daemon
    SecureListen [IP-address:]portnumber -Certificate-Name [MUTUAL]sB
    Enables SSL encryption for the specified port
    SeeRequestTail On|Off Off sC
    Determine if mod_status displays the first 63 characters +
    SecureListen [IP-address:]portnumber +Certificate-Name [MUTUAL]sB
    Enables SSL encryption for the specified port
    SeeRequestTail On|Off Off sC
    Determine if mod_status displays the first 63 characters of a request or the last 63, assuming the request itself is greater than 63 chars.
    SendBufferSize bytes 0 sM
    TCP buffer size
    ServerAdmin email-address|URLsvC
    Email address that the server includes in error +
    SendBufferSize bytes 0 sM
    TCP buffer size
    ServerAdmin email-address|URLsvC
    Email address that the server includes in error messages sent to the client
    ServerAlias hostname [hostname] ...vC
    Alternate names for a host used when matching requests +
    ServerAlias hostname [hostname] ...vC
    Alternate names for a host used when matching requests to name-virtual hosts
    ServerLimit numbersM
    Upper limit on configurable number of processes
    ServerName [scheme://]domain-name|ip-address[:port]svC
    Hostname and port that the server uses to identify +
    ServerLimit numbersM
    Upper limit on configurable number of processes
    ServerName [scheme://]domain-name|ip-address[:port]svC
    Hostname and port that the server uses to identify itself
    ServerPath URL-pathvC
    Legacy URL pathname for a name-based virtual host that +
    ServerPath URL-pathvC
    Legacy URL pathname for a name-based virtual host that is accessed by an incompatible browser
    ServerRoot directory-path /usr/local/apache sC
    Base directory for the server installation
    ServerSignature On|Off|EMail Off svdhC
    Configures the footer on server-generated documents
    ServerTokens Major|Minor|Min[imal]|Prod[uctOnly]|OS|Full Full sC
    Configures the Server HTTP response +
    ServerRoot directory-path /usr/local/apache sC
    Base directory for the server installation
    ServerSignature On|Off|EMail Off svdhC
    Configures the footer on server-generated documents
    ServerTokens Major|Minor|Min[imal]|Prod[uctOnly]|OS|Full Full sC
    Configures the Server HTTP response header
    Session On|Off Off svdhE
    Enables a session for the current directory or location
    SessionCookieName name attributessvdhE
    Name and attributes for the RFC2109 cookie storing the session
    SessionCookieName2 name attributessvdhE
    Name and attributes for the RFC2965 cookie storing the session
    SessionCookieRemove On|Off Off svdhE
    Control for whether session cookies should be removed from incoming HTTP headers
    SessionCryptoCipher name aes256 svdhX
    The crypto cipher to be used to encrypt the session
    SessionCryptoDriver name [param[=value]]sX
    The crypto driver to be used to encrypt the session
    SessionCryptoPassphrase secret [ secret ... ] svdhX
    The key used to encrypt the session
    SessionCryptoPassphraseFile filenamesvdX
    File containing keys used to encrypt the session
    SessionDBDCookieName name attributessvdhE
    Name and attributes for the RFC2109 cookie storing the session ID
    SessionDBDCookieName2 name attributessvdhE
    Name and attributes for the RFC2965 cookie storing the session ID
    SessionDBDCookieRemove On|Off On svdhE
    Control for whether session ID cookies should be removed from incoming HTTP headers
    SessionDBDDeleteLabel label deletesession svdhE
    The SQL query to use to remove sessions from the database
    SessionDBDInsertLabel label insertsession svdhE
    The SQL query to use to insert sessions into the database
    SessionDBDPerUser On|Off Off svdhE
    Enable a per user session
    SessionDBDSelectLabel label selectsession svdhE
    The SQL query to use to select sessions from the database
    SessionDBDUpdateLabel label updatesession svdhE
    The SQL query to use to update existing sessions in the database
    SessionEnv On|Off Off svdhE
    Control whether the contents of the session are written to the +
    Session On|Off Off svdhE
    Enables a session for the current directory or location
    SessionCookieName name attributessvdhE
    Name and attributes for the RFC2109 cookie storing the session
    SessionCookieName2 name attributessvdhE
    Name and attributes for the RFC2965 cookie storing the session
    SessionCookieRemove On|Off Off svdhE
    Control for whether session cookies should be removed from incoming HTTP headers
    SessionCryptoCipher name aes256 svdhX
    The crypto cipher to be used to encrypt the session
    SessionCryptoDriver name [param[=value]]sX
    The crypto driver to be used to encrypt the session
    SessionCryptoPassphrase secret [ secret ... ] svdhX
    The key used to encrypt the session
    SessionCryptoPassphraseFile filenamesvdX
    File containing keys used to encrypt the session
    SessionDBDCookieName name attributessvdhE
    Name and attributes for the RFC2109 cookie storing the session ID
    SessionDBDCookieName2 name attributessvdhE
    Name and attributes for the RFC2965 cookie storing the session ID
    SessionDBDCookieRemove On|Off On svdhE
    Control for whether session ID cookies should be removed from incoming HTTP headers
    SessionDBDDeleteLabel label deletesession svdhE
    The SQL query to use to remove sessions from the database
    SessionDBDInsertLabel label insertsession svdhE
    The SQL query to use to insert sessions into the database
    SessionDBDPerUser On|Off Off svdhE
    Enable a per user session
    SessionDBDSelectLabel label selectsession svdhE
    The SQL query to use to select sessions from the database
    SessionDBDUpdateLabel label updatesession svdhE
    The SQL query to use to update existing sessions in the database
    SessionEnv On|Off Off svdhE
    Control whether the contents of the session are written to the HTTP_SESSION environment variable
    SessionExclude pathsvdhE
    Define URL prefixes for which a session is ignored
    SessionExpiryUpdateInterval interval 0 (always update) svdhE
    Define the number of seconds a session's expiry may change without +
    SessionExclude pathsvdhE
    Define URL prefixes for which a session is ignored
    SessionExpiryUpdateInterval interval 0 (always update) svdhE
    Define the number of seconds a session's expiry may change without the session being updated
    SessionHeader headersvdhE
    Import session updates from a given HTTP response header
    SessionInclude pathsvdhE
    Define URL prefixes for which a session is valid
    SessionMaxAge maxage 0 svdhE
    Define a maximum age in seconds for a session
    SetEnv env-variable [value]svdhB
    Sets environment variables
    SetEnvIf attribute +
    SessionHeader headersvdhE
    Import session updates from a given HTTP response header
    SessionInclude pathsvdhE
    Define URL prefixes for which a session is valid
    SessionMaxAge maxage 0 svdhE
    Define a maximum age in seconds for a session
    SetEnv env-variable [value]svdhB
    Sets environment variables
    SetEnvIf attribute regex [!]env-variable[=value] - [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on attributes of the request + [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on attributes of the request
    SetEnvIfExpr expr +
    SetEnvIfExpr expr [!]env-variable[=value] - [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on an ap_expr expression
    SetEnvIfNoCase attribute regex + [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on an ap_expr expression
    SetEnvIfNoCase attribute regex [!]env-variable[=value] - [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on attributes of the request + [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on attributes of the request without respect to case
    SetHandler handler-name|none|expressionsvdhC
    Forces all matching files to be processed by a +
    SetHandler handler-name|none|expressionsvdhC
    Forces all matching files to be processed by a handler
    SetInputFilter filter[;filter...]svdhC
    Sets the filters that will process client requests and POST +
    SetInputFilter filter[;filter...]svdhC
    Sets the filters that will process client requests and POST input
    SetOutputFilter filter[;filter...]svdhC
    Sets the filters that will process responses from the +
    SetOutputFilter filter[;filter...]svdhC
    Sets the filters that will process responses from the server
    SSIEndTag tag "-->" svB
    String that ends an include element
    SSIErrorMsg message "[an error occurred +svdhB
    Error message displayed when there is an SSI +
    SSIEndTag tag "-->" svB
    String that ends an include element
    SSIErrorMsg message "[an error occurred +svdhB
    Error message displayed when there is an SSI error
    SSIETag on|off off dhB
    Controls whether ETags are generated by the server.
    SSILastModified on|off off dhB
    Controls whether Last-Modified headers are generated by the +
    SSIETag on|off off dhB
    Controls whether ETags are generated by the server.
    SSILastModified on|off off dhB
    Controls whether Last-Modified headers are generated by the server.
    SSILegacyExprParser on|off off dhB
    Enable compatibility mode for conditional expressions.
    SSIStartTag tag "<!--#" svB
    String that starts an include element
    SSITimeFormat formatstring "%A, %d-%b-%Y %H:%M +svdhB
    Configures the format in which date strings are +
    SSILegacyExprParser on|off off dhB
    Enable compatibility mode for conditional expressions.
    SSIStartTag tag "<!--#" svB
    String that starts an include element
    SSITimeFormat formatstring "%A, %d-%b-%Y %H:%M +svdhB
    Configures the format in which date strings are displayed
    SSIUndefinedEcho string "(none)" svdhB
    String displayed when an unset variable is echoed
    SSLCACertificateFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates +
    SSIUndefinedEcho string "(none)" svdhB
    String displayed when an unset variable is echoed
    SSLCACertificateFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates for Client Auth
    SSLCACertificatePath directory-pathsvE
    Directory of PEM-encoded CA Certificates for +
    SSLCACertificatePath directory-pathsvE
    Directory of PEM-encoded CA Certificates for Client Auth
    SSLCADNRequestFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates +
    SSLCADNRequestFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates for defining acceptable CA names
    SSLCADNRequestPath directory-pathsvE
    Directory of PEM-encoded CA Certificates for +
    SSLCADNRequestPath directory-pathsvE
    Directory of PEM-encoded CA Certificates for defining acceptable CA names
    SSLCARevocationCheck chain|leaf|none [flags ...] none svE
    Enable CRL-based revocation checking
    SSLCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for +
    SSLCARevocationCheck chain|leaf|none [flags ...] none svE
    Enable CRL-based revocation checking
    SSLCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for Client Auth
    SSLCARevocationPath directory-pathsvE
    Directory of PEM-encoded CA CRLs for +
    SSLCARevocationPath directory-pathsvE
    Directory of PEM-encoded CA CRLs for Client Auth
    SSLCertificateChainFile file-pathsvE
    File of PEM-encoded Server CA Certificates
    SSLCertificateFile file-path|certidsvE
    Server PEM-encoded X.509 certificate data file or token identifier
    SSLCertificateKeyFile file-path|keyidsvE
    Server PEM-encoded private key file
    SSLCipherSuite [protocol] cipher-spec DEFAULT (depends on +svdhE
    Cipher Suite available for negotiation in SSL +
    SSLCertificateChainFile file-pathsvE
    File of PEM-encoded Server CA Certificates
    SSLCertificateFile file-path|certidsvE
    Server PEM-encoded X.509 certificate data file or token identifier
    SSLCertificateKeyFile file-path|keyidsvE
    Server PEM-encoded private key file
    SSLCipherSuite [protocol] cipher-spec DEFAULT (depends on +svdhE
    Cipher Suite available for negotiation in SSL handshake
    SSLCompression on|off off svE
    Enable compression on the SSL level
    SSLCryptoDevice engine builtin sE
    Enable use of a cryptographic hardware accelerator
    SSLEngine on|off off svE
    SSL Engine Operation Switch
    SSLFIPS on|off off sE
    SSL FIPS mode Switch
    SSLHonorCipherOrder on|off off svE
    Option to prefer the server's cipher preference order
    SSLInsecureRenegotiation on|off off svE
    Option to enable support for insecure renegotiation
    SSLOCSPDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|leaf|off [flags] off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPNoverify on|off off svE
    skip the OCSP responder certificates verification
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    SSLOCSPProxyURL urlsvE
    Proxy URL to use for OCSP requests
    SSLOCSPResponderCertificateFile filesvE
    Set of trusted PEM encoded OCSP responder certificates
    SSLOCSPResponderTimeout seconds 10 svE
    Timeout for OCSP queries
    SSLOCSPResponseMaxAge seconds -1 svE
    Maximum allowable age for OCSP responses
    SSLOCSPResponseTimeSkew seconds 300 svE
    Maximum allowable time skew for OCSP response validation
    SSLOCSPUseRequestNonce on|off on svE
    Use a nonce within OCSP queries
    SSLOpenSSLConfCmd command-name command-valuesvE
    Configure OpenSSL parameters through its SSL_CONF API
    SSLOptions [+|-]option ...svdhE
    Configure various SSL engine run-time options
    SSLPassPhraseDialog type builtin sE
    Type of pass phrase dialog for encrypted private +
    SSLCompression on|off off svE
    Enable compression on the SSL level
    SSLCryptoDevice engine builtin sE
    Enable use of a cryptographic hardware accelerator
    SSLEngine on|off off svE
    SSL Engine Operation Switch
    SSLFIPS on|off off sE
    SSL FIPS mode Switch
    SSLHonorCipherOrder on|off off svE
    Option to prefer the server's cipher preference order
    SSLInsecureRenegotiation on|off off svE
    Option to enable support for insecure renegotiation
    SSLOCSPDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|leaf|off [flags] off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPNoverify on|off off svE
    skip the OCSP responder certificates verification
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    SSLOCSPProxyURL urlsvE
    Proxy URL to use for OCSP requests
    SSLOCSPResponderCertificateFile filesvE
    Set of trusted PEM encoded OCSP responder certificates
    SSLOCSPResponderTimeout seconds 10 svE
    Timeout for OCSP queries
    SSLOCSPResponseMaxAge seconds -1 svE
    Maximum allowable age for OCSP responses
    SSLOCSPResponseTimeSkew seconds 300 svE
    Maximum allowable time skew for OCSP response validation
    SSLOCSPUseRequestNonce on|off on svE
    Use a nonce within OCSP queries
    SSLOpenSSLConfCmd command-name command-valuesvE
    Configure OpenSSL parameters through its SSL_CONF API
    SSLOptions [+|-]option ...svdhE
    Configure various SSL engine run-time options
    SSLPassPhraseDialog type builtin sE
    Type of pass phrase dialog for encrypted private keys
    SSLProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svE
    Configure usable SSL/TLS protocol versions
    SSLProxyCACertificateFile file-pathsvpE
    File of concatenated PEM-encoded CA Certificates +
    SSLProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svE
    Configure usable SSL/TLS protocol versions
    SSLProxyCACertificateFile file-pathsvpE
    File of concatenated PEM-encoded CA Certificates for Remote Server Auth
    SSLProxyCACertificatePath directory-pathsvpE
    Directory of PEM-encoded CA Certificates for +
    SSLProxyCACertificatePath directory-pathsvpE
    Directory of PEM-encoded CA Certificates for Remote Server Auth
    SSLProxyCARevocationCheck chain|leaf|none none svpE
    Enable CRL-based revocation checking for Remote Server Auth
    SSLProxyCARevocationFile file-pathsvpE
    File of concatenated PEM-encoded CA CRLs for +
    SSLProxyCARevocationCheck chain|leaf|none none svpE
    Enable CRL-based revocation checking for Remote Server Auth
    SSLProxyCARevocationFile file-pathsvpE
    File of concatenated PEM-encoded CA CRLs for Remote Server Auth
    SSLProxyCARevocationPath directory-pathsvpE
    Directory of PEM-encoded CA CRLs for +
    SSLProxyCARevocationPath directory-pathsvpE
    Directory of PEM-encoded CA CRLs for Remote Server Auth
    SSLProxyCheckPeerCN on|off on svpE
    Whether to check the remote server certificate's CN field +
    SSLProxyCheckPeerCN on|off on svpE
    Whether to check the remote server certificate's CN field
    SSLProxyCheckPeerExpire on|off on svpE
    Whether to check if remote server certificate is expired +
    SSLProxyCheckPeerExpire on|off on svpE
    Whether to check if remote server certificate is expired
    SSLProxyCheckPeerName on|off on svpE
    Configure host name checking for remote server certificates +
    SSLProxyCheckPeerName on|off on svpE
    Configure host name checking for remote server certificates
    SSLProxyCipherSuite [protocol] cipher-spec ALL:!ADH:RC4+RSA:+H +svpE
    Cipher Suite available for negotiation in SSL +
    SSLProxyCipherSuite [protocol] cipher-spec ALL:!ADH:RC4+RSA:+H +svpE
    Cipher Suite available for negotiation in SSL proxy handshake
    SSLProxyEngine on|off off svpE
    SSL Proxy Engine Operation Switch
    SSLProxyMachineCertificateChainFile filenamesvpE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesvpE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysvpE
    Directory of PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svpE
    Configure usable SSL protocol flavors for proxy usage
    SSLProxyVerify level none svpE
    Type of remote server Certificate verification
    SSLProxyVerifyDepth number 1 svpE
    Maximum depth of CA Certificates in Remote Server +
    SSLProxyEngine on|off off svpE
    SSL Proxy Engine Operation Switch
    SSLProxyMachineCertificateChainFile filenamesvpE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesvpE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysvpE
    Directory of PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svpE
    Configure usable SSL protocol flavors for proxy usage
    SSLProxyVerify level none svpE
    Type of remote server Certificate verification
    SSLProxyVerifyDepth number 1 svpE
    Maximum depth of CA Certificates in Remote Server Certificate verification
    SSLRandomSeed context source -[bytes]sE
    Pseudo Random Number Generator (PRNG) seeding +
    SSLRandomSeed context source +[bytes]sE
    Pseudo Random Number Generator (PRNG) seeding source
    SSLRenegBufferSize bytes 131072 dhE
    Set the size for the SSL renegotiation buffer
    SSLRequire expressiondhE
    Allow access only when an arbitrarily complex +
    SSLRenegBufferSize bytes 131072 dhE
    Set the size for the SSL renegotiation buffer
    SSLRequire expressiondhE
    Allow access only when an arbitrarily complex boolean expression is true
    SSLRequireSSLdhE
    Deny access when SSL is not used for the +
    SSLRequireSSLdhE
    Deny access when SSL is not used for the HTTP request
    SSLSessionCache type none sE
    Type of the global/inter-process SSL Session +
    SSLSessionCache type none sE
    Type of the global/inter-process SSL Session Cache
    SSLSessionCacheTimeout seconds 300 svE
    Number of seconds before an SSL session expires +
    SSLSessionCacheTimeout seconds 300 svE
    Number of seconds before an SSL session expires in the Session Cache
    SSLSessionTicketKeyFile file-pathsvE
    Persistent encryption/decryption key for TLS session tickets
    SSLSessionTickets on|off on svE
    Enable or disable use of TLS session tickets
    SSLSRPUnknownUserSeed secret-stringsvE
    SRP unknown user seed
    SSLSRPVerifierFile file-pathsvE
    Path to SRP verifier file
    SSLStaplingCache typesE
    Configures the OCSP stapling cache
    SSLStaplingErrorCacheTimeout seconds 600 svE
    Number of seconds before expiring invalid responses in the OCSP stapling cache
    SSLStaplingFakeTryLater on|off on svE
    Synthesize "tryLater" responses for failed OCSP stapling queries
    SSLStaplingForceURL urisvE
    Override the OCSP responder URI specified in the certificate's AIA extension
    SSLStaplingResponderTimeout seconds 10 svE
    Timeout for OCSP stapling queries
    SSLStaplingResponseMaxAge seconds -1 svE
    Maximum allowable age for OCSP stapling responses
    SSLStaplingResponseTimeSkew seconds 300 svE
    Maximum allowable time skew for OCSP stapling response validation
    SSLStaplingReturnResponderErrors on|off on svE
    Pass stapling related OCSP errors on to client
    SSLStaplingStandardCacheTimeout seconds 3600 svE
    Number of seconds before expiring responses in the OCSP stapling cache
    SSLStrictSNIVHostCheck on|off off svE
    Whether to allow non-SNI clients to access a name-based virtual +
    SSLSessionTicketKeyFile file-pathsvE
    Persistent encryption/decryption key for TLS session tickets
    SSLSessionTickets on|off on svE
    Enable or disable use of TLS session tickets
    SSLSRPUnknownUserSeed secret-stringsvE
    SRP unknown user seed
    SSLSRPVerifierFile file-pathsvE
    Path to SRP verifier file
    SSLStaplingCache typesE
    Configures the OCSP stapling cache
    SSLStaplingErrorCacheTimeout seconds 600 svE
    Number of seconds before expiring invalid responses in the OCSP stapling cache
    SSLStaplingFakeTryLater on|off on svE
    Synthesize "tryLater" responses for failed OCSP stapling queries
    SSLStaplingForceURL urisvE
    Override the OCSP responder URI specified in the certificate's AIA extension
    SSLStaplingResponderTimeout seconds 10 svE
    Timeout for OCSP stapling queries
    SSLStaplingResponseMaxAge seconds -1 svE
    Maximum allowable age for OCSP stapling responses
    SSLStaplingResponseTimeSkew seconds 300 svE
    Maximum allowable time skew for OCSP stapling response validation
    SSLStaplingReturnResponderErrors on|off on svE
    Pass stapling related OCSP errors on to client
    SSLStaplingStandardCacheTimeout seconds 3600 svE
    Number of seconds before expiring responses in the OCSP stapling cache
    SSLStrictSNIVHostCheck on|off off svE
    Whether to allow non-SNI clients to access a name-based virtual host.
    SSLUserName varnamesdhE
    Variable name to determine user name
    SSLUseStapling on|off off svE
    Enable stapling of OCSP responses in the TLS handshake
    SSLVerifyClient level none svdhE
    Type of Client Certificate verification
    SSLVerifyDepth number 1 svdhE
    Maximum depth of CA Certificates in Client +
    SSLUserName varnamesdhE
    Variable name to determine user name
    SSLUseStapling on|off off svE
    Enable stapling of OCSP responses in the TLS handshake
    SSLVerifyClient level none svdhE
    Type of Client Certificate verification
    SSLVerifyDepth number 1 svdhE
    Maximum depth of CA Certificates in Client Certificate verification
    SSLVHostSNIPolicy strict|secure|authonly|insecure secure sE
    Set compatibility policy for SNI client access to virtual hosts.
    StartServers numbersM
    Number of child server processes created at startup
    StartThreads numbersM
    Number of threads created on startup
    StrictHostCheck ON|OFF OFF svC
    Controls whether the server requires the requested hostname be +
    SSLVHostSNIPolicy strict|secure|authonly|insecure secure sE
    Set compatibility policy for SNI client access to virtual hosts.
    StartServers numbersM
    Number of child server processes created at startup
    StartThreads numbersM
    Number of threads created on startup
    StrictHostCheck ON|OFF OFF svC
    Controls whether the server requires the requested hostname be listed enumerated in the virtual host handling the request
    Substitute s/pattern/substitution/[infq]dhE
    Pattern to filter the response content
    SubstituteInheritBefore on|off off dhE
    Change the merge order of inherited patterns
    SubstituteMaxLineLength bytes(b|B|k|K|m|M|g|G) 1m dhE
    Set the maximum line size
    Suexec On|OffsB
    Enable or disable the suEXEC feature
    SuexecUserGroup User GroupsvE
    User and group for CGI programs to run as
    ThreadLimit numbersM
    Sets the upper limit on the configurable number of threads +
    Substitute s/pattern/substitution/[infq]dhE
    Pattern to filter the response content
    SubstituteInheritBefore on|off off dhE
    Change the merge order of inherited patterns
    SubstituteMaxLineLength bytes(b|B|k|K|m|M|g|G) 1m dhE
    Set the maximum line size
    Suexec On|OffsB
    Enable or disable the suEXEC feature
    SuexecUserGroup User GroupsvE
    User and group for CGI programs to run as
    ThreadLimit numbersM
    Sets the upper limit on the configurable number of threads per child process
    ThreadsPerChild numbersM
    Number of threads created by each child process
    ThreadStackSize sizesM
    The size in bytes of the stack used by threads handling +
    ThreadsPerChild numbersM
    Number of threads created by each child process
    ThreadStackSize sizesM
    The size in bytes of the stack used by threads handling client connections
    TimeOut seconds 60 svC
    Amount of time the server will wait for +
    TimeOut seconds 60 svC
    Amount of time the server will wait for certain events before failing a request
    TraceEnable [on|off|extended] on svC
    Determines the behavior on TRACE requests
    TransferLog file|pipesvB
    Specify location of a log file
    TypesConfig file-path conf/mime.types sB
    The location of the mime.types file
    UNCList hostname [hostname...]sC
    Controls what UNC host names can be accessed by the server +
    TraceEnable [on|off|extended] on svC
    Determines the behavior on TRACE requests
    TransferLog file|pipesvB
    Specify location of a log file
    TypesConfig file-path conf/mime.types sB
    The location of the mime.types file
    UNCList hostname [hostname...]sC
    Controls what UNC host names can be accessed by the server
    UnDefine parameter-namesC
    Undefine the existence of a variable
    UndefMacro namesvdB
    Undefine a macro
    UnsetEnv env-variable [env-variable] -...svdhB
    Removes variables from the environment
    Use name [value1 ... valueN] -svdB
    Use a macro
    UseCanonicalName On|Off|DNS Off svdC
    Configures how the server determines its own name and +
    UnDefine parameter-namesC
    Undefine the existence of a variable
    UndefMacro namesvdB
    Undefine a macro
    UnsetEnv env-variable [env-variable] +...svdhB
    Removes variables from the environment
    Use name [value1 ... valueN] +svdB
    Use a macro
    UseCanonicalName On|Off|DNS Off svdC
    Configures how the server determines its own name and port
    UseCanonicalPhysicalPort On|Off Off svdC
    Configures how the server determines its own port
    User unix-userid #-1 sB
    The userid under which the server will answer +
    UseCanonicalPhysicalPort On|Off Off svdC
    Configures how the server determines its own port
    User unix-userid #-1 sB
    The userid under which the server will answer requests
    UserDir directory-filename [directory-filename] ... -svB
    Location of the user-specific directories
    VHostCGIMode On|Off|Secure On vD
    Determines whether the virtualhost can run +
    UserDir directory-filename [directory-filename] ... +svB
    Location of the user-specific directories
    VHostCGIMode On|Off|Secure On vD
    Determines whether the virtualhost can run subprocesses, and the privileges available to subprocesses.
    VHostCGIPrivs [+-]?privilege-name [[+-]?privilege-name] ...vD
    Assign arbitrary privileges to subprocesses created +
    VHostCGIPrivs [+-]?privilege-name [[+-]?privilege-name] ...vD
    Assign arbitrary privileges to subprocesses created by a virtual host.
    VHostGroup unix-groupidvD
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vD
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vD
    Determines whether the server runs with enhanced security +
    VHostGroup unix-groupidvD
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vD
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vD
    Determines whether the server runs with enhanced security for the virtualhost.
    VHostUser unix-useridvD
    Sets the User ID under which a virtual host runs.
    VirtualDocumentRoot interpolated-directory|none none svE
    Dynamically configure the location of the document root +
    VHostUser unix-useridvD
    Sets the User ID under which a virtual host runs.
    VirtualDocumentRoot interpolated-directory|none none svE
    Dynamically configure the location of the document root for a given virtual host
    VirtualDocumentRootIP interpolated-directory|none none svE
    Dynamically configure the location of the document root +
    VirtualDocumentRootIP interpolated-directory|none none svE
    Dynamically configure the location of the document root for a given virtual host
    <VirtualHost +
    <VirtualHost addr[:port] [addr[:port]] - ...> ... </VirtualHost>sC
    Contains directives that apply only to a specific + ...> ... </VirtualHost>sC
    Contains directives that apply only to a specific hostname or IP address
    VirtualScriptAlias interpolated-directory|none none svE
    Dynamically configure the location of the CGI directory for +
    VirtualScriptAlias interpolated-directory|none none svE
    Dynamically configure the location of the CGI directory for a given virtual host
    VirtualScriptAliasIP interpolated-directory|none none svE
    Dynamically configure the location of the CGI directory for +
    VirtualScriptAliasIP interpolated-directory|none none svE
    Dynamically configure the location of the CGI directory for a given virtual host
    WatchdogInterval time-interval[s] 1 sB
    Watchdog interval in seconds
    XBitHack on|off|full off svdhB
    Parse SSI directives in files with the execute bit +
    WatchdogInterval time-interval[s] 1 sB
    Watchdog interval in seconds
    XBitHack on|off|full off svdhB
    Parse SSI directives in files with the execute bit set
    xml2EncAlias charset alias [alias ...]sB
    Recognize Aliases for encoding values
    xml2EncDefault namesvdhB
    Sets a default encoding to assume when absolutely no information +
    xml2EncAlias charset alias [alias ...]sB
    Recognize Aliases for encoding values
    xml2EncDefault namesvdhB
    Sets a default encoding to assume when absolutely no information can be automatically detected
    xml2StartParse element [element ...]svdhB
    Advise the parser to skip leading junk.
    xml2StartParse element [element ...]svdhB
    Advise the parser to skip leading junk.

    Available Languages:  de  | diff -Nru apache2-2.4.69/docs/manual/mod/quickreference.html.es apache2-2.4.71/docs/manual/mod/quickreference.html.es --- apache2-2.4.69/docs/manual/mod/quickreference.html.es 2026-09-29 11:57:11.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/quickreference.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -719,7 +719,7 @@

    MDCertificateAuthority url letsencrypt sX
    The URL(s) of the ACME Certificate Authority to use.
    MDCertificateCheck name urlsX
    Set name and URL pattern for a certificate monitoring site.
    MDCertificateFile path-to-pem-filesX
    Specify a static certificate file for the MD.
    MDCertificateKeyFile path-to-filesX
    Specify a static private key for for the static cerrtificate.
    MDCertificateKeyFile path-to-filesX
    Specify a static private key for the static certificate.
    MDCertificateMonitor name url crt.sh https://crt. +sX
    The URL of a certificate log monitor.
    MDCertificateProtocol protocol ACME sX
    The protocol to use with the Certificate Authority.
    MDCertificateStatus on|off on sX
    Exposes public certificate information in JSON.
    MDDriveMode always|auto|manual auto sX
    former name of MDRenewMode.
    MDExternalAccountBinding key-id hmac-64 | none | file none sX
    Set the external account binding keyid and hmac values to use at CA
    MDHttpProxy urlsX
    Define a proxy for outgoing connections.
    MDInitialDelay duration 0s sX
    How long to delay the first certificate check.
    MDMatchNames all|servernames all sX
    Determines how DNS names are matched to vhosts
    MDMember hostnamesX
    Additional hostname for the managed domain.
    MDMembers auto|manual auto sX
    Control if the alias domain names are automatically added.
    MDMessageCmd path-to-cmd optional-argssX
    Handle events for Manage Domains
    MDMustStaple on|off off sX
    Control if new certificates carry the OCSP Must Staple flag.
    MDNotifyCmd path [ args ]sX
    Run a program when a Managed Domain is ready.
    MDomain dns-name [ other-dns-name... ] [auto|manual]sX
    Define list of domain names that belong to one group.
    <MDomainSet dns-name [ other-dns-name... ]>...</MDomainSet>sX
    Container for directives applied to the same managed domains.
    MDPortMap map1 [ map2 ] http:80 https:443 sX
    Map external to internal ports for domain ownership verification.
    MDPrivateKeys type [ params... ] RSA 2048 sX
    Set type and size of the private keys generated.
    MDProfile namesX
    Use a specific ACME profile from the CA
    MDProfileMandatory on|off off sX
    Control if an MDProfile is mandatory.
    MDRenewMode always|auto|manual auto sX
    Controls if certificates shall be renewed.
    MDRenewViaARI on|off on sX
    usage of the ACME ARI extension (rfc9773).
    MDRenewWindow duration 33% sX
    Control when a certificate will be renewed.
    MDRequireHttps off|temporary|permanent off sX
    Redirects http: traffic to https: for Managed Domains.
    MDRetryDelay duration 30s sX
    Time length for first retry, doubled on every consecutive error.
    MDRetryFailover number 13 sX
    The number of errors before a failover to another CA is triggered
    MDServerStatus on|off off sX
    Control if Managed Domain information is added to server-status.
    MDStapleOthers on|off on sX
    Enable stapling for certificates not managed by mod_md.
    MDStapling on|off off sX
    Enable stapling for all or a particular MDomain.
    MDStaplingKeepResponse duration 7d sX
    Controls when old responses should be removed.
    MDStaplingRenewWindow duration 33% sX
    Control when the stapling responses will be renewed.
    MDStoreDir path md sX
    Path on the local file system to store the Managed Domains data.
    MDStoreLocks on|off|duration off sX
    Configure locking of store for updates
    MDWarnWindow duration 10% sX
    Define the time window when you want to be warned about an expiring certificate.
    MemcacheConnTTL num[units] 15s svE
    Keepalive time for idle connections
    MergeSlashes ON|OFF ON svC
    Controls whether the server merges consecutive slashes in URLs. +
    MDHttpProxyCACertificateFile path-to-pem-file none sX
    Sets the root (CA) certificates to use for TLS connections to the http-proxy.
    MDInitialDelay duration 0s sX
    How long to delay the first certificate check.
    MDMatchNames all|servernames all sX
    Determines how DNS names are matched to vhosts
    MDMember hostnamesX
    Additional hostname for the managed domain.
    MDMembers auto|manual auto sX
    Control if the alias domain names are automatically added.
    MDMessageCmd path-to-cmd optional-argssX
    Handle events for Manage Domains
    MDMustStaple on|off off sX
    Control if new certificates carry the OCSP Must Staple flag.
    MDNotifyCmd path [ args ]sX
    Run a program when a Managed Domain is ready.
    MDomain dns-name [ other-dns-name... ] [auto|manual]sX
    Define list of domain names that belong to one group.
    <MDomainSet dns-name [ other-dns-name... ]>...</MDomainSet>sX
    Container for directives applied to the same managed domains.
    MDPortMap map1 [ map2 ] http:80 https:443 sX
    Map external to internal ports for domain ownership verification.
    MDPrivateKeys type [ params... ] RSA 2048 sX
    Set type and size of the private keys generated.
    MDProfile namesX
    Use a specific ACME profile from the CA
    MDProfileMandatory on|off off sX
    Control if an MDProfile is mandatory.
    MDRenewMode always|auto|manual auto sX
    Controls if certificates shall be renewed.
    MDRenewViaARI on|off on sX
    usage of the ACME ARI extension (rfc9773).
    MDRenewWindow duration 33% sX
    Control when a certificate will be renewed.
    MDRequireHttps off|temporary|permanent off sX
    Redirects http: traffic to https: for Managed Domains.
    MDRetryDelay duration 30s sX
    Time length for first retry, doubled on every consecutive error.
    MDRetryFailover number 13 sX
    The number of errors before a failover to another CA is triggered
    MDServerStatus on|off off sX
    Control if Managed Domain information is added to server-status.
    MDStapleOthers on|off on sX
    Enable stapling for certificates not managed by mod_md.
    MDStapling on|off off sX
    Enable stapling for all or a particular MDomain.
    MDStaplingKeepResponse duration 7d sX
    Controls when old responses should be removed.
    MDStaplingRenewWindow duration 33% sX
    Control when the stapling responses will be renewed.
    MDStoreDir path md sX
    Path on the local file system to store the Managed Domains data.
    MDStoreLocks on|off|duration off sX
    Configure locking of store for updates
    MDWarnWindow duration 10% sX
    Define the time window when you want to be warned about an expiring certificate.
    MemcacheConnTTL num[units] 15s svE
    Keepalive time for idle connections
    MergeSlashes ON|OFF ON svC
    Controls whether the server merges consecutive slashes in URLs.
    MergeTrailers [on|off] off svC
    Determines whether trailers are merged into headers
    MetaDir directory .web svdhD
    Name of the directory to find CERN-style meta information +
    MergeTrailers [on|off] off svC
    Determines whether trailers are merged into headers
    MetaDir directory .web svdhD
    Name of the directory to find CERN-style meta information files
    MetaFiles on|off off svdhD
    Activates CERN meta-file processing
    MetaSuffix suffix .meta svdhD
    File name suffix for the file containing CERN-style +
    MetaFiles on|off off svdhD
    Activates CERN meta-file processing
    MetaSuffix suffix .meta svdhD
    File name suffix for the file containing CERN-style meta information
    MimeMagicFile file-pathsvE
    Enable MIME-type determination based on file contents +
    MimeMagicFile file-pathsvE
    Enable MIME-type determination based on file contents using the specified magic file
    MinSpareServers number 5 sM
    Minimum number of idle child server processes
    MinSpareThreads numbersM
    Minimum number of idle threads available to handle request +
    MinSpareServers number 5 sM
    Minimum number of idle child server processes
    MinSpareThreads numbersM
    Minimum number of idle threads available to handle request spikes
    MMapFile file-path [file-path] ...sX
    Map a list of files into memory at startup time
    ModemStandard V.21|V.26bis|V.32|V.34|V.92dX
    Modem standard to simulate
    ModMimeUsePathInfo On|Off Off dB
    Tells mod_mime to treat path_info +
    MMapFile file-path [file-path] ...sX
    Map a list of files into memory at startup time
    ModemStandard V.21|V.26bis|V.32|V.34|V.92dX
    Modem standard to simulate
    ModMimeUsePathInfo On|Off Off dB
    Tells mod_mime to treat path_info components as part of the filename
    MultiviewsMatch Any|NegotiatedOnly|Filters|Handlers -[Handlers|Filters] NegotiatedOnly svdhB
    The types of files that will be included when searching for +
    MultiviewsMatch Any|NegotiatedOnly|Filters|Handlers +[Handlers|Filters] NegotiatedOnly svdhB
    The types of files that will be included when searching for a matching file with MultiViews
    Mutex mechanism [default|mutex-name] ... [OmitPID] default sC
    Configures mutex mechanism and lock file directory for all +
    Mutex mechanism [default|mutex-name] ... [OmitPID] default sC
    Configures mutex mechanism and lock file directory for all or specified mutexes
    NameVirtualHost addr[:port]sC
    Designates an IP address for name-virtual +
    NameVirtualHost addr[:port]sC
    Designates an IP address for name-virtual hosting
    NoProxy host [host] ...svE
    Hosts, domains, or networks that will be connected to +
    NoProxy host [host] ...svE
    Hosts, domains, or networks that will be connected to directly
    NWSSLTrustedCerts filename [filename] ...sB
    List of additional client certificates
    NWSSLUpgradeable [IP-address:]portnumbersB
    Allows a connection to be upgraded to an SSL connection upon request
    Options - [+|-]option [[+|-]option] ... All svdhC
    Configures what features are available in a particular +
    NWSSLTrustedCerts filename [filename] ...sB
    List of additional client certificates
    NWSSLUpgradeable [IP-address:]portnumbersB
    Allows a connection to be upgraded to an SSL connection upon request
    Options + [+|-]option [[+|-]option] ... All svdhC
    Configures what features are available in a particular directory
    Order ordering Deny,Allow dhD
    Controls the default access state and the order in which +
    Order ordering Deny,Allow dhD
    Controls the default access state and the order in which Allow and Deny are evaluated.
    OutputSed sed-commanddhX
    Sed command for filtering response content
    PassEnv env-variable [env-variable] -...svdhB
    Passes environment variables from the shell
    PidFile filename logs/httpd.pid sM
    File where the server records the process ID +
    OutputSed sed-commanddhX
    Sed command for filtering response content
    PassEnv env-variable [env-variable] +...svdhB
    Passes environment variables from the shell
    PidFile filename logs/httpd.pid sM
    File where the server records the process ID of the daemon
    PrivilegesMode FAST|SECURE|SELECTIVE FAST svdD
    Trade off processing speed and efficiency vs security against +
    PrivilegesMode FAST|SECURE|SELECTIVE FAST svdD
    Trade off processing speed and efficiency vs security against malicious privileges-aware code.
    Protocol protocolsvC
    Protocol for a listening socket
    ProtocolEcho On|Off Off svX
    Turn the echo server on or off
    Protocols protocol ... http/1.1 svC
    Protocols available for a server/virtual host
    ProtocolsHonorOrder On|Off On svC
    Determines if order of Protocols determines precedence during negotiation
    <Proxy wildcard-url> ...</Proxy>svE
    Container for directives applied to proxied resources
    Proxy100Continue Off|On On svdE
    Forward 100-continue expectation to the origin server
    ProxyAddHeaders Off|On On svdE
    Add proxy information in X-Forwarded-* headers
    ProxyBadHeader IsError|Ignore|StartBody IsError svE
    Determines how to handle bad header lines in a +
    Protocol protocolsvC
    Protocol for a listening socket
    ProtocolEcho On|Off Off svX
    Turn the echo server on or off
    Protocols protocol ... http/1.1 svC
    Protocols available for a server/virtual host
    ProtocolsHonorOrder On|Off On svC
    Determines if order of Protocols determines precedence during negotiation
    <Proxy wildcard-url> ...</Proxy>svE
    Container for directives applied to proxied resources
    Proxy100Continue Off|On On svdE
    Forward 100-continue expectation to the origin server
    ProxyAddHeaders Off|On On svdE
    Add proxy information in X-Forwarded-* headers
    ProxyBadHeader IsError|Ignore|StartBody IsError svE
    Determines how to handle bad header lines in a response
    ProxyBlock *|word|host|domain -[word|host|domain] ...svE
    Words, hosts, or domains that are banned from being +
    ProxyBlock *|word|host|domain +[word|host|domain] ...svE
    Words, hosts, or domains that are banned from being proxied
    ProxyDomain DomainsvE
    Default domain name for proxied requests
    ProxyErrorOverride Off|On [code ...] Off svdE
    Override error pages for proxied content
    ProxyExpressDBMFile pathnamesvE
    Pathname to DBM file.
    ProxyExpressDBMType type default svE
    DBM type of file.
    ProxyExpressEnable on|off off svE
    Enable the module functionality.
    ProxyFCGIBackendType FPM|GENERIC FPM svdhE
    Specify the type of backend FastCGI application
    ProxyFCGISetEnvIf conditional-expression +
    ProxyDomain DomainsvE
    Default domain name for proxied requests
    ProxyErrorOverride Off|On [code ...] Off svdE
    Override error pages for proxied content
    ProxyExpressDBMFile pathnamesvE
    Pathname to DBM file.
    ProxyExpressDBMType type default svE
    DBM type of file.
    ProxyExpressEnable on|off off svE
    Enable the module functionality.
    ProxyFCGIBackendType FPM|GENERIC FPM svdhE
    Specify the type of backend FastCGI application
    ProxyFCGISetEnvIf conditional-expression [!]environment-variable-name - [value-expression]svdhE
    Allow variables sent to FastCGI servers to be fixed up
    ProxyFtpDirCharset character_set ISO-8859-1 svdE
    Define the character set for proxied FTP listings
    ProxyFtpEscapeWildcards on|off on svdE
    Whether wildcards in requested filenames are escaped when sent to the FTP server
    ProxyFtpListOnWildcard on|off on svdE
    Whether wildcards in requested filenames trigger a file listing
    ProxyHCExpr name {ap_expr expression}svE
    Creates a named condition expression to use to determine health of the backend based on its response
    ProxyHCTemplate name parameter=setting [...]svE
    Creates a named template for setting various health check parameters
    ProxyHCTPsize size 16 sE
    Sets the total server-wide size of the threadpool used for the health check workers
    ProxyHTMLBufSize bytes 8192 svdB
    Sets the buffer size increment for buffering inline scripts and + [value-expression]svdhE
    Allow variables sent to FastCGI servers to be fixed up
    ProxyFtpDirCharset character_set ISO-8859-1 svdE
    Define the character set for proxied FTP listings
    ProxyFtpEscapeWildcards on|off on svdE
    Whether wildcards in requested filenames are escaped when sent to the FTP server
    ProxyFtpListOnWildcard on|off on svdE
    Whether wildcards in requested filenames trigger a file listing
    ProxyHCExpr name {ap_expr expression}svE
    Creates a named condition expression to use to determine health of the backend based on its response
    ProxyHCTemplate name parameter=setting [...]svE
    Creates a named template for setting various health check parameters
    ProxyHCTPsize size 16 sE
    Sets the total server-wide size of the threadpool used for the health check workers
    ProxyHTMLBufSize bytes 8192 svdB
    Sets the buffer size increment for buffering inline scripts and stylesheets.
    ProxyHTMLCharsetOut Charset | * UTF-8 svdB
    Specify a charset for mod_proxy_html output.
    ProxyHTMLDocType HTML|XHTML [Legacy]
    OR -
    ProxyHTMLDocType fpi [SGML|XML]
    svdB
    Sets an HTML or XHTML document type declaration.
    ProxyHTMLEnable On|Off Off svdB
    Turns the proxy_html filter on or off.
    ProxyHTMLEvents attribute [attribute ...]svdB
    Specify attributes to treat as scripting events.
    ProxyHTMLExtended On|Off Off svdB
    Determines whether to fix links in inline scripts, stylesheets, +
    ProxyHTMLCharsetOut Charset | * UTF-8 svdB
    Specify a charset for mod_proxy_html output.
    ProxyHTMLDocType HTML|XHTML [Legacy]
    OR +
    ProxyHTMLDocType fpi [SGML|XML]
    svdB
    Sets an HTML or XHTML document type declaration.
    ProxyHTMLEnable On|Off Off svdB
    Turns the proxy_html filter on or off.
    ProxyHTMLEvents attribute [attribute ...]svdB
    Specify attributes to treat as scripting events.
    ProxyHTMLExtended On|Off Off svdB
    Determines whether to fix links in inline scripts, stylesheets, and scripting events.
    ProxyHTMLFixups [lowercase] [dospath] [reset] none svdB
    Fixes for simple HTML errors.
    ProxyHTMLInterp On|Off Off svdB
    Enables per-request interpolation of +
    ProxyHTMLFixups [lowercase] [dospath] [reset] none svdB
    Fixes for simple HTML errors.
    ProxyHTMLInterp On|Off Off svdB
    Enables per-request interpolation of ProxyHTMLURLMap rules.
    ProxyHTMLLinks element attribute [attribute2 ...]svdB
    Specify HTML elements that have URL attributes to be rewritten.
    ProxyHTMLMeta On|Off Off svdB
    Turns on or off extra pre-parsing of metadata in HTML +
    ProxyHTMLLinks element attribute [attribute2 ...]svdB
    Specify HTML elements that have URL attributes to be rewritten.
    ProxyHTMLMeta On|Off Off svdB
    Turns on or off extra pre-parsing of metadata in HTML <head> sections.
    ProxyHTMLStripComments On|Off Off svdB
    Determines whether to strip HTML comments.
    ProxyHTMLURLMap from-pattern to-pattern [flags] [cond]svdB
    Defines a rule to rewrite HTML links
    ProxyIOBufferSize bytes 8192 svE
    Determine size of internal data throughput buffer
    <ProxyMatch regex> ...</ProxyMatch>svE
    Container for directives applied to regular-expression-matched +
    ProxyHTMLStripComments On|Off Off svdB
    Determines whether to strip HTML comments.
    ProxyHTMLURLMap from-pattern to-pattern [flags] [cond]svdB
    Defines a rule to rewrite HTML links
    ProxyIOBufferSize bytes 8192 svE
    Determine size of internal data throughput buffer
    <ProxyMatch regex> ...</ProxyMatch>svE
    Container for directives applied to regular-expression-matched proxied resources
    ProxyMaxForwards number -1 svE
    Maximum number of proxies that a request can be forwarded +
    ProxyMaxForwards number -1 svE
    Maximum number of proxies that a request can be forwarded through
    ProxyPass [path] !|url [key=value - [key=value ...]] [nocanon] [interpolate] [noquery]svdE
    Maps remote servers into the local server URL-space
    ProxyPassInherit On|Off On svE
    Inherit ProxyPass directives defined from the main server
    ProxyPassInterpolateEnv On|Off Off svdE
    Enable Environment Variable interpolation in Reverse Proxy configurations
    ProxyPassMatch [regex] !|url [key=value - [key=value ...]]svdE
    Maps remote servers into the local server URL-space using regular expressions
    ProxyPassReverse [path] url -[interpolate]svdE
    Adjusts the URL in HTTP response headers sent from a reverse +
    ProxyPass [path] !|url [key=value + [key=value ...]] [nocanon] [interpolate] [noquery]svdE
    Maps remote servers into the local server URL-space
    ProxyPassInherit On|Off On svE
    Inherit ProxyPass directives defined from the main server
    ProxyPassInterpolateEnv On|Off Off svdE
    Enable Environment Variable interpolation in Reverse Proxy configurations
    ProxyPassMatch [regex] !|url [key=value + [key=value ...]]svdE
    Maps remote servers into the local server URL-space using regular expressions
    ProxyPassReverse [path] url +[interpolate]svdE
    Adjusts the URL in HTTP response headers sent from a reverse proxied server
    ProxyPassReverseCookieDomain internal-domain -public-domain [interpolate]svdE
    Adjusts the Domain string in Set-Cookie headers from a reverse- +
    ProxyPassReverseCookieDomain internal-domain +public-domain [interpolate]svdE
    Adjusts the Domain string in Set-Cookie headers from a reverse- proxied server
    ProxyPassReverseCookiePath internal-path -public-path [interpolate]svdE
    Adjusts the Path string in Set-Cookie headers from a reverse- +
    ProxyPassReverseCookiePath internal-path +public-path [interpolate]svdE
    Adjusts the Path string in Set-Cookie headers from a reverse- proxied server
    ProxyPreserveHost On|Off Off svdE
    Use incoming Host HTTP request header for proxy +
    ProxyPreserveHost On|Off Off svdE
    Use incoming Host HTTP request header for proxy request
    ProxyReceiveBufferSize bytes 0 svE
    Network buffer size for proxied HTTP and FTP +
    ProxyReceiveBufferSize bytes 0 svE
    Network buffer size for proxied HTTP and FTP connections
    ProxyRemote match remote-server [username:password]svE
    Remote proxy used to handle certain requests
    ProxyRemoteMatch regex remote-server [username:password]svE
    Remote proxy used to handle requests matched by regular +
    ProxyRemote match remote-server [username:password]svE
    Remote proxy used to handle certain requests
    ProxyRemoteMatch regex remote-server [username:password]svE
    Remote proxy used to handle requests matched by regular expressions
    ProxyRequests On|Off Off svE
    Enables forward (standard) proxy requests
    ProxySCGIInternalRedirect On|Off|Headername On svdE
    Enable or disable internal redirect responses from the +
    ProxyRequests On|Off Off svE
    Enables forward (standard) proxy requests
    ProxySCGIInternalRedirect On|Off|Headername On svdE
    Enable or disable internal redirect responses from the backend
    ProxySCGISendfile On|Off|Headername Off svdE
    Enable evaluation of X-Sendfile pseudo response +
    ProxySCGISendfile On|Off|Headername Off svdE
    Enable evaluation of X-Sendfile pseudo response header
    ProxySet url key=value [key=value ...]svdE
    Set various Proxy balancer or member parameters
    ProxySourceAddress addresssvE
    Set local IP address for outgoing proxy connections
    ProxyStatus Off|On|Full Off svE
    Show Proxy LoadBalancer status in mod_status
    ProxyTimeout secondssvE
    Network timeout for proxied requests
    ProxyVia On|Off|Full|Block Off svE
    Information provided in the Via HTTP response +
    ProxySet url key=value [key=value ...]svdE
    Set various Proxy balancer or member parameters
    ProxySourceAddress addresssvE
    Set local IP address for outgoing proxy connections
    ProxyStatus Off|On|Full Off svE
    Show Proxy LoadBalancer status in mod_status
    ProxyTimeout secondssvE
    Network timeout for proxied requests
    ProxyVia On|Off|Full|Block Off svE
    Information provided in the Via HTTP response header for proxied requests
    ProxyWebsocketFallbackToProxyHttp On|Off On svD
    Instructs this module to let mod_proxy_http handle the request
    QualifyRedirectURL On|Off Off svdC
    Controls whether the REDIRECT_URL environment variable is +
    ProxyWebsocketFallbackToProxyHttp On|Off On svD
    Instructs this module to let mod_proxy_http handle the request
    QualifyRedirectURL On|Off Off svdC
    Controls whether the REDIRECT_URL environment variable is fully qualified
    ReadBufferSize bytes 8192 svdC
    Size of the buffers used to read data
    ReadmeName filenamesvdhB
    Name of the file that will be inserted at the end +
    ReadBufferSize bytes 8192 svdC
    Size of the buffers used to read data
    ReadmeName filenamesvdhB
    Name of the file that will be inserted at the end of the index listing
    ReceiveBufferSize bytes 0 sM
    TCP receive buffer size
    Redirect [status] [URL-path] -URLsvdhB
    Sends an external redirect asking the client to fetch +
    ReceiveBufferSize bytes 0 sM
    TCP receive buffer size
    Redirect [status] [URL-path] +URLsvdhB
    Sends an external redirect asking the client to fetch a different URL
    RedirectMatch [status] regex -URLsvdhB
    Sends an external redirect based on a regular expression match +
    RedirectMatch [status] regex +URLsvdhB
    Sends an external redirect based on a regular expression match of the current URL
    RedirectPermanent URL-path URLsvdhB
    Sends an external permanent redirect asking the client to fetch +
    RedirectPermanent URL-path URLsvdhB
    Sends an external permanent redirect asking the client to fetch a different URL
    RedirectRelative On|Off Off svdB
    Allows relative redirect targets.
    RedirectTemp URL-path URLsvdhB
    Sends an external temporary redirect asking the client to fetch +
    RedirectRelative On|Off Off svdB
    Allows relative redirect targets.
    RedirectTemp URL-path URLsvdhB
    Sends an external temporary redirect asking the client to fetch a different URL
    RedisConnPoolTTL num[units] 15s svE
    TTL used for the connection pool with the Redis server(s)
    RedisTimeout num[units] 5s svE
    R/W timeout used for the connection with the Redis server(s)
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    RegexDefaultOptions [none] [+|-]option [[+|-]option] ... DOTALL DOLLAR_ENDON +sC
    Allow to configure global/default options for regexes
    RegisterHttpMethod method [method [...]]sC
    Register non-standard HTTP methods
    RemoteIPHeader header-fieldsvB
    Declare the header field which should be parsed for useragent IP addresses
    RemoteIPInternalProxy proxy-ip|proxy-ip/subnet|hostname ...svB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPInternalProxyList filenamesvB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPProxiesHeader HeaderFieldNamesvB
    Declare the header field which will record all intermediate IP addresses
    RemoteIPProxyProtocol On|OffsvB
    Enable or disable PROXY protocol handling
    RemoteIPProxyProtocolExceptions host|range [host|range] [host|range]svB
    Disable processing of PROXY header for certain hosts or networks
    RemoteIPTrustedProxy proxy-ip|proxy-ip/subnet|hostname ...svB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPTrustedProxyList filenamesvB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoveCharset extension [extension] -...vdhB
    Removes any character set associations for a set of file +
    RedisConnPoolTTL num[units] 15s svE
    TTL used for the connection pool with the Redis server(s)
    RedisTimeout num[units] 5s svE
    R/W timeout used for the connection with the Redis server(s)
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    RegexDefaultOptions [none] [+|-]option [[+|-]option] ... DOTALL DOLLAR_ENDON +sC
    Allow to configure global/default options for regexes
    RegisterHttpMethod method [method [...]]sC
    Register non-standard HTTP methods
    RemoteIPHeader header-fieldsvB
    Declare the header field which should be parsed for useragent IP addresses
    RemoteIPInternalProxy proxy-ip|proxy-ip/subnet|hostname ...svB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPInternalProxyList filenamesvB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPProxiesHeader HeaderFieldNamesvB
    Declare the header field which will record all intermediate IP addresses
    RemoteIPProxyProtocol On|OffsvB
    Enable or disable PROXY protocol handling
    RemoteIPProxyProtocolExceptions host|range [host|range] [host|range]svB
    Disable processing of PROXY header for certain hosts or networks
    RemoteIPTrustedProxy proxy-ip|proxy-ip/subnet|hostname ...svB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPTrustedProxyList filenamesvB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoveCharset extension [extension] +...vdhB
    Removes any character set associations for a set of file extensions
    RemoveEncoding extension [extension] -...vdhB
    Removes any content encoding associations for a set of file +
    RemoveEncoding extension [extension] +...vdhB
    Removes any content encoding associations for a set of file extensions
    RemoveHandler extension [extension] -...vdhB
    Removes any handler associations for a set of file +
    RemoveHandler extension [extension] +...vdhB
    Removes any handler associations for a set of file extensions
    RemoveInputFilter extension [extension] -...vdhB
    Removes any input filter associations for a set of file +
    RemoveInputFilter extension [extension] +...vdhB
    Removes any input filter associations for a set of file extensions
    RemoveLanguage extension [extension] -...vdhB
    Removes any language associations for a set of file +
    RemoveLanguage extension [extension] +...vdhB
    Removes any language associations for a set of file extensions
    RemoveOutputFilter extension [extension] -...vdhB
    Removes any output filter associations for a set of file +
    RemoveOutputFilter extension [extension] +...vdhB
    Removes any output filter associations for a set of file extensions
    RemoveType extension [extension] -...vdhB
    Removes any content type associations for a set of file +
    RemoveType extension [extension] +...vdhB
    Removes any content type associations for a set of file extensions
    RequestHeader add|append|edit|edit*|merge|set|setifempty|unset +
    RequestHeader add|append|edit|edit*|merge|set|setifempty|unset header [[expr=]value [replacement] [early|env=[!]varname|expr=expression]] -svdhE
    Configure HTTP request headers
    RequestReadTimeout +svdhE
    Configure HTTP request headers
    RequestReadTimeout [handshake=timeout[-maxtimeout][,MinRate=rate] [header=timeout[-maxtimeout][,MinRate=rate] [body=timeout[-maxtimeout][,MinRate=rate] - handshake=0 header= +svE
    Set timeout values for completing the TLS handshake, receiving + handshake=0 header= +svE
    Set timeout values for completing the TLS handshake, receiving the request headers and/or body from client.
    Require [not] entity-name - [entity-name] ...dhB
    Tests whether an authenticated user is authorized by +
    Require [not] entity-name + [entity-name] ...dhB
    Tests whether an authenticated user is authorized by an authorization provider.
    <RequireAll> ... </RequireAll>dhB
    Enclose a group of authorization directives of which none +
    <RequireAll> ... </RequireAll>dhB
    Enclose a group of authorization directives of which none must fail and at least one must succeed for the enclosing directive to succeed.
    <RequireAny> ... </RequireAny>dhB
    Enclose a group of authorization directives of which one +
    <RequireAny> ... </RequireAny>dhB
    Enclose a group of authorization directives of which one must succeed for the enclosing directive to succeed.
    <RequireNone> ... </RequireNone>dhB
    Enclose a group of authorization directives of which none +
    <RequireNone> ... </RequireNone>dhB
    Enclose a group of authorization directives of which none must succeed for the enclosing directive to not fail.
    RewriteBase URL-pathdhE
    Sets the base URL for per-directory rewrites
    RewriteCond - TestString [!]CondPattern [flags]svdhE
    Defines a condition under which rewriting will take place +
    RewriteBase URL-pathdhE
    Sets the base URL for per-directory rewrites
    RewriteCond + TestString [!]CondPattern [flags]svdhE
    Defines a condition under which rewriting will take place
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource +
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource [MapTypeOptions] -svE
    Defines a mapping function for key-lookup
    RewriteOptions OptionssvdhE
    Sets some special options for the rewrite engine
    RewriteRule - [!]Pattern Substitution [flags]svdhE
    Defines rules for the rewriting engine
    RLimitCPU seconds|max [seconds|max]svdhC
    Limits the CPU consumption of processes launched +svE
    Defines a mapping function for key-lookup
    RewriteOptions OptionssvdhE
    Sets some special options for the rewrite engine
    RewriteRule + [!]Pattern Substitution [flags]svdhE
    Defines rules for the rewriting engine
    RLimitCPU seconds|max [seconds|max]svdhC
    Limits the CPU consumption of processes launched by Apache httpd children
    RLimitMEM bytes|max [bytes|max]svdhC
    Limits the memory consumption of processes launched +
    RLimitMEM bytes|max [bytes|max]svdhC
    Limits the memory consumption of processes launched by Apache httpd children
    RLimitNPROC number|max [number|max]svdhC
    Limits the number of processes that can be launched by +
    RLimitNPROC number|max [number|max]svdhC
    Limits the number of processes that can be launched by processes launched by Apache httpd children
    Satisfy Any|All All dhD
    Interaction between host-level access control and +
    Satisfy Any|All All dhD
    Interaction between host-level access control and user authentication
    ScoreBoardFile file-path logs/apache_runtime +sM
    Location of the file used to store coordination data for +
    ScoreBoardFile file-path logs/apache_runtime +sM
    Location of the file used to store coordination data for the child processes
    Script method cgi-scriptsvdB
    Activates a CGI script for a particular request +
    Script method cgi-scriptsvdB
    Activates a CGI script for a particular request method.
    ScriptAlias [URL-path] -file-path|directory-pathsvdB
    Maps a URL to a filesystem location and designates the +
    ScriptAlias [URL-path] +file-path|directory-pathsvdB
    Maps a URL to a filesystem location and designates the target as a CGI script
    ScriptAliasMatch regex -file-path|directory-pathsvB
    Maps a URL to a filesystem location using a regular expression +
    ScriptAliasMatch regex +file-path|directory-pathsvB
    Maps a URL to a filesystem location using a regular expression and designates the target as a CGI script
    ScriptInterpreterSource Registry|Registry-Strict|Script Script svdhC
    Technique for locating the interpreter for CGI +
    ScriptInterpreterSource Registry|Registry-Strict|Script Script svdhC
    Technique for locating the interpreter for CGI scripts
    ScriptLog file-pathsvB
    Location of the CGI script error logfile
    ScriptLogBuffer bytes 1024 svB
    Maximum amount of PUT or POST requests that will be recorded +
    ScriptLog file-pathsvB
    Location of the CGI script error logfile
    ScriptLogBuffer bytes 1024 svB
    Maximum amount of PUT or POST requests that will be recorded in the scriptlog
    ScriptLogLength bytes 10385760 svB
    Size limit of the CGI script logfile
    ScriptSock file-path cgisock sB
    The filename prefix of the socket to use for communication with +
    ScriptLogLength bytes 10385760 svB
    Size limit of the CGI script logfile
    ScriptSock file-path cgisock sB
    The filename prefix of the socket to use for communication with the cgi daemon
    SecureListen [IP-address:]portnumber -Certificate-Name [MUTUAL]sB
    Enables SSL encryption for the specified port
    SeeRequestTail On|Off Off sC
    Determine if mod_status displays the first 63 characters +
    SecureListen [IP-address:]portnumber +Certificate-Name [MUTUAL]sB
    Enables SSL encryption for the specified port
    SeeRequestTail On|Off Off sC
    Determine if mod_status displays the first 63 characters of a request or the last 63, assuming the request itself is greater than 63 chars.
    SendBufferSize bytes 0 sM
    TCP buffer size
    ServerAdmin email-address|URLsvC
    Email address that the server includes in error +
    SendBufferSize bytes 0 sM
    TCP buffer size
    ServerAdmin email-address|URLsvC
    Email address that the server includes in error messages sent to the client
    ServerAlias hostname [hostname] ...vC
    Alternate names for a host used when matching requests +
    ServerAlias hostname [hostname] ...vC
    Alternate names for a host used when matching requests to name-virtual hosts
    ServerLimit numbersM
    Upper limit on configurable number of processes
    ServerName [scheme://]fully-qualified-domain-name[:port]svC
    Hostname and port that the server uses to identify +
    ServerLimit numbersM
    Upper limit on configurable number of processes
    ServerName [scheme://]fully-qualified-domain-name[:port]svC
    Hostname and port that the server uses to identify itself
    ServerPath URL-pathvC
    Legacy URL pathname for a name-based virtual host that +
    ServerPath URL-pathvC
    Legacy URL pathname for a name-based virtual host that is accessed by an incompatible browser
    ServerRoot directory-path /usr/local/apache sC
    Base directory for the server installation
    ServerSignature On|Off|EMail Off svdhC
    Configures the footer on server-generated documents
    ServerTokens Major|Minor|Min[imal]|Prod[uctOnly]|OS|Full Full sC
    Configures the Server HTTP response +
    ServerRoot directory-path /usr/local/apache sC
    Base directory for the server installation
    ServerSignature On|Off|EMail Off svdhC
    Configures the footer on server-generated documents
    ServerTokens Major|Minor|Min[imal]|Prod[uctOnly]|OS|Full Full sC
    Configures the Server HTTP response header
    Session On|Off Off svdhE
    Enables a session for the current directory or location
    SessionCookieName name attributessvdhE
    Name and attributes for the RFC2109 cookie storing the session
    SessionCookieName2 name attributessvdhE
    Name and attributes for the RFC2965 cookie storing the session
    SessionCookieRemove On|Off Off svdhE
    Control for whether session cookies should be removed from incoming HTTP headers
    SessionCryptoCipher name aes256 svdhX
    The crypto cipher to be used to encrypt the session
    SessionCryptoDriver name [param[=value]]sX
    The crypto driver to be used to encrypt the session
    SessionCryptoPassphrase secret [ secret ... ] svdhX
    The key used to encrypt the session
    SessionCryptoPassphraseFile filenamesvdX
    File containing keys used to encrypt the session
    SessionDBDCookieName name attributessvdhE
    Name and attributes for the RFC2109 cookie storing the session ID
    SessionDBDCookieName2 name attributessvdhE
    Name and attributes for the RFC2965 cookie storing the session ID
    SessionDBDCookieRemove On|Off On svdhE
    Control for whether session ID cookies should be removed from incoming HTTP headers
    SessionDBDDeleteLabel label deletesession svdhE
    The SQL query to use to remove sessions from the database
    SessionDBDInsertLabel label insertsession svdhE
    The SQL query to use to insert sessions into the database
    SessionDBDPerUser On|Off Off svdhE
    Enable a per user session
    SessionDBDSelectLabel label selectsession svdhE
    The SQL query to use to select sessions from the database
    SessionDBDUpdateLabel label updatesession svdhE
    The SQL query to use to update existing sessions in the database
    SessionEnv On|Off Off svdhE
    Control whether the contents of the session are written to the +
    Session On|Off Off svdhE
    Enables a session for the current directory or location
    SessionCookieName name attributessvdhE
    Name and attributes for the RFC2109 cookie storing the session
    SessionCookieName2 name attributessvdhE
    Name and attributes for the RFC2965 cookie storing the session
    SessionCookieRemove On|Off Off svdhE
    Control for whether session cookies should be removed from incoming HTTP headers
    SessionCryptoCipher name aes256 svdhX
    The crypto cipher to be used to encrypt the session
    SessionCryptoDriver name [param[=value]]sX
    The crypto driver to be used to encrypt the session
    SessionCryptoPassphrase secret [ secret ... ] svdhX
    The key used to encrypt the session
    SessionCryptoPassphraseFile filenamesvdX
    File containing keys used to encrypt the session
    SessionDBDCookieName name attributessvdhE
    Name and attributes for the RFC2109 cookie storing the session ID
    SessionDBDCookieName2 name attributessvdhE
    Name and attributes for the RFC2965 cookie storing the session ID
    SessionDBDCookieRemove On|Off On svdhE
    Control for whether session ID cookies should be removed from incoming HTTP headers
    SessionDBDDeleteLabel label deletesession svdhE
    The SQL query to use to remove sessions from the database
    SessionDBDInsertLabel label insertsession svdhE
    The SQL query to use to insert sessions into the database
    SessionDBDPerUser On|Off Off svdhE
    Enable a per user session
    SessionDBDSelectLabel label selectsession svdhE
    The SQL query to use to select sessions from the database
    SessionDBDUpdateLabel label updatesession svdhE
    The SQL query to use to update existing sessions in the database
    SessionEnv On|Off Off svdhE
    Control whether the contents of the session are written to the HTTP_SESSION environment variable
    SessionExclude pathsvdhE
    Define URL prefixes for which a session is ignored
    SessionExpiryUpdateInterval interval 0 (always update) svdhE
    Define the number of seconds a session's expiry may change without +
    SessionExclude pathsvdhE
    Define URL prefixes for which a session is ignored
    SessionExpiryUpdateInterval interval 0 (always update) svdhE
    Define the number of seconds a session's expiry may change without the session being updated
    SessionHeader headersvdhE
    Import session updates from a given HTTP response header
    SessionInclude pathsvdhE
    Define URL prefixes for which a session is valid
    SessionMaxAge maxage 0 svdhE
    Define a maximum age in seconds for a session
    SetEnv env-variable [value]svdhB
    Sets environment variables
    SetEnvIf attribute +
    SessionHeader headersvdhE
    Import session updates from a given HTTP response header
    SessionInclude pathsvdhE
    Define URL prefixes for which a session is valid
    SessionMaxAge maxage 0 svdhE
    Define a maximum age in seconds for a session
    SetEnv env-variable [value]svdhB
    Sets environment variables
    SetEnvIf attribute regex [!]env-variable[=value] - [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on attributes of the request + [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on attributes of the request
    SetEnvIfExpr expr +
    SetEnvIfExpr expr [!]env-variable[=value] - [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on an ap_expr expression
    SetEnvIfNoCase attribute regex + [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on an ap_expr expression
    SetEnvIfNoCase attribute regex [!]env-variable[=value] - [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on attributes of the request + [[!]env-variable[=value]] ...svdhB
    Sets environment variables based on attributes of the request without respect to case
    SetHandler handler-name|NonesvdhC
    Forces all matching files to be processed by a +
    SetHandler handler-name|NonesvdhC
    Forces all matching files to be processed by a handler
    SetInputFilter filter[;filter...]svdhC
    Sets the filters that will process client requests and POST +
    SetInputFilter filter[;filter...]svdhC
    Sets the filters that will process client requests and POST input
    SetOutputFilter filter[;filter...]svdhC
    Sets the filters that will process responses from the +
    SetOutputFilter filter[;filter...]svdhC
    Sets the filters that will process responses from the server
    SSIEndTag tag "-->" svB
    String that ends an include element
    SSIErrorMsg message "[an error occurred +svdhB
    Error message displayed when there is an SSI +
    SSIEndTag tag "-->" svB
    String that ends an include element
    SSIErrorMsg message "[an error occurred +svdhB
    Error message displayed when there is an SSI error
    SSIETag on|off off dhB
    Controls whether ETags are generated by the server.
    SSILastModified on|off off dhB
    Controls whether Last-Modified headers are generated by the +
    SSIETag on|off off dhB
    Controls whether ETags are generated by the server.
    SSILastModified on|off off dhB
    Controls whether Last-Modified headers are generated by the server.
    SSILegacyExprParser on|off off dhB
    Enable compatibility mode for conditional expressions.
    SSIStartTag tag "<!--#" svB
    String that starts an include element
    SSITimeFormat formatstring "%A, %d-%b-%Y %H:%M +svdhB
    Configures the format in which date strings are +
    SSILegacyExprParser on|off off dhB
    Enable compatibility mode for conditional expressions.
    SSIStartTag tag "<!--#" svB
    String that starts an include element
    SSITimeFormat formatstring "%A, %d-%b-%Y %H:%M +svdhB
    Configures the format in which date strings are displayed
    SSIUndefinedEcho string "(none)" svdhB
    String displayed when an unset variable is echoed
    SSLCACertificateFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates +
    SSIUndefinedEcho string "(none)" svdhB
    String displayed when an unset variable is echoed
    SSLCACertificateFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates for Client Auth
    SSLCACertificatePath directory-pathsvE
    Directory of PEM-encoded CA Certificates for +
    SSLCACertificatePath directory-pathsvE
    Directory of PEM-encoded CA Certificates for Client Auth
    SSLCADNRequestFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates +
    SSLCADNRequestFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates for defining acceptable CA names
    SSLCADNRequestPath directory-pathsvE
    Directory of PEM-encoded CA Certificates for +
    SSLCADNRequestPath directory-pathsvE
    Directory of PEM-encoded CA Certificates for defining acceptable CA names
    SSLCARevocationCheck chain|leaf|none [flags ...] none svE
    Enable CRL-based revocation checking
    SSLCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for +
    SSLCARevocationCheck chain|leaf|none [flags ...] none svE
    Enable CRL-based revocation checking
    SSLCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for Client Auth
    SSLCARevocationPath directory-pathsvE
    Directory of PEM-encoded CA CRLs for +
    SSLCARevocationPath directory-pathsvE
    Directory of PEM-encoded CA CRLs for Client Auth
    SSLCertificateChainFile file-pathsvE
    File of PEM-encoded Server CA Certificates
    SSLCertificateFile file-path|certidsvE
    Server PEM-encoded X.509 certificate data file or token identifier
    SSLCertificateKeyFile file-path|keyidsvE
    Server PEM-encoded private key file
    SSLCipherSuite [protocol] cipher-spec DEFAULT (depends on +svdhE
    Cipher Suite available for negotiation in SSL +
    SSLCertificateChainFile file-pathsvE
    File of PEM-encoded Server CA Certificates
    SSLCertificateFile file-path|certidsvE
    Server PEM-encoded X.509 certificate data file or token identifier
    SSLCertificateKeyFile file-path|keyidsvE
    Server PEM-encoded private key file
    SSLCipherSuite [protocol] cipher-spec DEFAULT (depends on +svdhE
    Cipher Suite available for negotiation in SSL handshake
    SSLCompression on|off off svE
    Enable compression on the SSL level
    SSLCryptoDevice engine builtin sE
    Enable use of a cryptographic hardware accelerator
    SSLEngine on|off off svE
    SSL Engine Operation Switch
    SSLFIPS on|off off sE
    SSL FIPS mode Switch
    SSLHonorCipherOrder on|off off svE
    Option to prefer the server's cipher preference order
    SSLInsecureRenegotiation on|off off svE
    Option to enable support for insecure renegotiation
    SSLOCSPDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|leaf|off [flags] off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPNoverify on|off off svE
    skip the OCSP responder certificates verification
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    SSLOCSPProxyURL urlsvE
    Proxy URL to use for OCSP requests
    SSLOCSPResponderCertificateFile filesvE
    Set of trusted PEM encoded OCSP responder certificates
    SSLOCSPResponderTimeout seconds 10 svE
    Timeout for OCSP queries
    SSLOCSPResponseMaxAge seconds -1 svE
    Maximum allowable age for OCSP responses
    SSLOCSPResponseTimeSkew seconds 300 svE
    Maximum allowable time skew for OCSP response validation
    SSLOCSPUseRequestNonce on|off on svE
    Use a nonce within OCSP queries
    SSLOpenSSLConfCmd command-name command-valuesvE
    Configure OpenSSL parameters through its SSL_CONF API
    SSLOptions [+|-]option ...svdhE
    Configure various SSL engine run-time options
    SSLPassPhraseDialog type builtin sE
    Type of pass phrase dialog for encrypted private +
    SSLCompression on|off off svE
    Enable compression on the SSL level
    SSLCryptoDevice engine builtin sE
    Enable use of a cryptographic hardware accelerator
    SSLEngine on|off off svE
    SSL Engine Operation Switch
    SSLFIPS on|off off sE
    SSL FIPS mode Switch
    SSLHonorCipherOrder on|off off svE
    Option to prefer the server's cipher preference order
    SSLInsecureRenegotiation on|off off svE
    Option to enable support for insecure renegotiation
    SSLOCSPDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|leaf|off [flags] off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPNoverify on|off off svE
    skip the OCSP responder certificates verification
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    SSLOCSPProxyURL urlsvE
    Proxy URL to use for OCSP requests
    SSLOCSPResponderCertificateFile filesvE
    Set of trusted PEM encoded OCSP responder certificates
    SSLOCSPResponderTimeout seconds 10 svE
    Timeout for OCSP queries
    SSLOCSPResponseMaxAge seconds -1 svE
    Maximum allowable age for OCSP responses
    SSLOCSPResponseTimeSkew seconds 300 svE
    Maximum allowable time skew for OCSP response validation
    SSLOCSPUseRequestNonce on|off on svE
    Use a nonce within OCSP queries
    SSLOpenSSLConfCmd command-name command-valuesvE
    Configure OpenSSL parameters through its SSL_CONF API
    SSLOptions [+|-]option ...svdhE
    Configure various SSL engine run-time options
    SSLPassPhraseDialog type builtin sE
    Type of pass phrase dialog for encrypted private keys
    SSLProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svE
    Configure usable SSL/TLS protocol versions
    SSLProxyCACertificateFile file-pathsvpE
    File of concatenated PEM-encoded CA Certificates +
    SSLProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svE
    Configure usable SSL/TLS protocol versions
    SSLProxyCACertificateFile file-pathsvpE
    File of concatenated PEM-encoded CA Certificates for Remote Server Auth
    SSLProxyCACertificatePath directory-pathsvpE
    Directory of PEM-encoded CA Certificates for +
    SSLProxyCACertificatePath directory-pathsvpE
    Directory of PEM-encoded CA Certificates for Remote Server Auth
    SSLProxyCARevocationCheck chain|leaf|none none svpE
    Enable CRL-based revocation checking for Remote Server Auth
    SSLProxyCARevocationFile file-pathsvpE
    File of concatenated PEM-encoded CA CRLs for +
    SSLProxyCARevocationCheck chain|leaf|none none svpE
    Enable CRL-based revocation checking for Remote Server Auth
    SSLProxyCARevocationFile file-pathsvpE
    File of concatenated PEM-encoded CA CRLs for Remote Server Auth
    SSLProxyCARevocationPath directory-pathsvpE
    Directory of PEM-encoded CA CRLs for +
    SSLProxyCARevocationPath directory-pathsvpE
    Directory of PEM-encoded CA CRLs for Remote Server Auth
    SSLProxyCheckPeerCN on|off on svpE
    Whether to check the remote server certificate's CN field +
    SSLProxyCheckPeerCN on|off on svpE
    Whether to check the remote server certificate's CN field
    SSLProxyCheckPeerExpire on|off on svpE
    Whether to check if remote server certificate is expired +
    SSLProxyCheckPeerExpire on|off on svpE
    Whether to check if remote server certificate is expired
    SSLProxyCheckPeerName on|off on svpE
    Configure host name checking for remote server certificates +
    SSLProxyCheckPeerName on|off on svpE
    Configure host name checking for remote server certificates
    SSLProxyCipherSuite [protocol] cipher-spec ALL:!ADH:RC4+RSA:+H +svpE
    Cipher Suite available for negotiation in SSL +
    SSLProxyCipherSuite [protocol] cipher-spec ALL:!ADH:RC4+RSA:+H +svpE
    Cipher Suite available for negotiation in SSL proxy handshake
    SSLProxyEngine on|off off svpE
    SSL Proxy Engine Operation Switch
    SSLProxyMachineCertificateChainFile filenamesvpE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesvpE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysvpE
    Directory of PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svpE
    Configure usable SSL protocol flavors for proxy usage
    SSLProxyVerify level none svpE
    Type of remote server Certificate verification
    SSLProxyVerifyDepth number 1 svpE
    Maximum depth of CA Certificates in Remote Server +
    SSLProxyEngine on|off off svpE
    SSL Proxy Engine Operation Switch
    SSLProxyMachineCertificateChainFile filenamesvpE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesvpE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysvpE
    Directory of PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svpE
    Configure usable SSL protocol flavors for proxy usage
    SSLProxyVerify level none svpE
    Type of remote server Certificate verification
    SSLProxyVerifyDepth number 1 svpE
    Maximum depth of CA Certificates in Remote Server Certificate verification
    SSLRandomSeed context source -[bytes]sE
    Pseudo Random Number Generator (PRNG) seeding +
    SSLRandomSeed context source +[bytes]sE
    Pseudo Random Number Generator (PRNG) seeding source
    SSLRenegBufferSize bytes 131072 dhE
    Set the size for the SSL renegotiation buffer
    SSLRequire expressiondhE
    Allow access only when an arbitrarily complex +
    SSLRenegBufferSize bytes 131072 dhE
    Set the size for the SSL renegotiation buffer
    SSLRequire expressiondhE
    Allow access only when an arbitrarily complex boolean expression is true
    SSLRequireSSLdhE
    Deny access when SSL is not used for the +
    SSLRequireSSLdhE
    Deny access when SSL is not used for the HTTP request
    SSLSessionCache type none sE
    Type of the global/inter-process SSL Session +
    SSLSessionCache type none sE
    Type of the global/inter-process SSL Session Cache
    SSLSessionCacheTimeout seconds 300 svE
    Number of seconds before an SSL session expires +
    SSLSessionCacheTimeout seconds 300 svE
    Number of seconds before an SSL session expires in the Session Cache
    SSLSessionTicketKeyFile file-pathsvE
    Persistent encryption/decryption key for TLS session tickets
    SSLSessionTickets on|off on svE
    Enable or disable use of TLS session tickets
    SSLSRPUnknownUserSeed secret-stringsvE
    SRP unknown user seed
    SSLSRPVerifierFile file-pathsvE
    Path to SRP verifier file
    SSLStaplingCache typesE
    Configures the OCSP stapling cache
    SSLStaplingErrorCacheTimeout seconds 600 svE
    Number of seconds before expiring invalid responses in the OCSP stapling cache
    SSLStaplingFakeTryLater on|off on svE
    Synthesize "tryLater" responses for failed OCSP stapling queries
    SSLStaplingForceURL urisvE
    Override the OCSP responder URI specified in the certificate's AIA extension
    SSLStaplingResponderTimeout seconds 10 svE
    Timeout for OCSP stapling queries
    SSLStaplingResponseMaxAge seconds -1 svE
    Maximum allowable age for OCSP stapling responses
    SSLStaplingResponseTimeSkew seconds 300 svE
    Maximum allowable time skew for OCSP stapling response validation
    SSLStaplingReturnResponderErrors on|off on svE
    Pass stapling related OCSP errors on to client
    SSLStaplingStandardCacheTimeout seconds 3600 svE
    Number of seconds before expiring responses in the OCSP stapling cache
    SSLStrictSNIVHostCheck on|off off svE
    Whether to allow non-SNI clients to access a name-based virtual +
    SSLSessionTicketKeyFile file-pathsvE
    Persistent encryption/decryption key for TLS session tickets
    SSLSessionTickets on|off on svE
    Enable or disable use of TLS session tickets
    SSLSRPUnknownUserSeed secret-stringsvE
    SRP unknown user seed
    SSLSRPVerifierFile file-pathsvE
    Path to SRP verifier file
    SSLStaplingCache typesE
    Configures the OCSP stapling cache
    SSLStaplingErrorCacheTimeout seconds 600 svE
    Number of seconds before expiring invalid responses in the OCSP stapling cache
    SSLStaplingFakeTryLater on|off on svE
    Synthesize "tryLater" responses for failed OCSP stapling queries
    SSLStaplingForceURL urisvE
    Override the OCSP responder URI specified in the certificate's AIA extension
    SSLStaplingResponderTimeout seconds 10 svE
    Timeout for OCSP stapling queries
    SSLStaplingResponseMaxAge seconds -1 svE
    Maximum allowable age for OCSP stapling responses
    SSLStaplingResponseTimeSkew seconds 300 svE
    Maximum allowable time skew for OCSP stapling response validation
    SSLStaplingReturnResponderErrors on|off on svE
    Pass stapling related OCSP errors on to client
    SSLStaplingStandardCacheTimeout seconds 3600 svE
    Number of seconds before expiring responses in the OCSP stapling cache
    SSLStrictSNIVHostCheck on|off off svE
    Whether to allow non-SNI clients to access a name-based virtual host.
    SSLUserName varnamesdhE
    Variable name to determine user name
    SSLUseStapling on|off off svE
    Enable stapling of OCSP responses in the TLS handshake
    SSLVerifyClient level none svdhE
    Type of Client Certificate verification
    SSLVerifyDepth number 1 svdhE
    Maximum depth of CA Certificates in Client +
    SSLUserName varnamesdhE
    Variable name to determine user name
    SSLUseStapling on|off off svE
    Enable stapling of OCSP responses in the TLS handshake
    SSLVerifyClient level none svdhE
    Type of Client Certificate verification
    SSLVerifyDepth number 1 svdhE
    Maximum depth of CA Certificates in Client Certificate verification
    SSLVHostSNIPolicy strict|secure|authonly|insecure secure sE
    Set compatibility policy for SNI client access to virtual hosts.
    StartServers numbersM
    Number of child server processes created at startup
    StartThreads numbersM
    Number of threads created on startup
    StrictHostCheck ON|OFF OFF svC
    Controls whether the server requires the requested hostname be +
    SSLVHostSNIPolicy strict|secure|authonly|insecure secure sE
    Set compatibility policy for SNI client access to virtual hosts.
    StartServers numbersM
    Number of child server processes created at startup
    StartThreads numbersM
    Number of threads created on startup
    StrictHostCheck ON|OFF OFF svC
    Controls whether the server requires the requested hostname be listed enumerated in the virtual host handling the request
    Substitute s/pattern/substitution/[infq]dhE
    Pattern to filter the response content
    SubstituteInheritBefore on|off off dhE
    Change the merge order of inherited patterns
    SubstituteMaxLineLength bytes(b|B|k|K|m|M|g|G) 1m dhE
    Set the maximum line size
    Suexec On|OffsB
    Enable or disable the suEXEC feature
    SuexecUserGroup User GroupsvE
    User and group for CGI programs to run as
    ThreadLimit numbersM
    Sets the upper limit on the configurable number of threads +
    Substitute s/pattern/substitution/[infq]dhE
    Pattern to filter the response content
    SubstituteInheritBefore on|off off dhE
    Change the merge order of inherited patterns
    SubstituteMaxLineLength bytes(b|B|k|K|m|M|g|G) 1m dhE
    Set the maximum line size
    Suexec On|OffsB
    Enable or disable the suEXEC feature
    SuexecUserGroup User GroupsvE
    User and group for CGI programs to run as
    ThreadLimit numbersM
    Sets the upper limit on the configurable number of threads per child process
    ThreadsPerChild numbersM
    Number of threads created by each child process
    ThreadStackSize sizesM
    The size in bytes of the stack used by threads handling +
    ThreadsPerChild numbersM
    Number of threads created by each child process
    ThreadStackSize sizesM
    The size in bytes of the stack used by threads handling client connections
    TimeOut seconds 60 svC
    Amount of time the server will wait for +
    TimeOut seconds 60 svC
    Amount of time the server will wait for certain events before failing a request
    TraceEnable [on|off|extended] on svC
    Determines the behaviour on TRACE requests
    TransferLog file|pipesvB
    Specify location of a log file
    TypesConfig file-path conf/mime.types sB
    The location of the mime.types file
    UNCList hostname [hostname...]sC
    Controls what UNC host names can be accessed by the server +
    TraceEnable [on|off|extended] on svC
    Determines the behaviour on TRACE requests
    TransferLog file|pipesvB
    Specify location of a log file
    TypesConfig file-path conf/mime.types sB
    The location of the mime.types file
    UNCList hostname [hostname...]sC
    Controls what UNC host names can be accessed by the server
    UnDefine parameter-namesC
    Undefine the existence of a variable
    UndefMacro namesvdB
    Undefine a macro
    UnsetEnv env-variable [env-variable] -...svdhB
    Removes variables from the environment
    Use name [value1 ... valueN] -svdB
    Use a macro
    UseCanonicalName On|Off|DNS Off svdC
    Configures how the server determines its own name and +
    UnDefine parameter-namesC
    Undefine the existence of a variable
    UndefMacro namesvdB
    Undefine a macro
    UnsetEnv env-variable [env-variable] +...svdhB
    Removes variables from the environment
    Use name [value1 ... valueN] +svdB
    Use a macro
    UseCanonicalName On|Off|DNS Off svdC
    Configures how the server determines its own name and port
    UseCanonicalPhysicalPort On|Off Off svdC
    Configures how the server determines its own name and +
    UseCanonicalPhysicalPort On|Off Off svdC
    Configures how the server determines its own name and port
    User unix-userid #-1 sB
    The userid under which the server will answer +
    User unix-userid #-1 sB
    The userid under which the server will answer requests
    UserDir directory-filename [directory-filename] ... -svB
    Location of the user-specific directories
    VHostCGIMode On|Off|Secure On vD
    Determines whether the virtualhost can run +
    UserDir directory-filename [directory-filename] ... +svB
    Location of the user-specific directories
    VHostCGIMode On|Off|Secure On vD
    Determines whether the virtualhost can run subprocesses, and the privileges available to subprocesses.
    VHostCGIPrivs [+-]?privilege-name [[+-]?privilege-name] ...vD
    Assign arbitrary privileges to subprocesses created +
    VHostCGIPrivs [+-]?privilege-name [[+-]?privilege-name] ...vD
    Assign arbitrary privileges to subprocesses created by a virtual host.
    VHostGroup unix-groupidvD
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vD
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vD
    Determines whether the server runs with enhanced security +
    VHostGroup unix-groupidvD
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vD
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vD
    Determines whether the server runs with enhanced security for the virtualhost.
    VHostUser unix-useridvD
    Sets the User ID under which a virtual host runs.
    VirtualDocumentRoot interpolated-directory|none none svE
    Dynamically configure the location of the document root +
    VHostUser unix-useridvD
    Sets the User ID under which a virtual host runs.
    VirtualDocumentRoot interpolated-directory|none none svE
    Dynamically configure the location of the document root for a given virtual host
    VirtualDocumentRootIP interpolated-directory|none none svE
    Dynamically configure the location of the document root +
    VirtualDocumentRootIP interpolated-directory|none none svE
    Dynamically configure the location of the document root for a given virtual host
    <VirtualHost +
    <VirtualHost addr[:port] [addr[:port]] - ...> ... </VirtualHost>sC
    Contains directives that apply only to a specific + ...> ... </VirtualHost>sC
    Contains directives that apply only to a specific hostname or IP address
    VirtualScriptAlias interpolated-directory|none none svE
    Dynamically configure the location of the CGI directory for +
    VirtualScriptAlias interpolated-directory|none none svE
    Dynamically configure the location of the CGI directory for a given virtual host
    VirtualScriptAliasIP interpolated-directory|none none svE
    Dynamically configure the location of the CGI directory for +
    VirtualScriptAliasIP interpolated-directory|none none svE
    Dynamically configure the location of the CGI directory for a given virtual host
    WatchdogInterval time-interval[s] 1 sB
    Watchdog interval in seconds
    XBitHack on|off|full off svdhB
    Parse SSI directives in files with the execute bit +
    WatchdogInterval time-interval[s] 1 sB
    Watchdog interval in seconds
    XBitHack on|off|full off svdhB
    Parse SSI directives in files with the execute bit set
    xml2EncAlias charset alias [alias ...]sB
    Recognize Aliases for encoding values
    xml2EncDefault namesvdhB
    Sets a default encoding to assume when absolutely no information +
    xml2EncAlias charset alias [alias ...]sB
    Recognize Aliases for encoding values
    xml2EncDefault namesvdhB
    Sets a default encoding to assume when absolutely no information can be automatically detected
    xml2StartParse element [element ...]svdhB
    Advise the parser to skip leading junk.
    xml2StartParse element [element ...]svdhB
    Advise the parser to skip leading junk.

    Idiomas disponibles:  de  | diff -Nru apache2-2.4.69/docs/manual/mod/quickreference.html.fr.utf8 apache2-2.4.71/docs/manual/mod/quickreference.html.fr.utf8 --- apache2-2.4.69/docs/manual/mod/quickreference.html.fr.utf8 2026-08-01 12:35:47.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/quickreference.html.fr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -920,647 +920,649 @@ comptes externes à utiliser dans les CA

    MDHttpProxy urlsX
    Spécifie un serveur mandataire pour les connexions sortantes.
    MDInitialDelay duration 0s sX
    Le délai avant la première vérification de certificat.
    MDMatchNames all|servernames all sX
    Définit comment les noms DNS sont comparés aux vhosts
    MDMember hostnamesX
    Nom d'hôte additionnel pour le domaine géré.
    MDMembers auto|manual auto sX
    Définit si les alias de noms de domaines sont +
    MDHttpProxyCACertificateFile path-to-pem-file none sX
    Définition des certificats racine (CA) à utiliser pour les + connexions TLS avec le mandataire http.
    MDInitialDelay duration 0s sX
    Le délai avant la première vérification de certificat.
    MDMatchNames all|servernames all sX
    Définit comment les noms DNS sont comparés aux vhosts
    MDMember hostnamesX
    Nom d'hôte additionnel pour le domaine géré.
    MDMembers auto|manual auto sX
    Définit si les alias de noms de domaines sont automatiquement ajoutés.
    MDMessageCmd path-to-cmd optional-argssX
    Gère les évènements pour les domaines gérés
    MDMustStaple on|off off sX
    Définit si les nouveaux certificats doivent avoir le +
    MDMessageCmd path-to-cmd optional-argssX
    Gère les évènements pour les domaines gérés
    MDMustStaple on|off off sX
    Définit si les nouveaux certificats doivent avoir le drapeau OCSP Must Staple activé.
    MDNotifyCmd path [ args ]sX
    Lance un programme lorsqu'un domaine géré est opérationnel.
    MDomain dns-name [ other-dns-name... ] [auto|manual]sX
    Définit une liste de noms de domaines qui appartiennent à +
    MDNotifyCmd path [ args ]sX
    Lance un programme lorsqu'un domaine géré est opérationnel.
    MDomain dns-name [ other-dns-name... ] [auto|manual]sX
    Définit une liste de noms de domaines qui appartiennent à un groupe.
    <MDomainSet dns-name [ other-dns-name... ]>...</MDomainSet>sX
    Conteneur de directives à appliquer à un ou plusieurs +
    <MDomainSet dns-name [ other-dns-name... ]>...</MDomainSet>sX
    Conteneur de directives à appliquer à un ou plusieurs domaines gérés.
    MDPortMap map1 [ map2 ] http:80 https:443 sX
    Mappage des ports externes avec les ports internes pour +
    MDPortMap map1 [ map2 ] http:80 https:443 sX
    Mappage des ports externes avec les ports internes pour vérifier à qui appartient le domaine.
    MDPrivateKeys type [ params... ] RSA 2048 sX
    Définit le type et la taille des clés privées générées.
    MDProfile namesX
    Utiliser un profile ACME spécifique depuis le CA
    MDProfileMandatory on|off off sX
    Contrôler si un MDProfile est obligatoire.
    MDRenewMode always|auto|manual auto sX
    Contrôle le renouvellement des certificats.
    MDRenewViaARI on|off on sX
    Utilisation de l'extension ACME ARI (rfc9773).
    MDRenewWindow duration 33% sX
    Définit le moment auquel un certificat doit être renouvelé.
    MDRequireHttps off|temporary|permanent off sX
    Redirige le trafic http: vers https: pour les domaines +
    MDPrivateKeys type [ params... ] RSA 2048 sX
    Définit le type et la taille des clés privées générées.
    MDProfile namesX
    Utiliser un profile ACME spécifique depuis le CA
    MDProfileMandatory on|off off sX
    Contrôler si un MDProfile est obligatoire.
    MDRenewMode always|auto|manual auto sX
    Contrôle le renouvellement des certificats.
    MDRenewViaARI on|off on sX
    Utilisation de l'extension ACME ARI (rfc9773).
    MDRenewWindow duration 33% sX
    Définit le moment auquel un certificat doit être renouvelé.
    MDRequireHttps off|temporary|permanent off sX
    Redirige le trafic http: vers https: pour les domaines gérés.
    MDRetryDelay duration 30s sX
    Temps d'attente avant de réessayer, doublé à chaque erreur +
    MDRetryDelay duration 30s sX
    Temps d'attente avant de réessayer, doublé à chaque erreur consécutive
    MDRetryFailover number 13 sX
    Le nombre d'erreurs avant de se tourner vers un autre CA
    MDServerStatus on|off off sX
    Définit si les informations à propos des domaines gérés +
    MDRetryFailover number 13 sX
    Le nombre d'erreurs avant de se tourner vers un autre CA
    MDServerStatus on|off off sX
    Définit si les informations à propos des domaines gérés sont ajoutés ou non à server-status.
    MDStapleOthers on|off on sX
    Active l'agrafage pour les certificats non gérés par +
    MDStapleOthers on|off on sX
    Active l'agrafage pour les certificats non gérés par mod_md.
    MDStapling on|off off sX
    Active l'agrafage pour un ou plusieurs domaines.
    MDStaplingKeepResponse duration 7d sX
    Contrôle la durée au bout de laquelle les anciennes +
    MDStapling on|off off sX
    Active l'agrafage pour un ou plusieurs domaines.
    MDStaplingKeepResponse duration 7d sX
    Contrôle la durée au bout de laquelle les anciennes réponses doivent être supprimées.
    MDStaplingRenewWindow duration 33% sX
    Contrôle l'ancienneté des réponses OCSP au dela de laquelle +
    MDStaplingRenewWindow duration 33% sX
    Contrôle l'ancienneté des réponses OCSP au dela de laquelle ces dernières seront renouvelées.
    MDStoreDir path md sX
    Chemin dans le système de fichiers local du répertoire où +
    MDStoreDir path md sX
    Chemin dans le système de fichiers local du répertoire où seront stockées les données à propos des domaines gérés.
    MDStoreLocks on|off|duration off sX
    Configure le verrouillage du magasin pour les mises à jour
    MDWarnWindow duration 10% sX
    Définit la fenêtre de temps pendant laquelle vous serez +
    MDStoreLocks on|off|duration off sX
    Configure le verrouillage du magasin pour les mises à jour
    MDWarnWindow duration 10% sX
    Définit la fenêtre de temps pendant laquelle vous serez informé de l'expiration prochaine d'un certificat.
    MemcacheConnTTL num[units] 15s svE
    Durée de conservation des connexions inactives
    MergeSlashes ON|OFF ON svC
    Fusion des slashes consécutifs dans les URLs par le serveur. +
    MemcacheConnTTL num[units] 15s svE
    Durée de conservation des connexions inactives
    MergeSlashes ON|OFF ON svC
    Fusion des slashes consécutifs dans les URLs par le serveur.
    MergeTrailers [on|off] off svC
    Détermine si les données supplémentaires (trailers) sont +
    MergeTrailers [on|off] off svC
    Détermine si les données supplémentaires (trailers) sont fusionnées avec les en-têtes
    MetaDir répertoire .web svdh
    Le nom du répertoire où trouver les fichiers de +
    MetaDir répertoire .web svdh
    Le nom du répertoire où trouver les fichiers de métainformations dans le style du CERN
    MetaFiles on|off off svdh
    Active le traitement des métafichiers du CERN
    MetaSuffix suffixe .meta svdh
    Suffixe du fichier contenant les métainformations dans le +
    MetaFiles on|off off svdh
    Active le traitement des métafichiers du CERN
    MetaSuffix suffixe .meta svdh
    Suffixe du fichier contenant les métainformations dans le style du CERN
    MimeMagicFile chemin-fichiersvE
    Active la détermination du type MIME en se basant sur le +
    MimeMagicFile chemin-fichiersvE
    Active la détermination du type MIME en se basant sur le contenu du fichier et en utilisant le fichier magique spécifié
    MinSpareServers nombre 5 sM
    Nombre minimum de processus serveurs enfants +
    MinSpareServers nombre 5 sM
    Nombre minimum de processus serveurs enfants inactifs
    MinSpareThreads nombresM
    Nombre minimum de threads inactifs qui seront disponibles +
    MinSpareThreads nombresM
    Nombre minimum de threads inactifs qui seront disponibles pour pouvoir traiter les pics de requêtes
    MMapFile chemin fichier [chemin fichier] ...sX
    Charge au démarrage une liste de fichiers en mémoire
    ModemStandard V.21|V.26bis|V.32|V.34|V.92dX
    Standard de modem à simuler
    ModMimeUsePathInfo On|Off Off dB
    Indique à mod_mime de traiter les éléments +
    MMapFile chemin fichier [chemin fichier] ...sX
    Charge au démarrage une liste de fichiers en mémoire
    ModemStandard V.21|V.26bis|V.32|V.34|V.92dX
    Standard de modem à simuler
    ModMimeUsePathInfo On|Off Off dB
    Indique à mod_mime de traiter les éléments de path_info en tant que parties du nom de fichier
    MultiviewsMatch Any|NegotiatedOnly|Filters|Handlers -[Handlers|Filters] NegotiatedOnly svdhB
    Les types de fichiers qui seront inclus lors d'une +
    MultiviewsMatch Any|NegotiatedOnly|Filters|Handlers +[Handlers|Filters] NegotiatedOnly svdhB
    Les types de fichiers qui seront inclus lors d'une recherche de correspondance de fichier avec les vues multiples (MultiViews)
    Mutex mécanisme [default|nom-mutex] ... [OmitPID] default sC
    Définit les mécanismes de mutex et le repertoire du fichier +
    Mutex mécanisme [default|nom-mutex] ... [OmitPID] default sC
    Définit les mécanismes de mutex et le repertoire du fichier verrou pour tous les mutex ou seulement les mutex spécifiés
    NameVirtualHost adresse[:port]sC
    OBSOLETE : Définit une adresse IP pour les serveurs virtuels à base de +
    NameVirtualHost adresse[:port]sC
    OBSOLETE : Définit une adresse IP pour les serveurs virtuels à base de nom
    NoProxy domaine [domaine] ...svE
    Serveurs, domaines ou réseaux auquels on se connectera +
    NoProxy domaine [domaine] ...svE
    Serveurs, domaines ou réseaux auquels on se connectera directement
    NWSSLTrustedCerts nom-fichier -[nom-fichier] ...sB
    Liste de certificats clients supplémentaires
    NWSSLUpgradeable [adresse-IP:]num-portsB
    Permet de promouvoir une connexion non SSL au statut de +
    NWSSLTrustedCerts nom-fichier +[nom-fichier] ...sB
    Liste de certificats clients supplémentaires
    NWSSLUpgradeable [adresse-IP:]num-portsB
    Permet de promouvoir une connexion non SSL au statut de connexion SSL à la demande
    Options - [+|-]option [[+|-]option] ... FollowSymlinks svdhC
    Définit les fonctionnalités disponibles pour un répertoire +
    Options + [+|-]option [[+|-]option] ... FollowSymlinks svdhC
    Définit les fonctionnalités disponibles pour un répertoire particulier
    Order ordre Deny,Allow dh
    Définit le statut d'accès par défaut et l'ordre dans lequel +
    Order ordre Deny,Allow dh
    Définit le statut d'accès par défaut et l'ordre dans lequel les directives Allow et Deny sont évaluées.
    OutputSed commande-seddh
    Commande sed pour le filtrage des contenus de type +
    OutputSed commande-seddh
    Commande sed pour le filtrage des contenus de type réponse
    PassEnv var-env [var-env] -...svdhB
    Transmet des variables d'environnement depuis le +
    PassEnv var-env [var-env] +...svdhB
    Transmet des variables d'environnement depuis le shell
    PidFile nom fichier logs/httpd.pid sM
    Ficher dans lequel le serveur enregistre l'identificateur +
    PidFile nom fichier logs/httpd.pid sM
    Ficher dans lequel le serveur enregistre l'identificateur de processus du démon
    PrivilegesMode FAST|SECURE|SELECTIVE FAST svd
    Fait un compromis entre d'une part l'efficacité et la +
    PrivilegesMode FAST|SECURE|SELECTIVE FAST svd
    Fait un compromis entre d'une part l'efficacité et la vitesse de traitement et d'autre part la sécurité à l'encontre des codes malicieux supportant les privilèges.
    Protocol protocolesvC
    Protocole pour une socket d'écoute
    ProtocolEcho On|Off Off svX
    Active ou désactive le serveur d'écho
    Protocols protocole ... http/1.1 svC
    Protocoles disponibles pour un serveur virtuel ou non
    ProtocolsHonorOrder On|Off On svC
    Détermine qui du client ou du serveur détermine l'ordre +
    Protocol protocolesvC
    Protocole pour une socket d'écoute
    ProtocolEcho On|Off Off svX
    Active ou désactive le serveur d'écho
    Protocols protocole ... http/1.1 svC
    Protocoles disponibles pour un serveur virtuel ou non
    ProtocolsHonorOrder On|Off On svC
    Détermine qui du client ou du serveur détermine l'ordre des protocoles au cours de la négociation de la connexion
    <Proxy url-avec-jokers> ...</Proxy>svE
    Conteneur de directives s'appliquant à des ressources +
    <Proxy url-avec-jokers> ...</Proxy>svE
    Conteneur de directives s'appliquant à des ressources mandatées
    Proxy100Continue Off|On On svdE
    Transmission du message "100-continue" au serveur d'origine
    ProxyAddHeaders Off|On On svdE
    Ajoute des informations à propos du mandataire aux +
    Proxy100Continue Off|On On svdE
    Transmission du message "100-continue" au serveur d'origine
    ProxyAddHeaders Off|On On svdE
    Ajoute des informations à propos du mandataire aux en-têtes X-Forwarded-*
    ProxyBadHeader IsError|Ignore|StartBody IsError svE
    Détermine la manière de traiter les lignes d'en-tête +
    ProxyBadHeader IsError|Ignore|StartBody IsError svE
    Détermine la manière de traiter les lignes d'en-tête incorrectes d'une réponse
    ProxyBlock *|terme|serveur|domaine -[terme|serveur|domaine] ...svE
    Termes, serveurs ou domaines bloqués par le +
    ProxyBlock *|terme|serveur|domaine +[terme|serveur|domaine] ...svE
    Termes, serveurs ou domaines bloqués par le mandataire
    ProxyDomain DomainesvE
    Nom de domaine par défaut pour les requêtes +
    ProxyDomain DomainesvE
    Nom de domaine par défaut pour les requêtes mandatées
    ProxyErrorOverride Off|On [code ...] Off svdE
    Outrepasser les pages d'erreur pour les contenus +
    ProxyErrorOverride Off|On [code ...] Off svdE
    Outrepasser les pages d'erreur pour les contenus mandatés
    ProxyExpressDBMFile pathnamesvE
    Chemin du fichier DBM.
    ProxyExpressDBMType type default svE
    Type de fichier DBM.
    ProxyExpressEnable on|off off svE
    Active la fonctionnalité du module.
    ProxyFCGIBackendType FPM|GENERIC FPM svdhE
    Spécifie le type de l'application FastCGI d'arrière-plan
    ProxyFCGISetEnvIf conditional-expression +
    ProxyExpressDBMFile pathnamesvE
    Chemin du fichier DBM.
    ProxyExpressDBMType type default svE
    Type de fichier DBM.
    ProxyExpressEnable on|off off svE
    Active la fonctionnalité du module.
    ProxyFCGIBackendType FPM|GENERIC FPM svdhE
    Spécifie le type de l'application FastCGI d'arrière-plan
    ProxyFCGISetEnvIf conditional-expression [!]environment-variable-name - [value-expression]svdhE
    Permet d'adapter la valeur des variables envoyées aux serveurs + [value-expression]svdhE
    Permet d'adapter la valeur des variables envoyées aux serveurs FastCGI
    ProxyFtpDirCharset character_set ISO-8859-1 svdE
    Définit le jeu de caractères des listings FTP +
    ProxyFtpDirCharset character_set ISO-8859-1 svdE
    Définit le jeu de caractères des listings FTP mandatés
    ProxyFtpEscapeWildcards on|off on svdE
    Les caractères génériques dans les noms de fichiers +
    ProxyFtpEscapeWildcards on|off on svdE
    Les caractères génériques dans les noms de fichiers doivent-ils être échappés lorsqu'ils sont envoyés au serveur FTP ?
    ProxyFtpListOnWildcard on|off on svdE
    Les caractères génériques dans les noms de fichiers +
    ProxyFtpListOnWildcard on|off on svdE
    Les caractères génériques dans les noms de fichiers demandés doivent-ils déclencher l'affichage d'un listing ?
    ProxyHCExpr name {ap_expr expression}svE
    Crée et nomme une expression conditionnelle à utiliser pour +
    ProxyHCExpr name {ap_expr expression}svE
    Crée et nomme une expression conditionnelle à utiliser pour déterminer la santé d'un serveur d'arrière-plan en fonction de sa valeur
    ProxyHCTemplate name parameter=setting [...]svE
    Crée et nomme un modèle permettant de définir différents +
    ProxyHCTemplate name parameter=setting [...]svE
    Crée et nomme un modèle permettant de définir différents paramètres de check up
    ProxyHCTPsize size 16 sE
    Définit la taille totale, pour l'ensemble du +
    ProxyHCTPsize size 16 sE
    Définit la taille totale, pour l'ensemble du serveur, du jeu de threads utilisé pour le check up des équipiers
    ProxyHTMLBufSize nb-octets 8192 svdB
    Définit l'incrément de la taille du tampon, ainsi que sa +
    ProxyHTMLBufSize nb-octets 8192 svdB
    Définit l'incrément de la taille du tampon, ainsi que sa taille initiale, pour la mise en tampon des scripts en ligne et des feuilles de style.
    ProxyHTMLCharsetOut jeu-de-caractères | * UTF-8 svdB
    Spécifie un jeu de caractères pour la sortie de +
    ProxyHTMLCharsetOut jeu-de-caractères | * UTF-8 svdB
    Spécifie un jeu de caractères pour la sortie de mod_proxy_html.
    ProxyHTMLDocType HTML|XHTML [Legacy]
    OR -
    ProxyHTMLDocType fpi [SGML|XML]
    svdB
    Définit une déclaration de type de document HTML ou XHTML.
    ProxyHTMLEnable On|Off Off svdB
    Permet d'activer/désactiver le filtre proxy_html.
    ProxyHTMLEvents attribut [attribut ...]svdB
    Spécifie les attributs à traiter comme des évènements de +
    ProxyHTMLDocType HTML|XHTML [Legacy]
    OR +
    ProxyHTMLDocType fpi [SGML|XML]
    svdB
    Définit une déclaration de type de document HTML ou XHTML.
    ProxyHTMLEnable On|Off Off svdB
    Permet d'activer/désactiver le filtre proxy_html.
    ProxyHTMLEvents attribut [attribut ...]svdB
    Spécifie les attributs à traiter comme des évènements de type scripting.
    ProxyHTMLExtended On|Off Off svdB
    Détermine si l'on doit corriger les liens dans les scripts +
    ProxyHTMLExtended On|Off Off svdB
    Détermine si l'on doit corriger les liens dans les scripts en ligne, les feuilles de style et les évènements de type scripting.
    ProxyHTMLFixups [lowercase] [dospath] [reset] none svdB
    Corrige les erreurs HTML simples.
    ProxyHTMLInterp On|Off Off svdB
    Active la réinterprétation des règles +
    ProxyHTMLFixups [lowercase] [dospath] [reset] none svdB
    Corrige les erreurs HTML simples.
    ProxyHTMLInterp On|Off Off svdB
    Active la réinterprétation des règles ProxyHTMLURLMap pour chaque requête.
    ProxyHTMLLinks élément attribut [attribut2 ...]svdB
    Spécifie les éléments HTML dont les attributs d'URL doivent +
    ProxyHTMLLinks élément attribut [attribut2 ...]svdB
    Spécifie les éléments HTML dont les attributs d'URL doivent être réécrits.
    ProxyHTMLMeta On|Off Off svdB
    Active ou désactive une préinterprétation supplémentaire +
    ProxyHTMLMeta On|Off Off svdB
    Active ou désactive une préinterprétation supplémentaire des métadonnées dans les sections HTML <head>.
    ProxyHTMLStripComments On|Off Off svdB
    Détermine si les commentaires HTML doivent être supprimés.
    ProxyHTMLURLMap modèle-source modèle-cible [drapeaux] [cond]svdB
    Définit une règle de réécriture des liens HTML
    ProxyIOBufferSize octets 8192 svE
    Détermine la taille du tampon interne de transfert de +
    ProxyHTMLStripComments On|Off Off svdB
    Détermine si les commentaires HTML doivent être supprimés.
    ProxyHTMLURLMap modèle-source modèle-cible [drapeaux] [cond]svdB
    Définit une règle de réécriture des liens HTML
    ProxyIOBufferSize octets 8192 svE
    Détermine la taille du tampon interne de transfert de données
    <ProxyMatch regex> ...</ProxyMatch>svE
    Conteneur de directives s'appliquant à des ressources +
    <ProxyMatch regex> ...</ProxyMatch>svE
    Conteneur de directives s'appliquant à des ressources mandatées correspondant à une expression rationnelle
    ProxyMaxForwards nombre -1 svE
    Nombre maximum de mandataires à travers lesquelles une +
    ProxyMaxForwards nombre -1 svE
    Nombre maximum de mandataires à travers lesquelles une requête peut être redirigée
    ProxyPass [chemin] !|url [clé=valeur - [clé=valeur ...]] [nocanon] [interpolate] [noquery]svdE
    Référencer des serveurs distants depuis +
    ProxyPass [chemin] !|url [clé=valeur + [clé=valeur ...]] [nocanon] [interpolate] [noquery]svdE
    Référencer des serveurs distants depuis l'espace d'URLs du serveur local
    ProxyPassInherit On|Off On svE
    Héritage des directives ProxyPass définies au niveau du +
    ProxyPassInherit On|Off On svE
    Héritage des directives ProxyPass définies au niveau du serveur principal
    ProxyPassInterpolateEnv On|Off Off svdE
    Active l'interpolation des variables d'environnement dans +
    ProxyPassInterpolateEnv On|Off Off svdE
    Active l'interpolation des variables d'environnement dans les configurations de mandataires inverses
    ProxyPassMatch [regex] !|url [key=value - [key=value ...]]svdE
    Fait correspondre des serveurs distants dans l'espace d'URL +
    ProxyPassMatch [regex] !|url [key=value + [key=value ...]]svdE
    Fait correspondre des serveurs distants dans l'espace d'URL du serveur local en utilisant des expressions rationnelles
    ProxyPassReverse [chemin] url -[interpolate]svdE
    Ajuste l'URL dans les en-têtes de la réponse HTTP envoyée +
    ProxyPassReverse [chemin] url +[interpolate]svdE
    Ajuste l'URL dans les en-têtes de la réponse HTTP envoyée par un serveur mandaté en inverse
    ProxyPassReverseCookieDomain domaine-interne -domaine-public [interpolate]svdE
    Ajuste la chaîne correspondant au domaine dans les en-têtes +
    ProxyPassReverseCookieDomain domaine-interne +domaine-public [interpolate]svdE
    Ajuste la chaîne correspondant au domaine dans les en-têtes Set-Cookie en provenance d'un serveur mandaté
    ProxyPassReverseCookiePath chemin-interne -chemin-public [interpolate]svdE
    Ajuste la chaîne correspondant au chemin dans les en-têtes +
    ProxyPassReverseCookiePath chemin-interne +chemin-public [interpolate]svdE
    Ajuste la chaîne correspondant au chemin dans les en-têtes Set-Cookie en provenance d'un serveur mandaté
    ProxyPreserveHost On|Off Off svdE
    Utilise l'en-tête de requête entrante Host pour la requête +
    ProxyPreserveHost On|Off Off svdE
    Utilise l'en-tête de requête entrante Host pour la requête du mandataire
    ProxyReceiveBufferSize octets 0 svE
    Taille du tampon réseau pour les connexions mandatées HTTP +
    ProxyReceiveBufferSize octets 0 svE
    Taille du tampon réseau pour les connexions mandatées HTTP et FTP
    ProxyRemote match remote-server [username:password]svE
    Mandataire distant à utiliser pour traiter certaines +
    ProxyRemote match remote-server [username:password]svE
    Mandataire distant à utiliser pour traiter certaines requêtes
    ProxyRemoteMatch regex remote-server [username:password]svE
    Le mandataire distant à utiliser pour traiter les requêtes +
    ProxyRemoteMatch regex remote-server [username:password]svE
    Le mandataire distant à utiliser pour traiter les requêtes correspondant à une expression rationnelle
    ProxyRequests On|Off Off svE
    Active la fonctionnalité (standard) de mandataire +
    ProxyRequests On|Off Off svE
    Active la fonctionnalité (standard) de mandataire direct
    ProxySCGIInternalRedirect On|Off|Headername On svdE
    Active ou désactive les réponses de redirection interne en +
    ProxySCGIInternalRedirect On|Off|Headername On svdE
    Active ou désactive les réponses de redirection interne en provenance du serveur cible.
    ProxySCGISendfile On|Off|nom-en-tête Off svdE
    Active l'évaluation du pseudo en-tête de réponse +
    ProxySCGISendfile On|Off|nom-en-tête Off svdE
    Active l'évaluation du pseudo en-tête de réponse X-Sendfile
    ProxySet url clé=valeur [clé=valeur ...]svdE
    Définit différents paramètres relatifs à la répartition de +
    ProxySet url clé=valeur [clé=valeur ...]svdE
    Définit différents paramètres relatifs à la répartition de charge des mandataires et aux membres des groupes de répartition de charge
    ProxySourceAddress adressesvE
    Définit l'adresse IP locale pour les connexions mandatées +
    ProxySourceAddress adressesvE
    Définit l'adresse IP locale pour les connexions mandatées sortantes
    ProxyStatus Off|On|Full Off svE
    Affiche l'état du répartiteur de charge du mandataire dans +
    ProxyStatus Off|On|Full Off svE
    Affiche l'état du répartiteur de charge du mandataire dans mod_status
    ProxyTimeout secondessvE
    Délai d'attente réseau pour les requêtes +
    ProxyTimeout secondessvE
    Délai d'attente réseau pour les requêtes mandatées
    ProxyVia On|Off|Full|Block Off svE
    Information fournie dans l'en-tête de réponse HTTP +
    ProxyVia On|Off|Full|Block Off svE
    Information fournie dans l'en-tête de réponse HTTP Via pour les requêtes mandatées
    ProxyWebsocketFallbackToProxyHttp On|Off On sv
    Demande à ce module de laisser mod_proxy_http +
    ProxyWebsocketFallbackToProxyHttp On|Off On sv
    Demande à ce module de laisser mod_proxy_http gérer la requête
    QualifyRedirectURL On|Off Off svdC
    Vérifie si la variable d'environnement REDIRECT_URL est +
    QualifyRedirectURL On|Off Off svdC
    Vérifie si la variable d'environnement REDIRECT_URL est pleinement qualifiée
    ReadBufferSize bytes 8192 svdC
    Taille des tampons utilisés pour lire les données
    ReadmeName nom-fichiersvdhB
    Nom du fichier dont le contenu sera inséré à la fin de +
    ReadBufferSize bytes 8192 svdC
    Taille des tampons utilisés pour lire les données
    ReadmeName nom-fichiersvdhB
    Nom du fichier dont le contenu sera inséré à la fin de l'index
    ReceiveBufferSize octets 0 sM
    Taille du tampon TCP en entrée
    Redirect [état] [URL-path] -URLsvdhB
    Envoie une redirection externe demandant au client +
    ReceiveBufferSize octets 0 sM
    Taille du tampon TCP en entrée
    Redirect [état] [URL-path] +URLsvdhB
    Envoie une redirection externe demandant au client d'effectuer une autre requête avec une URL différente
    RedirectMatch [état] regex -URLsvdhB
    Envoie une redirection externe faisant appel aux +
    RedirectMatch [état] regex +URLsvdhB
    Envoie une redirection externe faisant appel aux expressions rationnelles pour la mise en correspondance de l'URL courante
    RedirectPermanent chemin URL URLsvdhB
    Envoie une redirection externe permanente demandant au +
    RedirectPermanent chemin URL URLsvdhB
    Envoie une redirection externe permanente demandant au client d'effectuer une nouvelle requête avec une URL différente
    RedirectRelative On|Off Off svdB
    Redirection relative de cibles.
    RedirectTemp chemin URL URLsvdhB
    Envoie une redirection externe temporaire demandant au +
    RedirectRelative On|Off Off svdB
    Redirection relative de cibles.
    RedirectTemp chemin URL URLsvdhB
    Envoie une redirection externe temporaire demandant au client d'effectuer une nouvelle requête avec une URL différente
    RedisConnPoolTTL num[units] 15s svE
    Durée de vie du jeu de connexions avec le(s) serveur(s) Redis.
    RedisTimeout num[units] 5s svE
    Durée maximale de lecture/écriture sur la connexion avec le(s) +
    RedisConnPoolTTL num[units] 15s svE
    Durée de vie du jeu de connexions avec le(s) serveur(s) Redis.
    RedisTimeout num[units] 5s svE
    Durée maximale de lecture/écriture sur la connexion avec le(s) serveur(s) Redis.
    ReflectorHeader en-tête-entrée [en-tête-sortie]svdhB
    Renvoie un en-tête d'entrée dans les en-têtes de sortie
    RegexDefaultOptions [none] [+|-]option [[+|-]option] ... DOTALL DOLLAR_ENDON +sC
    Configuration des options globales par défaut pour les +
    ReflectorHeader en-tête-entrée [en-tête-sortie]svdhB
    Renvoie un en-tête d'entrée dans les en-têtes de sortie
    RegexDefaultOptions [none] [+|-]option [[+|-]option] ... DOTALL DOLLAR_ENDON +sC
    Configuration des options globales par défaut pour les expressions rationnelles
    RegisterHttpMethod méthode [méthode [...]]sC
    Enregistrement de méthodes HTTP non standards
    RemoteIPHeader en-têtesvB
    Définit le champ d'en-tête qui contiendra les adresses IP +
    RegisterHttpMethod méthode [méthode [...]]sC
    Enregistrement de méthodes HTTP non standards
    RemoteIPHeader en-têtesvB
    Définit le champ d'en-tête qui contiendra les adresses IP du client
    RemoteIPInternalProxy -ip-mandataire|ip-mandataire/sous-réseau|nom-hôte ...svB
    Déclare les adresses IP intranet clients comme dignes de +
    RemoteIPInternalProxy +ip-mandataire|ip-mandataire/sous-réseau|nom-hôte ...svB
    Déclare les adresses IP intranet clients comme dignes de confiance pour présenter la valeur RemoteIPHeader
    RemoteIPInternalProxyList nom-fichiersvB
    Déclare les adresses IP intranet clients comme dignes de +
    RemoteIPInternalProxyList nom-fichiersvB
    Déclare les adresses IP intranet clients comme dignes de confiance pour présenter la valeur RemoteIPHeader
    RemoteIPProxiesHeader Nom_en-têtesvB
    Déclare le champ d'en-tête qui contiendra toutes les +
    RemoteIPProxiesHeader Nom_en-têtesvB
    Déclare le champ d'en-tête qui contiendra toutes les adresses IP intermédiaires
    RemoteIPProxyProtocol On|OffsvB
    Active ou désactive la gestion du protocole PROXY
    RemoteIPProxyProtocolExceptions host|range [host|range] [host|range]svB
    Désactive la prise en compte de l'en-tête PROXY pour certains hôtes +
    RemoteIPProxyProtocol On|OffsvB
    Active ou désactive la gestion du protocole PROXY
    RemoteIPProxyProtocolExceptions host|range [host|range] [host|range]svB
    Désactive la prise en compte de l'en-tête PROXY pour certains hôtes ou réseaux
    RemoteIPTrustedProxy -ip-mandataire|ip-mandataire/sous-réseau|nom-hôte ...svB
    Déclare les adresses IP clientes de l'intranet dignes de +
    RemoteIPTrustedProxy +ip-mandataire|ip-mandataire/sous-réseau|nom-hôte ...svB
    Déclare les adresses IP clientes de l'intranet dignes de confiance pour présenter la valeur RemoteIPHeader
    RemoteIPTrustedProxyList nom-fichiersvB
    Déclare les adresses IP intranet clients comme dignes de +
    RemoteIPTrustedProxyList nom-fichiersvB
    Déclare les adresses IP intranet clients comme dignes de confiance pour présenter la valeur RemoteIPHeader
    RemoveCharset extension [extension] -...vdhB
    Supprime toute association de jeu de caractères pour un +
    RemoveCharset extension [extension] +...vdhB
    Supprime toute association de jeu de caractères pour un ensemble d'extensions de noms de fichiers
    RemoveEncoding extension [extension] -...vdhB
    Supprime toute association de codage de contenu pour un +
    RemoveEncoding extension [extension] +...vdhB
    Supprime toute association de codage de contenu pour un ensemble d'extensions de noms de fichiers
    RemoveHandler extension [extension] -...vdhB
    Supprime toute association de gestionnaire à un ensemble +
    RemoveHandler extension [extension] +...vdhB
    Supprime toute association de gestionnaire à un ensemble d'extensions de noms de fichiers
    RemoveInputFilter extension [extension] -...vdhB
    Supprime toute association de filtre en entrée à un +
    RemoveInputFilter extension [extension] +...vdhB
    Supprime toute association de filtre en entrée à un ensemble d'extensions de noms de fichiers
    RemoveLanguage extension [extension] -...vdhB
    Supprime toute association de langage à un ensemble +
    RemoveLanguage extension [extension] +...vdhB
    Supprime toute association de langage à un ensemble d'extensions de noms de fichiers
    RemoveOutputFilter extension [extension] -...vdhB
    Supprime toute association de filtre en sortie à un +
    RemoveOutputFilter extension [extension] +...vdhB
    Supprime toute association de filtre en sortie à un ensemble d'extensions de noms de fichiers
    RemoveType extension [extension] -...vdhB
    Supprime toute association de type de contenu à un ensemble +
    RemoveType extension [extension] +...vdhB
    Supprime toute association de type de contenu à un ensemble d'extensions de noms de fichiers
    RequestHeader add|append|edit|edit*|merge|set|setifempty|unset +
    RequestHeader add|append|edit|edit*|merge|set|setifempty|unset en-tête [[expr=]valeur [remplacement] [early|env=[!]variable|expr=expression]] -svdhE
    Configure les en-têtes d'une requête HTTP
    RequestReadTimeout +svdhE
    Configure les en-têtes d'une requête HTTP
    RequestReadTimeout [handshake=timeout[-maxtimeout][,MinRate=rate] [header=timeout[-maxtimeout][,MinRate=MinRate] [body=timeout[-maxtimeout][,MinRate=MinRate] - handshake=0 header= +svE
    Définit des délais maximums pour la négociation TLS, la réception + handshake=0 header= +svE
    Définit des délais maximums pour la négociation TLS, la réception des en-têtes et/ou corps des requêtes en provenance du client.
    Require [not] nom-entité [nom-entité] -...dhB
    Vérifie si un utilisateur authentifié a une +
    Require [not] nom-entité [nom-entité] +...dhB
    Vérifie si un utilisateur authentifié a une autorisation d'accès accordée par un fournisseur d'autorisation.
    <RequireAll> ... </RequireAll>dhB
    Regroupe plusieurs directives d'autorisation dont aucune ne +
    <RequireAll> ... </RequireAll>dhB
    Regroupe plusieurs directives d'autorisation dont aucune ne doit échouer et dont au moins une doit retourner un résultat positif pour que la directive globale retourne elle-même un résultat positif.
    <RequireAny> ... </RequireAny>dhB
    Regroupe des directives d'autorisation dont au moins une +
    <RequireAny> ... </RequireAny>dhB
    Regroupe des directives d'autorisation dont au moins une doit retourner un résultat positif pour que la directive globale retourne elle-même un résultat positif.
    <RequireNone> ... </RequireNone>dhB
    Regroupe des directives d'autorisation dont aucune ne doit +
    <RequireNone> ... </RequireNone>dhB
    Regroupe des directives d'autorisation dont aucune ne doit retourner un résultat positif pour que la directive globale n'échoue pas.
    RewriteBase chemin_URLdhE
    Définit l'URL de base pour les réécritures au niveau +
    RewriteBase chemin_URLdhE
    Définit l'URL de base pour les réécritures au niveau répertoire
    RewriteCond - TestString [!]CondPattern [flags]svdhE
    Définit une condition qui devra être satisfaite pour que +
    RewriteCond + TestString [!]CondPattern [flags]svdhE
    Définit une condition qui devra être satisfaite pour que la réécriture soit effectuée
    RewriteEngine on|off off svdhE
    Active ou désactive l'exécution du +
    RewriteEngine on|off off svdhE
    Active ou désactive l'exécution du moteur de réécriture
    RewriteMap MapName MapType:MapSource [MapTypeOptions] -svE
    Définit une fonction de mise en correspondance pour la +
    RewriteMap MapName MapType:MapSource [MapTypeOptions] +svE
    Définit une fonction de mise en correspondance pour la recherche de mots-clés
    RewriteOptions OptionssvdhE
    Configure certaines options spéciales +
    RewriteOptions OptionssvdhE
    Configure certaines options spéciales pour le moteur de réécriture
    RewriteRule - [!]Pattern Substitution [flags]svdhE
    Définit les règles pour le moteur de réécriture
    RLimitCPU secondes|max [secondes|max]svdhC
    Limite le temps CPU alloué aux processus initiés par les +
    RewriteRule + [!]Pattern Substitution [flags]svdhE
    Définit les règles pour le moteur de réécriture
    RLimitCPU secondes|max [secondes|max]svdhC
    Limite le temps CPU alloué aux processus initiés par les processus enfants d'Apache httpd
    RLimitMEM octets|max [octets|max]svdhC
    Limite la mémoire allouée aux processus initiés par les +
    RLimitMEM octets|max [octets|max]svdhC
    Limite la mémoire allouée aux processus initiés par les processus enfants d'Apache httpd
    RLimitNPROC nombre|max [nombre|max]svdhC
    Limite le nombre de processus qui peuvent être initiés par +
    RLimitNPROC nombre|max [nombre|max]svdhC
    Limite le nombre de processus qui peuvent être initiés par les processus initiés par les processus enfants d'Apache httpd
    Satisfy Any|All All dh
    Interaction entre le contrôle d'accès en fonction de l'hôte +
    Satisfy Any|All All dh
    Interaction entre le contrôle d'accès en fonction de l'hôte et l'authentification utilisateur
    ScoreBoardFile chemin fichier logs/apache_runtime +sM
    Chemin du fichier où sont stockées les données concernant +
    ScoreBoardFile chemin fichier logs/apache_runtime +sM
    Chemin du fichier où sont stockées les données concernant la coordination des processus enfants
    Script méthode script cgisvdB
    Active un script CGI dans le cas d'une méthode de requête +
    Script méthode script cgisvdB
    Active un script CGI dans le cas d'une méthode de requête particulière.
    ScriptAlias [chemin URL] -chemin fichier|chemin répertoiresvdB
    Fait correspondre une URL à une zone du système de fichiers +
    ScriptAlias [chemin URL] +chemin fichier|chemin répertoiresvdB
    Fait correspondre une URL à une zone du système de fichiers et désigne la cible comme script CGI
    ScriptAliasMatch regex -chemin fichier|chemin répertoiresvB
    Fait correspondre une URL à une zone du système de fichiers +
    ScriptAliasMatch regex +chemin fichier|chemin répertoiresvB
    Fait correspondre une URL à une zone du système de fichiers en faisant appel aux expressions rationnelles et en désignant la cible comme un script CGI
    ScriptInterpreterSource Registry|Registry-Strict|Script Script svdhC
    Permet de localiser l'interpréteur des scripts +
    ScriptInterpreterSource Registry|Registry-Strict|Script Script svdhC
    Permet de localiser l'interpréteur des scripts CGI
    ScriptLog chemin fichiersvB
    Chemin du fichier journal des erreurs du script +
    ScriptLog chemin fichiersvB
    Chemin du fichier journal des erreurs du script CGI
    ScriptLogBuffer octets 1024 svB
    Taille maximale des requêtes PUT ou POST qui seront +
    ScriptLogBuffer octets 1024 svB
    Taille maximale des requêtes PUT ou POST qui seront enregistrées dans le journal du script
    ScriptLogLength octets 10385760 svB
    Taille maximale du fichier journal des scripts +
    ScriptLogLength octets 10385760 svB
    Taille maximale du fichier journal des scripts CGI
    ScriptSock chemin fichier cgisock sB
    Le préfixe du nom de fichier du socket à utiliser pour +
    ScriptSock chemin fichier cgisock sB
    Le préfixe du nom de fichier du socket à utiliser pour communiquer avec le démon CGI
    SecureListen [adresse-IP:]num-port -nom-certificat [MUTUAL]sB
    Active le chiffrement SSL pour le port +
    SecureListen [adresse-IP:]num-port +nom-certificat [MUTUAL]sB
    Active le chiffrement SSL pour le port spécifié
    SeeRequestTail On|Off Off sC
    Détermine si mod_status affiche les 63 premiers caractères +
    SeeRequestTail On|Off Off sC
    Détermine si mod_status affiche les 63 premiers caractères d'une requête ou les 63 derniers, en supposant que la requête elle-même possède plus de 63 caractères.
    SendBufferSize octets 0 sM
    Taille du tampon TCP en sortie
    ServerAdmin adresse électronique|URLsvC
    L'adresse électronique que le serveur inclut dans les +
    SendBufferSize octets 0 sM
    Taille du tampon TCP en sortie
    ServerAdmin adresse électronique|URLsvC
    L'adresse électronique que le serveur inclut dans les messages d'erreur envoyés au client
    ServerAlias nom serveur [nom serveur] -...vC
    Autres noms d'un serveur utilisables pour atteindre des +
    ServerAlias nom serveur [nom serveur] +...vC
    Autres noms d'un serveur utilisables pour atteindre des serveurs virtuels à base de nom
    ServerLimit nombresM
    Limite supérieure de la définition du nombre de +
    ServerLimit nombresM
    Limite supérieure de la définition du nombre de processus
    ServerName -[protocole://]nom-de-domaine|adresse-ip[:port]svC
    Nom d'hôte et port que le serveur utilise pour +
    ServerName +[protocole://]nom-de-domaine|adresse-ip[:port]svC
    Nom d'hôte et port que le serveur utilise pour s'authentifier lui-même
    ServerPath chemin d'URLvC
    Nom de chemin d'URL hérité pour un serveur virtuel à base +
    ServerPath chemin d'URLvC
    Nom de chemin d'URL hérité pour un serveur virtuel à base de nom accédé par un navigateur incompatible
    ServerRoot chemin de répertoire /usr/local/apache sC
    Racine du répertoire d'installation du +
    ServerRoot chemin de répertoire /usr/local/apache sC
    Racine du répertoire d'installation du serveur
    ServerSignature On|Off|EMail Off svdhC
    Définit un pied de page pour les documents générés par le +
    ServerSignature On|Off|EMail Off svdhC
    Définit un pied de page pour les documents générés par le serveur
    ServerTokens Major|Minor|Min[imal]|Prod[uctOnly]|OS|Full Full sC
    Configure l'en-tête Server de la réponse +
    ServerTokens Major|Minor|Min[imal]|Prod[uctOnly]|OS|Full Full sC
    Configure l'en-tête Server de la réponse HTTP
    Session On|Off Off svdhE
    Ouvre une session pour le contexte courant
    SessionCookieName nom attributssvdhE
    Nom et attributs du cookie RFC2109 dans lequel la session +
    Session On|Off Off svdhE
    Ouvre une session pour le contexte courant
    SessionCookieName nom attributssvdhE
    Nom et attributs du cookie RFC2109 dans lequel la session est stockée
    SessionCookieName2 nom attributssvdhE
    Nom et attributs pour le cookie RFC2965 dans lequel est +
    SessionCookieName2 nom attributssvdhE
    Nom et attributs pour le cookie RFC2965 dans lequel est stockée la session
    SessionCookieRemove On|Off Off svdhE
    Détermine si les cookies de session doivent être supprimés +
    SessionCookieRemove On|Off Off svdhE
    Détermine si les cookies de session doivent être supprimés des en-têtes HTTP entrants
    SessionCryptoCipher algorithme aes256 svdhX
    L'algorithme à utiliser pour le chiffrement de la session
    SessionCryptoDriver nom [param[=valeur]]sX
    Le pilote de chiffrement à utiliser pour chiffrer les +
    SessionCryptoCipher algorithme aes256 svdhX
    L'algorithme à utiliser pour le chiffrement de la session
    SessionCryptoDriver nom [param[=valeur]]sX
    Le pilote de chiffrement à utiliser pour chiffrer les sessions
    SessionCryptoPassphrase secret [ secret ... ] svdhX
    La clé utilisée pour chiffrer la session
    SessionCryptoPassphraseFile nom-fichiersvdX
    Le fichier contenant les clés utilisées pour chiffrer la +
    SessionCryptoPassphrase secret [ secret ... ] svdhX
    La clé utilisée pour chiffrer la session
    SessionCryptoPassphraseFile nom-fichiersvdX
    Le fichier contenant les clés utilisées pour chiffrer la session
    SessionDBDCookieName nom attributssvdhE
    Nom et attributs du cookie RFC2109 qui contient +
    SessionDBDCookieName nom attributssvdhE
    Nom et attributs du cookie RFC2109 qui contient l'identifiant de session
    SessionDBDCookieName2 nom attributssvdhE
    Nom et attributs du cookie RFC2965 qui contient +
    SessionDBDCookieName2 nom attributssvdhE
    Nom et attributs du cookie RFC2965 qui contient l'identifiant de session
    SessionDBDCookieRemove On|Off On svdhE
    Détermine si les cookies de session doivent être supprimés +
    SessionDBDCookieRemove On|Off On svdhE
    Détermine si les cookies de session doivent être supprimés des en-têtes HTTP entrants
    SessionDBDDeleteLabel étiquette deletesession svdhE
    La requête SQL à utiliser pour supprimer des sessions de la +
    SessionDBDDeleteLabel étiquette deletesession svdhE
    La requête SQL à utiliser pour supprimer des sessions de la base de données
    SessionDBDInsertLabel étiquette insertsession svdhE
    La requête SQL à utiliser pour insérer des sessions dans la +
    SessionDBDInsertLabel étiquette insertsession svdhE
    La requête SQL à utiliser pour insérer des sessions dans la base de données
    SessionDBDPerUser On|Off Off svdhE
    Active une session propre à un utilisateur
    SessionDBDSelectLabel étiquette selectsession svdhE
    La requête SQL à utiliser pour sélectionner des sessions +
    SessionDBDPerUser On|Off Off svdhE
    Active une session propre à un utilisateur
    SessionDBDSelectLabel étiquette selectsession svdhE
    La requête SQL à utiliser pour sélectionner des sessions dans la base de données
    SessionDBDUpdateLabel étiquette updatesession svdhE
    La requête SQL à utiliser pour mettre à jour des sessions +
    SessionDBDUpdateLabel étiquette updatesession svdhE
    La requête SQL à utiliser pour mettre à jour des sessions préexistantes dans la base de données
    SessionEnv On|Off Off svdhE
    Définit si le contenu de la session doit être enregistré +
    SessionEnv On|Off Off svdhE
    Définit si le contenu de la session doit être enregistré dans la variable d'environnement HTTP_SESSION
    SessionExclude cheminsvdhE
    Définit les préfixes d'URLs pour lesquels une session sera +
    SessionExclude cheminsvdhE
    Définit les préfixes d'URLs pour lesquels une session sera ignorée
    SessionExpiryUpdateInterval interval 0 (mise à jour syst +svdhE
    Définit le nombre de secondes dont la durée d'expiration d'une +
    SessionExpiryUpdateInterval interval 0 (mise à jour syst +svdhE
    Définit le nombre de secondes dont la durée d'expiration d'une session peut changer sans que cette session soit mise à jour
    SessionHeader en-têtesvdhE
    Importation des mises à jour de session depuis l'en-tête de +
    SessionHeader en-têtesvdhE
    Importation des mises à jour de session depuis l'en-tête de réponse HTTP spécifié
    SessionInclude cheminsvdhE
    Définit les préfixes d'URL pour lesquels une session est +
    SessionInclude cheminsvdhE
    Définit les préfixes d'URL pour lesquels une session est valide
    SessionMaxAge durée de vie maximale 0 svdhE
    Définit une durée de vie maximale pour la session en +
    SessionMaxAge durée de vie maximale 0 svdhE
    Définit une durée de vie maximale pour la session en secondes
    SetEnv var-env [valeur]svdhB
    Définit des variables d'environnement
    SetEnvIf attribut +
    SetEnv var-env [valeur]svdhB
    Définit des variables d'environnement
    SetEnvIf attribut regex [!]env-variable[=valeur] - [[!]env-variable[=valeur]] ...svdhB
    Définit des variables d'environnement en fonction des + [[!]env-variable[=valeur]] ...svdhB
    Définit des variables d'environnement en fonction des attributs de la requête
    SetEnvIfExpr expr +
    SetEnvIfExpr expr [!]env-variable[=valeur] - [[!]env-variable[=valeur]] ...svdhB
    Définit des variables d'environnement en fonction d'une expression ap_expr
    SetEnvIfNoCase attribut regex + [[!]env-variable[=valeur]] ...svdhB
    Définit des variables d'environnement en fonction d'une expression ap_expr
    SetEnvIfNoCase attribut regex [!]env-variable[=valeur] - [[!]env-variable[=valeur]] ...svdhB
    Définit des variables d'environnement en fonction des + [[!]env-variable[=valeur]] ...svdhB
    Définit des variables d'environnement en fonction des attributs de la requête sans tenir compte de la casse
    SetHandler handler-name|none|expressionsvdhC
    Force le traitement des fichiers spécifiés par un +
    SetHandler handler-name|none|expressionsvdhC
    Force le traitement des fichiers spécifiés par un gestionnaire particulier
    SetInputFilter filtre[;filtre...]svdhC
    Définit les filtres par lesquels vont passer les requêtes +
    SetInputFilter filtre[;filtre...]svdhC
    Définit les filtres par lesquels vont passer les requêtes client et les données POST
    SetOutputFilter filtre[;filtre...]svdhC
    Définit les filtres par lesquels vont passer les réponses +
    SetOutputFilter filtre[;filtre...]svdhC
    Définit les filtres par lesquels vont passer les réponses du serveur
    SSIEndTag tag "-->" svB
    Chaîne qui termine l'élément include
    SSIErrorMsg message "[an error occurred +svdhB
    Message d'erreur affiché lorsqu'une erreur SSI +
    SSIEndTag tag "-->" svB
    Chaîne qui termine l'élément include
    SSIErrorMsg message "[an error occurred +svdhB
    Message d'erreur affiché lorsqu'une erreur SSI survient
    SSIETag on|off off dhB
    Définit si des en-têtes ETags sont générés par le serveur.
    SSILastModified on|off off dhB
    Définit si des en-têtes Last-Modified sont +
    SSIETag on|off off dhB
    Définit si des en-têtes ETags sont générés par le serveur.
    SSILastModified on|off off dhB
    Définit si des en-têtes Last-Modified sont générés par le serveur.
    SSILegacyExprParser on|off off dhB
    Active le mode de compatibilité pour les expressions +
    SSILegacyExprParser on|off off dhB
    Active le mode de compatibilité pour les expressions conditionnelles.
    SSIStartTag tag "<!--#" svB
    Chaîne qui marque le début d'un élément +
    SSIStartTag tag "<!--#" svB
    Chaîne qui marque le début d'un élément include
    SSITimeFormat chaîne de formatage "%A, %d-%b-%Y %H:%M +svdhB
    Configuration du format d'affichage des dates
    SSIUndefinedEcho chaîne "(none)" svdhB
    Chaîne à afficher lorsqu'on tente d'extraire le contenu +
    SSITimeFormat chaîne de formatage "%A, %d-%b-%Y %H:%M +svdhB
    Configuration du format d'affichage des dates
    SSIUndefinedEcho chaîne "(none)" svdhB
    Chaîne à afficher lorsqu'on tente d'extraire le contenu d'une variable non définie
    SSLCACertificateFile file-pathsvE
    Fichier contenant une concaténation des certificats de CA +
    SSLCACertificateFile file-pathsvE
    Fichier contenant une concaténation des certificats de CA codés en PEM pour l'authentification des clients
    SSLCACertificatePath chemin-répertoiresvE
    Répertoire des certificats de CA codés en PEM pour +
    SSLCACertificatePath chemin-répertoiresvE
    Répertoire des certificats de CA codés en PEM pour l'authentification des clients
    SSLCADNRequestFile file-pathsvE
    Fichier contenant la concaténation des certificats de CA +
    SSLCADNRequestFile file-pathsvE
    Fichier contenant la concaténation des certificats de CA codés en PEM pour la définition de noms de CA acceptables
    SSLCADNRequestPath chemin-répertoiresvE
    Répertoire contenant des fichiers de certificats de CA +
    SSLCADNRequestPath chemin-répertoiresvE
    Répertoire contenant des fichiers de certificats de CA codés en PEM pour la définition de noms de CA acceptables
    SSLCARevocationCheck chain|leaf|none [flags ...] none svE
    Active la vérification des révocations basée sur les CRL
    SSLCARevocationFile file-pathsvE
    Fichier contenant la concaténation des CRLs des CA codés en +
    SSLCARevocationCheck chain|leaf|none [flags ...] none svE
    Active la vérification des révocations basée sur les CRL
    SSLCARevocationFile file-pathsvE
    Fichier contenant la concaténation des CRLs des CA codés en PEM pour l'authentification des clients
    SSLCARevocationPath chemin-répertoiresvE
    Répertoire des CRLs de CA codés en PEM pour +
    SSLCARevocationPath chemin-répertoiresvE
    Répertoire des CRLs de CA codés en PEM pour l'authentification des clients
    SSLCertificateChainFile file-pathsvE
    Fichier contenant les certificats de CA du serveur codés en +
    SSLCertificateChainFile file-pathsvE
    Fichier contenant les certificats de CA du serveur codés en PEM
    SSLCertificateFile file-path|certidsvE
    Fichier de données contenant les informations de certificat X.509 du serveur +
    SSLCertificateFile file-path|certidsvE
    Fichier de données contenant les informations de certificat X.509 du serveur codées au format PEM ou identificateur de jeton
    SSLCertificateKeyFile file-path|keyidsvE
    Fichier contenant la clé privée du serveur codée en +
    SSLCertificateKeyFile file-path|keyidsvE
    Fichier contenant la clé privée du serveur codée en PEM
    SSLCipherSuite [protocol] cipher-spec DEFAULT (dépend de +svdhE
    Algorithmes de chiffrement disponibles pour la négociation +
    SSLCipherSuite [protocol] cipher-spec DEFAULT (dépend de +svdhE
    Algorithmes de chiffrement disponibles pour la négociation au cours de l'initialisation de la connexion SSL
    SSLCompression on|off off svE
    Permet d'activer la compression au niveau SSL
    SSLCryptoDevice moteur builtin sE
    Active l'utilisation d'un accélérateur matériel de +
    SSLCompression on|off off svE
    Permet d'activer la compression au niveau SSL
    SSLCryptoDevice moteur builtin sE
    Active l'utilisation d'un accélérateur matériel de chiffrement
    SSLEngine on|off off svE
    Interrupteur marche/arrêt du moteur SSL
    SSLFIPS on|off off sE
    Coimmutateur du mode SSL FIPS
    SSLHonorCipherOrder on|off off svE
    Option permettant de classer les algorithmes de chiffrement +
    SSLEngine on|off off svE
    Interrupteur marche/arrêt du moteur SSL
    SSLFIPS on|off off sE
    Coimmutateur du mode SSL FIPS
    SSLHonorCipherOrder on|off off svE
    Option permettant de classer les algorithmes de chiffrement du serveur par ordre de préférence
    SSLInsecureRenegotiation on|off off svE
    Option permettant d'activer le support de la renégociation +
    SSLInsecureRenegotiation on|off off svE
    Option permettant d'activer le support de la renégociation non sécurisée
    SSLOCSPDefaultResponder urisvE
    Définit l'URI du répondeur par défaut pour la validation +
    SSLOCSPDefaultResponder urisvE
    Définit l'URI du répondeur par défaut pour la validation OCSP
    SSLOCSPEnable on|leaf|off [flags] off svE
    Active la validation OCSP de la chaîne de certificats du +
    SSLOCSPEnable on|leaf|off [flags] off svE
    Active la validation OCSP de la chaîne de certificats du client
    SSLOCSPNoverify on|off off svE
    Evite la vérification des certificats des répondeurs OCSP
    SSLOCSPOverrideResponder on|off off svE
    Force l'utilisation de l'URI du répondeur par défaut pour +
    SSLOCSPNoverify on|off off svE
    Evite la vérification des certificats des répondeurs OCSP
    SSLOCSPOverrideResponder on|off off svE
    Force l'utilisation de l'URI du répondeur par défaut pour la validation OCSP
    SSLOCSPProxyURL urlsvE
    Adresse de mandataire à utiliser pour les requêtes OCSP
    SSLOCSPResponderCertificateFile filesvE
    Fournit un jeu de certificats de confiance du répondeur OCSP avec +
    SSLOCSPProxyURL urlsvE
    Adresse de mandataire à utiliser pour les requêtes OCSP
    SSLOCSPResponderCertificateFile filesvE
    Fournit un jeu de certificats de confiance du répondeur OCSP avec encodage PEM
    SSLOCSPResponderTimeout secondes 10 svE
    Délai d'attente pour les requêtes OCSP
    SSLOCSPResponseMaxAge secondes -1 svE
    Age maximum autorisé pour les réponses OCSP
    SSLOCSPResponseTimeSkew secondes 300 svE
    Dérive temporelle maximale autorisée pour la validation des +
    SSLOCSPResponderTimeout secondes 10 svE
    Délai d'attente pour les requêtes OCSP
    SSLOCSPResponseMaxAge secondes -1 svE
    Age maximum autorisé pour les réponses OCSP
    SSLOCSPResponseTimeSkew secondes 300 svE
    Dérive temporelle maximale autorisée pour la validation des réponses OCSP
    SSLOCSPUseRequestNonce on|off on svE
    Use a nonce within OCSP queries
    SSLOpenSSLConfCmd commande valeursvE
    Configuration des paramètres d'OpenSSL via son API SSL_CONF
    SSLOptions [+|-]option ...svdhE
    Configure différentes options d'exécution du moteur SSL
    SSLPassPhraseDialog type builtin sE
    Méthode utilisée pour entrer le mot de passe pour les clés +
    SSLOCSPUseRequestNonce on|off on svE
    Use a nonce within OCSP queries
    SSLOpenSSLConfCmd commande valeursvE
    Configuration des paramètres d'OpenSSL via son API SSL_CONF
    SSLOptions [+|-]option ...svdhE
    Configure différentes options d'exécution du moteur SSL
    SSLPassPhraseDialog type builtin sE
    Méthode utilisée pour entrer le mot de passe pour les clés privées chiffrées
    SSLProtocol [+|-]protocole ... all -SSLv3 (jusqu'à +svE
    Indique les versions du protocole SSL/TLS +
    SSLProtocol [+|-]protocole ... all -SSLv3 (jusqu'à +svE
    Indique les versions du protocole SSL/TLS disponibles
    SSLProxyCACertificateFile file-pathsvpE
    Fichier contenant la concaténation des certificats de CA +
    SSLProxyCACertificateFile file-pathsvpE
    Fichier contenant la concaténation des certificats de CA codés en PEM pour l'authentification des serveurs distants
    SSLProxyCACertificatePath chemin-répertoiresvpE
    Répertoire des certificats de CA codés en PEM pour +
    SSLProxyCACertificatePath chemin-répertoiresvpE
    Répertoire des certificats de CA codés en PEM pour l'authentification des serveurs distants
    SSLProxyCARevocationCheck chain|leaf|none none svpE
    Active la vérification des révocations basée sur les CRLs +
    SSLProxyCARevocationCheck chain|leaf|none none svpE
    Active la vérification des révocations basée sur les CRLs pour l'authentification du serveur distant
    SSLProxyCARevocationFile file-pathsvpE
    Fichier contenant la concaténation des CRLs de CA codés en +
    SSLProxyCARevocationFile file-pathsvpE
    Fichier contenant la concaténation des CRLs de CA codés en PEM pour l'authentification des serveurs distants
    SSLProxyCARevocationPath chemin-répertoiresvpE
    Répertoire des CRLs de CA codés en PEM pour +
    SSLProxyCARevocationPath chemin-répertoiresvpE
    Répertoire des CRLs de CA codés en PEM pour l'authentification des serveurs distants
    SSLProxyCheckPeerCN on|off on svpE
    Configuration de la vérification du champ CN du certificat +
    SSLProxyCheckPeerCN on|off on svpE
    Configuration de la vérification du champ CN du certificat du serveur distant
    SSLProxyCheckPeerExpire on|off on svpE
    Configuration de la vérification de l'expiration du +
    SSLProxyCheckPeerExpire on|off on svpE
    Configuration de la vérification de l'expiration du certificat du serveur distant
    SSLProxyCheckPeerName on|off on svpE
    Configure la vérification du nom d'hôte dans les +
    SSLProxyCheckPeerName on|off on svpE
    Configure la vérification du nom d'hôte dans les certificats serveur distants
    SSLProxyCipherSuite [protocol] cipher-spec ALL:!ADH:RC4+RSA:+H +svpE
    Algorithmes de chiffrement disponibles pour la négociation +
    SSLProxyCipherSuite [protocol] cipher-spec ALL:!ADH:RC4+RSA:+H +svpE
    Algorithmes de chiffrement disponibles pour la négociation lors de l'initialisation d'une connexion SSL de mandataire
    SSLProxyEngine on|off off svpE
    Interrupteur marche/arrêt du moteur de mandataire +
    SSLProxyEngine on|off off svpE
    Interrupteur marche/arrêt du moteur de mandataire SSL
    SSLProxyMachineCertificateChainFile nom-fichiersvpE
    Fichier de certificats de CA encodés PEM concaténés permettant au +
    SSLProxyMachineCertificateChainFile nom-fichiersvpE
    Fichier de certificats de CA encodés PEM concaténés permettant au mandataire de choisir un certificat
    SSLProxyMachineCertificateFile chemin-fichiersvpE
    Fichier contenant la concaténation des clés et certificats +
    SSLProxyMachineCertificateFile chemin-fichiersvpE
    Fichier contenant la concaténation des clés et certificats clients codés en PEM que le mandataire doit utiliser
    SSLProxyMachineCertificatePath chemin-répertoiresvpE
    Répertoire des clés et certificats clients codés en PEM que +
    SSLProxyMachineCertificatePath chemin-répertoiresvpE
    Répertoire des clés et certificats clients codés en PEM que le mandataire doit utiliser
    SSLProxyProtocol [+|-]protocole ... all -SSLv3 (jusqu'à +svpE
    Définit les protocoles SSL disponibles pour la fonction de +
    SSLProxyProtocol [+|-]protocole ... all -SSLv3 (jusqu'à +svpE
    Définit les protocoles SSL disponibles pour la fonction de mandataire
    SSLProxyVerify niveau none svpE
    Niveau de vérification du certificat du serveur +
    SSLProxyVerify niveau none svpE
    Niveau de vérification du certificat du serveur distant
    SSLProxyVerifyDepth niveau 1 svpE
    Niveau de profondeur maximum dans les certificats de CA +
    SSLProxyVerifyDepth niveau 1 svpE
    Niveau de profondeur maximum dans les certificats de CA lors de la vérification du certificat du serveur distant
    SSLRandomSeed contexte source -[nombre]sE
    Source de déclenchement du Générateur de Nombres +
    SSLRandomSeed contexte source +[nombre]sE
    Source de déclenchement du Générateur de Nombres Pseudo-Aléatoires (PRNG)
    SSLRenegBufferSize taille 131072 dhE
    Définit la taille du tampon de renégociation +
    SSLRenegBufferSize taille 131072 dhE
    Définit la taille du tampon de renégociation SSL
    SSLRequire expressiondhE
    N'autorise l'accès que lorsqu'une expression booléenne +
    SSLRequire expressiondhE
    N'autorise l'accès que lorsqu'une expression booléenne complexe et arbitraire est vraie
    SSLRequireSSLdhE
    Interdit l'accès lorsque la requête HTTP n'utilise pas +
    SSLRequireSSLdhE
    Interdit l'accès lorsque la requête HTTP n'utilise pas SSL
    SSLSessionCache type none sE
    Type du cache de session SSL global et +
    SSLSessionCache type none sE
    Type du cache de session SSL global et inter-processus
    SSLSessionCacheTimeout secondes 300 svE
    Nombre de secondes avant l'expiration d'une session SSL +
    SSLSessionCacheTimeout secondes 300 svE
    Nombre de secondes avant l'expiration d'une session SSL dans le cache de sessions
    SSLSessionTicketKeyFile file-pathsvE
    Clé de chiffrement/déchiffrement permanente pour les +
    SSLSessionTicketKeyFile file-pathsvE
    Clé de chiffrement/déchiffrement permanente pour les tickets de session TLS
    SSLSessionTickets on|off on svE
    Active ou désactive les tickets de session TLS
    SSLSRPUnknownUserSeed secret-stringsvE
    Source d'aléa pour utilisateur SRP inconnu
    SSLSRPVerifierFile file-pathsvE
    Chemin du fichier de vérification SRP
    SSLStaplingCache typesE
    Configuration du cache pour l'agrafage OCSP
    SSLStaplingErrorCacheTimeout secondes 600 svE
    Durée de vie des réponses invalides dans le cache pour +
    SSLSessionTickets on|off on svE
    Active ou désactive les tickets de session TLS
    SSLSRPUnknownUserSeed secret-stringsvE
    Source d'aléa pour utilisateur SRP inconnu
    SSLSRPVerifierFile file-pathsvE
    Chemin du fichier de vérification SRP
    SSLStaplingCache typesE
    Configuration du cache pour l'agrafage OCSP
    SSLStaplingErrorCacheTimeout secondes 600 svE
    Durée de vie des réponses invalides dans le cache pour agrafage OCSP
    SSLStaplingFakeTryLater on|off on svE
    Génère une réponse "tryLater" pour les requêtes OCSP échouées
    SSLStaplingForceURL urisvE
    Remplace l'URI du serveur OCSP spécifié dans l'extension +
    SSLStaplingFakeTryLater on|off on svE
    Génère une réponse "tryLater" pour les requêtes OCSP échouées
    SSLStaplingForceURL urisvE
    Remplace l'URI du serveur OCSP spécifié dans l'extension AIA du certificat
    SSLStaplingResponderTimeout secondes 10 svE
    Temps d'attente maximum pour les requêtes vers les serveurs +
    SSLStaplingResponderTimeout secondes 10 svE
    Temps d'attente maximum pour les requêtes vers les serveurs OCSP
    SSLStaplingResponseMaxAge secondes -1 svE
    Age maximum autorisé des réponses OCSP incluses dans la +
    SSLStaplingResponseMaxAge secondes -1 svE
    Age maximum autorisé des réponses OCSP incluses dans la négociation TLS
    SSLStaplingResponseTimeSkew secondes 300 svE
    Durée de vie maximale autorisée des réponses OCSP incluses dans la +
    SSLStaplingResponseTimeSkew secondes 300 svE
    Durée de vie maximale autorisée des réponses OCSP incluses dans la négociation TLS
    SSLStaplingReturnResponderErrors on|off on svE
    Transmet au client les erreurs survenues lors des requêtes +
    SSLStaplingReturnResponderErrors on|off on svE
    Transmet au client les erreurs survenues lors des requêtes OCSP
    SSLStaplingStandardCacheTimeout secondes 3600 svE
    Durée de vie des réponses OCSP dans le cache
    SSLStrictSNIVHostCheck on|off off svE
    Contrôle de l'accès des clients non-SNI à un serveur virtuel à +
    SSLStaplingStandardCacheTimeout secondes 3600 svE
    Durée de vie des réponses OCSP dans le cache
    SSLStrictSNIVHostCheck on|off off svE
    Contrôle de l'accès des clients non-SNI à un serveur virtuel à base de nom.
    SSLUserName nom-varsdhE
    Nom de la variable servant à déterminer le nom de +
    SSLUserName nom-varsdhE
    Nom de la variable servant à déterminer le nom de l'utilisateur
    SSLUseStapling on|off off svE
    Active l'ajout des réponses OCSP à la négociation TLS
    SSLVerifyClient niveau none svdhE
    Niveau de vérification du certificat client
    SSLVerifyDepth nombre 1 svdhE
    Profondeur maximale des certificats de CA pour la +
    SSLUseStapling on|off off svE
    Active l'ajout des réponses OCSP à la négociation TLS
    SSLVerifyClient niveau none svdhE
    Niveau de vérification du certificat client
    SSLVerifyDepth nombre 1 svdhE
    Profondeur maximale des certificats de CA pour la vérification des certificats clients
    SSLVHostSNIPolicy strict|secure|authonly|insecure secure sE
    Définir la politique de compatibilité pour l'accès des clients SNI +
    SSLVHostSNIPolicy strict|secure|authonly|insecure secure sE
    Définir la politique de compatibilité pour l'accès des clients SNI aux serveurs virtuels.
    StartServers nombresM
    Nombre de processus enfants du serveur créés au +
    StartServers nombresM
    Nombre de processus enfants du serveur créés au démarrage
    StartThreads nombresM
    Nombre de threads créés au démarrage
    StrictHostCheck ON|OFF OFF svC
    Détermine si le nom d'hôte contenu dans une requête doit être +
    StartThreads nombresM
    Nombre de threads créés au démarrage
    StrictHostCheck ON|OFF OFF svC
    Détermine si le nom d'hôte contenu dans une requête doit être explicitement spécifié au niveau du serveur virtuel qui a pris en compte cette dernière.
    Substitute s/modèle/substitution/[infq]dhE
    Modèle de substition dans le contenu de la +
    Substitute s/modèle/substitution/[infq]dhE
    Modèle de substition dans le contenu de la réponse
    SubstituteInheritBefore on|off on dhE
    Modifie l'ordre de fusion des modèles hérités
    SubstituteMaxLineLength octets(b|B|k|K|m|M|g|G) 1m dhE
    Définit la longueur de ligne maximale
    Suexec On|OffsB
    Active ou désactive la fonctionnalité suEXEC
    SuexecUserGroup Utilisateur GroupesvE
    L'utilisateur et le groupe sous lesquels les programmes CGI +
    SubstituteInheritBefore on|off on dhE
    Modifie l'ordre de fusion des modèles hérités
    SubstituteMaxLineLength octets(b|B|k|K|m|M|g|G) 1m dhE
    Définit la longueur de ligne maximale
    Suexec On|OffsB
    Active ou désactive la fonctionnalité suEXEC
    SuexecUserGroup Utilisateur GroupesvE
    L'utilisateur et le groupe sous lesquels les programmes CGI doivent s'exécuter
    ThreadLimit nombresM
    Le nombre de threads maximum que l'on peut définir par +
    ThreadLimit nombresM
    Le nombre de threads maximum que l'on peut définir par processus enfant
    ThreadsPerChild nombresM
    Nombre de threads créés par chaque processus +
    ThreadsPerChild nombresM
    Nombre de threads créés par chaque processus enfant
    ThreadStackSize taillesM
    La taille en octets de la pile qu'utilisent les threads qui +
    ThreadStackSize taillesM
    La taille en octets de la pile qu'utilisent les threads qui traitent les connexions clients
    TimeOut secondes 60 svC
    Temps pendant lequel le serveur va attendre certains +
    TimeOut secondes 60 svC
    Temps pendant lequel le serveur va attendre certains évènements avant de considérer qu'une requête a échoué
    TraceEnable [on|off|extended] on svC
    Détermine le comportement des requêtes +
    TraceEnable [on|off|extended] on svC
    Détermine le comportement des requêtes TRACE
    TransferLog fichier|pipesvB
    Spécifie l'emplacement d'un fichier journal
    TypesConfig chemin-fichier conf/mime.types sB
    Le chemin du fichier mime.types
    UNCList hostname [hostname...]sC
    Définit quels sont les noms d'hôte UNC auxquels le serveur peut accéder +
    TransferLog fichier|pipesvB
    Spécifie l'emplacement d'un fichier journal
    TypesConfig chemin-fichier conf/mime.types sB
    Le chemin du fichier mime.types
    UNCList hostname [hostname...]sC
    Définit quels sont les noms d'hôte UNC auxquels le serveur peut accéder
    UnDefine nom-variablesC
    Invalide la définition d'une variable
    UndefMacro nomsvdB
    Supprime une macro
    UnsetEnv var-env [var-env] -...svdhB
    Supprime des variables de l'environnement
    Use nom [valeur1 ... valeurN] -svdB
    Utilisation d'une macro
    UseCanonicalName On|Off|DNS Off svdC
    Définit la manière dont le serveur détermine son propre nom +
    UnDefine nom-variablesC
    Invalide la définition d'une variable
    UndefMacro nomsvdB
    Supprime une macro
    UnsetEnv var-env [var-env] +...svdhB
    Supprime des variables de l'environnement
    Use nom [valeur1 ... valeurN] +svdB
    Utilisation d'une macro
    UseCanonicalName On|Off|DNS Off svdC
    Définit la manière dont le serveur détermine son propre nom et son port
    UseCanonicalPhysicalPort On|Off Off svdC
    Définit la manière dont le serveur +
    UseCanonicalPhysicalPort On|Off Off svdC
    Définit la manière dont le serveur détermine son propre port
    User utilisateur unix #-1 sB
    L'utilisateur sous lequel le serveur va traiter les +
    User utilisateur unix #-1 sB
    L'utilisateur sous lequel le serveur va traiter les requêtes
    UserDir nom-répertoire [nom-répertoire] ... -svB
    Chemin des répertoires propres à un +
    UserDir nom-répertoire [nom-répertoire] ... +svB
    Chemin des répertoires propres à un utilisateur
    VHostCGIMode On|Off|Secure On v
    Détermine si le serveur virtuel peut exécuter des +
    VHostCGIMode On|Off|Secure On v
    Détermine si le serveur virtuel peut exécuter des sous-processus, et définit les privilèges disponibles pour ces dernier.
    VHostCGIPrivs [+-]?privilege-name [[+-]?privilege-name] ...v
    Assigne des privilèges au choix aux sous-processus créés +
    VHostCGIPrivs [+-]?privilege-name [[+-]?privilege-name] ...v
    Assigne des privilèges au choix aux sous-processus créés par un serveur virtuel.
    VHostGroup identifiant-groupe-unixv
    Définit l'identifiant du groupe sous lequel s'exécute un +
    VHostGroup identifiant-groupe-unixv
    Définit l'identifiant du groupe sous lequel s'exécute un serveur virtuel.
    VHostPrivs [+-]?nom-privilège [[+-]?nom-privilège] ...v
    Assigne des privilèges à un serveur virtuel.
    VHostSecure On|Off On v
    Détermine si le serveur s'exécute avec une sécurité avancée +
    VHostPrivs [+-]?nom-privilège [[+-]?nom-privilège] ...v
    Assigne des privilèges à un serveur virtuel.
    VHostSecure On|Off On v
    Détermine si le serveur s'exécute avec une sécurité avancée pour les serveurs virtuels.
    VHostUser identifiant-utilisateur-unixv
    Définit l'identifiant utilisateur sous lequel s'exécute un +
    VHostUser identifiant-utilisateur-unixv
    Définit l'identifiant utilisateur sous lequel s'exécute un serveur virtuel.
    VirtualDocumentRoot répertoire-interpolé|none none svE
    Permet une configuration dynamique de la racine des +
    VirtualDocumentRoot répertoire-interpolé|none none svE
    Permet une configuration dynamique de la racine des documents d'un serveur virtuel donné
    VirtualDocumentRootIP répertoire-interpolé|none none svE
    Configuration dynamique de la racine des documents pour un +
    VirtualDocumentRootIP répertoire-interpolé|none none svE
    Configuration dynamique de la racine des documents pour un serveur virtuel donné
    <VirtualHost +
    <VirtualHost adresse IP[:port] [adresse IP[:port]] ...> ... - </VirtualHost>sC
    Contient des directives qui ne s'appliquent qu'à un nom + </VirtualHost>sC
    Contient des directives qui ne s'appliquent qu'à un nom d'hôte spécifique ou à une adresse IP
    VirtualScriptAlias répertoire-interpolé|none none svE
    Configuration dynamique du répertoire des scripts CGI pour +
    VirtualScriptAlias répertoire-interpolé|none none svE
    Configuration dynamique du répertoire des scripts CGI pour un serveur virtuel donné
    VirtualScriptAliasIP répertoire-interpolé|none none svE
    Configuration dynamique du répertoire des scripts CGI pour +
    VirtualScriptAliasIP répertoire-interpolé|none none svE
    Configuration dynamique du répertoire des scripts CGI pour un serveur virtuel donné
    WatchdogInterval time-interval[s] 1 sB
    Intervalle Watchdog en secondes
    XBitHack on|off|full off svdhB
    Interprète les directives SSI dans les fichiers dont le bit +
    WatchdogInterval time-interval[s] 1 sB
    Intervalle Watchdog en secondes
    XBitHack on|off|full off svdhB
    Interprète les directives SSI dans les fichiers dont le bit d'exécution est positionné
    xml2EncAlias jeu-de-caractères alias [alias ...]sB
    Définit des alias pour les valeurs d'encodage
    xml2EncDefault nomsvdhB
    Définit un encodage par défaut à utiliser lorsqu'aucune +
    xml2EncAlias jeu-de-caractères alias [alias ...]sB
    Définit des alias pour les valeurs d'encodage
    xml2EncDefault nomsvdhB
    Définit un encodage par défaut à utiliser lorsqu'aucune information ne peut être automatiquement détectée
    xml2StartParse élément [élément ...]svdhB
    Indique à l'interpréteur à partir de quelle balise il doit +
    xml2StartParse élément [élément ...]svdhB
    Indique à l'interpréteur à partir de quelle balise il doit commencer son traitement.
    diff -Nru apache2-2.4.69/docs/manual/mod/quickreference.html.ja.utf8 apache2-2.4.71/docs/manual/mod/quickreference.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/quickreference.html.ja.utf8 2026-09-29 11:57:11.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/quickreference.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -682,7 +682,7 @@
    MDCertificateAuthority url letsencrypt sX
    The URL(s) of the ACME Certificate Authority to use.
    MDCertificateCheck name urlsX
    Set name and URL pattern for a certificate monitoring site.
    MDCertificateFile path-to-pem-filesX
    Specify a static certificate file for the MD.
    MDCertificateKeyFile path-to-filesX
    Specify a static private key for for the static cerrtificate.
    MDCertificateKeyFile path-to-filesX
    Specify a static private key for the static certificate.
    MDCertificateMonitor name url crt.sh https://crt. +sX
    The URL of a certificate log monitor.
    MDCertificateProtocol protocol ACME sX
    The protocol to use with the Certificate Authority.
    MDCertificateStatus on|off on sX
    Exposes public certificate information in JSON.
    MDDriveMode always|auto|manual auto sX
    former name of MDRenewMode.
    MDExternalAccountBinding key-id hmac-64 | none | file none sX
    Set the external account binding keyid and hmac values to use at CA
    MDHttpProxy urlsX
    Define a proxy for outgoing connections.
    MDInitialDelay duration 0s sX
    How long to delay the first certificate check.
    MDMatchNames all|servernames all sX
    Determines how DNS names are matched to vhosts
    MDMember hostnamesX
    Additional hostname for the managed domain.
    MDMembers auto|manual auto sX
    Control if the alias domain names are automatically added.
    MDMessageCmd path-to-cmd optional-argssX
    Handle events for Manage Domains
    MDMustStaple on|off off sX
    Control if new certificates carry the OCSP Must Staple flag.
    MDNotifyCmd path [ args ]sX
    Run a program when a Managed Domain is ready.
    MDomain dns-name [ other-dns-name... ] [auto|manual]sX
    Define list of domain names that belong to one group.
    <MDomainSet dns-name [ other-dns-name... ]>...</MDomainSet>sX
    Container for directives applied to the same managed domains.
    MDPortMap map1 [ map2 ] http:80 https:443 sX
    Map external to internal ports for domain ownership verification.
    MDPrivateKeys type [ params... ] RSA 2048 sX
    Set type and size of the private keys generated.
    MDProfile namesX
    Use a specific ACME profile from the CA
    MDProfileMandatory on|off off sX
    Control if an MDProfile is mandatory.
    MDRenewMode always|auto|manual auto sX
    Controls if certificates shall be renewed.
    MDRenewViaARI on|off on sX
    usage of the ACME ARI extension (rfc9773).
    MDRenewWindow duration 33% sX
    Control when a certificate will be renewed.
    MDRequireHttps off|temporary|permanent off sX
    Redirects http: traffic to https: for Managed Domains.
    MDRetryDelay duration 30s sX
    Time length for first retry, doubled on every consecutive error.
    MDRetryFailover number 13 sX
    The number of errors before a failover to another CA is triggered
    MDServerStatus on|off off sX
    Control if Managed Domain information is added to server-status.
    MDStapleOthers on|off on sX
    Enable stapling for certificates not managed by mod_md.
    MDStapling on|off off sX
    Enable stapling for all or a particular MDomain.
    MDStaplingKeepResponse duration 7d sX
    Controls when old responses should be removed.
    MDStaplingRenewWindow duration 33% sX
    Control when the stapling responses will be renewed.
    MDStoreDir path md sX
    Path on the local file system to store the Managed Domains data.
    MDStoreLocks on|off|duration off sX
    Configure locking of store for updates
    MDWarnWindow duration 10% sX
    Define the time window when you want to be warned about an expiring certificate.
    MemcacheConnTTL num[units] 15s svE
    Keepalive time for idle connections
    MergeSlashes ON|OFF ON svC
    Controls whether the server merges consecutive slashes in URLs. +
    MDHttpProxyCACertificateFile path-to-pem-file none sX
    Sets the root (CA) certificates to use for TLS connections to the http-proxy.
    MDInitialDelay duration 0s sX
    How long to delay the first certificate check.
    MDMatchNames all|servernames all sX
    Determines how DNS names are matched to vhosts
    MDMember hostnamesX
    Additional hostname for the managed domain.
    MDMembers auto|manual auto sX
    Control if the alias domain names are automatically added.
    MDMessageCmd path-to-cmd optional-argssX
    Handle events for Manage Domains
    MDMustStaple on|off off sX
    Control if new certificates carry the OCSP Must Staple flag.
    MDNotifyCmd path [ args ]sX
    Run a program when a Managed Domain is ready.
    MDomain dns-name [ other-dns-name... ] [auto|manual]sX
    Define list of domain names that belong to one group.
    <MDomainSet dns-name [ other-dns-name... ]>...</MDomainSet>sX
    Container for directives applied to the same managed domains.
    MDPortMap map1 [ map2 ] http:80 https:443 sX
    Map external to internal ports for domain ownership verification.
    MDPrivateKeys type [ params... ] RSA 2048 sX
    Set type and size of the private keys generated.
    MDProfile namesX
    Use a specific ACME profile from the CA
    MDProfileMandatory on|off off sX
    Control if an MDProfile is mandatory.
    MDRenewMode always|auto|manual auto sX
    Controls if certificates shall be renewed.
    MDRenewViaARI on|off on sX
    usage of the ACME ARI extension (rfc9773).
    MDRenewWindow duration 33% sX
    Control when a certificate will be renewed.
    MDRequireHttps off|temporary|permanent off sX
    Redirects http: traffic to https: for Managed Domains.
    MDRetryDelay duration 30s sX
    Time length for first retry, doubled on every consecutive error.
    MDRetryFailover number 13 sX
    The number of errors before a failover to another CA is triggered
    MDServerStatus on|off off sX
    Control if Managed Domain information is added to server-status.
    MDStapleOthers on|off on sX
    Enable stapling for certificates not managed by mod_md.
    MDStapling on|off off sX
    Enable stapling for all or a particular MDomain.
    MDStaplingKeepResponse duration 7d sX
    Controls when old responses should be removed.
    MDStaplingRenewWindow duration 33% sX
    Control when the stapling responses will be renewed.
    MDStoreDir path md sX
    Path on the local file system to store the Managed Domains data.
    MDStoreLocks on|off|duration off sX
    Configure locking of store for updates
    MDWarnWindow duration 10% sX
    Define the time window when you want to be warned about an expiring certificate.
    MemcacheConnTTL num[units] 15s svE
    Keepalive time for idle connections
    MergeSlashes ON|OFF ON svC
    Controls whether the server merges consecutive slashes in URLs.
    MergeTrailers [on|off] off svC
    Determines whether trailers are merged into headers
    MetaDir directory .web svdhD
    Name of the directory to find CERN-style meta information +
    MergeTrailers [on|off] off svC
    Determines whether trailers are merged into headers
    MetaDir directory .web svdhD
    Name of the directory to find CERN-style meta information files
    MetaFiles on|off off svdhD
    Activates CERN meta-file processing
    MetaSuffix suffix .meta svdhD
    File name suffix for the file containing CERN-style +
    MetaFiles on|off off svdhD
    Activates CERN meta-file processing
    MetaSuffix suffix .meta svdhD
    File name suffix for the file containing CERN-style meta information
    MimeMagicFile file-pathsvE
    Enable MIME-type determination based on file contents +
    MimeMagicFile file-pathsvE
    Enable MIME-type determination based on file contents using the specified magic file
    MinSpareServers number 5 sM
    アイドル��サー�プロセス�最�個数
    MinSpareThreads numbersM
    リクエスト�応答�る�����る +
    MinSpareServers number 5 sM
    アイドル��サー�プロセス�最�個数
    MinSpareThreads numbersM
    リクエスト�応答�る�����る アイドルスレッド数�最�数
    MMapFile file-path [file-path] ...sX
    Map a list of files into memory at startup time
    ModemStandard V.21|V.26bis|V.32|V.34|V.92dX
    Modem standard to simulate
    ModMimeUsePathInfo On|Off Off d
    path_info コン�ー�ントをファイル��一部���扱�よ�� +
    MMapFile file-path [file-path] ...sX
    Map a list of files into memory at startup time
    ModemStandard V.21|V.26bis|V.32|V.34|V.92dX
    Modem standard to simulate
    ModMimeUsePathInfo On|Off Off d
    path_info コン�ー�ントをファイル��一部���扱�よ�� mod_mime �通知�る
    MultiviewsMatch Any|NegotiatedOnly|Filters|Handlers -[Handlers|Filters] NegotiatedOnly svdh
    MultiViews ��マッ�ング�検索����る +
    MultiviewsMatch Any|NegotiatedOnly|Filters|Handlers +[Handlers|Filters] NegotiatedOnly svdh
    MultiViews ��マッ�ング�検索����る ファイル�タイプを指定�る
    Mutex mechanism [default|mutex-name] ... [OmitPID] default sC
    Configures mutex mechanism and lock file directory for all +
    Mutex mechanism [default|mutex-name] ... [OmitPID] default sC
    Configures mutex mechanism and lock file directory for all or specified mutexes
    NameVirtualHost addr[:port]sC
    ��ベース��ー�ャルホスト���� IP アドレスを指定
    NoProxy host [host] ...svE
    直接接続�る ホスト�ドメイン��ットワーク
    NWSSLTrustedCerts filename [filename] ...sB
    List of additional client certificates
    NWSSLUpgradeable [IP-address:]portnumbersB
    Allows a connection to be upgraded to an SSL connection upon request
    Options - [+|-]option [[+|-]option] ... All svdhC
    ディレクトリ�対��使用�能�機能を設定�る
    Order ordering Deny,Allow dhE
    デフォルト�アクセス�能�状態��Allow � +
    NameVirtualHost addr[:port]sC
    ��ベース��ー�ャルホスト���� IP アドレスを指定
    NoProxy host [host] ...svE
    直接接続�る ホスト�ドメイン��ットワーク
    NWSSLTrustedCerts filename [filename] ...sB
    List of additional client certificates
    NWSSLUpgradeable [IP-address:]portnumbersB
    Allows a connection to be upgraded to an SSL connection upon request
    Options + [+|-]option [[+|-]option] ... All svdhC
    ディレクトリ�対��使用�能�機能を設定�る
    Order ordering Deny,Allow dhE
    デフォルト�アクセス�能�状態��Allow � Deny �評価�れる順番を制御�る
    OutputSed sed-commanddhX
    Sed command for filtering response content
    PassEnv env-variable [env-variable] -...svdhB
    シェル�ら�環境変数を渡�
    PidFile filename logs/httpd.pid sM
    デーモン�プロセス ID +
    OutputSed sed-commanddhX
    Sed command for filtering response content
    PassEnv env-variable [env-variable] +...svdhB
    シェル�ら�環境変数を渡�
    PidFile filename logs/httpd.pid sM
    デーモン�プロセス ID をサー��記録�る���ファイル
    PrivilegesMode FAST|SECURE|SELECTIVE FAST svdD
    Trade off processing speed and efficiency vs security against +
    PrivilegesMode FAST|SECURE|SELECTIVE FAST svdD
    Trade off processing speed and efficiency vs security against malicious privileges-aware code.
    Protocol protocolsvC
    Protocol for a listening socket
    ProtocolEcho On|Off Off svX
    エコーサー��有効無効を設定���。
    Protocols protocol ... http/1.1 svC
    Protocols available for a server/virtual host
    ProtocolsHonorOrder On|Off On svC
    Determines if order of Protocols determines precedence during negotiation
    <Proxy wildcard-url> ...</Proxy>svE
    プロキシ�れるリソース��用�れるコンテナ
    Proxy100Continue Off|On On svdE
    Forward 100-continue expectation to the origin server
    ProxyAddHeaders Off|On On svdE
    Add proxy information in X-Forwarded-* headers
    ProxyBadHeader IsError|Ignore|StartBody IsError svE
    応答�����ヘッダ��る場��扱�方を決�る
    ProxyBlock *|word|host|domain -[word|host|domain] ...svE
    プロキシ接続を�止�る語��ホスト��ドメインを指定�る
    ProxyDomain DomainsvE
    プロキシ�れ�リクエスト�デフォルト�ドメイン�
    ProxyErrorOverride On|Off Off svdE
    プロキシ�れ�コンテンツ�エラーページを上書��る
    ProxyExpressDBMFile pathnamesvE
    Pathname to DBM file.
    ProxyExpressDBMType type default svE
    DBM type of file.
    ProxyExpressEnable on|off off svE
    Enable the module functionality.
    ProxyFCGIBackendType FPM|GENERIC FPM svdhE
    Specify the type of backend FastCGI application
    ProxyFCGISetEnvIf conditional-expression +
    Protocol protocolsvC
    Protocol for a listening socket
    ProtocolEcho On|Off Off svX
    エコーサー��有効無効を設定���。
    Protocols protocol ... http/1.1 svC
    Protocols available for a server/virtual host
    ProtocolsHonorOrder On|Off On svC
    Determines if order of Protocols determines precedence during negotiation
    <Proxy wildcard-url> ...</Proxy>svE
    プロキシ�れるリソース��用�れるコンテナ
    Proxy100Continue Off|On On svdE
    Forward 100-continue expectation to the origin server
    ProxyAddHeaders Off|On On svdE
    Add proxy information in X-Forwarded-* headers
    ProxyBadHeader IsError|Ignore|StartBody IsError svE
    応答�����ヘッダ��る場��扱�方を決�る
    ProxyBlock *|word|host|domain +[word|host|domain] ...svE
    プロキシ接続を�止�る語��ホスト��ドメインを指定�る
    ProxyDomain DomainsvE
    プロキシ�れ�リクエスト�デフォルト�ドメイン�
    ProxyErrorOverride On|Off Off svdE
    プロキシ�れ�コンテンツ�エラーページを上書��る
    ProxyExpressDBMFile pathnamesvE
    Pathname to DBM file.
    ProxyExpressDBMType type default svE
    DBM type of file.
    ProxyExpressEnable on|off off svE
    Enable the module functionality.
    ProxyFCGIBackendType FPM|GENERIC FPM svdhE
    Specify the type of backend FastCGI application
    ProxyFCGISetEnvIf conditional-expression [!]environment-variable-name - [value-expression]svdhE
    Allow variables sent to FastCGI servers to be fixed up
    ProxyFtpDirCharset character_set ISO-8859-1 svdE
    Define the character set for proxied FTP listings
    ProxyFtpEscapeWildcards on|off on svdE
    Whether wildcards in requested filenames are escaped when sent to the FTP server
    ProxyFtpListOnWildcard on|off on svdE
    Whether wildcards in requested filenames trigger a file listing
    ProxyHCExpr name {ap_expr expression}svE
    Creates a named condition expression to use to determine health of the backend based on its response
    ProxyHCTemplate name parameter=setting [...]svE
    Creates a named template for setting various health check parameters
    ProxyHCTPsize size 16 sE
    Sets the total server-wide size of the threadpool used for the health check workers
    ProxyHTMLBufSize bytes 8192 svdB
    Sets the buffer size increment for buffering inline scripts and + [value-expression]svdhE
    Allow variables sent to FastCGI servers to be fixed up
    ProxyFtpDirCharset character_set ISO-8859-1 svdE
    Define the character set for proxied FTP listings
    ProxyFtpEscapeWildcards on|off on svdE
    Whether wildcards in requested filenames are escaped when sent to the FTP server
    ProxyFtpListOnWildcard on|off on svdE
    Whether wildcards in requested filenames trigger a file listing
    ProxyHCExpr name {ap_expr expression}svE
    Creates a named condition expression to use to determine health of the backend based on its response
    ProxyHCTemplate name parameter=setting [...]svE
    Creates a named template for setting various health check parameters
    ProxyHCTPsize size 16 sE
    Sets the total server-wide size of the threadpool used for the health check workers
    ProxyHTMLBufSize bytes 8192 svdB
    Sets the buffer size increment for buffering inline scripts and stylesheets.
    ProxyHTMLCharsetOut Charset | * UTF-8 svdB
    Specify a charset for mod_proxy_html output.
    ProxyHTMLDocType HTML|XHTML [Legacy]
    OR -
    ProxyHTMLDocType fpi [SGML|XML]
    svdB
    Sets an HTML or XHTML document type declaration.
    ProxyHTMLEnable On|Off Off svdB
    Turns the proxy_html filter on or off.
    ProxyHTMLEvents attribute [attribute ...]svdB
    Specify attributes to treat as scripting events.
    ProxyHTMLExtended On|Off Off svdB
    Determines whether to fix links in inline scripts, stylesheets, +
    ProxyHTMLCharsetOut Charset | * UTF-8 svdB
    Specify a charset for mod_proxy_html output.
    ProxyHTMLDocType HTML|XHTML [Legacy]
    OR +
    ProxyHTMLDocType fpi [SGML|XML]
    svdB
    Sets an HTML or XHTML document type declaration.
    ProxyHTMLEnable On|Off Off svdB
    Turns the proxy_html filter on or off.
    ProxyHTMLEvents attribute [attribute ...]svdB
    Specify attributes to treat as scripting events.
    ProxyHTMLExtended On|Off Off svdB
    Determines whether to fix links in inline scripts, stylesheets, and scripting events.
    ProxyHTMLFixups [lowercase] [dospath] [reset] none svdB
    Fixes for simple HTML errors.
    ProxyHTMLInterp On|Off Off svdB
    Enables per-request interpolation of +
    ProxyHTMLFixups [lowercase] [dospath] [reset] none svdB
    Fixes for simple HTML errors.
    ProxyHTMLInterp On|Off Off svdB
    Enables per-request interpolation of ProxyHTMLURLMap rules.
    ProxyHTMLLinks element attribute [attribute2 ...]svdB
    Specify HTML elements that have URL attributes to be rewritten.
    ProxyHTMLMeta On|Off Off svdB
    Turns on or off extra pre-parsing of metadata in HTML +
    ProxyHTMLLinks element attribute [attribute2 ...]svdB
    Specify HTML elements that have URL attributes to be rewritten.
    ProxyHTMLMeta On|Off Off svdB
    Turns on or off extra pre-parsing of metadata in HTML <head> sections.
    ProxyHTMLStripComments On|Off Off svdB
    Determines whether to strip HTML comments.
    ProxyHTMLURLMap from-pattern to-pattern [flags] [cond]svdB
    Defines a rule to rewrite HTML links
    ProxyIOBufferSize bytes 8192 svE
    内部データスループット�ッファ�サイズを決定�る
    <ProxyMatch regex> ...</ProxyMatch>svE
    正�表���マッ��よるプロキシリソース用�ディレクティブコンテナ
    ProxyMaxForwards number 10 svE
    リクエスト�フォワード�れるプロキシ�最大数
    ProxyPass [path] !|url [key=value key=value ...]]svdE
    リモートサー�をローカルサー�� URL 空間�マップ�る
    ProxyPassInherit On|Off On svE
    Inherit ProxyPass directives defined from the main server
    svdE
    Enable Environment Variable interpolation in Reverse Proxy configurations
    svdE
    Maps remote servers into the local server URL-space using regular expressions
    ProxyPassReverse [path] urlsvdE
    リ�ースプロキシ�れ�サー��ら�られ� HTTP 応答ヘッダ� +
    ProxyHTMLStripComments On|Off Off svdB
    Determines whether to strip HTML comments.
    ProxyHTMLURLMap from-pattern to-pattern [flags] [cond]svdB
    Defines a rule to rewrite HTML links
    ProxyIOBufferSize bytes 8192 svE
    内部データスループット�ッファ�サイズを決定�る
    <ProxyMatch regex> ...</ProxyMatch>svE
    正�表���マッ��よるプロキシリソース用�ディレクティブコンテナ
    ProxyMaxForwards number 10 svE
    リクエスト�フォワード�れるプロキシ�最大数
    ProxyPass [path] !|url [key=value key=value ...]]svdE
    リモートサー�をローカルサー�� URL 空間�マップ�る
    ProxyPassInherit On|Off On svE
    Inherit ProxyPass directives defined from the main server
    svdE
    Enable Environment Variable interpolation in Reverse Proxy configurations
    svdE
    Maps remote servers into the local server URL-space using regular expressions
    ProxyPassReverse [path] urlsvdE
    リ�ースプロキシ�れ�サー��ら�られ� HTTP 応答ヘッダ� URL を調整�る
    ProxyPassReverseCookieDomain internal-domain public-domainsvdE
    リ�ースプロキシサー��ら� Set-Cookie ヘッダ� Domain 文字列を +
    ProxyPassReverseCookieDomain internal-domain public-domainsvdE
    リ�ースプロキシサー��ら� Set-Cookie ヘッダ� Domain 文字列を 調整�る
    ProxyPassReverseCookiePath internal-path public-pathsvdE
    Reverse プロキシサー��ら� Set-Cookie ヘッダ� Path 文字列を +
    ProxyPassReverseCookiePath internal-path public-pathsvdE
    Reverse プロキシサー��ら� Set-Cookie ヘッダ� Path 文字列を 調整�る
    ProxyPreserveHost On|Off Off svdE
    プロキシリクエスト����付�� Host HTTP ヘッダを使�
    ProxyReceiveBufferSize bytes 0 svE
    プロキシ�れる HTTP � FTP 接続�����ットワーク�ッファサイズ
    ProxyRemote match remote-serversvE
    特定�リクエストを扱�時�使�れるリモートプロキシを指定�る
    ProxyRemoteMatch regex remote-serversvE
    正�表���マッ��よるリクエストを扱�リモートプロキシ�指定
    ProxyRequests On|Off Off svE
    フォワード (標準�) プロキシリクエストを有効��る
    ProxySCGIInternalRedirect On|Off|Headername On svdE
    Enable or disable internal redirect responses from the +
    ProxyPreserveHost On|Off Off svdE
    プロキシリクエスト����付�� Host HTTP ヘッダを使�
    ProxyReceiveBufferSize bytes 0 svE
    プロキシ�れる HTTP � FTP 接続�����ットワーク�ッファサイズ
    ProxyRemote match remote-serversvE
    特定�リクエストを扱�時�使�れるリモートプロキシを指定�る
    ProxyRemoteMatch regex remote-serversvE
    正�表���マッ��よるリクエストを扱�リモートプロキシ�指定
    ProxyRequests On|Off Off svE
    フォワード (標準�) プロキシリクエストを有効��る
    ProxySCGIInternalRedirect On|Off|Headername On svdE
    Enable or disable internal redirect responses from the backend
    ProxySCGISendfile On|Off|Headername Off svdE
    Enable evaluation of X-Sendfile pseudo response +
    ProxySCGISendfile On|Off|Headername Off svdE
    Enable evaluation of X-Sendfile pseudo response header
    svdE
    Set various Proxy balancer or member parameters
    ProxySourceAddress addresssvE
    Set local IP address for outgoing proxy connections
    svE
    Show Proxy LoadBalancer status in mod_status
    ProxyTimeout seconds 300 svE
    プロキシ�れ�リクエスト��ットワークタイムアウト
    ProxyVia On|Off|Full|Block Off svE
    プロキシ�れ�リクエスト� Via HTTP 応答ヘッダ +
    svdE
    Set various Proxy balancer or member parameters
    ProxySourceAddress addresssvE
    Set local IP address for outgoing proxy connections
    svE
    Show Proxy LoadBalancer status in mod_status
    ProxyTimeout seconds 300 svE
    プロキシ�れ�リクエスト��ットワークタイムアウト
    ProxyVia On|Off|Full|Block Off svE
    プロキシ�れ�リクエスト� Via HTTP 応答ヘッダ �より�供�れる情報
    ProxyWebsocketFallbackToProxyHttp On|Off On svD
    Instructs this module to let mod_proxy_http handle the request
    QualifyRedirectURL On|Off Off svdC
    Controls whether the REDIRECT_URL environment variable is +
    ProxyWebsocketFallbackToProxyHttp On|Off On svD
    Instructs this module to let mod_proxy_http handle the request
    QualifyRedirectURL On|Off Off svdC
    Controls whether the REDIRECT_URL environment variable is fully qualified
    ReadBufferSize bytes 8192 svdC
    Size of the buffers used to read data
    ReadmeName filenamesvdhB
    インデックス一覧�最後�挿入�れるファイル���
    ReceiveBufferSize bytes 0 sM
    TCP �信�ッファサイズ
    Redirect [status] URL-path -URLsvdhB
    クライアント��� URL を�得�るよ��外部��リダイレクトを +
    ReadBufferSize bytes 8192 svdC
    Size of the buffers used to read data
    ReadmeName filenamesvdhB
    インデックス一覧�最後�挿入�れるファイル���
    ReceiveBufferSize bytes 0 sM
    TCP �信�ッファサイズ
    Redirect [status] URL-path +URLsvdhB
    クライアント��� URL を�得�るよ��外部��リダイレクトを �る
    RedirectMatch [status] regex -URLsvdhB
    �在� URL ��正�表��マッ��より +
    RedirectMatch [status] regex +URLsvdhB
    �在� URL ��正�表��マッ��より 外部��リダイレクトを�る
    RedirectPermanent URL-path URLsvdhB
    クライアント��� URL を�得�るよ��外部��永久的� +
    RedirectPermanent URL-path URLsvdhB
    クライアント��� URL を�得�るよ��外部��永久的� リダイレクトを�る
    RedirectRelative On|Off Off svdB
    Allows relative redirect targets.
    RedirectTemp URL-path URLsvdhB
    クライアント��� URL を�得�るよ��外部��一時的� +
    RedirectRelative On|Off Off svdB
    Allows relative redirect targets.
    RedirectTemp URL-path URLsvdhB
    クライアント��� URL を�得�るよ��外部��一時的� リダイレクトを�る
    RedisConnPoolTTL num[units] 15s svE
    TTL used for the connection pool with the Redis server(s)
    RedisTimeout num[units] 5s svE
    R/W timeout used for the connection with the Redis server(s)
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    RegexDefaultOptions [none] [+|-]option [[+|-]option] ... DOTALL DOLLAR_ENDON +sC
    Allow to configure global/default options for regexes
    RegisterHttpMethod method [method [...]]sC
    Register non-standard HTTP methods
    RemoteIPHeader header-fieldsvB
    Declare the header field which should be parsed for useragent IP addresses
    RemoteIPInternalProxy proxy-ip|proxy-ip/subnet|hostname ...svB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPInternalProxyList filenamesvB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPProxiesHeader HeaderFieldNamesvB
    Declare the header field which will record all intermediate IP addresses
    RemoteIPProxyProtocol On|OffsvB
    Enable or disable PROXY protocol handling
    RemoteIPProxyProtocolExceptions host|range [host|range] [host|range]svB
    Disable processing of PROXY header for certain hosts or networks
    RemoteIPTrustedProxy proxy-ip|proxy-ip/subnet|hostname ...svB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPTrustedProxyList filenamesvB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoveCharset extension [extension] -...vdh
    ファイル�拡張��関連付�られ�����文字セット +
    RedisConnPoolTTL num[units] 15s svE
    TTL used for the connection pool with the Redis server(s)
    RedisTimeout num[units] 5s svE
    R/W timeout used for the connection with the Redis server(s)
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    RegexDefaultOptions [none] [+|-]option [[+|-]option] ... DOTALL DOLLAR_ENDON +sC
    Allow to configure global/default options for regexes
    RegisterHttpMethod method [method [...]]sC
    Register non-standard HTTP methods
    RemoteIPHeader header-fieldsvB
    Declare the header field which should be parsed for useragent IP addresses
    RemoteIPInternalProxy proxy-ip|proxy-ip/subnet|hostname ...svB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPInternalProxyList filenamesvB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPProxiesHeader HeaderFieldNamesvB
    Declare the header field which will record all intermediate IP addresses
    RemoteIPProxyProtocol On|OffsvB
    Enable or disable PROXY protocol handling
    RemoteIPProxyProtocolExceptions host|range [host|range] [host|range]svB
    Disable processing of PROXY header for certain hosts or networks
    RemoteIPTrustedProxy proxy-ip|proxy-ip/subnet|hostname ...svB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPTrustedProxyList filenamesvB
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoveCharset extension [extension] +...vdh
    ファイル�拡張��関連付�られ�����文字セット を解除�る
    RemoveEncoding extension [extension] -...vdh
    ファイル�拡張��関連付�られ�����コンテントエンコーディング +
    RemoveEncoding extension [extension] +...vdh
    ファイル�拡張��関連付�られ�����コンテントエンコーディング を解除�る
    RemoveHandler extension [extension] -...vdh
    ファイル�拡張��関連付�られ������ンドラを +
    RemoveHandler extension [extension] +...vdh
    ファイル�拡張��関連付�られ������ンドラを 解除�る
    RemoveInputFilter extension [extension] -...vdh
    ファイル拡張��関連付�られ�入力フィルタを解除�る
    RemoveLanguage extension [extension] -...vdh
    ファイル拡張��関連付�られ�言語を解除�る
    RemoveOutputFilter extension [extension] -...vdh
    ファイル拡張��関連付�られ�出力フィルタを解除�る
    RemoveType extension [extension] -...vdh
    ファイル�拡張��関連付�られ�コンテントタイプを +
    RemoveInputFilter extension [extension] +...vdh
    ファイル拡張��関連付�られ�入力フィルタを解除�る
    RemoveLanguage extension [extension] +...vdh
    ファイル拡張��関連付�られ�言語を解除�る
    RemoveOutputFilter extension [extension] +...vdh
    ファイル拡張��関連付�られ�出力フィルタを解除�る
    RemoveType extension [extension] +...vdh
    ファイル�拡張��関連付�られ�コンテントタイプを 解除�る
    RequestHeader set|append|add|unset header -[value] [early|env=[!]variable]svdhE
    HTTP リクエストヘッダ�設定
    RequestReadTimeout +
    RequestHeader set|append|add|unset header +[value] [early|env=[!]variable]svdhE
    HTTP リクエストヘッダ�設定
    RequestReadTimeout [handshake=timeout[-maxtimeout][,MinRate=rate] [header=timeout[-maxtimeout][,MinRate=rate] [body=timeout[-maxtimeout][,MinRate=rate] - handshake=0 header= +svE
    Set timeout values for completing the TLS handshake, receiving + handshake=0 header= +svE
    Set timeout values for completing the TLS handshake, receiving the request headers and/or body from client.
    Require [not] entity-name - [entity-name] ...dhB
    Tests whether an authenticated user is authorized by +
    Require [not] entity-name + [entity-name] ...dhB
    Tests whether an authenticated user is authorized by an authorization provider.
    <RequireAll> ... </RequireAll>dhB
    Enclose a group of authorization directives of which none +
    <RequireAll> ... </RequireAll>dhB
    Enclose a group of authorization directives of which none must fail and at least one must succeed for the enclosing directive to succeed.
    <RequireAny> ... </RequireAny>dhB
    Enclose a group of authorization directives of which one +
    <RequireAny> ... </RequireAny>dhB
    Enclose a group of authorization directives of which one must succeed for the enclosing directive to succeed.
    <RequireNone> ... </RequireNone>dhB
    Enclose a group of authorization directives of which none +
    <RequireNone> ... </RequireNone>dhB
    Enclose a group of authorization directives of which none must succeed for the enclosing directive to not fail.
    RewriteBase URL-pathdhE
    Sets the base URL for per-directory rewrites
    RewriteCond - TestString [!]CondPattern [flags]svdhE
    Defines a condition under which rewriting will take place +
    RewriteBase URL-pathdhE
    Sets the base URL for per-directory rewrites
    RewriteCond + TestString [!]CondPattern [flags]svdhE
    Defines a condition under which rewriting will take place
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource +
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource [MapTypeOptions] -svE
    Defines a mapping function for key-lookup
    RewriteOptions OptionssvdhE
    Sets some special options for the rewrite engine
    RewriteRule - [!]Pattern Substitution [flags]svdhE
    Defines rules for the rewriting engine
    RLimitCPU seconds|max [seconds|max]svdhC
    Apache ��プロセス�ら起動�れ�プロセス� CPU 消費�を +svE
    Defines a mapping function for key-lookup
    RewriteOptions OptionssvdhE
    Sets some special options for the rewrite engine
    RewriteRule + [!]Pattern Substitution [flags]svdhE
    Defines rules for the rewriting engine
    RLimitCPU seconds|max [seconds|max]svdhC
    Apache ��プロセス�ら起動�れ�プロセス� CPU 消費�を 制��る
    RLimitMEM bytes|max [bytes|max]svdhC
    Apache ��プロセス�ら起動�れ�プロセス�メモリ消費�を +
    RLimitMEM bytes|max [bytes|max]svdhC
    Apache ��プロセス�ら起動�れ�プロセス�メモリ消費�を 制��る
    RLimitNPROC number|max [number|max]svdhC
    Apache ��プロセス�ら起動�れ�プロセス�起動�るプロセス� +
    RLimitNPROC number|max [number|max]svdhC
    Apache ��プロセス�ら起動�れ�プロセス�起動�るプロセス� 数を制��る
    Satisfy Any|All All dhE
    ホストレベル�アクセス制御�ユーザ�証��相互作用を指定
    ScoreBoardFile file-path logs/apache_status sM
    �プロセス�連��る���データを�存�る +
    Satisfy Any|All All dhE
    ホストレベル�アクセス制御�ユーザ�証��相互作用を指定
    ScoreBoardFile file-path logs/apache_status sM
    �プロセス�連��る���データを�存�る ファイル��置
    Script method cgi-scriptsvdB
    特定�リクエストメソッド�対�� CGI スクリプトを +
    Script method cgi-scriptsvdB
    特定�リクエストメソッド�対�� CGI スクリプトを 実行�るよ��設定
    ScriptAlias URL-path -file-path|directory-pathsvdB
    URL をファイルシステム��置�マップ��マップ先を +
    ScriptAlias URL-path +file-path|directory-pathsvdB
    URL をファイルシステム��置�マップ��マップ先を CGI スクリプト�指定
    ScriptAliasMatch regex -file-path|directory-pathsvB
    URL を正�表�を使��ファイルシステム��置�マップ��マップ先を +
    ScriptAliasMatch regex +file-path|directory-pathsvB
    URL を正�表�を使��ファイルシステム��置�マップ��マップ先を CGI スクリプト�指定
    ScriptInterpreterSource Registry|Registry-Strict|Script Script svdhC
    CGI スクリプト�インタープリタ��置を調�る���手法
    ScriptLog file-pathsvB
    CGI スクリプト�エラーログファイル�場所
    ScriptLogBuffer bytes 1024 svB
    スクリプトログ�記録�れる PUT や POST リクエスト�内容�上�
    ScriptLogLength bytes 10385760 svB
    CGI スクリプト�ログファイル�大���上�
    ScriptSock file-path logs/cgisock sB
    CGI デーモン��通信�使�れるソケット�ファイル��接頭辞
    SecureListen [IP-address:]portnumber -Certificate-Name [MUTUAL]sB
    Enables SSL encryption for the specified port
    SeeRequestTail On|Off Off sC
    Determine if mod_status displays the first 63 characters +
    ScriptInterpreterSource Registry|Registry-Strict|Script Script svdhC
    CGI スクリプト�インタープリタ��置を調�る���手法
    ScriptLog file-pathsvB
    CGI スクリプト�エラーログファイル�場所
    ScriptLogBuffer bytes 1024 svB
    スクリプトログ�記録�れる PUT や POST リクエスト�内容�上�
    ScriptLogLength bytes 10385760 svB
    CGI スクリプト�ログファイル�大���上�
    ScriptSock file-path logs/cgisock sB
    CGI デーモン��通信�使�れるソケット�ファイル��接頭辞
    SecureListen [IP-address:]portnumber +Certificate-Name [MUTUAL]sB
    Enables SSL encryption for the specified port
    SeeRequestTail On|Off Off sC
    Determine if mod_status displays the first 63 characters of a request or the last 63, assuming the request itself is greater than 63 chars.
    SendBufferSize bytes 0 sM
    TCP �ッファサイズ
    ServerAdmin email-address|URLsvC
    サー��クライアント��るエラーメッセージ���る電�メール� +
    SendBufferSize bytes 0 sM
    TCP �ッファサイズ
    ServerAdmin email-address|URLsvC
    サー��クライアント��るエラーメッセージ���る電�メール� アドレス
    ServerAlias hostname [hostname] ...vC
    リクエストを��ベース��ー�ャルホスト�マッ�����る��� +
    ServerAlias hostname [hostname] ...vC
    リクエストを��ベース��ー�ャルホスト�マッ�����る��� 使用�れるホスト�別�
    ServerLimit numbersM
    設定�能�サー�プロセス数�上�
    ServerName [scheme://]fully-qualified-domain-name[:port]svC
    サー��自分自身を示����使�ホスト���ート
    ServerPath URL-pathvC
    �互��ブラウザ���ベース��ー�ャルホスト�アクセス����� +
    ServerLimit numbersM
    設定�能�サー�プロセス数�上�
    ServerName [scheme://]fully-qualified-domain-name[:port]svC
    サー��自分自身を示����使�ホスト���ート
    ServerPath URL-pathvC
    �互��ブラウザ���ベース��ー�ャルホスト�アクセス����� ���互�用 URL パス�
    ServerRoot directory-path /usr/local/apache sC
    インストール�れ�サー��ベースディレクトリ
    ServerSignature On|Off|EMail Off svdhC
    サー��生��るドキュメント�フッタを設定
    ServerTokens Major|Minor|Min[imal]|Prod[uctOnly]|OS|Full Full sC
    Server HTTP 応答ヘッダを設定�る
    Session On|Off Off svdhE
    Enables a session for the current directory or location
    SessionCookieName name attributessvdhE
    Name and attributes for the RFC2109 cookie storing the session
    SessionCookieName2 name attributessvdhE
    Name and attributes for the RFC2965 cookie storing the session
    SessionCookieRemove On|Off Off svdhE
    Control for whether session cookies should be removed from incoming HTTP headers
    SessionCryptoCipher name aes256 svdhX
    The crypto cipher to be used to encrypt the session
    SessionCryptoDriver name [param[=value]]sX
    The crypto driver to be used to encrypt the session
    SessionCryptoPassphrase secret [ secret ... ] svdhX
    The key used to encrypt the session
    SessionCryptoPassphraseFile filenamesvdX
    File containing keys used to encrypt the session
    SessionDBDCookieName name attributessvdhE
    Name and attributes for the RFC2109 cookie storing the session ID
    SessionDBDCookieName2 name attributessvdhE
    Name and attributes for the RFC2965 cookie storing the session ID
    SessionDBDCookieRemove On|Off On svdhE
    Control for whether session ID cookies should be removed from incoming HTTP headers
    SessionDBDDeleteLabel label deletesession svdhE
    The SQL query to use to remove sessions from the database
    SessionDBDInsertLabel label insertsession svdhE
    The SQL query to use to insert sessions into the database
    SessionDBDPerUser On|Off Off svdhE
    Enable a per user session
    SessionDBDSelectLabel label selectsession svdhE
    The SQL query to use to select sessions from the database
    SessionDBDUpdateLabel label updatesession svdhE
    The SQL query to use to update existing sessions in the database
    SessionEnv On|Off Off svdhE
    Control whether the contents of the session are written to the +
    ServerRoot directory-path /usr/local/apache sC
    インストール�れ�サー��ベースディレクトリ
    ServerSignature On|Off|EMail Off svdhC
    サー��生��るドキュメント�フッタを設定
    ServerTokens Major|Minor|Min[imal]|Prod[uctOnly]|OS|Full Full sC
    Server HTTP 応答ヘッダを設定�る
    Session On|Off Off svdhE
    Enables a session for the current directory or location
    SessionCookieName name attributessvdhE
    Name and attributes for the RFC2109 cookie storing the session
    SessionCookieName2 name attributessvdhE
    Name and attributes for the RFC2965 cookie storing the session
    SessionCookieRemove On|Off Off svdhE
    Control for whether session cookies should be removed from incoming HTTP headers
    SessionCryptoCipher name aes256 svdhX
    The crypto cipher to be used to encrypt the session
    SessionCryptoDriver name [param[=value]]sX
    The crypto driver to be used to encrypt the session
    SessionCryptoPassphrase secret [ secret ... ] svdhX
    The key used to encrypt the session
    SessionCryptoPassphraseFile filenamesvdX
    File containing keys used to encrypt the session
    SessionDBDCookieName name attributessvdhE
    Name and attributes for the RFC2109 cookie storing the session ID
    SessionDBDCookieName2 name attributessvdhE
    Name and attributes for the RFC2965 cookie storing the session ID
    SessionDBDCookieRemove On|Off On svdhE
    Control for whether session ID cookies should be removed from incoming HTTP headers
    SessionDBDDeleteLabel label deletesession svdhE
    The SQL query to use to remove sessions from the database
    SessionDBDInsertLabel label insertsession svdhE
    The SQL query to use to insert sessions into the database
    SessionDBDPerUser On|Off Off svdhE
    Enable a per user session
    SessionDBDSelectLabel label selectsession svdhE
    The SQL query to use to select sessions from the database
    SessionDBDUpdateLabel label updatesession svdhE
    The SQL query to use to update existing sessions in the database
    SessionEnv On|Off Off svdhE
    Control whether the contents of the session are written to the HTTP_SESSION environment variable
    SessionExclude pathsvdhE
    Define URL prefixes for which a session is ignored
    SessionExpiryUpdateInterval interval 0 (always update) svdhE
    Define the number of seconds a session's expiry may change without +
    SessionExclude pathsvdhE
    Define URL prefixes for which a session is ignored
    SessionExpiryUpdateInterval interval 0 (always update) svdhE
    Define the number of seconds a session's expiry may change without the session being updated
    SessionHeader headersvdhE
    Import session updates from a given HTTP response header
    SessionInclude pathsvdhE
    Define URL prefixes for which a session is valid
    SessionMaxAge maxage 0 svdhE
    Define a maximum age in seconds for a session
    SetEnv env-variable valuesvdhB
    環境変数を設定�る
    SetEnvIf attribute +
    SessionHeader headersvdhE
    Import session updates from a given HTTP response header
    SessionInclude pathsvdhE
    Define URL prefixes for which a session is valid
    SessionMaxAge maxage 0 svdhE
    Define a maximum age in seconds for a session
    SetEnv env-variable valuesvdhB
    環境変数を設定�る
    SetEnvIf attribute regex [!]env-variable[=value] - [[!]env-variable[=value]] ...svdhB
    リクエスト�属性�基���環境変数を設定�る + [[!]env-variable[=value]] ...svdhB
    リクエスト�属性�基���環境変数を設定�る
    svdhB
    Sets environment variables based on an ap_expr expression
    SetEnvIfNoCase attribute regex +
    svdhB
    Sets environment variables based on an ap_expr expression
    SetEnvIfNoCase attribute regex [!]env-variable[=value] - [[!]env-variable[=value]] ...svdhB
    リクエスト�属性�基���大文字�文字を区別���環境変数を設定�る
    SetHandler handler-name|NonesvdhC
    マッ��るファイル��ンドラ�処��れるよ���る
    SetInputFilter filter[;filter...]svdhC
    クライアント�リクエストや POST �入力を処��るフィルタを設定�る
    SetOutputFilter filter[;filter...]svdhC
    サー��応答を処��るフィルタを設定�る
    SSIEndTag tag "-->" svB
    include �素を終了��る文字列
    SSIErrorMsg message "[an error occurred +svdhB
    SSI �エラー�������表示�れるエラーメッセージ
    SSIETag on|off off dhB
    Controls whether ETags are generated by the server.
    SSILastModified on|off off dhB
    Controls whether Last-Modified headers are generated by the + [[!]env-variable[=value]] ...svdhB
    リクエスト�属性�基���大文字�文字を区別���環境変数を設定�る
    SetHandler handler-name|NonesvdhC
    マッ��るファイル��ンドラ�処��れるよ���る
    SetInputFilter filter[;filter...]svdhC
    クライアント�リクエストや POST �入力を処��るフィルタを設定�る
    SetOutputFilter filter[;filter...]svdhC
    サー��応答を処��るフィルタを設定�る
    SSIEndTag tag "-->" svB
    include �素を終了��る文字列
    SSIErrorMsg message "[an error occurred +svdhB
    SSI �エラー�������表示�れるエラーメッセージ
    SSIETag on|off off dhB
    Controls whether ETags are generated by the server.
    SSILastModified on|off off dhB
    Controls whether Last-Modified headers are generated by the server.
    SSILegacyExprParser on|off off dhB
    Enable compatibility mode for conditional expressions.
    SSIStartTag tag "<!--#" svB
    include �素を開始�る文字列
    SSITimeFormat formatstring "%A, %d-%b-%Y %H:%M +svdhB
    日付�を��文字列�書�を設定�る
    SSIUndefinedEcho string "(none)" svdhB
    未定義�変数� echo �れ����表示�れる文字列
    SSLCACertificateFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates +
    SSILegacyExprParser on|off off dhB
    Enable compatibility mode for conditional expressions.
    SSIStartTag tag "<!--#" svB
    include �素を開始�る文字列
    SSITimeFormat formatstring "%A, %d-%b-%Y %H:%M +svdhB
    日付�を��文字列�書�を設定�る
    SSIUndefinedEcho string "(none)" svdhB
    未定義�変数� echo �れ����表示�れる文字列
    SSLCACertificateFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates for Client Auth
    SSLCACertificatePath directory-pathsvE
    Directory of PEM-encoded CA Certificates for +
    SSLCACertificatePath directory-pathsvE
    Directory of PEM-encoded CA Certificates for Client Auth
    SSLCADNRequestFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates +
    SSLCADNRequestFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates for defining acceptable CA names
    SSLCADNRequestPath directory-pathsvE
    Directory of PEM-encoded CA Certificates for +
    SSLCADNRequestPath directory-pathsvE
    Directory of PEM-encoded CA Certificates for defining acceptable CA names
    SSLCARevocationCheck chain|leaf|none [flags ...] none svE
    Enable CRL-based revocation checking
    SSLCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for +
    SSLCARevocationCheck chain|leaf|none [flags ...] none svE
    Enable CRL-based revocation checking
    SSLCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for Client Auth
    SSLCARevocationPath directory-pathsvE
    Directory of PEM-encoded CA CRLs for +
    SSLCARevocationPath directory-pathsvE
    Directory of PEM-encoded CA CRLs for Client Auth
    SSLCertificateChainFile file-pathsvE
    File of PEM-encoded Server CA Certificates
    SSLCertificateFile file-path|certidsvE
    Server PEM-encoded X.509 certificate data file or token identifier
    SSLCertificateKeyFile file-path|keyidsvE
    Server PEM-encoded private key file
    SSLCipherSuite [protocol] cipher-spec DEFAULT (depends on +svdhE
    Cipher Suite available for negotiation in SSL +
    SSLCertificateChainFile file-pathsvE
    File of PEM-encoded Server CA Certificates
    SSLCertificateFile file-path|certidsvE
    Server PEM-encoded X.509 certificate data file or token identifier
    SSLCertificateKeyFile file-path|keyidsvE
    Server PEM-encoded private key file
    SSLCipherSuite [protocol] cipher-spec DEFAULT (depends on +svdhE
    Cipher Suite available for negotiation in SSL handshake
    SSLCompression on|off off svE
    Enable compression on the SSL level
    SSLCryptoDevice engine builtin sE
    Enable use of a cryptographic hardware accelerator
    SSLEngine on|off off svE
    SSL Engine Operation Switch
    SSLFIPS on|off off sE
    SSL FIPS mode Switch
    SSLHonorCipherOrder on|off off svE
    Option to prefer the server's cipher preference order
    SSLInsecureRenegotiation on|off off svE
    Option to enable support for insecure renegotiation
    SSLOCSPDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|leaf|off [flags] off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPNoverify on|off off svE
    skip the OCSP responder certificates verification
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    SSLOCSPProxyURL urlsvE
    Proxy URL to use for OCSP requests
    SSLOCSPResponderCertificateFile filesvE
    Set of trusted PEM encoded OCSP responder certificates
    SSLOCSPResponderTimeout seconds 10 svE
    Timeout for OCSP queries
    SSLOCSPResponseMaxAge seconds -1 svE
    Maximum allowable age for OCSP responses
    SSLOCSPResponseTimeSkew seconds 300 svE
    Maximum allowable time skew for OCSP response validation
    SSLOCSPUseRequestNonce on|off on svE
    Use a nonce within OCSP queries
    SSLOpenSSLConfCmd command-name command-valuesvE
    Configure OpenSSL parameters through its SSL_CONF API
    SSLOptions [+|-]option ...svdhE
    Configure various SSL engine run-time options
    SSLPassPhraseDialog type builtin sE
    Type of pass phrase dialog for encrypted private +
    SSLCompression on|off off svE
    Enable compression on the SSL level
    SSLCryptoDevice engine builtin sE
    Enable use of a cryptographic hardware accelerator
    SSLEngine on|off off svE
    SSL Engine Operation Switch
    SSLFIPS on|off off sE
    SSL FIPS mode Switch
    SSLHonorCipherOrder on|off off svE
    Option to prefer the server's cipher preference order
    SSLInsecureRenegotiation on|off off svE
    Option to enable support for insecure renegotiation
    SSLOCSPDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|leaf|off [flags] off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPNoverify on|off off svE
    skip the OCSP responder certificates verification
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    SSLOCSPProxyURL urlsvE
    Proxy URL to use for OCSP requests
    SSLOCSPResponderCertificateFile filesvE
    Set of trusted PEM encoded OCSP responder certificates
    SSLOCSPResponderTimeout seconds 10 svE
    Timeout for OCSP queries
    SSLOCSPResponseMaxAge seconds -1 svE
    Maximum allowable age for OCSP responses
    SSLOCSPResponseTimeSkew seconds 300 svE
    Maximum allowable time skew for OCSP response validation
    SSLOCSPUseRequestNonce on|off on svE
    Use a nonce within OCSP queries
    SSLOpenSSLConfCmd command-name command-valuesvE
    Configure OpenSSL parameters through its SSL_CONF API
    SSLOptions [+|-]option ...svdhE
    Configure various SSL engine run-time options
    SSLPassPhraseDialog type builtin sE
    Type of pass phrase dialog for encrypted private keys
    SSLProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svE
    Configure usable SSL/TLS protocol versions
    SSLProxyCACertificateFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates +
    SSLProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svE
    Configure usable SSL/TLS protocol versions
    SSLProxyCACertificateFile file-pathsvE
    File of concatenated PEM-encoded CA Certificates for Remote Server Auth
    SSLProxyCACertificatePath directory-pathsvE
    Directory of PEM-encoded CA Certificates for +
    SSLProxyCACertificatePath directory-pathsvE
    Directory of PEM-encoded CA Certificates for Remote Server Auth
    SSLProxyCARevocationCheck chain|leaf|none none svE
    Enable CRL-based revocation checking for Remote Server Auth
    SSLProxyCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for +
    SSLProxyCARevocationCheck chain|leaf|none none svE
    Enable CRL-based revocation checking for Remote Server Auth
    SSLProxyCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for Remote Server Auth
    SSLProxyCARevocationPath directory-pathsvE
    Directory of PEM-encoded CA CRLs for +
    SSLProxyCARevocationPath directory-pathsvE
    Directory of PEM-encoded CA CRLs for Remote Server Auth
    SSLProxyCheckPeerCN on|off on svE
    Whether to check the remote server certificate's CN field +
    SSLProxyCheckPeerCN on|off on svE
    Whether to check the remote server certificate's CN field
    SSLProxyCheckPeerExpire on|off on svE
    Whether to check if remote server certificate is expired +
    SSLProxyCheckPeerExpire on|off on svE
    Whether to check if remote server certificate is expired
    SSLProxyCheckPeerName on|off on svE
    Configure host name checking for remote server certificates +
    SSLProxyCheckPeerName on|off on svE
    Configure host name checking for remote server certificates
    SSLProxyCipherSuite [protocol] cipher-spec ALL:!ADH:RC4+RSA:+H +svE
    Cipher Suite available for negotiation in SSL +
    SSLProxyCipherSuite [protocol] cipher-spec ALL:!ADH:RC4+RSA:+H +svE
    Cipher Suite available for negotiation in SSL proxy handshake
    SSLProxyEngine on|off off svE
    SSL Proxy Engine Operation Switch
    SSLProxyMachineCertificateChainFile filenamesvE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesvE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysvE
    Directory of PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svE
    Configure usable SSL protocol flavors for proxy usage
    SSLProxyVerify level none svE
    Type of remote server Certificate verification
    SSLProxyVerifyDepth number 1 svE
    Maximum depth of CA Certificates in Remote Server +
    SSLProxyEngine on|off off svE
    SSL Proxy Engine Operation Switch
    SSLProxyMachineCertificateChainFile filenamesvE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesvE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysvE
    Directory of PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svE
    Configure usable SSL protocol flavors for proxy usage
    SSLProxyVerify level none svE
    Type of remote server Certificate verification
    SSLProxyVerifyDepth number 1 svE
    Maximum depth of CA Certificates in Remote Server Certificate verification
    SSLRandomSeed context source -[bytes]sE
    Pseudo Random Number Generator (PRNG) seeding +
    SSLRandomSeed context source +[bytes]sE
    Pseudo Random Number Generator (PRNG) seeding source
    SSLRenegBufferSize bytes 131072 dhE
    Set the size for the SSL renegotiation buffer
    SSLRequire expressiondhE
    Allow access only when an arbitrarily complex +
    SSLRenegBufferSize bytes 131072 dhE
    Set the size for the SSL renegotiation buffer
    SSLRequire expressiondhE
    Allow access only when an arbitrarily complex boolean expression is true
    SSLRequireSSLdhE
    Deny access when SSL is not used for the +
    SSLRequireSSLdhE
    Deny access when SSL is not used for the HTTP request
    SSLSessionCache type none sE
    Type of the global/inter-process SSL Session +
    SSLSessionCache type none sE
    Type of the global/inter-process SSL Session Cache
    SSLSessionCacheTimeout seconds 300 svE
    Number of seconds before an SSL session expires +
    SSLSessionCacheTimeout seconds 300 svE
    Number of seconds before an SSL session expires in the Session Cache
    SSLSessionTicketKeyFile file-pathsvE
    Persistent encryption/decryption key for TLS session tickets
    SSLSessionTickets on|off on svE
    Enable or disable use of TLS session tickets
    SSLSRPUnknownUserSeed secret-stringsvE
    SRP unknown user seed
    SSLSRPVerifierFile file-pathsvE
    Path to SRP verifier file
    SSLStaplingCache typesE
    Configures the OCSP stapling cache
    SSLStaplingErrorCacheTimeout seconds 600 svE
    Number of seconds before expiring invalid responses in the OCSP stapling cache
    SSLStaplingFakeTryLater on|off on svE
    Synthesize "tryLater" responses for failed OCSP stapling queries
    SSLStaplingForceURL urisvE
    Override the OCSP responder URI specified in the certificate's AIA extension
    SSLStaplingResponderTimeout seconds 10 svE
    Timeout for OCSP stapling queries
    SSLStaplingResponseMaxAge seconds -1 svE
    Maximum allowable age for OCSP stapling responses
    SSLStaplingResponseTimeSkew seconds 300 svE
    Maximum allowable time skew for OCSP stapling response validation
    SSLStaplingReturnResponderErrors on|off on svE
    Pass stapling related OCSP errors on to client
    SSLStaplingStandardCacheTimeout seconds 3600 svE
    Number of seconds before expiring responses in the OCSP stapling cache
    SSLStrictSNIVHostCheck on|off off svE
    Whether to allow non-SNI clients to access a name-based virtual +
    SSLSessionTicketKeyFile file-pathsvE
    Persistent encryption/decryption key for TLS session tickets
    SSLSessionTickets on|off on svE
    Enable or disable use of TLS session tickets
    SSLSRPUnknownUserSeed secret-stringsvE
    SRP unknown user seed
    SSLSRPVerifierFile file-pathsvE
    Path to SRP verifier file
    SSLStaplingCache typesE
    Configures the OCSP stapling cache
    SSLStaplingErrorCacheTimeout seconds 600 svE
    Number of seconds before expiring invalid responses in the OCSP stapling cache
    SSLStaplingFakeTryLater on|off on svE
    Synthesize "tryLater" responses for failed OCSP stapling queries
    SSLStaplingForceURL urisvE
    Override the OCSP responder URI specified in the certificate's AIA extension
    SSLStaplingResponderTimeout seconds 10 svE
    Timeout for OCSP stapling queries
    SSLStaplingResponseMaxAge seconds -1 svE
    Maximum allowable age for OCSP stapling responses
    SSLStaplingResponseTimeSkew seconds 300 svE
    Maximum allowable time skew for OCSP stapling response validation
    SSLStaplingReturnResponderErrors on|off on svE
    Pass stapling related OCSP errors on to client
    SSLStaplingStandardCacheTimeout seconds 3600 svE
    Number of seconds before expiring responses in the OCSP stapling cache
    SSLStrictSNIVHostCheck on|off off svE
    Whether to allow non-SNI clients to access a name-based virtual host.
    SSLUserName varnamesdhE
    Variable name to determine user name
    SSLUseStapling on|off off svE
    Enable stapling of OCSP responses in the TLS handshake
    SSLVerifyClient level none svdhE
    Type of Client Certificate verification
    SSLVerifyDepth number 1 svdhE
    Maximum depth of CA Certificates in Client +
    SSLUserName varnamesdhE
    Variable name to determine user name
    SSLUseStapling on|off off svE
    Enable stapling of OCSP responses in the TLS handshake
    SSLVerifyClient level none svdhE
    Type of Client Certificate verification
    SSLVerifyDepth number 1 svdhE
    Maximum depth of CA Certificates in Client Certificate verification
    SSLVHostSNIPolicy strict|secure|authonly|insecure secure sE
    Set compatibility policy for SNI client access to virtual hosts.
    StartServers numbersM
    起動時�生��れる�サー�プロセス�数
    StartThreads numbersM
    起動時�生��れるスレッド�数
    StrictHostCheck ON|OFF OFF svC
    Controls whether the server requires the requested hostname be +
    SSLVHostSNIPolicy strict|secure|authonly|insecure secure sE
    Set compatibility policy for SNI client access to virtual hosts.
    StartServers numbersM
    起動時�生��れる�サー�プロセス�数
    StartThreads numbersM
    起動時�生��れるスレッド�数
    StrictHostCheck ON|OFF OFF svC
    Controls whether the server requires the requested hostname be listed enumerated in the virtual host handling the request
    Substitute s/pattern/substitution/[infq]dhE
    Pattern to filter the response content
    SubstituteInheritBefore on|off off dhE
    Change the merge order of inherited patterns
    SubstituteMaxLineLength bytes(b|B|k|K|m|M|g|G) 1m dhE
    Set the maximum line size
    Suexec On|OffsB
    Enable or disable the suEXEC feature
    SuexecUserGroup User GroupsvE
    CGI プログラム�ユーザパーミッション�グループパーミッション
    ThreadLimit numbersM
    設定�能��プロセス毎�スレッド数�上�を +
    Substitute s/pattern/substitution/[infq]dhE
    Pattern to filter the response content
    SubstituteInheritBefore on|off off dhE
    Change the merge order of inherited patterns
    SubstituteMaxLineLength bytes(b|B|k|K|m|M|g|G) 1m dhE
    Set the maximum line size
    Suexec On|OffsB
    Enable or disable the suEXEC feature
    SuexecUserGroup User GroupsvE
    CGI プログラム�ユーザパーミッション�グループパーミッション
    ThreadLimit numbersM
    設定�能��プロセス毎�スレッド数�上�を 設定���
    ThreadsPerChild numbersM
    �プロセス�れ�れ�生��れるスレッド数
    ThreadStackSize sizesM
    クライアント�コ�クションを����スレッド�使用�る +
    ThreadsPerChild numbersM
    �プロセス�れ�れ�生��れるスレッド数
    ThreadStackSize sizesM
    クライアント�コ�クションを����スレッド�使用�る スタック��イト数
    TimeOut seconds 60 svC
    �イベント�����リクエストを失敗��る���サー�� +
    TimeOut seconds 60 svC
    �イベント�����リクエストを失敗��る���サー�� 待�時間を設定
    TraceEnable [on|off|extended] on svC
    TRACE メソッド�リクエスト�対�る応答方法を決�る +
    TraceEnable [on|off|extended] on svC
    TRACE メソッド�リクエスト�対�る応答方法を決�る
    TransferLog file|pipesvB
    ログファイル��置を指定
    TypesConfig file-path conf/mime.types s
    mime.types ファイル��置
    UNCList hostname [hostname...]sC
    Controls what UNC host names can be accessed by the server -
    UnDefine parameter-namesC
    Undefine the existence of a variable
    UndefMacro namesvdB
    Undefine a macro
    UnsetEnv env-variable [env-variable] -...svdhB
    環境�ら変数を�り除�
    Use name [value1 ... valueN] -svdB
    Use a macro
    UseCanonicalName On|Off|Dns Off svdC
    サー��自分自身�����ートを決定�る方法を設定�る
    UseCanonicalPhysicalPort On|Off Off svdC
    自分自身�����ート番�を解決�る方法を設定�る +
    TransferLog file|pipesvB
    ログファイル��置を指定
    TypesConfig file-path conf/mime.types s
    mime.types ファイル��置
    UNCList hostname [hostname...]sC
    Controls what UNC host names can be accessed by the server +
    UnDefine parameter-namesC
    Undefine the existence of a variable
    UndefMacro namesvdB
    Undefine a macro
    UnsetEnv env-variable [env-variable] +...svdhB
    環境�ら変数を�り除�
    Use name [value1 ... valueN] +svdB
    Use a macro
    UseCanonicalName On|Off|Dns Off svdC
    サー��自分自身�����ートを決定�る方法を設定�る
    UseCanonicalPhysicalPort On|Off Off svdC
    自分自身�����ート番�を解決�る方法を設定�る
    User unix-userid #-1 sB
    The userid under which the server will answer +
    User unix-userid #-1 sB
    The userid under which the server will answer requests
    UserDir directory-filename [directory-filename] ...svB
    ユーザ専用ディレクトリ��置
    VHostCGIMode On|Off|Secure On vD
    Determines whether the virtualhost can run +
    UserDir directory-filename [directory-filename] ...svB
    ユーザ専用ディレクトリ��置
    VHostCGIMode On|Off|Secure On vD
    Determines whether the virtualhost can run subprocesses, and the privileges available to subprocesses.
    VHostCGIPrivs [+-]?privilege-name [[+-]?privilege-name] ...vD
    Assign arbitrary privileges to subprocesses created +
    VHostCGIPrivs [+-]?privilege-name [[+-]?privilege-name] ...vD
    Assign arbitrary privileges to subprocesses created by a virtual host.
    VHostGroup unix-groupidvD
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vD
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vD
    Determines whether the server runs with enhanced security +
    VHostGroup unix-groupidvD
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vD
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vD
    Determines whether the server runs with enhanced security for the virtualhost.
    VHostUser unix-useridvD
    Sets the User ID under which a virtual host runs.
    VirtualDocumentRoot interpolated-directory|none none svE
    Dynamically configure the location of the document root +
    VHostUser unix-useridvD
    Sets the User ID under which a virtual host runs.
    VirtualDocumentRoot interpolated-directory|none none svE
    Dynamically configure the location of the document root for a given virtual host
    VirtualDocumentRootIP interpolated-directory|none none svE
    Dynamically configure the location of the document root +
    VirtualDocumentRootIP interpolated-directory|none none svE
    Dynamically configure the location of the document root for a given virtual host
    <VirtualHost +
    <VirtualHost addr[:port] [addr[:port]] - ...> ... </VirtualHost>sC
    特定�ホスト�や IP アドレス����用�れるディレクティブを + ...> ... </VirtualHost>sC
    特定�ホスト�や IP アドレス����用�れるディレクティブを 囲む
    VirtualScriptAlias interpolated-directory|none none svE
    Dynamically configure the location of the CGI directory for +
    VirtualScriptAlias interpolated-directory|none none svE
    Dynamically configure the location of the CGI directory for a given virtual host
    VirtualScriptAliasIP interpolated-directory|none none svE
    Dynamically configure the location of the CGI directory for +
    VirtualScriptAliasIP interpolated-directory|none none svE
    Dynamically configure the location of the CGI directory for a given virtual host
    WatchdogInterval time-interval[s] 1 sB
    Watchdog interval in seconds
    XBitHack on|off|full off svdhB
    実行ビット�設定�れ�ファイル� SSI ディレクティブを +
    WatchdogInterval time-interval[s] 1 sB
    Watchdog interval in seconds
    XBitHack on|off|full off svdhB
    実行ビット�設定�れ�ファイル� SSI ディレクティブを 解��る
    xml2EncAlias charset alias [alias ...]sB
    Recognize Aliases for encoding values
    xml2EncDefault namesvdhB
    Sets a default encoding to assume when absolutely no information +
    xml2EncAlias charset alias [alias ...]sB
    Recognize Aliases for encoding values
    xml2EncDefault namesvdhB
    Sets a default encoding to assume when absolutely no information can be automatically detected
    xml2StartParse element [element ...]svdhB
    Advise the parser to skip leading junk.
    xml2StartParse element [element ...]svdhB
    Advise the parser to skip leading junk.

    翻訳済ã�¿è¨€èªž:  de  | diff -Nru apache2-2.4.69/docs/manual/mod/quickreference.html.ko.euc-kr apache2-2.4.71/docs/manual/mod/quickreference.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mod/quickreference.html.ko.euc-kr 2026-09-29 11:57:11.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/quickreference.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -34,8 +34,6 @@  tr  |  zh-cn 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    ÀÌ ¹®¼­´Â ¾ÆÆÄÄ¡ ¼³Á¤Áö½Ã¾î °¢°¢ÀÇ ¿ëµµ, ±âº»°ª, »óÅÂ, »ç¿ëÀå¼Ò¸¦ º¸¿©ÁØ´Ù. À̵éÀº Áö½Ã¾î »çÀü¿¡¼­ ¼³¸íÇÑ´Ù.

    @@ -685,7 +683,7 @@ MDCertificateAuthority url letsencrypt sXThe URL(s) of the ACME Certificate Authority to use. MDCertificateCheck name urlsXSet name and URL pattern for a certificate monitoring site. MDCertificateFile path-to-pem-filesXSpecify a static certificate file for the MD. -MDCertificateKeyFile path-to-filesXSpecify a static private key for for the static cerrtificate. +MDCertificateKeyFile path-to-filesXSpecify a static private key for the static certificate. MDCertificateMonitor name url crt.sh https://crt. +sXThe URL of a certificate log monitor. MDCertificateProtocol protocol ACME sXThe protocol to use with the Certificate Authority. MDCertificateStatus on|off on sXExposes public certificate information in JSON. @@ -696,489 +694,490 @@ MDDriveMode always|auto|manual auto sXformer name of MDRenewMode. MDExternalAccountBinding key-id hmac-64 | none | file none sXSet the external account binding keyid and hmac values to use at CA MDHttpProxy urlsXDefine a proxy for outgoing connections. -MDInitialDelay duration 0s sXHow long to delay the first certificate check. -MDMatchNames all|servernames all sXDetermines how DNS names are matched to vhosts -MDMember hostnamesXAdditional hostname for the managed domain. -MDMembers auto|manual auto sXControl if the alias domain names are automatically added. -MDMessageCmd path-to-cmd optional-argssXHandle events for Manage Domains -MDMustStaple on|off off sXControl if new certificates carry the OCSP Must Staple flag. -MDNotifyCmd path [ args ]sXRun a program when a Managed Domain is ready. -MDomain dns-name [ other-dns-name... ] [auto|manual]sXDefine list of domain names that belong to one group. -<MDomainSet dns-name [ other-dns-name... ]>...</MDomainSet>sXContainer for directives applied to the same managed domains. -MDPortMap map1 [ map2 ] http:80 https:443 sXMap external to internal ports for domain ownership verification. -MDPrivateKeys type [ params... ] RSA 2048 sXSet type and size of the private keys generated. -MDProfile namesXUse a specific ACME profile from the CA -MDProfileMandatory on|off off sXControl if an MDProfile is mandatory. -MDRenewMode always|auto|manual auto sXControls if certificates shall be renewed. -MDRenewViaARI on|off on sXusage of the ACME ARI extension (rfc9773). -MDRenewWindow duration 33% sXControl when a certificate will be renewed. -MDRequireHttps off|temporary|permanent off sXRedirects http: traffic to https: for Managed Domains. -MDRetryDelay duration 30s sXTime length for first retry, doubled on every consecutive error. -MDRetryFailover number 13 sXThe number of errors before a failover to another CA is triggered -MDServerStatus on|off off sXControl if Managed Domain information is added to server-status. -MDStapleOthers on|off on sXEnable stapling for certificates not managed by mod_md. -MDStapling on|off off sXEnable stapling for all or a particular MDomain. -MDStaplingKeepResponse duration 7d sXControls when old responses should be removed. -MDStaplingRenewWindow duration 33% sXControl when the stapling responses will be renewed. -MDStoreDir path md sXPath on the local file system to store the Managed Domains data. -MDStoreLocks on|off|duration off sXConfigure locking of store for updates -MDWarnWindow duration 10% sXDefine the time window when you want to be warned about an expiring certificate. -MemcacheConnTTL num[units] 15s svEKeepalive time for idle connections -MergeSlashes ON|OFF ON svCControls whether the server merges consecutive slashes in URLs. +MDHttpProxyCACertificateFile path-to-pem-file none sXSets the root (CA) certificates to use for TLS connections to the http-proxy. +MDInitialDelay duration 0s sXHow long to delay the first certificate check. +MDMatchNames all|servernames all sXDetermines how DNS names are matched to vhosts +MDMember hostnamesXAdditional hostname for the managed domain. +MDMembers auto|manual auto sXControl if the alias domain names are automatically added. +MDMessageCmd path-to-cmd optional-argssXHandle events for Manage Domains +MDMustStaple on|off off sXControl if new certificates carry the OCSP Must Staple flag. +MDNotifyCmd path [ args ]sXRun a program when a Managed Domain is ready. +MDomain dns-name [ other-dns-name... ] [auto|manual]sXDefine list of domain names that belong to one group. +<MDomainSet dns-name [ other-dns-name... ]>...</MDomainSet>sXContainer for directives applied to the same managed domains. +MDPortMap map1 [ map2 ] http:80 https:443 sXMap external to internal ports for domain ownership verification. +MDPrivateKeys type [ params... ] RSA 2048 sXSet type and size of the private keys generated. +MDProfile namesXUse a specific ACME profile from the CA +MDProfileMandatory on|off off sXControl if an MDProfile is mandatory. +MDRenewMode always|auto|manual auto sXControls if certificates shall be renewed. +MDRenewViaARI on|off on sXusage of the ACME ARI extension (rfc9773). +MDRenewWindow duration 33% sXControl when a certificate will be renewed. +MDRequireHttps off|temporary|permanent off sXRedirects http: traffic to https: for Managed Domains. +MDRetryDelay duration 30s sXTime length for first retry, doubled on every consecutive error. +MDRetryFailover number 13 sXThe number of errors before a failover to another CA is triggered +MDServerStatus on|off off sXControl if Managed Domain information is added to server-status. +MDStapleOthers on|off on sXEnable stapling for certificates not managed by mod_md. +MDStapling on|off off sXEnable stapling for all or a particular MDomain. +MDStaplingKeepResponse duration 7d sXControls when old responses should be removed. +MDStaplingRenewWindow duration 33% sXControl when the stapling responses will be renewed. +MDStoreDir path md sXPath on the local file system to store the Managed Domains data. +MDStoreLocks on|off|duration off sXConfigure locking of store for updates +MDWarnWindow duration 10% sXDefine the time window when you want to be warned about an expiring certificate. +MemcacheConnTTL num[units] 15s svEKeepalive time for idle connections +MergeSlashes ON|OFF ON svCControls whether the server merges consecutive slashes in URLs. -MergeTrailers [on|off] off svCDetermines whether trailers are merged into headers -MetaDir directory .web svdhECERN ¸ÞŸÁ¤º¸¸¦ ãÀ» µð·ºÅ丮 À̸§ -MetaFiles on|off off svdhECERN ¸ÞŸÆÄÀÏÀ» ó¸®ÇÑ´Ù -MetaSuffix suffix .meta svdhECERN ¸ÞŸÁ¤º¸¸¦ ÀúÀåÇÏ´Â ÆÄÀÏÀÇ Á¢¹Ì»ç -MimeMagicFile file-pathsvEEnable MIME-type determination based on file contents +MergeTrailers [on|off] off svCDetermines whether trailers are merged into headers +MetaDir directory .web svdhECERN ¸ÞŸÁ¤º¸¸¦ ãÀ» µð·ºÅ丮 À̸§ +MetaFiles on|off off svdhECERN ¸ÞŸÆÄÀÏÀ» ó¸®ÇÑ´Ù +MetaSuffix suffix .meta svdhECERN ¸ÞŸÁ¤º¸¸¦ ÀúÀåÇÏ´Â ÆÄÀÏÀÇ Á¢¹Ì»ç +MimeMagicFile file-pathsvEEnable MIME-type determination based on file contents using the specified magic file -MinSpareServers number 5 sMMinimum number of idle child server processes -MinSpareThreads numbersMMinimum number of idle threads available to handle request +MinSpareServers number 5 sMMinimum number of idle child server processes +MinSpareThreads numbersMMinimum number of idle threads available to handle request spikes -MMapFile file-path [file-path] ...sX½ÃÀ۽à ¿©·¯ ÆÄÀÏÀ» ¸Þ¸ð¸®¿¡ ´ëÀÀÇÑ´Ù -ModemStandard V.21|V.26bis|V.32|V.34|V.92dXModem standard to simulate -ModMimeUsePathInfo On|Off Off dBTells mod_mime to treat path_info +MMapFile file-path [file-path] ...sX½ÃÀ۽à ¿©·¯ ÆÄÀÏÀ» ¸Þ¸ð¸®¿¡ ´ëÀÀÇÑ´Ù +ModemStandard V.21|V.26bis|V.32|V.34|V.92dXModem standard to simulate +ModMimeUsePathInfo On|Off Off dBTells mod_mime to treat path_info components as part of the filename -MultiviewsMatch Any|NegotiatedOnly|Filters|Handlers -[Handlers|Filters] NegotiatedOnly svdhBThe types of files that will be included when searching for +MultiviewsMatch Any|NegotiatedOnly|Filters|Handlers +[Handlers|Filters] NegotiatedOnly svdhBThe types of files that will be included when searching for a matching file with MultiViews -Mutex mechanism [default|mutex-name] ... [OmitPID] default sCConfigures mutex mechanism and lock file directory for all +Mutex mechanism [default|mutex-name] ... [OmitPID] default sCConfigures mutex mechanism and lock file directory for all or specified mutexes -NameVirtualHost addr[:port]sCDEPRECATED: Designates an IP address for name-virtual +NameVirtualHost addr[:port]sCDEPRECATED: Designates an IP address for name-virtual hosting -NoProxy host [host] ...svEHosts, domains, or networks that will be connected to +NoProxy host [host] ...svEHosts, domains, or networks that will be connected to directly -NWSSLTrustedCerts filename [filename] ...sBList of additional client certificates -NWSSLUpgradeable [IP-address:]portnumbersBAllows a connection to be upgraded to an SSL connection upon request -Options - [+|-]option [[+|-]option] ... FollowSymlinks svdhCConfigures what features are available in a particular +NWSSLTrustedCerts filename [filename] ...sBList of additional client certificates +NWSSLUpgradeable [IP-address:]portnumbersBAllows a connection to be upgraded to an SSL connection upon request +Options + [+|-]option [[+|-]option] ... FollowSymlinks svdhCConfigures what features are available in a particular directory - Order ordering Deny,Allow dhDControls the default access state and the order in which + Order ordering Deny,Allow dhDControls the default access state and the order in which Allow and Deny are evaluated. -OutputSed sed-commanddhXSed command for filtering response content -PassEnv env-variable [env-variable] -...svdhB½©¿¡¼­ ȯ°æº¯¼ö¸¦ °¡Á®¿Â´Ù -PidFile filename logs/httpd.pid sMFile where the server records the process ID +OutputSed sed-commanddhXSed command for filtering response content +PassEnv env-variable [env-variable] +...svdhB½©¿¡¼­ ȯ°æº¯¼ö¸¦ °¡Á®¿Â´Ù +PidFile filename logs/httpd.pid sMFile where the server records the process ID of the daemon -PrivilegesMode FAST|SECURE|SELECTIVE FAST svdDTrade off processing speed and efficiency vs security against +PrivilegesMode FAST|SECURE|SELECTIVE FAST svdDTrade off processing speed and efficiency vs security against malicious privileges-aware code. -Protocol protocolsvCProtocol for a listening socket -ProtocolEcho On|OffsvXecho ¼­¹ö¸¦ Ű°í ²ö´Ù -Protocols protocol ... http/1.1 svCProtocols available for a server/virtual host -ProtocolsHonorOrder On|Off On svCDetermines if order of Protocols determines precedence during negotiation -<Proxy wildcard-url> ...</Proxy>svEContainer for directives applied to proxied resources -Proxy100Continue Off|On On svdEForward 100-continue expectation to the origin server -ProxyAddHeaders Off|On On svdEAdd proxy information in X-Forwarded-* headers -ProxyBadHeader IsError|Ignore|StartBody IsError svEDetermines how to handle bad header lines in a +Protocol protocolsvCProtocol for a listening socket +ProtocolEcho On|OffsvXecho ¼­¹ö¸¦ Ű°í ²ö´Ù +Protocols protocol ... http/1.1 svCProtocols available for a server/virtual host +ProtocolsHonorOrder On|Off On svCDetermines if order of Protocols determines precedence during negotiation +<Proxy wildcard-url> ...</Proxy>svEContainer for directives applied to proxied resources +Proxy100Continue Off|On On svdEForward 100-continue expectation to the origin server +ProxyAddHeaders Off|On On svdEAdd proxy information in X-Forwarded-* headers +ProxyBadHeader IsError|Ignore|StartBody IsError svEDetermines how to handle bad header lines in a response -ProxyBlock *|word|host|domain -[word|host|domain] ...svEWords, hosts, or domains that are banned from being +ProxyBlock *|word|host|domain +[word|host|domain] ...svEWords, hosts, or domains that are banned from being proxied -ProxyDomain DomainsvEDefault domain name for proxied requests -ProxyErrorOverride Off|On [code ...] Off svdEOverride error pages for proxied content -ProxyExpressDBMFile pathnamesvEPathname to DBM file. -ProxyExpressDBMType type default svEDBM type of file. -ProxyExpressEnable on|off off svEEnable the module functionality. -ProxyFCGIBackendType FPM|GENERIC FPM svdhESpecify the type of backend FastCGI application -ProxyFCGISetEnvIf conditional-expression +ProxyDomain DomainsvEDefault domain name for proxied requests +ProxyErrorOverride Off|On [code ...] Off svdEOverride error pages for proxied content +ProxyExpressDBMFile pathnamesvEPathname to DBM file. +ProxyExpressDBMType type default svEDBM type of file. +ProxyExpressEnable on|off off svEEnable the module functionality. +ProxyFCGIBackendType FPM|GENERIC FPM svdhESpecify the type of backend FastCGI application +ProxyFCGISetEnvIf conditional-expression [!]environment-variable-name - [value-expression]svdhEAllow variables sent to FastCGI servers to be fixed up -ProxyFtpDirCharset character_set ISO-8859-1 svdEDefine the character set for proxied FTP listings -ProxyFtpEscapeWildcards on|off on svdEWhether wildcards in requested filenames are escaped when sent to the FTP server -ProxyFtpListOnWildcard on|off on svdEWhether wildcards in requested filenames trigger a file listing -ProxyHCExpr name {ap_expr expression}svECreates a named condition expression to use to determine health of the backend based on its response -ProxyHCTemplate name parameter=setting [...]svECreates a named template for setting various health check parameters -ProxyHCTPsize size 16 sESets the total server-wide size of the threadpool used for the health check workers -ProxyHTMLBufSize bytes 8192 svdBSets the buffer size increment for buffering inline scripts and + [value-expression]svdhEAllow variables sent to FastCGI servers to be fixed up +ProxyFtpDirCharset character_set ISO-8859-1 svdEDefine the character set for proxied FTP listings +ProxyFtpEscapeWildcards on|off on svdEWhether wildcards in requested filenames are escaped when sent to the FTP server +ProxyFtpListOnWildcard on|off on svdEWhether wildcards in requested filenames trigger a file listing +ProxyHCExpr name {ap_expr expression}svECreates a named condition expression to use to determine health of the backend based on its response +ProxyHCTemplate name parameter=setting [...]svECreates a named template for setting various health check parameters +ProxyHCTPsize size 16 sESets the total server-wide size of the threadpool used for the health check workers +ProxyHTMLBufSize bytes 8192 svdBSets the buffer size increment for buffering inline scripts and stylesheets. -ProxyHTMLCharsetOut Charset | * UTF-8 svdBSpecify a charset for mod_proxy_html output. -ProxyHTMLDocType HTML|XHTML [Legacy]
    OR -
    ProxyHTMLDocType fpi [SGML|XML]
    svdBSets an HTML or XHTML document type declaration. -ProxyHTMLEnable On|Off Off svdBTurns the proxy_html filter on or off. -ProxyHTMLEvents attribute [attribute ...]svdBSpecify attributes to treat as scripting events. -ProxyHTMLExtended On|Off Off svdBDetermines whether to fix links in inline scripts, stylesheets, +ProxyHTMLCharsetOut Charset | * UTF-8 svdBSpecify a charset for mod_proxy_html output. +ProxyHTMLDocType HTML|XHTML [Legacy]
    OR +
    ProxyHTMLDocType fpi [SGML|XML]
    svdBSets an HTML or XHTML document type declaration. +ProxyHTMLEnable On|Off Off svdBTurns the proxy_html filter on or off. +ProxyHTMLEvents attribute [attribute ...]svdBSpecify attributes to treat as scripting events. +ProxyHTMLExtended On|Off Off svdBDetermines whether to fix links in inline scripts, stylesheets, and scripting events. -ProxyHTMLFixups [lowercase] [dospath] [reset] none svdBFixes for simple HTML errors. -ProxyHTMLInterp On|Off Off svdBEnables per-request interpolation of +ProxyHTMLFixups [lowercase] [dospath] [reset] none svdBFixes for simple HTML errors. +ProxyHTMLInterp On|Off Off svdBEnables per-request interpolation of ProxyHTMLURLMap rules. -ProxyHTMLLinks element attribute [attribute2 ...]svdBSpecify HTML elements that have URL attributes to be rewritten. -ProxyHTMLMeta On|Off Off svdBTurns on or off extra pre-parsing of metadata in HTML +ProxyHTMLLinks element attribute [attribute2 ...]svdBSpecify HTML elements that have URL attributes to be rewritten. +ProxyHTMLMeta On|Off Off svdBTurns on or off extra pre-parsing of metadata in HTML <head> sections. -ProxyHTMLStripComments On|Off Off svdBDetermines whether to strip HTML comments. -ProxyHTMLURLMap from-pattern to-pattern [flags] [cond]svdBDefines a rule to rewrite HTML links -ProxyIOBufferSize bytes 8192 svEDetermine size of internal data throughput buffer -<ProxyMatch regex> ...</ProxyMatch>svEContainer for directives applied to regular-expression-matched +ProxyHTMLStripComments On|Off Off svdBDetermines whether to strip HTML comments. +ProxyHTMLURLMap from-pattern to-pattern [flags] [cond]svdBDefines a rule to rewrite HTML links +ProxyIOBufferSize bytes 8192 svEDetermine size of internal data throughput buffer +<ProxyMatch regex> ...</ProxyMatch>svEContainer for directives applied to regular-expression-matched proxied resources -ProxyMaxForwards number -1 svEMaximum number of proxies that a request can be forwarded +ProxyMaxForwards number -1 svEMaximum number of proxies that a request can be forwarded through -ProxyPass [path] !|url [key=value - [key=value ...]] [nocanon] [interpolate] [noquery]svdEMaps remote servers into the local server URL-space -ProxyPassInherit On|Off On svEInherit ProxyPass directives defined from the main server -ProxyPassInterpolateEnv On|Off Off svdEEnable Environment Variable interpolation in Reverse Proxy configurations -ProxyPassMatch [regex] !|url [key=value - [key=value ...]]svdEMaps remote servers into the local server URL-space using regular expressions -ProxyPassReverse [path] url -[interpolate]svdEAdjusts the URL in HTTP response headers sent from a reverse +ProxyPass [path] !|url [key=value + [key=value ...]] [nocanon] [interpolate] [noquery]svdEMaps remote servers into the local server URL-space +ProxyPassInherit On|Off On svEInherit ProxyPass directives defined from the main server +ProxyPassInterpolateEnv On|Off Off svdEEnable Environment Variable interpolation in Reverse Proxy configurations +ProxyPassMatch [regex] !|url [key=value + [key=value ...]]svdEMaps remote servers into the local server URL-space using regular expressions +ProxyPassReverse [path] url +[interpolate]svdEAdjusts the URL in HTTP response headers sent from a reverse proxied server -ProxyPassReverseCookieDomain internal-domain -public-domain [interpolate]svdEAdjusts the Domain string in Set-Cookie headers from a reverse- +ProxyPassReverseCookieDomain internal-domain +public-domain [interpolate]svdEAdjusts the Domain string in Set-Cookie headers from a reverse- proxied server -ProxyPassReverseCookiePath internal-path -public-path [interpolate]svdEAdjusts the Path string in Set-Cookie headers from a reverse- +ProxyPassReverseCookiePath internal-path +public-path [interpolate]svdEAdjusts the Path string in Set-Cookie headers from a reverse- proxied server -ProxyPreserveHost On|Off Off svdEUse incoming Host HTTP request header for proxy +ProxyPreserveHost On|Off Off svdEUse incoming Host HTTP request header for proxy request -ProxyReceiveBufferSize bytes 0 svENetwork buffer size for proxied HTTP and FTP +ProxyReceiveBufferSize bytes 0 svENetwork buffer size for proxied HTTP and FTP connections -ProxyRemote match remote-server [username:password]svERemote proxy used to handle certain requests -ProxyRemoteMatch regex remote-server [username:password]svERemote proxy used to handle requests matched by regular +ProxyRemote match remote-server [username:password]svERemote proxy used to handle certain requests +ProxyRemoteMatch regex remote-server [username:password]svERemote proxy used to handle requests matched by regular expressions -ProxyRequests On|Off Off svEEnables forward (standard) proxy requests -ProxySCGIInternalRedirect On|Off|Headername On svdEEnable or disable internal redirect responses from the +ProxyRequests On|Off Off svEEnables forward (standard) proxy requests +ProxySCGIInternalRedirect On|Off|Headername On svdEEnable or disable internal redirect responses from the backend -ProxySCGISendfile On|Off|Headername Off svdEEnable evaluation of X-Sendfile pseudo response +ProxySCGISendfile On|Off|Headername Off svdEEnable evaluation of X-Sendfile pseudo response header -ProxySet url key=value [key=value ...]svdESet various Proxy balancer or member parameters -ProxySourceAddress addresssvESet local IP address for outgoing proxy connections -ProxyStatus Off|On|Full Off svEShow Proxy LoadBalancer status in mod_status -ProxyTimeout secondssvENetwork timeout for proxied requests -ProxyVia On|Off|Full|Block Off svEInformation provided in the Via HTTP response +ProxySet url key=value [key=value ...]svdESet various Proxy balancer or member parameters +ProxySourceAddress addresssvESet local IP address for outgoing proxy connections +ProxyStatus Off|On|Full Off svEShow Proxy LoadBalancer status in mod_status +ProxyTimeout secondssvENetwork timeout for proxied requests +ProxyVia On|Off|Full|Block Off svEInformation provided in the Via HTTP response header for proxied requests -ProxyWebsocketFallbackToProxyHttp On|Off On svDInstructs this module to let mod_proxy_http handle the request -QualifyRedirectURL On|Off Off svdCControls whether the REDIRECT_URL environment variable is +ProxyWebsocketFallbackToProxyHttp On|Off On svDInstructs this module to let mod_proxy_http handle the request +QualifyRedirectURL On|Off Off svdCControls whether the REDIRECT_URL environment variable is fully qualified -ReadBufferSize bytes 8192 svdCSize of the buffers used to read data -ReadmeName filenamesvdhBÆÄÀϸñ·Ï ¸¶Áö¸·¿¡ »ðÀÔÇÒ ÆÄÀÏÀÇ À̸§ -ReceiveBufferSize bytes 0 sMTCP receive buffer size -Redirect [status] URL-path -URLsvdhBŬ¶óÀÌ¾ðÆ®°¡ ´Ù¸¥ URL¿¡ Á¢¼ÓÇϵµ·Ï ¿äûÇÏ´Â ¿ÜºÎ +ReadBufferSize bytes 8192 svdCSize of the buffers used to read data +ReadmeName filenamesvdhBÆÄÀϸñ·Ï ¸¶Áö¸·¿¡ »ðÀÔÇÒ ÆÄÀÏÀÇ À̸§ +ReceiveBufferSize bytes 0 sMTCP receive buffer size +Redirect [status] URL-path +URLsvdhBŬ¶óÀÌ¾ðÆ®°¡ ´Ù¸¥ URL¿¡ Á¢¼ÓÇϵµ·Ï ¿äûÇÏ´Â ¿ÜºÎ ¸®´ÙÀÌ·º¼ÇÀ» º¸³½´Ù -RedirectMatch [status] regex -URLsvdhBÇöÀç URLÀÌ Á¤±ÔÇ¥Çö½Ä¿¡ ÇØ´çÇÏ¸é ¿ÜºÎ ¸®´ÙÀÌ·º¼ÇÀ» +RedirectMatch [status] regex +URLsvdhBÇöÀç URLÀÌ Á¤±ÔÇ¥Çö½Ä¿¡ ÇØ´çÇÏ¸é ¿ÜºÎ ¸®´ÙÀÌ·º¼ÇÀ» º¸³½´Ù -RedirectPermanent URL-path URLsvdhBŬ¶óÀÌ¾ðÆ®°¡ ´Ù¸¥ URL¿¡ Á¢¼ÓÇϵµ·Ï ¿äûÇÏ´Â ¿ÜºÎ +RedirectPermanent URL-path URLsvdhBŬ¶óÀÌ¾ðÆ®°¡ ´Ù¸¥ URL¿¡ Á¢¼ÓÇϵµ·Ï ¿äûÇÏ´Â ¿ÜºÎ ¿µ±¸ ¸®´ÙÀÌ·º¼ÇÀ» º¸³½´Ù -RedirectRelative On|Off Off svdBAllows relative redirect targets. -RedirectTemp URL-path URLsvdhBŬ¶óÀÌ¾ðÆ®°¡ ´Ù¸¥ URL¿¡ Á¢¼ÓÇϵµ·Ï ¿äûÇÏ´Â ¿ÜºÎ +RedirectRelative On|Off Off svdBAllows relative redirect targets. +RedirectTemp URL-path URLsvdhBŬ¶óÀÌ¾ðÆ®°¡ ´Ù¸¥ URL¿¡ Á¢¼ÓÇϵµ·Ï ¿äûÇÏ´Â ¿ÜºÎ Àӽà ¸®´ÙÀÌ·º¼ÇÀ» º¸³½´Ù -RedisConnPoolTTL num[units] 15s svETTL used for the connection pool with the Redis server(s) -RedisTimeout num[units] 5s svER/W timeout used for the connection with the Redis server(s) -ReflectorHeader inputheader [outputheader]svdhBReflect an input header to the output headers -RegexDefaultOptions [none] [+|-]option [[+|-]option] ... DOTALL DOLLAR_ENDON +sCAllow to configure global/default options for regexes -RegisterHttpMethod method [method [...]]sCRegister non-standard HTTP methods -RemoteIPHeader header-fieldsvBDeclare the header field which should be parsed for useragent IP addresses -RemoteIPInternalProxy proxy-ip|proxy-ip/subnet|hostname ...svBDeclare client intranet IP addresses trusted to present the RemoteIPHeader value -RemoteIPInternalProxyList filenamesvBDeclare client intranet IP addresses trusted to present the RemoteIPHeader value -RemoteIPProxiesHeader HeaderFieldNamesvBDeclare the header field which will record all intermediate IP addresses -RemoteIPProxyProtocol On|OffsvBEnable or disable PROXY protocol handling -RemoteIPProxyProtocolExceptions host|range [host|range] [host|range]svBDisable processing of PROXY header for certain hosts or networks -RemoteIPTrustedProxy proxy-ip|proxy-ip/subnet|hostname ...svBDeclare client intranet IP addresses trusted to present the RemoteIPHeader value -RemoteIPTrustedProxyList filenamesvBDeclare client intranet IP addresses trusted to present the RemoteIPHeader value -RemoveCharset extension [extension] -...vdhBRemoves any character set associations for a set of file +RedisConnPoolTTL num[units] 15s svETTL used for the connection pool with the Redis server(s) +RedisTimeout num[units] 5s svER/W timeout used for the connection with the Redis server(s) +ReflectorHeader inputheader [outputheader]svdhBReflect an input header to the output headers +RegexDefaultOptions [none] [+|-]option [[+|-]option] ... DOTALL DOLLAR_ENDON +sCAllow to configure global/default options for regexes +RegisterHttpMethod method [method [...]]sCRegister non-standard HTTP methods +RemoteIPHeader header-fieldsvBDeclare the header field which should be parsed for useragent IP addresses +RemoteIPInternalProxy proxy-ip|proxy-ip/subnet|hostname ...svBDeclare client intranet IP addresses trusted to present the RemoteIPHeader value +RemoteIPInternalProxyList filenamesvBDeclare client intranet IP addresses trusted to present the RemoteIPHeader value +RemoteIPProxiesHeader HeaderFieldNamesvBDeclare the header field which will record all intermediate IP addresses +RemoteIPProxyProtocol On|OffsvBEnable or disable PROXY protocol handling +RemoteIPProxyProtocolExceptions host|range [host|range] [host|range]svBDisable processing of PROXY header for certain hosts or networks +RemoteIPTrustedProxy proxy-ip|proxy-ip/subnet|hostname ...svBDeclare client intranet IP addresses trusted to present the RemoteIPHeader value +RemoteIPTrustedProxyList filenamesvBDeclare client intranet IP addresses trusted to present the RemoteIPHeader value +RemoveCharset extension [extension] +...vdhBRemoves any character set associations for a set of file extensions -RemoveEncoding extension [extension] -...vdhBRemoves any content encoding associations for a set of file +RemoveEncoding extension [extension] +...vdhBRemoves any content encoding associations for a set of file extensions -RemoveHandler extension [extension] -...vdhBRemoves any handler associations for a set of file +RemoveHandler extension [extension] +...vdhBRemoves any handler associations for a set of file extensions -RemoveInputFilter extension [extension] -...vdhBRemoves any input filter associations for a set of file +RemoveInputFilter extension [extension] +...vdhBRemoves any input filter associations for a set of file extensions -RemoveLanguage extension [extension] -...vdhBRemoves any language associations for a set of file +RemoveLanguage extension [extension] +...vdhBRemoves any language associations for a set of file extensions -RemoveOutputFilter extension [extension] -...vdhBRemoves any output filter associations for a set of file +RemoveOutputFilter extension [extension] +...vdhBRemoves any output filter associations for a set of file extensions -RemoveType extension [extension] -...vdhBRemoves any content type associations for a set of file +RemoveType extension [extension] +...vdhBRemoves any content type associations for a set of file extensions -RequestHeader set|append|add|unset header -[value] [early|env=[!]variable]svdhEHTTP ¿äû Çì´õ¸¦ ±¸¼ºÇÑ´Ù -RequestReadTimeout +RequestHeader set|append|add|unset header +[value] [early|env=[!]variable]svdhEHTTP ¿äû Çì´õ¸¦ ±¸¼ºÇÑ´Ù +RequestReadTimeout [handshake=timeout[-maxtimeout][,MinRate=rate] [header=timeout[-maxtimeout][,MinRate=rate] [body=timeout[-maxtimeout][,MinRate=rate] - handshake=0 header= +svESet timeout values for completing the TLS handshake, receiving + handshake=0 header= +svESet timeout values for completing the TLS handshake, receiving the request headers and/or body from client. -Require [not] entity-name - [entity-name] ...dhBTests whether an authenticated user is authorized by +Require [not] entity-name + [entity-name] ...dhBTests whether an authenticated user is authorized by an authorization provider. -<RequireAll> ... </RequireAll>dhBEnclose a group of authorization directives of which none +<RequireAll> ... </RequireAll>dhBEnclose a group of authorization directives of which none must fail and at least one must succeed for the enclosing directive to succeed. -<RequireAny> ... </RequireAny>dhBEnclose a group of authorization directives of which one +<RequireAny> ... </RequireAny>dhBEnclose a group of authorization directives of which one must succeed for the enclosing directive to succeed. -<RequireNone> ... </RequireNone>dhBEnclose a group of authorization directives of which none +<RequireNone> ... </RequireNone>dhBEnclose a group of authorization directives of which none must succeed for the enclosing directive to not fail. -RewriteBase URL-pathdhESets the base URL for per-directory rewrites - RewriteCond - TestString [!]CondPattern [flags]svdhEDefines a condition under which rewriting will take place +RewriteBase URL-pathdhESets the base URL for per-directory rewrites + RewriteCond + TestString [!]CondPattern [flags]svdhEDefines a condition under which rewriting will take place -RewriteEngine on|off off svdhEEnables or disables runtime rewriting engine -RewriteMap MapName MapType:MapSource +RewriteEngine on|off off svdhEEnables or disables runtime rewriting engine +RewriteMap MapName MapType:MapSource [MapTypeOptions] -svEDefines a mapping function for key-lookup -RewriteOptions OptionssvdhESets some special options for the rewrite engine -RewriteRule - [!]Pattern Substitution [flags]svdhEDefines rules for the rewriting engine -RLimitCPU seconds|max [seconds|max]svdhCLimits the CPU consumption of processes launched +svEDefines a mapping function for key-lookup +RewriteOptions OptionssvdhESets some special options for the rewrite engine +RewriteRule + [!]Pattern Substitution [flags]svdhEDefines rules for the rewriting engine +RLimitCPU seconds|max [seconds|max]svdhCLimits the CPU consumption of processes launched by Apache httpd children -RLimitMEM bytes|max [bytes|max]svdhCLimits the memory consumption of processes launched +RLimitMEM bytes|max [bytes|max]svdhCLimits the memory consumption of processes launched by Apache httpd children -RLimitNPROC number|max [number|max]svdhCLimits the number of processes that can be launched by +RLimitNPROC number|max [number|max]svdhCLimits the number of processes that can be launched by processes launched by Apache httpd children -Satisfy Any|All All dhDInteraction between host-level access control and +Satisfy Any|All All dhDInteraction between host-level access control and user authentication -ScoreBoardFile file-path logs/apache_runtime +sMLocation of the file used to store coordination data for +ScoreBoardFile file-path logs/apache_runtime +sMLocation of the file used to store coordination data for the child processes -Script method cgi-scriptsvdBƯÁ¤ ¿äû¸Þ¼­µå¿¡ ´ëÇØ CGI ½ºÅ©¸³Æ®¸¦ +Script method cgi-scriptsvdBƯÁ¤ ¿äû¸Þ¼­µå¿¡ ´ëÇØ CGI ½ºÅ©¸³Æ®¸¦ »ç¿ëÇÑ´Ù. -ScriptAlias URL-path -file-path|directory-pathsvdBURLÀ» ƯÁ¤ ÆÄÀϽýºÅÛ Àå¼Ò·Î ´ëÀÀÇÏ°í ´ë»óÀÌ CGI +ScriptAlias URL-path +file-path|directory-pathsvdBURLÀ» ƯÁ¤ ÆÄÀϽýºÅÛ Àå¼Ò·Î ´ëÀÀÇÏ°í ´ë»óÀÌ CGI ½ºÅ©¸³Æ®¶ó°í ¾Ë¸°´Ù -ScriptAliasMatch regex -file-path|directory-pathsvBÁ¤±ÔÇ¥Çö½ÄÀ» »ç¿ëÇÏ¿© URLÀ» ƯÁ¤ ÆÄÀϽýºÅÛ Àå¼Ò·Î +ScriptAliasMatch regex +file-path|directory-pathsvBÁ¤±ÔÇ¥Çö½ÄÀ» »ç¿ëÇÏ¿© URLÀ» ƯÁ¤ ÆÄÀϽýºÅÛ Àå¼Ò·Î ´ëÀÀÇÏ°í ´ë»óÀÌ CGI ½ºÅ©¸³Æ®¶ó°í ¾Ë¸°´Ù -ScriptInterpreterSource Registry|Registry-Strict|Script Script svdhCTechnique for locating the interpreter for CGI +ScriptInterpreterSource Registry|Registry-Strict|Script Script svdhCTechnique for locating the interpreter for CGI scripts -ScriptLog file-pathsvBCGI ½ºÅ©¸³Æ® ¿À·ù·Î±×ÆÄÀÏÀÇ À§Ä¡ -ScriptLogBuffer bytes 1024 svB½ºÅ©¸³Æ® ·Î±×¿¡ ±â·ÏÇÒ PUT ȤÀº POST ¿äûÀÇ ÃÖ´ë·® -ScriptLogLength bytes 10385760 svBCGI ½ºÅ©¸³Æ® ·Î±×ÆÄÀÏÀÇ Å©±â Á¦ÇÑ -ScriptSock file-path logs/cgisock sBcgi µ¥¸ó°ú Åë½ÅÀ» À§ÇØ »ç¿ëÇÒ ¼ÒÄÏÀÇ À̸§ -SecureListen [IP-address:]portnumber -Certificate-Name [MUTUAL]sBEnables SSL encryption for the specified port -SeeRequestTail On|Off Off sCDetermine if mod_status displays the first 63 characters +ScriptLog file-pathsvBCGI ½ºÅ©¸³Æ® ¿À·ù·Î±×ÆÄÀÏÀÇ À§Ä¡ +ScriptLogBuffer bytes 1024 svB½ºÅ©¸³Æ® ·Î±×¿¡ ±â·ÏÇÒ PUT ȤÀº POST ¿äûÀÇ ÃÖ´ë·® +ScriptLogLength bytes 10385760 svBCGI ½ºÅ©¸³Æ® ·Î±×ÆÄÀÏÀÇ Å©±â Á¦ÇÑ +ScriptSock file-path logs/cgisock sBcgi µ¥¸ó°ú Åë½ÅÀ» À§ÇØ »ç¿ëÇÒ ¼ÒÄÏÀÇ À̸§ +SecureListen [IP-address:]portnumber +Certificate-Name [MUTUAL]sBEnables SSL encryption for the specified port +SeeRequestTail On|Off Off sCDetermine if mod_status displays the first 63 characters of a request or the last 63, assuming the request itself is greater than 63 chars. -SendBufferSize bytes 0 sMTCP buffer size -ServerAdmin email-address|URLsvCEmail address that the server includes in error +SendBufferSize bytes 0 sMTCP buffer size +ServerAdmin email-address|URLsvCEmail address that the server includes in error messages sent to the client -ServerAlias hostname [hostname] ...vCAlternate names for a host used when matching requests +ServerAlias hostname [hostname] ...vCAlternate names for a host used when matching requests to name-virtual hosts -ServerLimit numbersMUpper limit on configurable number of processes -ServerName [scheme://]domain-name|ip-address[:port]svCHostname and port that the server uses to identify +ServerLimit numbersMUpper limit on configurable number of processes +ServerName [scheme://]domain-name|ip-address[:port]svCHostname and port that the server uses to identify itself -ServerPath URL-pathvCLegacy URL pathname for a name-based virtual host that +ServerPath URL-pathvCLegacy URL pathname for a name-based virtual host that is accessed by an incompatible browser -ServerRoot directory-path /usr/local/apache sCBase directory for the server installation -ServerSignature On|Off|EMail Off svdhCConfigures the footer on server-generated documents -ServerTokens Major|Minor|Min[imal]|Prod[uctOnly]|OS|Full Full sCConfigures the Server HTTP response +ServerRoot directory-path /usr/local/apache sCBase directory for the server installation +ServerSignature On|Off|EMail Off svdhCConfigures the footer on server-generated documents +ServerTokens Major|Minor|Min[imal]|Prod[uctOnly]|OS|Full Full sCConfigures the Server HTTP response header -Session On|Off Off svdhEEnables a session for the current directory or location -SessionCookieName name attributessvdhEName and attributes for the RFC2109 cookie storing the session -SessionCookieName2 name attributessvdhEName and attributes for the RFC2965 cookie storing the session -SessionCookieRemove On|Off Off svdhEControl for whether session cookies should be removed from incoming HTTP headers -SessionCryptoCipher name aes256 svdhXThe crypto cipher to be used to encrypt the session -SessionCryptoDriver name [param[=value]]sXThe crypto driver to be used to encrypt the session -SessionCryptoPassphrase secret [ secret ... ] svdhXThe key used to encrypt the session -SessionCryptoPassphraseFile filenamesvdXFile containing keys used to encrypt the session -SessionDBDCookieName name attributessvdhEName and attributes for the RFC2109 cookie storing the session ID -SessionDBDCookieName2 name attributessvdhEName and attributes for the RFC2965 cookie storing the session ID -SessionDBDCookieRemove On|Off On svdhEControl for whether session ID cookies should be removed from incoming HTTP headers -SessionDBDDeleteLabel label deletesession svdhEThe SQL query to use to remove sessions from the database -SessionDBDInsertLabel label insertsession svdhEThe SQL query to use to insert sessions into the database -SessionDBDPerUser On|Off Off svdhEEnable a per user session -SessionDBDSelectLabel label selectsession svdhEThe SQL query to use to select sessions from the database -SessionDBDUpdateLabel label updatesession svdhEThe SQL query to use to update existing sessions in the database -SessionEnv On|Off Off svdhEControl whether the contents of the session are written to the +Session On|Off Off svdhEEnables a session for the current directory or location +SessionCookieName name attributessvdhEName and attributes for the RFC2109 cookie storing the session +SessionCookieName2 name attributessvdhEName and attributes for the RFC2965 cookie storing the session +SessionCookieRemove On|Off Off svdhEControl for whether session cookies should be removed from incoming HTTP headers +SessionCryptoCipher name aes256 svdhXThe crypto cipher to be used to encrypt the session +SessionCryptoDriver name [param[=value]]sXThe crypto driver to be used to encrypt the session +SessionCryptoPassphrase secret [ secret ... ] svdhXThe key used to encrypt the session +SessionCryptoPassphraseFile filenamesvdXFile containing keys used to encrypt the session +SessionDBDCookieName name attributessvdhEName and attributes for the RFC2109 cookie storing the session ID +SessionDBDCookieName2 name attributessvdhEName and attributes for the RFC2965 cookie storing the session ID +SessionDBDCookieRemove On|Off On svdhEControl for whether session ID cookies should be removed from incoming HTTP headers +SessionDBDDeleteLabel label deletesession svdhEThe SQL query to use to remove sessions from the database +SessionDBDInsertLabel label insertsession svdhEThe SQL query to use to insert sessions into the database +SessionDBDPerUser On|Off Off svdhEEnable a per user session +SessionDBDSelectLabel label selectsession svdhEThe SQL query to use to select sessions from the database +SessionDBDUpdateLabel label updatesession svdhEThe SQL query to use to update existing sessions in the database +SessionEnv On|Off Off svdhEControl whether the contents of the session are written to the HTTP_SESSION environment variable -SessionExclude pathsvdhEDefine URL prefixes for which a session is ignored -SessionExpiryUpdateInterval interval 0 (always update) svdhEDefine the number of seconds a session's expiry may change without +SessionExclude pathsvdhEDefine URL prefixes for which a session is ignored +SessionExpiryUpdateInterval interval 0 (always update) svdhEDefine the number of seconds a session's expiry may change without the session being updated -SessionHeader headersvdhEImport session updates from a given HTTP response header -SessionInclude pathsvdhEDefine URL prefixes for which a session is valid -SessionMaxAge maxage 0 svdhEDefine a maximum age in seconds for a session -SetEnv env-variable valuesvdhBȯ°æº¯¼ö¸¦ ¼³Á¤ÇÑ´Ù -SetEnvIf attribute +SessionHeader headersvdhEImport session updates from a given HTTP response header +SessionInclude pathsvdhEDefine URL prefixes for which a session is valid +SessionMaxAge maxage 0 svdhEDefine a maximum age in seconds for a session +SetEnv env-variable valuesvdhBȯ°æº¯¼ö¸¦ ¼³Á¤ÇÑ´Ù +SetEnvIf attribute regex [!]env-variable[=value] - [[!]env-variable[=value]] ...svdhB¿äûÀÇ ¼ºÁú¿¡ µû¶ó ȯ°æº¯¼ö¸¦ ¼³Á¤ÇÑ´Ù -svdhBSets environment variables based on an ap_expr expression -SetEnvIfNoCase attribute regex + [[!]env-variable[=value]] ...svdhB¿äûÀÇ ¼ºÁú¿¡ µû¶ó ȯ°æº¯¼ö¸¦ ¼³Á¤ÇÑ´Ù +svdhBSets environment variables based on an ap_expr expression +SetEnvIfNoCase attribute regex [!]env-variable[=value] - [[!]env-variable[=value]] ...svdhB´ë¼Ò¹®ÀÚ¸¦ ±¸º°ÇÏÁö¾Ê°í ¿äûÀÇ ¼ºÁú¿¡ µû¶ó ȯ°æº¯¼ö¸¦ + [[!]env-variable[=value]] ...svdhB´ë¼Ò¹®ÀÚ¸¦ ±¸º°ÇÏÁö¾Ê°í ¿äûÀÇ ¼ºÁú¿¡ µû¶ó ȯ°æº¯¼ö¸¦ ¼³Á¤ÇÑ´Ù -SetHandler handler-name|none|expressionsvdhCForces all matching files to be processed by a +SetHandler handler-name|none|expressionsvdhCForces all matching files to be processed by a handler -SetInputFilter filter[;filter...]svdhCSets the filters that will process client requests and POST +SetInputFilter filter[;filter...]svdhCSets the filters that will process client requests and POST input -SetOutputFilter filter[;filter...]svdhCSets the filters that will process responses from the +SetOutputFilter filter[;filter...]svdhCSets the filters that will process responses from the server -SSIEndTag tag "-->" svBString that ends an include element -SSIErrorMsg message "[an error occurred +svdhBError message displayed when there is an SSI +SSIEndTag tag "-->" svBString that ends an include element +SSIErrorMsg message "[an error occurred +svdhBError message displayed when there is an SSI error -SSIETag on|off off dhBControls whether ETags are generated by the server. -SSILastModified on|off off dhBControls whether Last-Modified headers are generated by the +SSIETag on|off off dhBControls whether ETags are generated by the server. +SSILastModified on|off off dhBControls whether Last-Modified headers are generated by the server. -SSILegacyExprParser on|off off dhBEnable compatibility mode for conditional expressions. -SSIStartTag tag "<!--#" svBString that starts an include element -SSITimeFormat formatstring "%A, %d-%b-%Y %H:%M +svdhBConfigures the format in which date strings are +SSILegacyExprParser on|off off dhBEnable compatibility mode for conditional expressions. +SSIStartTag tag "<!--#" svBString that starts an include element +SSITimeFormat formatstring "%A, %d-%b-%Y %H:%M +svdhBConfigures the format in which date strings are displayed -SSIUndefinedEcho string "(none)" svdhBString displayed when an unset variable is echoed -SSLCACertificateFile file-pathsvEFile of concatenated PEM-encoded CA Certificates +SSIUndefinedEcho string "(none)" svdhBString displayed when an unset variable is echoed +SSLCACertificateFile file-pathsvEFile of concatenated PEM-encoded CA Certificates for Client Auth -SSLCACertificatePath directory-pathsvEDirectory of PEM-encoded CA Certificates for +SSLCACertificatePath directory-pathsvEDirectory of PEM-encoded CA Certificates for Client Auth -SSLCADNRequestFile file-pathsvEFile of concatenated PEM-encoded CA Certificates +SSLCADNRequestFile file-pathsvEFile of concatenated PEM-encoded CA Certificates for defining acceptable CA names -SSLCADNRequestPath directory-pathsvEDirectory of PEM-encoded CA Certificates for +SSLCADNRequestPath directory-pathsvEDirectory of PEM-encoded CA Certificates for defining acceptable CA names -SSLCARevocationCheck chain|leaf|none [flags ...] none svEEnable CRL-based revocation checking -SSLCARevocationFile file-pathsvEFile of concatenated PEM-encoded CA CRLs for +SSLCARevocationCheck chain|leaf|none [flags ...] none svEEnable CRL-based revocation checking +SSLCARevocationFile file-pathsvEFile of concatenated PEM-encoded CA CRLs for Client Auth -SSLCARevocationPath directory-pathsvEDirectory of PEM-encoded CA CRLs for +SSLCARevocationPath directory-pathsvEDirectory of PEM-encoded CA CRLs for Client Auth -SSLCertificateChainFile file-pathsvEFile of PEM-encoded Server CA Certificates -SSLCertificateFile file-path|certidsvEServer PEM-encoded X.509 certificate data file or token identifier -SSLCertificateKeyFile file-path|keyidsvEServer PEM-encoded private key file -SSLCipherSuite [protocol] cipher-spec DEFAULT (depends on +svdhECipher Suite available for negotiation in SSL +SSLCertificateChainFile file-pathsvEFile of PEM-encoded Server CA Certificates +SSLCertificateFile file-path|certidsvEServer PEM-encoded X.509 certificate data file or token identifier +SSLCertificateKeyFile file-path|keyidsvEServer PEM-encoded private key file +SSLCipherSuite [protocol] cipher-spec DEFAULT (depends on +svdhECipher Suite available for negotiation in SSL handshake -SSLCompression on|off off svEEnable compression on the SSL level -SSLCryptoDevice engine builtin sEEnable use of a cryptographic hardware accelerator -SSLEngine on|off off svESSL Engine Operation Switch -SSLFIPS on|off off sESSL FIPS mode Switch -SSLHonorCipherOrder on|off off svEOption to prefer the server's cipher preference order -SSLInsecureRenegotiation on|off off svEOption to enable support for insecure renegotiation -SSLOCSPDefaultResponder urisvESet the default responder URI for OCSP validation -SSLOCSPEnable on|leaf|off [flags] off svEEnable OCSP validation of the client certificate chain -SSLOCSPNoverify on|off off svEskip the OCSP responder certificates verification -SSLOCSPOverrideResponder on|off off svEForce use of the default responder URI for OCSP validation -SSLOCSPProxyURL urlsvEProxy URL to use for OCSP requests -SSLOCSPResponderCertificateFile filesvESet of trusted PEM encoded OCSP responder certificates -SSLOCSPResponderTimeout seconds 10 svETimeout for OCSP queries -SSLOCSPResponseMaxAge seconds -1 svEMaximum allowable age for OCSP responses -SSLOCSPResponseTimeSkew seconds 300 svEMaximum allowable time skew for OCSP response validation -SSLOCSPUseRequestNonce on|off on svEUse a nonce within OCSP queries -SSLOpenSSLConfCmd command-name command-valuesvEConfigure OpenSSL parameters through its SSL_CONF API -SSLOptions [+|-]option ...svdhEConfigure various SSL engine run-time options -SSLPassPhraseDialog type builtin sEType of pass phrase dialog for encrypted private +SSLCompression on|off off svEEnable compression on the SSL level +SSLCryptoDevice engine builtin sEEnable use of a cryptographic hardware accelerator +SSLEngine on|off off svESSL Engine Operation Switch +SSLFIPS on|off off sESSL FIPS mode Switch +SSLHonorCipherOrder on|off off svEOption to prefer the server's cipher preference order +SSLInsecureRenegotiation on|off off svEOption to enable support for insecure renegotiation +SSLOCSPDefaultResponder urisvESet the default responder URI for OCSP validation +SSLOCSPEnable on|leaf|off [flags] off svEEnable OCSP validation of the client certificate chain +SSLOCSPNoverify on|off off svEskip the OCSP responder certificates verification +SSLOCSPOverrideResponder on|off off svEForce use of the default responder URI for OCSP validation +SSLOCSPProxyURL urlsvEProxy URL to use for OCSP requests +SSLOCSPResponderCertificateFile filesvESet of trusted PEM encoded OCSP responder certificates +SSLOCSPResponderTimeout seconds 10 svETimeout for OCSP queries +SSLOCSPResponseMaxAge seconds -1 svEMaximum allowable age for OCSP responses +SSLOCSPResponseTimeSkew seconds 300 svEMaximum allowable time skew for OCSP response validation +SSLOCSPUseRequestNonce on|off on svEUse a nonce within OCSP queries +SSLOpenSSLConfCmd command-name command-valuesvEConfigure OpenSSL parameters through its SSL_CONF API +SSLOptions [+|-]option ...svdhEConfigure various SSL engine run-time options +SSLPassPhraseDialog type builtin sEType of pass phrase dialog for encrypted private keys -SSLProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svEConfigure usable SSL/TLS protocol versions -SSLProxyCACertificateFile file-pathsvEFile of concatenated PEM-encoded CA Certificates +SSLProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svEConfigure usable SSL/TLS protocol versions +SSLProxyCACertificateFile file-pathsvEFile of concatenated PEM-encoded CA Certificates for Remote Server Auth -SSLProxyCACertificatePath directory-pathsvEDirectory of PEM-encoded CA Certificates for +SSLProxyCACertificatePath directory-pathsvEDirectory of PEM-encoded CA Certificates for Remote Server Auth -SSLProxyCARevocationCheck chain|leaf|none none svEEnable CRL-based revocation checking for Remote Server Auth -SSLProxyCARevocationFile file-pathsvEFile of concatenated PEM-encoded CA CRLs for +SSLProxyCARevocationCheck chain|leaf|none none svEEnable CRL-based revocation checking for Remote Server Auth +SSLProxyCARevocationFile file-pathsvEFile of concatenated PEM-encoded CA CRLs for Remote Server Auth -SSLProxyCARevocationPath directory-pathsvEDirectory of PEM-encoded CA CRLs for +SSLProxyCARevocationPath directory-pathsvEDirectory of PEM-encoded CA CRLs for Remote Server Auth -SSLProxyCheckPeerCN on|off on svEWhether to check the remote server certificate's CN field +SSLProxyCheckPeerCN on|off on svEWhether to check the remote server certificate's CN field -SSLProxyCheckPeerExpire on|off on svEWhether to check if remote server certificate is expired +SSLProxyCheckPeerExpire on|off on svEWhether to check if remote server certificate is expired -SSLProxyCheckPeerName on|off on svEConfigure host name checking for remote server certificates +SSLProxyCheckPeerName on|off on svEConfigure host name checking for remote server certificates -SSLProxyCipherSuite [protocol] cipher-spec ALL:!ADH:RC4+RSA:+H +svECipher Suite available for negotiation in SSL +SSLProxyCipherSuite [protocol] cipher-spec ALL:!ADH:RC4+RSA:+H +svECipher Suite available for negotiation in SSL proxy handshake -SSLProxyEngine on|off off svESSL Proxy Engine Operation Switch -SSLProxyMachineCertificateChainFile filenamesvEFile of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate -SSLProxyMachineCertificateFile filenamesvEFile of concatenated PEM-encoded client certificates and keys to be used by the proxy -SSLProxyMachineCertificatePath directorysvEDirectory of PEM-encoded client certificates and keys to be used by the proxy -SSLProxyProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svEConfigure usable SSL protocol flavors for proxy usage -SSLProxyVerify level none svEType of remote server Certificate verification -SSLProxyVerifyDepth number 1 svEMaximum depth of CA Certificates in Remote Server +SSLProxyEngine on|off off svESSL Proxy Engine Operation Switch +SSLProxyMachineCertificateChainFile filenamesvEFile of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate +SSLProxyMachineCertificateFile filenamesvEFile of concatenated PEM-encoded client certificates and keys to be used by the proxy +SSLProxyMachineCertificatePath directorysvEDirectory of PEM-encoded client certificates and keys to be used by the proxy +SSLProxyProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svEConfigure usable SSL protocol flavors for proxy usage +SSLProxyVerify level none svEType of remote server Certificate verification +SSLProxyVerifyDepth number 1 svEMaximum depth of CA Certificates in Remote Server Certificate verification -SSLRandomSeed context source -[bytes]sEPseudo Random Number Generator (PRNG) seeding +SSLRandomSeed context source +[bytes]sEPseudo Random Number Generator (PRNG) seeding source -SSLRenegBufferSize bytes 131072 dhESet the size for the SSL renegotiation buffer -SSLRequire expressiondhEAllow access only when an arbitrarily complex +SSLRenegBufferSize bytes 131072 dhESet the size for the SSL renegotiation buffer +SSLRequire expressiondhEAllow access only when an arbitrarily complex boolean expression is true -SSLRequireSSLdhEDeny access when SSL is not used for the +SSLRequireSSLdhEDeny access when SSL is not used for the HTTP request -SSLSessionCache type none sEType of the global/inter-process SSL Session +SSLSessionCache type none sEType of the global/inter-process SSL Session Cache -SSLSessionCacheTimeout seconds 300 svENumber of seconds before an SSL session expires +SSLSessionCacheTimeout seconds 300 svENumber of seconds before an SSL session expires in the Session Cache -SSLSessionTicketKeyFile file-pathsvEPersistent encryption/decryption key for TLS session tickets -SSLSessionTickets on|off on svEEnable or disable use of TLS session tickets -SSLSRPUnknownUserSeed secret-stringsvESRP unknown user seed -SSLSRPVerifierFile file-pathsvEPath to SRP verifier file -SSLStaplingCache typesEConfigures the OCSP stapling cache -SSLStaplingErrorCacheTimeout seconds 600 svENumber of seconds before expiring invalid responses in the OCSP stapling cache -SSLStaplingFakeTryLater on|off on svESynthesize "tryLater" responses for failed OCSP stapling queries -SSLStaplingForceURL urisvEOverride the OCSP responder URI specified in the certificate's AIA extension -SSLStaplingResponderTimeout seconds 10 svETimeout for OCSP stapling queries -SSLStaplingResponseMaxAge seconds -1 svEMaximum allowable age for OCSP stapling responses -SSLStaplingResponseTimeSkew seconds 300 svEMaximum allowable time skew for OCSP stapling response validation -SSLStaplingReturnResponderErrors on|off on svEPass stapling related OCSP errors on to client -SSLStaplingStandardCacheTimeout seconds 3600 svENumber of seconds before expiring responses in the OCSP stapling cache -SSLStrictSNIVHostCheck on|off off svEWhether to allow non-SNI clients to access a name-based virtual +SSLSessionTicketKeyFile file-pathsvEPersistent encryption/decryption key for TLS session tickets +SSLSessionTickets on|off on svEEnable or disable use of TLS session tickets +SSLSRPUnknownUserSeed secret-stringsvESRP unknown user seed +SSLSRPVerifierFile file-pathsvEPath to SRP verifier file +SSLStaplingCache typesEConfigures the OCSP stapling cache +SSLStaplingErrorCacheTimeout seconds 600 svENumber of seconds before expiring invalid responses in the OCSP stapling cache +SSLStaplingFakeTryLater on|off on svESynthesize "tryLater" responses for failed OCSP stapling queries +SSLStaplingForceURL urisvEOverride the OCSP responder URI specified in the certificate's AIA extension +SSLStaplingResponderTimeout seconds 10 svETimeout for OCSP stapling queries +SSLStaplingResponseMaxAge seconds -1 svEMaximum allowable age for OCSP stapling responses +SSLStaplingResponseTimeSkew seconds 300 svEMaximum allowable time skew for OCSP stapling response validation +SSLStaplingReturnResponderErrors on|off on svEPass stapling related OCSP errors on to client +SSLStaplingStandardCacheTimeout seconds 3600 svENumber of seconds before expiring responses in the OCSP stapling cache +SSLStrictSNIVHostCheck on|off off svEWhether to allow non-SNI clients to access a name-based virtual host. -SSLUserName varnamesdhEVariable name to determine user name -SSLUseStapling on|off off svEEnable stapling of OCSP responses in the TLS handshake -SSLVerifyClient level none svdhEType of Client Certificate verification -SSLVerifyDepth number 1 svdhEMaximum depth of CA Certificates in Client +SSLUserName varnamesdhEVariable name to determine user name +SSLUseStapling on|off off svEEnable stapling of OCSP responses in the TLS handshake +SSLVerifyClient level none svdhEType of Client Certificate verification +SSLVerifyDepth number 1 svdhEMaximum depth of CA Certificates in Client Certificate verification -SSLVHostSNIPolicy strict|secure|authonly|insecure secure sESet compatibility policy for SNI client access to virtual hosts. -StartServers numbersMNumber of child server processes created at startup -StartThreads numbersMNumber of threads created on startup -StrictHostCheck ON|OFF OFF svCControls whether the server requires the requested hostname be +SSLVHostSNIPolicy strict|secure|authonly|insecure secure sESet compatibility policy for SNI client access to virtual hosts. +StartServers numbersMNumber of child server processes created at startup +StartThreads numbersMNumber of threads created on startup +StrictHostCheck ON|OFF OFF svCControls whether the server requires the requested hostname be listed enumerated in the virtual host handling the request -Substitute s/pattern/substitution/[infq]dhEPattern to filter the response content -SubstituteInheritBefore on|off off dhEChange the merge order of inherited patterns -SubstituteMaxLineLength bytes(b|B|k|K|m|M|g|G) 1m dhESet the maximum line size -Suexec On|OffsBEnable or disable the suEXEC feature -SuexecUserGroup User GroupsvECGI ÇÁ·Î±×·¥ÀÌ »ç¿ëÇÒ »ç¿ëÀÚ¿Í ±×·ì ±ÇÇÑ -ThreadLimit numbersMSets the upper limit on the configurable number of threads +Substitute s/pattern/substitution/[infq]dhEPattern to filter the response content +SubstituteInheritBefore on|off off dhEChange the merge order of inherited patterns +SubstituteMaxLineLength bytes(b|B|k|K|m|M|g|G) 1m dhESet the maximum line size +Suexec On|OffsBEnable or disable the suEXEC feature +SuexecUserGroup User GroupsvECGI ÇÁ·Î±×·¥ÀÌ »ç¿ëÇÒ »ç¿ëÀÚ¿Í ±×·ì ±ÇÇÑ +ThreadLimit numbersMSets the upper limit on the configurable number of threads per child process -ThreadsPerChild numbersMNumber of threads created by each child process -ThreadStackSize sizesMThe size in bytes of the stack used by threads handling +ThreadsPerChild numbersMNumber of threads created by each child process +ThreadStackSize sizesMThe size in bytes of the stack used by threads handling client connections -TimeOut seconds 60 svCAmount of time the server will wait for +TimeOut seconds 60 svCAmount of time the server will wait for certain events before failing a request -TraceEnable [on|off|extended] on svCDetermines the behavior on TRACE requests -TransferLog file|pipesvB·Î±×ÆÄÀÏ À§Ä¡¸¦ ¼³Á¤ÇÑ´Ù -TypesConfig file-path conf/mime.types sBThe location of the mime.types file -UNCList hostname [hostname...]sCControls what UNC host names can be accessed by the server +TraceEnable [on|off|extended] on svCDetermines the behavior on TRACE requests +TransferLog file|pipesvB·Î±×ÆÄÀÏ À§Ä¡¸¦ ¼³Á¤ÇÑ´Ù +TypesConfig file-path conf/mime.types sBThe location of the mime.types file +UNCList hostname [hostname...]sCControls what UNC host names can be accessed by the server -UnDefine parameter-namesCUndefine the existence of a variable -UndefMacro namesvdBUndefine a macro -UnsetEnv env-variable [env-variable] -...svdhBȯ°æº¯¼ö¸¦ Á¦°ÅÇÑ´Ù -Use name [value1 ... valueN] -svdBUse a macro -UseCanonicalName On|Off|DNS Off svdCConfigures how the server determines its own name and +UnDefine parameter-namesCUndefine the existence of a variable +UndefMacro namesvdBUndefine a macro +UnsetEnv env-variable [env-variable] +...svdhBȯ°æº¯¼ö¸¦ Á¦°ÅÇÑ´Ù +Use name [value1 ... valueN] +svdBUse a macro +UseCanonicalName On|Off|DNS Off svdCConfigures how the server determines its own name and port -UseCanonicalPhysicalPort On|Off Off svdCConfigures how the server determines its own port -User unix-userid #-1 sBThe userid under which the server will answer +UseCanonicalPhysicalPort On|Off Off svdCConfigures how the server determines its own port +User unix-userid #-1 sBThe userid under which the server will answer requests -UserDir directory-filename public_html svB»ç¿ëÀÚº° µð·ºÅ丮 À§Ä¡ -VHostCGIMode On|Off|Secure On vDDetermines whether the virtualhost can run +UserDir directory-filename public_html svB»ç¿ëÀÚº° µð·ºÅ丮 À§Ä¡ +VHostCGIMode On|Off|Secure On vDDetermines whether the virtualhost can run subprocesses, and the privileges available to subprocesses. -VHostCGIPrivs [+-]?privilege-name [[+-]?privilege-name] ...vDAssign arbitrary privileges to subprocesses created +VHostCGIPrivs [+-]?privilege-name [[+-]?privilege-name] ...vDAssign arbitrary privileges to subprocesses created by a virtual host. -VHostGroup unix-groupidvDSets the Group ID under which a virtual host runs. -VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vDAssign arbitrary privileges to a virtual host. -VHostSecure On|Off On vDDetermines whether the server runs with enhanced security +VHostGroup unix-groupidvDSets the Group ID under which a virtual host runs. +VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vDAssign arbitrary privileges to a virtual host. +VHostSecure On|Off On vDDetermines whether the server runs with enhanced security for the virtualhost. -VHostUser unix-useridvDSets the User ID under which a virtual host runs. -VirtualDocumentRoot interpolated-directory|none none svEDynamically configure the location of the document root +VHostUser unix-useridvDSets the User ID under which a virtual host runs. +VirtualDocumentRoot interpolated-directory|none none svEDynamically configure the location of the document root for a given virtual host -VirtualDocumentRootIP interpolated-directory|none none svEDynamically configure the location of the document root +VirtualDocumentRootIP interpolated-directory|none none svEDynamically configure the location of the document root for a given virtual host -<VirtualHost +<VirtualHost addr[:port] [addr[:port]] - ...> ... </VirtualHost>sCContains directives that apply only to a specific + ...> ... </VirtualHost>sCContains directives that apply only to a specific hostname or IP address -VirtualScriptAlias interpolated-directory|none none svEDynamically configure the location of the CGI directory for +VirtualScriptAlias interpolated-directory|none none svEDynamically configure the location of the CGI directory for a given virtual host -VirtualScriptAliasIP interpolated-directory|none none svEDynamically configure the location of the CGI directory for +VirtualScriptAliasIP interpolated-directory|none none svEDynamically configure the location of the CGI directory for a given virtual host -WatchdogInterval time-interval[s] 1 sBWatchdog interval in seconds -XBitHack on|off|full off svdhBParse SSI directives in files with the execute bit +WatchdogInterval time-interval[s] 1 sBWatchdog interval in seconds +XBitHack on|off|full off svdhBParse SSI directives in files with the execute bit set -xml2EncAlias charset alias [alias ...]sBRecognize Aliases for encoding values -xml2EncDefault namesvdhBSets a default encoding to assume when absolutely no information +xml2EncAlias charset alias [alias ...]sBRecognize Aliases for encoding values +xml2EncDefault namesvdhBSets a default encoding to assume when absolutely no information can be automatically detected -xml2StartParse element [element ...]svdhBAdvise the parser to skip leading junk. +xml2StartParse element [element ...]svdhBAdvise the parser to skip leading junk.

    °¡´ÉÇÑ ¾ð¾î:  de  | diff -Nru apache2-2.4.69/docs/manual/mod/quickreference.html.tr.utf8 apache2-2.4.71/docs/manual/mod/quickreference.html.tr.utf8 --- apache2-2.4.69/docs/manual/mod/quickreference.html.tr.utf8 2026-09-29 11:57:11.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/quickreference.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -713,7 +713,7 @@ MDCertificateAuthority url letsencrypt sDThe URL(s) of the ACME Certificate Authority to use. MDCertificateCheck name urlsDSet name and URL pattern for a certificate monitoring site. MDCertificateFile path-to-pem-filesDSpecify a static certificate file for the MD. -MDCertificateKeyFile path-to-filesDSpecify a static private key for for the static cerrtificate. +MDCertificateKeyFile path-to-filesDSpecify a static private key for the static certificate. MDCertificateMonitor name url crt.sh https://crt. +sDThe URL of a certificate log monitor. MDCertificateProtocol protocol ACME sDThe protocol to use with the Certificate Authority. MDCertificateStatus on|off on sDExposes public certificate information in JSON. @@ -724,500 +724,501 @@ MDDriveMode always|auto|manual auto sDformer name of MDRenewMode. MDExternalAccountBinding key-id hmac-64 | none | file none sDSet the external account binding keyid and hmac values to use at CA MDHttpProxy urlsDDefine a proxy for outgoing connections. -MDInitialDelay duration 0s sDHow long to delay the first certificate check. -MDMatchNames all|servernames all sDDetermines how DNS names are matched to vhosts -MDMember hostnamesDAdditional hostname for the managed domain. -MDMembers auto|manual auto sDControl if the alias domain names are automatically added. -MDMessageCmd path-to-cmd optional-argssDHandle events for Manage Domains -MDMustStaple on|off off sDControl if new certificates carry the OCSP Must Staple flag. -MDNotifyCmd path [ args ]sDRun a program when a Managed Domain is ready. -MDomain dns-name [ other-dns-name... ] [auto|manual]sDDefine list of domain names that belong to one group. -<MDomainSet dns-name [ other-dns-name... ]>...</MDomainSet>sDContainer for directives applied to the same managed domains. -MDPortMap map1 [ map2 ] http:80 https:443 sDMap external to internal ports for domain ownership verification. -MDPrivateKeys type [ params... ] RSA 2048 sDSet type and size of the private keys generated. -MDProfile namesDUse a specific ACME profile from the CA -MDProfileMandatory on|off off sDControl if an MDProfile is mandatory. -MDRenewMode always|auto|manual auto sDControls if certificates shall be renewed. -MDRenewViaARI on|off on sDusage of the ACME ARI extension (rfc9773). -MDRenewWindow duration 33% sDControl when a certificate will be renewed. -MDRequireHttps off|temporary|permanent off sDRedirects http: traffic to https: for Managed Domains. -MDRetryDelay duration 30s sDTime length for first retry, doubled on every consecutive error. -MDRetryFailover number 13 sDThe number of errors before a failover to another CA is triggered -MDServerStatus on|off off sDControl if Managed Domain information is added to server-status. -MDStapleOthers on|off on sDEnable stapling for certificates not managed by mod_md. -MDStapling on|off off sDEnable stapling for all or a particular MDomain. -MDStaplingKeepResponse duration 7d sDControls when old responses should be removed. -MDStaplingRenewWindow duration 33% sDControl when the stapling responses will be renewed. -MDStoreDir path md sDPath on the local file system to store the Managed Domains data. -MDStoreLocks on|off|duration off sDConfigure locking of store for updates -MDWarnWindow duration 10% sDDefine the time window when you want to be warned about an expiring certificate. -MemcacheConnTTL num[units] 15s skEKeepalive time for idle connections -MergeSlashes ON|OFF ON skÇSunucunun URL'lerde ardışık bölü çizgilerini birleÅŸtirip birleÅŸtirmeyeceÄŸini denetler. +MDHttpProxyCACertificateFile path-to-pem-file none sDSets the root (CA) certificates to use for TLS connections to the http-proxy. +MDInitialDelay duration 0s sDHow long to delay the first certificate check. +MDMatchNames all|servernames all sDDetermines how DNS names are matched to vhosts +MDMember hostnamesDAdditional hostname for the managed domain. +MDMembers auto|manual auto sDControl if the alias domain names are automatically added. +MDMessageCmd path-to-cmd optional-argssDHandle events for Manage Domains +MDMustStaple on|off off sDControl if new certificates carry the OCSP Must Staple flag. +MDNotifyCmd path [ args ]sDRun a program when a Managed Domain is ready. +MDomain dns-name [ other-dns-name... ] [auto|manual]sDDefine list of domain names that belong to one group. +<MDomainSet dns-name [ other-dns-name... ]>...</MDomainSet>sDContainer for directives applied to the same managed domains. +MDPortMap map1 [ map2 ] http:80 https:443 sDMap external to internal ports for domain ownership verification. +MDPrivateKeys type [ params... ] RSA 2048 sDSet type and size of the private keys generated. +MDProfile namesDUse a specific ACME profile from the CA +MDProfileMandatory on|off off sDControl if an MDProfile is mandatory. +MDRenewMode always|auto|manual auto sDControls if certificates shall be renewed. +MDRenewViaARI on|off on sDusage of the ACME ARI extension (rfc9773). +MDRenewWindow duration 33% sDControl when a certificate will be renewed. +MDRequireHttps off|temporary|permanent off sDRedirects http: traffic to https: for Managed Domains. +MDRetryDelay duration 30s sDTime length for first retry, doubled on every consecutive error. +MDRetryFailover number 13 sDThe number of errors before a failover to another CA is triggered +MDServerStatus on|off off sDControl if Managed Domain information is added to server-status. +MDStapleOthers on|off on sDEnable stapling for certificates not managed by mod_md. +MDStapling on|off off sDEnable stapling for all or a particular MDomain. +MDStaplingKeepResponse duration 7d sDControls when old responses should be removed. +MDStaplingRenewWindow duration 33% sDControl when the stapling responses will be renewed. +MDStoreDir path md sDPath on the local file system to store the Managed Domains data. +MDStoreLocks on|off|duration off sDConfigure locking of store for updates +MDWarnWindow duration 10% sDDefine the time window when you want to be warned about an expiring certificate. +MemcacheConnTTL num[units] 15s skEKeepalive time for idle connections +MergeSlashes ON|OFF ON skÇSunucunun URL'lerde ardışık bölü çizgilerini birleÅŸtirip birleÅŸtirmeyeceÄŸini denetler. -MergeTrailers [on|off] off skÇTrailer alanlarının baÅŸlığa dahil edilip edilmeyeceÄŸini belirler -MetaDir directory .web skdhKName of the directory to find CERN-style meta information +MergeTrailers [on|off] off skÇTrailer alanlarının baÅŸlığa dahil edilip edilmeyeceÄŸini belirler +MetaDir directory .web skdhKName of the directory to find CERN-style meta information files -MetaFiles on|off off skdhKActivates CERN meta-file processing -MetaSuffix suffix .meta skdhKFile name suffix for the file containing CERN-style +MetaFiles on|off off skdhKActivates CERN meta-file processing +MetaSuffix suffix .meta skdhKFile name suffix for the file containing CERN-style meta information -MimeMagicFile file-pathskEEnable MIME-type determination based on file contents +MimeMagicFile file-pathskEEnable MIME-type determination based on file contents using the specified magic file -MinSpareServers sayı 5 sMBoÅŸtaki çocuk süreçlerin asgari sayısı -MinSpareThreads sayısMİsteklerin ani artışında devreye girecek boÅŸtaki evrelerin asgari +MinSpareServers sayı 5 sMBoÅŸtaki çocuk süreçlerin asgari sayısı +MinSpareThreads sayısMİsteklerin ani artışında devreye girecek boÅŸtaki evrelerin asgari sayısını belirler. -MMapFile file-path [file-path] ...sDMap a list of files into memory at startup time -ModemStandard V.21|V.26bis|V.32|V.34|V.92dDModem standard to simulate -ModMimeUsePathInfo On|Off Off dTTells mod_mime to treat path_info +MMapFile file-path [file-path] ...sDMap a list of files into memory at startup time +ModemStandard V.21|V.26bis|V.32|V.34|V.92dDModem standard to simulate +ModMimeUsePathInfo On|Off Off dTTells mod_mime to treat path_info components as part of the filename -MultiviewsMatch Any|NegotiatedOnly|Filters|Handlers -[Handlers|Filters] NegotiatedOnly skdhTThe types of files that will be included when searching for +MultiviewsMatch Any|NegotiatedOnly|Filters|Handlers +[Handlers|Filters] NegotiatedOnly skdhTThe types of files that will be included when searching for a matching file with MultiViews -Mutex mekanizma [default|muteks-ismi] ... [OmitPID] default sÇMuteks mekanizmasını ve kilit dosyası dizinini tüm muteksler veya belirtilenler için yapılandırır -NameVirtualHost adres[:port]sÇÖNERİLMİYOR: İsme dayalı sanal konaklar için IP adresi belirtir -NoProxy host [host] ...skEHosts, domains, or networks that will be connected to +Mutex mekanizma [default|muteks-ismi] ... [OmitPID] default sÇMuteks mekanizmasını ve kilit dosyası dizinini tüm muteksler veya belirtilenler için yapılandırır +NameVirtualHost adres[:port]sÇÖNERİLMİYOR: İsme dayalı sanal konaklar için IP adresi belirtir +NoProxy host [host] ...skEHosts, domains, or networks that will be connected to directly -NWSSLTrustedCerts filename [filename] ...sTList of additional client certificates -NWSSLUpgradeable [IP-address:]portnumbersTAllows a connection to be upgraded to an SSL connection upon request -Options - [+|-]seçenek [[+|-]seçenek] ... FollowSymlinks skdhÇBelli bir dizinde geçerli olacak özellikleri yapılandırır. +NWSSLTrustedCerts filename [filename] ...sTList of additional client certificates +NWSSLUpgradeable [IP-address:]portnumbersTAllows a connection to be upgraded to an SSL connection upon request +Options + [+|-]seçenek [[+|-]seçenek] ... FollowSymlinks skdhÇBelli bir dizinde geçerli olacak özellikleri yapılandırır. - Order ordering Deny,Allow dhKControls the default access state and the order in which + Order ordering Deny,Allow dhKControls the default access state and the order in which Allow and Deny are evaluated. -OutputSed sed-commanddhDSed command for filtering response content -PassEnv ortam-deÄŸiÅŸkeni [ortam-deÄŸiÅŸkeni] -...skdhTOrtam deÄŸiÅŸkenlerini kabuktan aktarır. -PidFile dosya logs/httpd.pid sMAna sürecin süreç kimliÄŸinin (PID) kaydedileceÄŸi dosyayı belirler. -PrivilegesMode FAST|SECURE|SELECTIVE FAST skdKTrade off processing speed and efficiency vs security against +OutputSed sed-commanddhDSed command for filtering response content +PassEnv ortam-deÄŸiÅŸkeni [ortam-deÄŸiÅŸkeni] +...skdhTOrtam deÄŸiÅŸkenlerini kabuktan aktarır. +PidFile dosya logs/httpd.pid sMAna sürecin süreç kimliÄŸinin (PID) kaydedileceÄŸi dosyayı belirler. +PrivilegesMode FAST|SECURE|SELECTIVE FAST skdKTrade off processing speed and efficiency vs security against malicious privileges-aware code. -Protocol protokolskÇDinlenen bir soket için protokol -ProtocolEcho On|Off Off skDTurn the echo server on or off -Protocols protokol ... http/1.1 skÇSunucu/sanal konak için kullanılabilecek protokoller -ProtocolsHonorOrder On|Off On skÇUzlaÅŸma sırasında protokollerin öncelik sırasını belirler -<Proxy wildcard-url> ...</Proxy>skEContainer for directives applied to proxied resources -Proxy100Continue Off|On On skdEForward 100-continue expectation to the origin server -ProxyAddHeaders Off|On On skdEAdd proxy information in X-Forwarded-* headers -ProxyBadHeader IsError|Ignore|StartBody IsError skEDetermines how to handle bad header lines in a +Protocol protokolskÇDinlenen bir soket için protokol +ProtocolEcho On|Off Off skDTurn the echo server on or off +Protocols protokol ... http/1.1 skÇSunucu/sanal konak için kullanılabilecek protokoller +ProtocolsHonorOrder On|Off On skÇUzlaÅŸma sırasında protokollerin öncelik sırasını belirler +<Proxy wildcard-url> ...</Proxy>skEContainer for directives applied to proxied resources +Proxy100Continue Off|On On skdEForward 100-continue expectation to the origin server +ProxyAddHeaders Off|On On skdEAdd proxy information in X-Forwarded-* headers +ProxyBadHeader IsError|Ignore|StartBody IsError skEDetermines how to handle bad header lines in a response -ProxyBlock *|word|host|domain -[word|host|domain] ...skEWords, hosts, or domains that are banned from being +ProxyBlock *|word|host|domain +[word|host|domain] ...skEWords, hosts, or domains that are banned from being proxied -ProxyDomain DomainskEDefault domain name for proxied requests -ProxyErrorOverride Off|On [code ...] Off skdEOverride error pages for proxied content -ProxyExpressDBMFile pathnameskEPathname to DBM file. -ProxyExpressDBMType type default skEDBM type of file. -ProxyExpressEnable on|off off skEEnable the module functionality. -ProxyFCGIBackendType FPM|GENERIC FPM skdhESpecify the type of backend FastCGI application -ProxyFCGISetEnvIf conditional-expression +ProxyDomain DomainskEDefault domain name for proxied requests +ProxyErrorOverride Off|On [code ...] Off skdEOverride error pages for proxied content +ProxyExpressDBMFile pathnameskEPathname to DBM file. +ProxyExpressDBMType type default skEDBM type of file. +ProxyExpressEnable on|off off skEEnable the module functionality. +ProxyFCGIBackendType FPM|GENERIC FPM skdhESpecify the type of backend FastCGI application +ProxyFCGISetEnvIf conditional-expression [!]environment-variable-name - [value-expression]skdhEAllow variables sent to FastCGI servers to be fixed up -ProxyFtpDirCharset character_set ISO-8859-1 skdEDefine the character set for proxied FTP listings -ProxyFtpEscapeWildcards on|off on skdEWhether wildcards in requested filenames are escaped when sent to the FTP server -ProxyFtpListOnWildcard on|off on skdEWhether wildcards in requested filenames trigger a file listing -ProxyHCExpr name {ap_expr expression}skECreates a named condition expression to use to determine health of the backend based on its response -ProxyHCTemplate name parameter=setting [...]skECreates a named template for setting various health check parameters -ProxyHCTPsize size 16 sESets the total server-wide size of the threadpool used for the health check workers -ProxyHTMLBufSize bytes 8192 skdTSets the buffer size increment for buffering inline scripts and + [value-expression]skdhEAllow variables sent to FastCGI servers to be fixed up +ProxyFtpDirCharset character_set ISO-8859-1 skdEDefine the character set for proxied FTP listings +ProxyFtpEscapeWildcards on|off on skdEWhether wildcards in requested filenames are escaped when sent to the FTP server +ProxyFtpListOnWildcard on|off on skdEWhether wildcards in requested filenames trigger a file listing +ProxyHCExpr name {ap_expr expression}skECreates a named condition expression to use to determine health of the backend based on its response +ProxyHCTemplate name parameter=setting [...]skECreates a named template for setting various health check parameters +ProxyHCTPsize size 16 sESets the total server-wide size of the threadpool used for the health check workers +ProxyHTMLBufSize bytes 8192 skdTSets the buffer size increment for buffering inline scripts and stylesheets. -ProxyHTMLCharsetOut Charset | * UTF-8 skdTSpecify a charset for mod_proxy_html output. -ProxyHTMLDocType HTML|XHTML [Legacy]
    OR -
    ProxyHTMLDocType fpi [SGML|XML]
    skdTSets an HTML or XHTML document type declaration. -ProxyHTMLEnable On|Off Off skdTTurns the proxy_html filter on or off. -ProxyHTMLEvents attribute [attribute ...]skdTSpecify attributes to treat as scripting events. -ProxyHTMLExtended On|Off Off skdTDetermines whether to fix links in inline scripts, stylesheets, +ProxyHTMLCharsetOut Charset | * UTF-8 skdTSpecify a charset for mod_proxy_html output. +ProxyHTMLDocType HTML|XHTML [Legacy]
    OR +
    ProxyHTMLDocType fpi [SGML|XML]
    skdTSets an HTML or XHTML document type declaration. +ProxyHTMLEnable On|Off Off skdTTurns the proxy_html filter on or off. +ProxyHTMLEvents attribute [attribute ...]skdTSpecify attributes to treat as scripting events. +ProxyHTMLExtended On|Off Off skdTDetermines whether to fix links in inline scripts, stylesheets, and scripting events. -ProxyHTMLFixups [lowercase] [dospath] [reset] none skdTFixes for simple HTML errors. -ProxyHTMLInterp On|Off Off skdTEnables per-request interpolation of +ProxyHTMLFixups [lowercase] [dospath] [reset] none skdTFixes for simple HTML errors. +ProxyHTMLInterp On|Off Off skdTEnables per-request interpolation of ProxyHTMLURLMap rules. -ProxyHTMLLinks element attribute [attribute2 ...]skdTSpecify HTML elements that have URL attributes to be rewritten. -ProxyHTMLMeta On|Off Off skdTTurns on or off extra pre-parsing of metadata in HTML +ProxyHTMLLinks element attribute [attribute2 ...]skdTSpecify HTML elements that have URL attributes to be rewritten. +ProxyHTMLMeta On|Off Off skdTTurns on or off extra pre-parsing of metadata in HTML <head> sections. -ProxyHTMLStripComments On|Off Off skdTDetermines whether to strip HTML comments. -ProxyHTMLURLMap from-pattern to-pattern [flags] [cond]skdTDefines a rule to rewrite HTML links -ProxyIOBufferSize bytes 8192 skEDetermine size of internal data throughput buffer -<ProxyMatch regex> ...</ProxyMatch>skEContainer for directives applied to regular-expression-matched +ProxyHTMLStripComments On|Off Off skdTDetermines whether to strip HTML comments. +ProxyHTMLURLMap from-pattern to-pattern [flags] [cond]skdTDefines a rule to rewrite HTML links +ProxyIOBufferSize bytes 8192 skEDetermine size of internal data throughput buffer +<ProxyMatch regex> ...</ProxyMatch>skEContainer for directives applied to regular-expression-matched proxied resources -ProxyMaxForwards number -1 skEMaximum number of proxies that a request can be forwarded +ProxyMaxForwards number -1 skEMaximum number of proxies that a request can be forwarded through -ProxyPass [path] !|url [key=value - [key=value ...]] [nocanon] [interpolate] [noquery]skdEMaps remote servers into the local server URL-space -ProxyPassInherit On|Off On skEInherit ProxyPass directives defined from the main server -ProxyPassInterpolateEnv On|Off Off skdEEnable Environment Variable interpolation in Reverse Proxy configurations -ProxyPassMatch [regex] !|url [key=value - [key=value ...]]skdEMaps remote servers into the local server URL-space using regular expressions -ProxyPassReverse [path] url -[interpolate]skdEAdjusts the URL in HTTP response headers sent from a reverse +ProxyPass [path] !|url [key=value + [key=value ...]] [nocanon] [interpolate] [noquery]skdEMaps remote servers into the local server URL-space +ProxyPassInherit On|Off On skEInherit ProxyPass directives defined from the main server +ProxyPassInterpolateEnv On|Off Off skdEEnable Environment Variable interpolation in Reverse Proxy configurations +ProxyPassMatch [regex] !|url [key=value + [key=value ...]]skdEMaps remote servers into the local server URL-space using regular expressions +ProxyPassReverse [path] url +[interpolate]skdEAdjusts the URL in HTTP response headers sent from a reverse proxied server -ProxyPassReverseCookieDomain internal-domain -public-domain [interpolate]skdEAdjusts the Domain string in Set-Cookie headers from a reverse- +ProxyPassReverseCookieDomain internal-domain +public-domain [interpolate]skdEAdjusts the Domain string in Set-Cookie headers from a reverse- proxied server -ProxyPassReverseCookiePath internal-path -public-path [interpolate]skdEAdjusts the Path string in Set-Cookie headers from a reverse- +ProxyPassReverseCookiePath internal-path +public-path [interpolate]skdEAdjusts the Path string in Set-Cookie headers from a reverse- proxied server -ProxyPreserveHost On|Off Off skdEUse incoming Host HTTP request header for proxy +ProxyPreserveHost On|Off Off skdEUse incoming Host HTTP request header for proxy request -ProxyReceiveBufferSize bytes 0 skENetwork buffer size for proxied HTTP and FTP +ProxyReceiveBufferSize bytes 0 skENetwork buffer size for proxied HTTP and FTP connections -ProxyRemote match remote-server [username:password]skERemote proxy used to handle certain requests -ProxyRemoteMatch regex remote-server [username:password]skERemote proxy used to handle requests matched by regular +ProxyRemote match remote-server [username:password]skERemote proxy used to handle certain requests +ProxyRemoteMatch regex remote-server [username:password]skERemote proxy used to handle requests matched by regular expressions -ProxyRequests On|Off Off skEEnables forward (standard) proxy requests -ProxySCGIInternalRedirect On|Off|Headername On skdEEnable or disable internal redirect responses from the +ProxyRequests On|Off Off skEEnables forward (standard) proxy requests +ProxySCGIInternalRedirect On|Off|Headername On skdEEnable or disable internal redirect responses from the backend -ProxySCGISendfile On|Off|Headername Off skdEEnable evaluation of X-Sendfile pseudo response +ProxySCGISendfile On|Off|Headername Off skdEEnable evaluation of X-Sendfile pseudo response header -ProxySet url key=value [key=value ...]skdESet various Proxy balancer or member parameters -ProxySourceAddress addressskESet local IP address for outgoing proxy connections -ProxyStatus Off|On|Full Off skEShow Proxy LoadBalancer status in mod_status -ProxyTimeout secondsskENetwork timeout for proxied requests -ProxyVia On|Off|Full|Block Off skEInformation provided in the Via HTTP response +ProxySet url key=value [key=value ...]skdESet various Proxy balancer or member parameters +ProxySourceAddress addressskESet local IP address for outgoing proxy connections +ProxyStatus Off|On|Full Off skEShow Proxy LoadBalancer status in mod_status +ProxyTimeout secondsskENetwork timeout for proxied requests +ProxyVia On|Off|Full|Block Off skEInformation provided in the Via HTTP response header for proxied requests -ProxyWebsocketFallbackToProxyHttp On|Off On skKInstructs this module to let mod_proxy_http handle the request -QualifyRedirectURL On|Off Off skdÇREDIRECT_URL ortam deÄŸiÅŸkeninin tamamen nitelenmiÅŸ olup +ProxyWebsocketFallbackToProxyHttp On|Off On skKInstructs this module to let mod_proxy_http handle the request +QualifyRedirectURL On|Off Off skdÇREDIRECT_URL ortam deÄŸiÅŸkeninin tamamen nitelenmiÅŸ olup olmayacağını denetler -ReadBufferSize bayt-sayısı 8192 skdÇVeriyi okumakta kullanılacak tampon sayısı -ReadmeName dosya-ismiskdhTDizin listesinin sonuna yerleÅŸtirilecek dosyanın ismini +ReadBufferSize bayt-sayısı 8192 skdÇVeriyi okumakta kullanılacak tampon sayısı +ReadmeName dosya-ismiskdhTDizin listesinin sonuna yerleÅŸtirilecek dosyanın ismini belirler. -ReceiveBufferSize bayt-sayısı 0 sMTCP alım tamponu boyu -Redirect [durum] [URL-yolu] -URLskdhTİstemciyi, bir yönlendirme isteÄŸi döndürerek farklı bir URL'ye +ReceiveBufferSize bayt-sayısı 0 sMTCP alım tamponu boyu +Redirect [durum] [URL-yolu] +URLskdhTİstemciyi, bir yönlendirme isteÄŸi döndürerek farklı bir URL'ye yönlendirir. -RedirectMatch [durum] düzenli-ifade -URLskdhTGeçerli URL ile eÅŸleÅŸen bir düzenli ifadeye dayanarak bir harici +RedirectMatch [durum] düzenli-ifade +URLskdhTGeçerli URL ile eÅŸleÅŸen bir düzenli ifadeye dayanarak bir harici yönlendirme gönderir. -RedirectPermanent URL-yolu URLskdhTİstemciyi, kalıcı bir yönlendirme isteÄŸi döndürerek farklı bir +RedirectPermanent URL-yolu URLskdhTİstemciyi, kalıcı bir yönlendirme isteÄŸi döndürerek farklı bir URL'ye yönlendirir. -RedirectRelative On|Off Off skdTAllows relative redirect targets. -RedirectTemp URL-yolu URLskdhTİstemciyi, geçici bir yönlendirme isteÄŸi döndürerek farklı bir +RedirectRelative On|Off Off skdTAllows relative redirect targets. +RedirectTemp URL-yolu URLskdhTİstemciyi, geçici bir yönlendirme isteÄŸi döndürerek farklı bir URL'ye yönlendirir. -RedisConnPoolTTL num[units] 15s skETTL used for the connection pool with the Redis server(s) -RedisTimeout num[units] 5s skER/W timeout used for the connection with the Redis server(s) -ReflectorHeader inputheader [outputheader]skdhTReflect an input header to the output headers -RegexDefaultOptions [none] [+|-]seçenek [[+|-]seçenek] ... DOTALL DOLLAR_ENDON +sÇRegex düzenli ifadeleri için öntanımlı/küresel seçenekleri yapılandırır -RegisterHttpMethod yöntem [yöntem [...]]sÇStandart olmayan HTTP yöntemlerini devreye alır -RemoteIPHeader header-fieldskTDeclare the header field which should be parsed for useragent IP addresses -RemoteIPInternalProxy proxy-ip|proxy-ip/subnet|hostname ...skTDeclare client intranet IP addresses trusted to present the RemoteIPHeader value -RemoteIPInternalProxyList filenameskTDeclare client intranet IP addresses trusted to present the RemoteIPHeader value -RemoteIPProxiesHeader HeaderFieldNameskTDeclare the header field which will record all intermediate IP addresses -RemoteIPProxyProtocol On|OffskTEnable or disable PROXY protocol handling -RemoteIPProxyProtocolExceptions host|range [host|range] [host|range]skTDisable processing of PROXY header for certain hosts or networks -RemoteIPTrustedProxy proxy-ip|proxy-ip/subnet|hostname ...skTDeclare client intranet IP addresses trusted to present the RemoteIPHeader value -RemoteIPTrustedProxyList filenameskTDeclare client intranet IP addresses trusted to present the RemoteIPHeader value -RemoveCharset extension [extension] -...kdhTRemoves any character set associations for a set of file +RedisConnPoolTTL num[units] 15s skETTL used for the connection pool with the Redis server(s) +RedisTimeout num[units] 5s skER/W timeout used for the connection with the Redis server(s) +ReflectorHeader inputheader [outputheader]skdhTReflect an input header to the output headers +RegexDefaultOptions [none] [+|-]seçenek [[+|-]seçenek] ... DOTALL DOLLAR_ENDON +sÇRegex düzenli ifadeleri için öntanımlı/küresel seçenekleri yapılandırır +RegisterHttpMethod yöntem [yöntem [...]]sÇStandart olmayan HTTP yöntemlerini devreye alır +RemoteIPHeader header-fieldskTDeclare the header field which should be parsed for useragent IP addresses +RemoteIPInternalProxy proxy-ip|proxy-ip/subnet|hostname ...skTDeclare client intranet IP addresses trusted to present the RemoteIPHeader value +RemoteIPInternalProxyList filenameskTDeclare client intranet IP addresses trusted to present the RemoteIPHeader value +RemoteIPProxiesHeader HeaderFieldNameskTDeclare the header field which will record all intermediate IP addresses +RemoteIPProxyProtocol On|OffskTEnable or disable PROXY protocol handling +RemoteIPProxyProtocolExceptions host|range [host|range] [host|range]skTDisable processing of PROXY header for certain hosts or networks +RemoteIPTrustedProxy proxy-ip|proxy-ip/subnet|hostname ...skTDeclare client intranet IP addresses trusted to present the RemoteIPHeader value +RemoteIPTrustedProxyList filenameskTDeclare client intranet IP addresses trusted to present the RemoteIPHeader value +RemoveCharset extension [extension] +...kdhTRemoves any character set associations for a set of file extensions -RemoveEncoding extension [extension] -...kdhTRemoves any content encoding associations for a set of file +RemoveEncoding extension [extension] +...kdhTRemoves any content encoding associations for a set of file extensions -RemoveHandler extension [extension] -...kdhTRemoves any handler associations for a set of file +RemoveHandler extension [extension] +...kdhTRemoves any handler associations for a set of file extensions -RemoveInputFilter extension [extension] -...kdhTRemoves any input filter associations for a set of file +RemoveInputFilter extension [extension] +...kdhTRemoves any input filter associations for a set of file extensions -RemoveLanguage extension [extension] -...kdhTRemoves any language associations for a set of file +RemoveLanguage extension [extension] +...kdhTRemoves any language associations for a set of file extensions -RemoveOutputFilter extension [extension] -...kdhTRemoves any output filter associations for a set of file +RemoveOutputFilter extension [extension] +...kdhTRemoves any output filter associations for a set of file extensions -RemoveType extension [extension] -...kdhTRemoves any content type associations for a set of file +RemoveType extension [extension] +...kdhTRemoves any content type associations for a set of file extensions -RequestHeader add|append|edit|edit*|merge|set|setifempty|unset +RequestHeader add|append|edit|edit*|merge|set|setifempty|unset header [[expr=]value [replacement] [early|env=[!]varname|expr=expression]] -skdhEConfigure HTTP request headers -RequestReadTimeout +skdhEConfigure HTTP request headers +RequestReadTimeout [handshake=timeout[-maxtimeout][,MinRate=rate] [header=timeout[-maxtimeout][,MinRate=rate] [body=timeout[-maxtimeout][,MinRate=rate] - handshake=0 header= +skESet timeout values for completing the TLS handshake, receiving + handshake=0 header= +skESet timeout values for completing the TLS handshake, receiving the request headers and/or body from client. -Require [not] entity-name - [entity-name] ...dhTTests whether an authenticated user is authorized by +Require [not] entity-name + [entity-name] ...dhTTests whether an authenticated user is authorized by an authorization provider. -<RequireAll> ... </RequireAll>dhTEnclose a group of authorization directives of which none +<RequireAll> ... </RequireAll>dhTEnclose a group of authorization directives of which none must fail and at least one must succeed for the enclosing directive to succeed. -<RequireAny> ... </RequireAny>dhTEnclose a group of authorization directives of which one +<RequireAny> ... </RequireAny>dhTEnclose a group of authorization directives of which one must succeed for the enclosing directive to succeed. -<RequireNone> ... </RequireNone>dhTEnclose a group of authorization directives of which none +<RequireNone> ... </RequireNone>dhTEnclose a group of authorization directives of which none must succeed for the enclosing directive to not fail. -RewriteBase URL-pathdhESets the base URL for per-directory rewrites - RewriteCond - TestString [!]CondPattern [flags]skdhEDefines a condition under which rewriting will take place +RewriteBase URL-pathdhESets the base URL for per-directory rewrites + RewriteCond + TestString [!]CondPattern [flags]skdhEDefines a condition under which rewriting will take place -RewriteEngine on|off off skdhEEnables or disables runtime rewriting engine -RewriteMap MapName MapType:MapSource +RewriteEngine on|off off skdhEEnables or disables runtime rewriting engine +RewriteMap MapName MapType:MapSource [MapTypeOptions] -skEDefines a mapping function for key-lookup -RewriteOptions OptionsskdhESets some special options for the rewrite engine -RewriteRule - [!]Pattern Substitution [flags]skdhEDefines rules for the rewriting engine -RLimitCPU saniye|max [saniye|max]skdhÇApache httpd alt süreçleri tarafından çalıştırılan süreçlerin +skEDefines a mapping function for key-lookup +RewriteOptions OptionsskdhESets some special options for the rewrite engine +RewriteRule + [!]Pattern Substitution [flags]skdhEDefines rules for the rewriting engine +RLimitCPU saniye|max [saniye|max]skdhÇApache httpd alt süreçleri tarafından çalıştırılan süreçlerin iÅŸlemci tüketimine sınırlama getirir. -RLimitMEM bayt-sayısı|max [bayt-sayısı|max] -skdhÇApache httpd alt süreçleri tarafından çalıştırılan süreçlerin +RLimitMEM bayt-sayısı|max [bayt-sayısı|max] +skdhÇApache httpd alt süreçleri tarafından çalıştırılan süreçlerin bellek tüketimine sınırlama getirir. -RLimitNPROC sayı|max [sayı|max]skdhÇApache httpd alt süreçleri tarafından çalıştırılabilecek süreç +RLimitNPROC sayı|max [sayı|max]skdhÇApache httpd alt süreçleri tarafından çalıştırılabilecek süreç sayısına sınırlama getirir. -Satisfy Any|All All dhKInteraction between host-level access control and +Satisfy Any|All All dhKInteraction between host-level access control and user authentication -ScoreBoardFile dosya-yolu logs/apache_runtime +sMÇocuk süreçler için eÅŸgüdüm verisini saklamakta kullanılan +ScoreBoardFile dosya-yolu logs/apache_runtime +sMÇocuk süreçler için eÅŸgüdüm verisini saklamakta kullanılan dosyanın yerini belirler. -Script method cgi-scriptskdTActivates a CGI script for a particular request +Script method cgi-scriptskdTActivates a CGI script for a particular request method. -ScriptAlias [URL-yolu] -dosya-yolu|dizin-yoluskdTBir URL'yi dosya sistemindeki bir yere eÅŸler ve hedefi bir CGI betiÄŸi olarak çalıştırır. -ScriptAliasMatch düzenli-ifade -dosya-yolu|dizin-yoluskTBir URL'yi dosya sistemindeki bir yere düzenli ifade kullanarak +ScriptAlias [URL-yolu] +dosya-yolu|dizin-yoluskdTBir URL'yi dosya sistemindeki bir yere eÅŸler ve hedefi bir CGI betiÄŸi olarak çalıştırır. +ScriptAliasMatch düzenli-ifade +dosya-yolu|dizin-yoluskTBir URL'yi dosya sistemindeki bir yere düzenli ifade kullanarak eÅŸler ve hedefi bir CGI betiÄŸi olarak çalıştırır. -ScriptInterpreterSource Registry|Registry-Strict|Script Script skdhÇCGI betikleri için yorumlayıcı belirleme tekniÄŸi -ScriptLog file-pathskTLocation of the CGI script error logfile -ScriptLogBuffer bytes 1024 skTMaximum amount of PUT or POST requests that will be recorded +ScriptInterpreterSource Registry|Registry-Strict|Script Script skdhÇCGI betikleri için yorumlayıcı belirleme tekniÄŸi +ScriptLog file-pathskTLocation of the CGI script error logfile +ScriptLogBuffer bytes 1024 skTMaximum amount of PUT or POST requests that will be recorded in the scriptlog -ScriptLogLength bytes 10385760 skTSize limit of the CGI script logfile -ScriptSock file-path cgisock sTThe filename prefix of the socket to use for communication with +ScriptLogLength bytes 10385760 skTSize limit of the CGI script logfile +ScriptSock file-path cgisock sTThe filename prefix of the socket to use for communication with the cgi daemon -SecureListen [IP-address:]portnumber -Certificate-Name [MUTUAL]sTEnables SSL encryption for the specified port -SeeRequestTail On|Off Off sÇİsteÄŸin 63 karakterden büyük olduÄŸu varsayımıyla, mod_status'un +SecureListen [IP-address:]portnumber +Certificate-Name [MUTUAL]sTEnables SSL encryption for the specified port +SeeRequestTail On|Off Off sÇİsteÄŸin 63 karakterden büyük olduÄŸu varsayımıyla, mod_status'un ilk 63 karakteri mi yoksa son 63 karakteri mi göstereceÄŸini belirler. -SendBufferSize bayt-sayısı 0 sMTCP tamponu boyu -ServerAdmin eposta-adresi|URLskÇSunucunun hata iletilerinde istemciye göstereceÄŸi eposta adresi +SendBufferSize bayt-sayısı 0 sMTCP tamponu boyu +ServerAdmin eposta-adresi|URLskÇSunucunun hata iletilerinde istemciye göstereceÄŸi eposta adresi -ServerAlias konakadı [konakadı] ...kÇİstekleri isme dayalı sanal konaklarla eÅŸleÅŸtirilirken +ServerAlias konakadı [konakadı] ...kÇİstekleri isme dayalı sanal konaklarla eÅŸleÅŸtirilirken kullanılacak konak adları için baÅŸka isimler belirtebilmeyi saÄŸlar. -ServerLimit sayısMAyarlanabilir süreç sayısının üst sınırını belirler. -ServerName [ÅŸema://]alan-adı|ip-adresi[:port] -skÇSunucunun özdeÅŸleÅŸeceÄŸi konak ismi ve port. -ServerPath URL-yolukÇUyumsuz bir tarayıcı tarafından eriÅŸilmesi için bir isme dayalı sanal konak için meÅŸru URL yolu -ServerRoot dizin-yolu /usr/local/apache sÇSunucu yapılandırması için kök dizin -ServerSignature On|Off|EMail Off skdhÇSunucu tarafından üretilen belgelerin dipnotunu ayarlar. - -ServerTokens Major|Minor|Min[imal]|Prod[uctOnly]|OS|Full Full sÇServer HTTP yanıt baÅŸlığını yapılandırır. - -Session On|Off Off skdhEEnables a session for the current directory or location -SessionCookieName name attributesskdhEName and attributes for the RFC2109 cookie storing the session -SessionCookieName2 name attributesskdhEName and attributes for the RFC2965 cookie storing the session -SessionCookieRemove On|Off Off skdhEControl for whether session cookies should be removed from incoming HTTP headers -SessionCryptoCipher name aes256 skdhDThe crypto cipher to be used to encrypt the session -SessionCryptoDriver name [param[=value]]sDThe crypto driver to be used to encrypt the session -SessionCryptoPassphrase secret [ secret ... ] skdhDThe key used to encrypt the session -SessionCryptoPassphraseFile filenameskdDFile containing keys used to encrypt the session -SessionDBDCookieName name attributesskdhEName and attributes for the RFC2109 cookie storing the session ID -SessionDBDCookieName2 name attributesskdhEName and attributes for the RFC2965 cookie storing the session ID -SessionDBDCookieRemove On|Off On skdhEControl for whether session ID cookies should be removed from incoming HTTP headers -SessionDBDDeleteLabel label deletesession skdhEThe SQL query to use to remove sessions from the database -SessionDBDInsertLabel label insertsession skdhEThe SQL query to use to insert sessions into the database -SessionDBDPerUser On|Off Off skdhEEnable a per user session -SessionDBDSelectLabel label selectsession skdhEThe SQL query to use to select sessions from the database -SessionDBDUpdateLabel label updatesession skdhEThe SQL query to use to update existing sessions in the database -SessionEnv On|Off Off skdhEControl whether the contents of the session are written to the +ServerLimit sayısMAyarlanabilir süreç sayısının üst sınırını belirler. +ServerName [ÅŸema://]alan-adı|ip-adresi[:port] +skÇSunucunun özdeÅŸleÅŸeceÄŸi konak ismi ve port. +ServerPath URL-yolukÇUyumsuz bir tarayıcı tarafından eriÅŸilmesi için bir isme dayalı sanal konak için meÅŸru URL yolu +ServerRoot dizin-yolu /usr/local/apache sÇSunucu yapılandırması için kök dizin +ServerSignature On|Off|EMail Off skdhÇSunucu tarafından üretilen belgelerin dipnotunu ayarlar. + +ServerTokens Major|Minor|Min[imal]|Prod[uctOnly]|OS|Full Full sÇServer HTTP yanıt baÅŸlığını yapılandırır. + +Session On|Off Off skdhEEnables a session for the current directory or location +SessionCookieName name attributesskdhEName and attributes for the RFC2109 cookie storing the session +SessionCookieName2 name attributesskdhEName and attributes for the RFC2965 cookie storing the session +SessionCookieRemove On|Off Off skdhEControl for whether session cookies should be removed from incoming HTTP headers +SessionCryptoCipher name aes256 skdhDThe crypto cipher to be used to encrypt the session +SessionCryptoDriver name [param[=value]]sDThe crypto driver to be used to encrypt the session +SessionCryptoPassphrase secret [ secret ... ] skdhDThe key used to encrypt the session +SessionCryptoPassphraseFile filenameskdDFile containing keys used to encrypt the session +SessionDBDCookieName name attributesskdhEName and attributes for the RFC2109 cookie storing the session ID +SessionDBDCookieName2 name attributesskdhEName and attributes for the RFC2965 cookie storing the session ID +SessionDBDCookieRemove On|Off On skdhEControl for whether session ID cookies should be removed from incoming HTTP headers +SessionDBDDeleteLabel label deletesession skdhEThe SQL query to use to remove sessions from the database +SessionDBDInsertLabel label insertsession skdhEThe SQL query to use to insert sessions into the database +SessionDBDPerUser On|Off Off skdhEEnable a per user session +SessionDBDSelectLabel label selectsession skdhEThe SQL query to use to select sessions from the database +SessionDBDUpdateLabel label updatesession skdhEThe SQL query to use to update existing sessions in the database +SessionEnv On|Off Off skdhEControl whether the contents of the session are written to the HTTP_SESSION environment variable -SessionExclude pathskdhEDefine URL prefixes for which a session is ignored -SessionExpiryUpdateInterval interval 0 (always update) skdhEDefine the number of seconds a session's expiry may change without +SessionExclude pathskdhEDefine URL prefixes for which a session is ignored +SessionExpiryUpdateInterval interval 0 (always update) skdhEDefine the number of seconds a session's expiry may change without the session being updated -SessionHeader headerskdhEImport session updates from a given HTTP response header -SessionInclude pathskdhEDefine URL prefixes for which a session is valid -SessionMaxAge maxage 0 skdhEDefine a maximum age in seconds for a session -SetEnv ortam-deÄŸiÅŸkeni [deÄŸer]skdhTOrtam deÄŸiÅŸkenlerini tanımlar. -SetEnvIf öznitelik +SessionHeader headerskdhEImport session updates from a given HTTP response header +SessionInclude pathskdhEDefine URL prefixes for which a session is valid +SessionMaxAge maxage 0 skdhEDefine a maximum age in seconds for a session +SetEnv ortam-deÄŸiÅŸkeni [deÄŸer]skdhTOrtam deÄŸiÅŸkenlerini tanımlar. +SetEnvIf öznitelik düzifd [!]ort-deÄŸiÅŸkeni[=deÄŸer] - [[!]ort-deÄŸiÅŸkeni[=deÄŸer]] ...skdhTOrtam deÄŸiÅŸkenlerini isteÄŸin özniteliklerine göre atar. + [[!]ort-deÄŸiÅŸkeni[=deÄŸer]] ...skdhTOrtam deÄŸiÅŸkenlerini isteÄŸin özniteliklerine göre atar. -SetEnvIfExpr ifade +SetEnvIfExpr ifade [!]ort-deÄŸiÅŸkeni[=deÄŸer] - [[!]ort-deÄŸiÅŸkeni[=deÄŸer]] ...skdhTBir ap_expr ifadesine dayanarak ortam deÄŸiÅŸkenlerine deÄŸer atar -SetEnvIfNoCase öznitelik + [[!]ort-deÄŸiÅŸkeni[=deÄŸer]] ...skdhTBir ap_expr ifadesine dayanarak ortam deÄŸiÅŸkenlerine deÄŸer atar +SetEnvIfNoCase öznitelik düzifd [!]ort-deÄŸiÅŸkeni[=deÄŸer] - [[!]ort-deÄŸiÅŸkeni[=deÄŸer]] ...skdhTOrtam deÄŸiÅŸkenlerini isteÄŸin özniteliklerinde harf büyüklüÄŸüne + [[!]ort-deÄŸiÅŸkeni[=deÄŸer]] ...skdhTOrtam deÄŸiÅŸkenlerini isteÄŸin özniteliklerinde harf büyüklüÄŸüne baÄŸlı olmaksızın yapılmış tanımlara göre atar. -SetHandler eylemci-ismi|none|ifadeskdhÇEÅŸleÅŸen tüm dosyaların belli bir eylemci tarafından iÅŸlenmesine +SetHandler eylemci-ismi|none|ifadeskdhÇEÅŸleÅŸen tüm dosyaların belli bir eylemci tarafından iÅŸlenmesine sebep olur. -SetInputFilter süzgeç[;süzgeç...]skdhÇPOST girdilerini ve istemci isteklerini iÅŸleyecek süzgeçleri +SetInputFilter süzgeç[;süzgeç...]skdhÇPOST girdilerini ve istemci isteklerini iÅŸleyecek süzgeçleri belirler. -SetOutputFilter süzgeç[;süzgeç...]skdhÇSunucunun yanıtlarını iÅŸleyecek süzgeçleri belirler. -SSIEndTag tag "-->" skTString that ends an include element -SSIErrorMsg message "[an error occurred +skdhTError message displayed when there is an SSI +SetOutputFilter süzgeç[;süzgeç...]skdhÇSunucunun yanıtlarını iÅŸleyecek süzgeçleri belirler. +SSIEndTag tag "-->" skTString that ends an include element +SSIErrorMsg message "[an error occurred +skdhTError message displayed when there is an SSI error -SSIETag on|off off dhTControls whether ETags are generated by the server. -SSILastModified on|off off dhTControls whether Last-Modified headers are generated by the +SSIETag on|off off dhTControls whether ETags are generated by the server. +SSILastModified on|off off dhTControls whether Last-Modified headers are generated by the server. -SSILegacyExprParser on|off off dhTEnable compatibility mode for conditional expressions. -SSIStartTag tag "<!--#" skTString that starts an include element -SSITimeFormat formatstring "%A, %d-%b-%Y %H:%M +skdhTConfigures the format in which date strings are +SSILegacyExprParser on|off off dhTEnable compatibility mode for conditional expressions. +SSIStartTag tag "<!--#" skTString that starts an include element +SSITimeFormat formatstring "%A, %d-%b-%Y %H:%M +skdhTConfigures the format in which date strings are displayed -SSIUndefinedEcho string "(none)" skdhTString displayed when an unset variable is echoed -SSLCACertificateFile file-pathskEFile of concatenated PEM-encoded CA Certificates +SSIUndefinedEcho string "(none)" skdhTString displayed when an unset variable is echoed +SSLCACertificateFile file-pathskEFile of concatenated PEM-encoded CA Certificates for Client Auth -SSLCACertificatePath directory-pathskEDirectory of PEM-encoded CA Certificates for +SSLCACertificatePath directory-pathskEDirectory of PEM-encoded CA Certificates for Client Auth -SSLCADNRequestFile file-pathskEFile of concatenated PEM-encoded CA Certificates +SSLCADNRequestFile file-pathskEFile of concatenated PEM-encoded CA Certificates for defining acceptable CA names -SSLCADNRequestPath directory-pathskEDirectory of PEM-encoded CA Certificates for +SSLCADNRequestPath directory-pathskEDirectory of PEM-encoded CA Certificates for defining acceptable CA names -SSLCARevocationCheck chain|leaf|none [flags ...] none skEEnable CRL-based revocation checking -SSLCARevocationFile file-pathskEFile of concatenated PEM-encoded CA CRLs for +SSLCARevocationCheck chain|leaf|none [flags ...] none skEEnable CRL-based revocation checking +SSLCARevocationFile file-pathskEFile of concatenated PEM-encoded CA CRLs for Client Auth -SSLCARevocationPath directory-pathskEDirectory of PEM-encoded CA CRLs for +SSLCARevocationPath directory-pathskEDirectory of PEM-encoded CA CRLs for Client Auth -SSLCertificateChainFile file-pathskEFile of PEM-encoded Server CA Certificates -SSLCertificateFile file-path|certidskEServer PEM-encoded X.509 certificate data file or token identifier -SSLCertificateKeyFile file-path|keyidskEServer PEM-encoded private key file -SSLCipherSuite [protocol] cipher-spec DEFAULT (depends on +skdhECipher Suite available for negotiation in SSL +SSLCertificateChainFile file-pathskEFile of PEM-encoded Server CA Certificates +SSLCertificateFile file-path|certidskEServer PEM-encoded X.509 certificate data file or token identifier +SSLCertificateKeyFile file-path|keyidskEServer PEM-encoded private key file +SSLCipherSuite [protocol] cipher-spec DEFAULT (depends on +skdhECipher Suite available for negotiation in SSL handshake -SSLCompression on|off off skEEnable compression on the SSL level -SSLCryptoDevice engine builtin sEEnable use of a cryptographic hardware accelerator -SSLEngine on|off off skESSL Engine Operation Switch -SSLFIPS on|off off sESSL FIPS mode Switch -SSLHonorCipherOrder on|off off skEOption to prefer the server's cipher preference order -SSLInsecureRenegotiation on|off off skEOption to enable support for insecure renegotiation -SSLOCSPDefaultResponder uriskESet the default responder URI for OCSP validation -SSLOCSPEnable on|leaf|off [flags] off skEEnable OCSP validation of the client certificate chain -SSLOCSPNoverify on|off off skEskip the OCSP responder certificates verification -SSLOCSPOverrideResponder on|off off skEForce use of the default responder URI for OCSP validation -SSLOCSPProxyURL urlskEProxy URL to use for OCSP requests -SSLOCSPResponderCertificateFile fileskESet of trusted PEM encoded OCSP responder certificates -SSLOCSPResponderTimeout seconds 10 skETimeout for OCSP queries -SSLOCSPResponseMaxAge seconds -1 skEMaximum allowable age for OCSP responses -SSLOCSPResponseTimeSkew seconds 300 skEMaximum allowable time skew for OCSP response validation -SSLOCSPUseRequestNonce on|off on skEUse a nonce within OCSP queries -SSLOpenSSLConfCmd command-name command-valueskEConfigure OpenSSL parameters through its SSL_CONF API -SSLOptions [+|-]option ...skdhEConfigure various SSL engine run-time options -SSLPassPhraseDialog type builtin sEType of pass phrase dialog for encrypted private +SSLCompression on|off off skEEnable compression on the SSL level +SSLCryptoDevice engine builtin sEEnable use of a cryptographic hardware accelerator +SSLEngine on|off off skESSL Engine Operation Switch +SSLFIPS on|off off sESSL FIPS mode Switch +SSLHonorCipherOrder on|off off skEOption to prefer the server's cipher preference order +SSLInsecureRenegotiation on|off off skEOption to enable support for insecure renegotiation +SSLOCSPDefaultResponder uriskESet the default responder URI for OCSP validation +SSLOCSPEnable on|leaf|off [flags] off skEEnable OCSP validation of the client certificate chain +SSLOCSPNoverify on|off off skEskip the OCSP responder certificates verification +SSLOCSPOverrideResponder on|off off skEForce use of the default responder URI for OCSP validation +SSLOCSPProxyURL urlskEProxy URL to use for OCSP requests +SSLOCSPResponderCertificateFile fileskESet of trusted PEM encoded OCSP responder certificates +SSLOCSPResponderTimeout seconds 10 skETimeout for OCSP queries +SSLOCSPResponseMaxAge seconds -1 skEMaximum allowable age for OCSP responses +SSLOCSPResponseTimeSkew seconds 300 skEMaximum allowable time skew for OCSP response validation +SSLOCSPUseRequestNonce on|off on skEUse a nonce within OCSP queries +SSLOpenSSLConfCmd command-name command-valueskEConfigure OpenSSL parameters through its SSL_CONF API +SSLOptions [+|-]option ...skdhEConfigure various SSL engine run-time options +SSLPassPhraseDialog type builtin sEType of pass phrase dialog for encrypted private keys -SSLProtocol [+|-]protocol ... all -SSLv3 (up to 2 +skEConfigure usable SSL/TLS protocol versions -SSLProxyCACertificateFile file-pathskvEFile of concatenated PEM-encoded CA Certificates +SSLProtocol [+|-]protocol ... all -SSLv3 (up to 2 +skEConfigure usable SSL/TLS protocol versions +SSLProxyCACertificateFile file-pathskvEFile of concatenated PEM-encoded CA Certificates for Remote Server Auth -SSLProxyCACertificatePath directory-pathskvEDirectory of PEM-encoded CA Certificates for +SSLProxyCACertificatePath directory-pathskvEDirectory of PEM-encoded CA Certificates for Remote Server Auth -SSLProxyCARevocationCheck chain|leaf|none none skvEEnable CRL-based revocation checking for Remote Server Auth -SSLProxyCARevocationFile file-pathskvEFile of concatenated PEM-encoded CA CRLs for +SSLProxyCARevocationCheck chain|leaf|none none skvEEnable CRL-based revocation checking for Remote Server Auth +SSLProxyCARevocationFile file-pathskvEFile of concatenated PEM-encoded CA CRLs for Remote Server Auth -SSLProxyCARevocationPath directory-pathskvEDirectory of PEM-encoded CA CRLs for +SSLProxyCARevocationPath directory-pathskvEDirectory of PEM-encoded CA CRLs for Remote Server Auth -SSLProxyCheckPeerCN on|off on skvEWhether to check the remote server certificate's CN field +SSLProxyCheckPeerCN on|off on skvEWhether to check the remote server certificate's CN field -SSLProxyCheckPeerExpire on|off on skvEWhether to check if remote server certificate is expired +SSLProxyCheckPeerExpire on|off on skvEWhether to check if remote server certificate is expired -SSLProxyCheckPeerName on|off on skvEConfigure host name checking for remote server certificates +SSLProxyCheckPeerName on|off on skvEConfigure host name checking for remote server certificates -SSLProxyCipherSuite [protocol] cipher-spec ALL:!ADH:RC4+RSA:+H +skvECipher Suite available for negotiation in SSL +SSLProxyCipherSuite [protocol] cipher-spec ALL:!ADH:RC4+RSA:+H +skvECipher Suite available for negotiation in SSL proxy handshake -SSLProxyEngine on|off off skvESSL Proxy Engine Operation Switch -SSLProxyMachineCertificateChainFile filenameskvEFile of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate -SSLProxyMachineCertificateFile filenameskvEFile of concatenated PEM-encoded client certificates and keys to be used by the proxy -SSLProxyMachineCertificatePath directoryskvEDirectory of PEM-encoded client certificates and keys to be used by the proxy -SSLProxyProtocol [+|-]protocol ... all -SSLv3 (up to 2 +skvEConfigure usable SSL protocol flavors for proxy usage -SSLProxyVerify level none skvEType of remote server Certificate verification -SSLProxyVerifyDepth number 1 skvEMaximum depth of CA Certificates in Remote Server +SSLProxyEngine on|off off skvESSL Proxy Engine Operation Switch +SSLProxyMachineCertificateChainFile filenameskvEFile of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate +SSLProxyMachineCertificateFile filenameskvEFile of concatenated PEM-encoded client certificates and keys to be used by the proxy +SSLProxyMachineCertificatePath directoryskvEDirectory of PEM-encoded client certificates and keys to be used by the proxy +SSLProxyProtocol [+|-]protocol ... all -SSLv3 (up to 2 +skvEConfigure usable SSL protocol flavors for proxy usage +SSLProxyVerify level none skvEType of remote server Certificate verification +SSLProxyVerifyDepth number 1 skvEMaximum depth of CA Certificates in Remote Server Certificate verification -SSLRandomSeed context source -[bytes]sEPseudo Random Number Generator (PRNG) seeding +SSLRandomSeed context source +[bytes]sEPseudo Random Number Generator (PRNG) seeding source -SSLRenegBufferSize bytes 131072 dhESet the size for the SSL renegotiation buffer -SSLRequire expressiondhEAllow access only when an arbitrarily complex +SSLRenegBufferSize bytes 131072 dhESet the size for the SSL renegotiation buffer +SSLRequire expressiondhEAllow access only when an arbitrarily complex boolean expression is true -SSLRequireSSLdhEDeny access when SSL is not used for the +SSLRequireSSLdhEDeny access when SSL is not used for the HTTP request -SSLSessionCache type none sEType of the global/inter-process SSL Session +SSLSessionCache type none sEType of the global/inter-process SSL Session Cache -SSLSessionCacheTimeout seconds 300 skENumber of seconds before an SSL session expires +SSLSessionCacheTimeout seconds 300 skENumber of seconds before an SSL session expires in the Session Cache -SSLSessionTicketKeyFile file-pathskEPersistent encryption/decryption key for TLS session tickets -SSLSessionTickets on|off on skEEnable or disable use of TLS session tickets -SSLSRPUnknownUserSeed secret-stringskESRP unknown user seed -SSLSRPVerifierFile file-pathskEPath to SRP verifier file -SSLStaplingCache typesEConfigures the OCSP stapling cache -SSLStaplingErrorCacheTimeout seconds 600 skENumber of seconds before expiring invalid responses in the OCSP stapling cache -SSLStaplingFakeTryLater on|off on skESynthesize "tryLater" responses for failed OCSP stapling queries -SSLStaplingForceURL uriskEOverride the OCSP responder URI specified in the certificate's AIA extension -SSLStaplingResponderTimeout seconds 10 skETimeout for OCSP stapling queries -SSLStaplingResponseMaxAge seconds -1 skEMaximum allowable age for OCSP stapling responses -SSLStaplingResponseTimeSkew seconds 300 skEMaximum allowable time skew for OCSP stapling response validation -SSLStaplingReturnResponderErrors on|off on skEPass stapling related OCSP errors on to client -SSLStaplingStandardCacheTimeout seconds 3600 skENumber of seconds before expiring responses in the OCSP stapling cache -SSLStrictSNIVHostCheck on|off off skEWhether to allow non-SNI clients to access a name-based virtual +SSLSessionTicketKeyFile file-pathskEPersistent encryption/decryption key for TLS session tickets +SSLSessionTickets on|off on skEEnable or disable use of TLS session tickets +SSLSRPUnknownUserSeed secret-stringskESRP unknown user seed +SSLSRPVerifierFile file-pathskEPath to SRP verifier file +SSLStaplingCache typesEConfigures the OCSP stapling cache +SSLStaplingErrorCacheTimeout seconds 600 skENumber of seconds before expiring invalid responses in the OCSP stapling cache +SSLStaplingFakeTryLater on|off on skESynthesize "tryLater" responses for failed OCSP stapling queries +SSLStaplingForceURL uriskEOverride the OCSP responder URI specified in the certificate's AIA extension +SSLStaplingResponderTimeout seconds 10 skETimeout for OCSP stapling queries +SSLStaplingResponseMaxAge seconds -1 skEMaximum allowable age for OCSP stapling responses +SSLStaplingResponseTimeSkew seconds 300 skEMaximum allowable time skew for OCSP stapling response validation +SSLStaplingReturnResponderErrors on|off on skEPass stapling related OCSP errors on to client +SSLStaplingStandardCacheTimeout seconds 3600 skENumber of seconds before expiring responses in the OCSP stapling cache +SSLStrictSNIVHostCheck on|off off skEWhether to allow non-SNI clients to access a name-based virtual host. -SSLUserName varnamesdhEVariable name to determine user name -SSLUseStapling on|off off skEEnable stapling of OCSP responses in the TLS handshake -SSLVerifyClient level none skdhEType of Client Certificate verification -SSLVerifyDepth number 1 skdhEMaximum depth of CA Certificates in Client +SSLUserName varnamesdhEVariable name to determine user name +SSLUseStapling on|off off skEEnable stapling of OCSP responses in the TLS handshake +SSLVerifyClient level none skdhEType of Client Certificate verification +SSLVerifyDepth number 1 skdhEMaximum depth of CA Certificates in Client Certificate verification -SSLVHostSNIPolicy strict|secure|authonly|insecure secure sESet compatibility policy for SNI client access to virtual hosts. -StartServers sayısMSunucunun baÅŸlatılması sırasında oluÅŸturulan çocuk süreçlerin +SSLVHostSNIPolicy strict|secure|authonly|insecure secure sESet compatibility policy for SNI client access to virtual hosts. +StartServers sayısMSunucunun baÅŸlatılması sırasında oluÅŸturulan çocuk süreçlerin sayısını belirler. -StartThreads sayısMSunucunun baÅŸlatılması sırasında oluÅŸturulan evrelerin sayısını +StartThreads sayısMSunucunun baÅŸlatılması sırasında oluÅŸturulan evrelerin sayısını belirler. -StrictHostCheck ON|OFF OFF skÇSunucunun, istenen konak adının, isteÄŸi iÅŸleyen sanal konakta +StrictHostCheck ON|OFF OFF skÇSunucunun, istenen konak adının, isteÄŸi iÅŸleyen sanal konakta listelenmesini gerektirip gerektirmediÄŸini denetler -Substitute s/pattern/substitution/[infq]dhEPattern to filter the response content -SubstituteInheritBefore on|off off dhEChange the merge order of inherited patterns -SubstituteMaxLineLength bytes(b|B|k|K|m|M|g|G) 1m dhESet the maximum line size -Suexec On|OffsTsuEXEC özelliÄŸini etkin veya etkisiz yapar -SuexecUserGroup Kullanıcı GrupskECGI betiklerini çalıştıracak kullanıcı ve grup belirtilir. +Substitute s/pattern/substitution/[infq]dhEPattern to filter the response content +SubstituteInheritBefore on|off off dhEChange the merge order of inherited patterns +SubstituteMaxLineLength bytes(b|B|k|K|m|M|g|G) 1m dhESet the maximum line size +Suexec On|OffsTsuEXEC özelliÄŸini etkin veya etkisiz yapar +SuexecUserGroup Kullanıcı GrupskECGI betiklerini çalıştıracak kullanıcı ve grup belirtilir. -ThreadLimit sayısMÇocuk süreç başına ayarlanabilir evre sayısının üst sınırını +ThreadLimit sayısMÇocuk süreç başına ayarlanabilir evre sayısının üst sınırını belirler. -ThreadsPerChild sayısMHer çocuk süreç tarafından oluÅŸturulan evrelerin sayısını +ThreadsPerChild sayısMHer çocuk süreç tarafından oluÅŸturulan evrelerin sayısını belirler. -ThreadStackSize boyutsMİstemci baÄŸlantılarını elde eden evreler tarafından kullanılan +ThreadStackSize boyutsMİstemci baÄŸlantılarını elde eden evreler tarafından kullanılan yığıtın bayt cinsinden uzunluÄŸunu belirler. -TimeOut saniye 60 skÇBir istek için baÅŸarısız olmadan önce belirli olayların +TimeOut saniye 60 skÇBir istek için baÅŸarısız olmadan önce belirli olayların gerçekleÅŸmesi için sunucunun geçmesini bekleyeceÄŸi süre. -TraceEnable [on|off|extended] on skÇTRACE isteklerinde davranış ÅŸeklini belirler +TraceEnable [on|off|extended] on skÇTRACE isteklerinde davranış ÅŸeklini belirler -TransferLog dosya|borulu-süreç -[takma-ad]skTBir günlük dosyasının yerini belirtir. -TypesConfig file-path conf/mime.types sTThe location of the mime.types file -UNCList hostname [hostname...]sÇControls what UNC host names can be accessed by the server - -UnDefine deÄŸiÅŸken-ismisÇBir deÄŸiÅŸkeni tanımsız yapar -UndefMacro nameskdTUndefine a macro -UnsetEnv ortam-deÄŸiÅŸkeni [ortam-deÄŸiÅŸkeni] -...skdhTOrtamdaki deÄŸiÅŸkenleri tanımsız hale getirir. -Use name [value1 ... valueN] -skdTUse a macro -UseCanonicalName On|Off|DNS Off skdÇSunucunun kendi adını ve portunu nasıl belirleyeceÄŸini ayarlar +TransferLog dosya|borulu-süreç +[takma-ad]skTBir günlük dosyasının yerini belirtir. +TypesConfig file-path conf/mime.types sTThe location of the mime.types file +UNCList hostname [hostname...]sÇControls what UNC host names can be accessed by the server + +UnDefine deÄŸiÅŸken-ismisÇBir deÄŸiÅŸkeni tanımsız yapar +UndefMacro nameskdTUndefine a macro +UnsetEnv ortam-deÄŸiÅŸkeni [ortam-deÄŸiÅŸkeni] +...skdhTOrtamdaki deÄŸiÅŸkenleri tanımsız hale getirir. +Use name [value1 ... valueN] +skdTUse a macro +UseCanonicalName On|Off|DNS Off skdÇSunucunun kendi adını ve portunu nasıl belirleyeceÄŸini ayarlar -UseCanonicalPhysicalPort On|Off Off skdÇSunucunun kendi adını ve portunu nasıl belirleyeceÄŸini ayarlar +UseCanonicalPhysicalPort On|Off Off skdÇSunucunun kendi adını ve portunu nasıl belirleyeceÄŸini ayarlar -User unix-kullanıcısı #-1 sTİsteklere yanıt verecek sunucunun ait olacağı kullanıcıyı +User unix-kullanıcısı #-1 sTİsteklere yanıt verecek sunucunun ait olacağı kullanıcıyı belirler. -UserDir dizin [dizin] ...skTKullanıcıya özel dizinlerin yeri -VHostCGIMode On|Off|Secure On kKDetermines whether the virtualhost can run +UserDir dizin [dizin] ...skTKullanıcıya özel dizinlerin yeri +VHostCGIMode On|Off|Secure On kKDetermines whether the virtualhost can run subprocesses, and the privileges available to subprocesses. -VHostCGIPrivs [+-]?privilege-name [[+-]?privilege-name] ...kKAssign arbitrary privileges to subprocesses created +VHostCGIPrivs [+-]?privilege-name [[+-]?privilege-name] ...kKAssign arbitrary privileges to subprocesses created by a virtual host. -VHostGroup unix-groupidkKSets the Group ID under which a virtual host runs. -VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...kKAssign arbitrary privileges to a virtual host. -VHostSecure On|Off On kKDetermines whether the server runs with enhanced security +VHostGroup unix-groupidkKSets the Group ID under which a virtual host runs. +VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...kKAssign arbitrary privileges to a virtual host. +VHostSecure On|Off On kKDetermines whether the server runs with enhanced security for the virtualhost. -VHostUser unix-useridkKSets the User ID under which a virtual host runs. -VirtualDocumentRoot hesaplanan-dizin|none none skEBir sanal konağın belge kök dizinini devingen olarak yapılandırır. +VHostUser unix-useridkKSets the User ID under which a virtual host runs. +VirtualDocumentRoot hesaplanan-dizin|none none skEBir sanal konağın belge kök dizinini devingen olarak yapılandırır. -VirtualDocumentRootIP hesaplanan-dizin|none none skEBir sanal konağın belge kök dizinini devingen olarak yapılandırır. +VirtualDocumentRootIP hesaplanan-dizin|none none skEBir sanal konağın belge kök dizinini devingen olarak yapılandırır. -<VirtualHost +<VirtualHost adres[:port] [adres[:port]] - ...> ... </VirtualHost>sÇSadece belli bir konak ismine ve porta uygulanacak yönergeleri barındırır. -VirtualScriptAlias hesaplanan-dizin|none none skEBir sanal konağın CGI dizinini devingen olarak yapılandırır. + ...> ... </VirtualHost>sÇSadece belli bir konak ismine ve porta uygulanacak yönergeleri barındırır. +VirtualScriptAlias hesaplanan-dizin|none none skEBir sanal konağın CGI dizinini devingen olarak yapılandırır. -VirtualScriptAliasIP hesaplanan-dizin|none none skEBir sanal konağın CGI dizinini devingen olarak yapılandırır. +VirtualScriptAliasIP hesaplanan-dizin|none none skEBir sanal konağın CGI dizinini devingen olarak yapılandırır. -WatchdogInterval time-interval[s] 1 sTWatchdog interval in seconds -XBitHack on|off|full off skdhTParse SSI directives in files with the execute bit +WatchdogInterval time-interval[s] 1 sTWatchdog interval in seconds +XBitHack on|off|full off skdhTParse SSI directives in files with the execute bit set -xml2EncAlias charset alias [alias ...]sTRecognize Aliases for encoding values -xml2EncDefault nameskdhTSets a default encoding to assume when absolutely no information +xml2EncAlias charset alias [alias ...]sTRecognize Aliases for encoding values +xml2EncDefault nameskdhTSets a default encoding to assume when absolutely no information can be automatically detected -xml2StartParse element [element ...]skdhTAdvise the parser to skip leading junk. +xml2StartParse element [element ...]skdhTAdvise the parser to skip leading junk.

    Mevcut Diller:  de  | diff -Nru apache2-2.4.69/docs/manual/mod/quickreference.html.zh-cn.utf8 apache2-2.4.71/docs/manual/mod/quickreference.html.zh-cn.utf8 --- apache2-2.4.69/docs/manual/mod/quickreference.html.zh-cn.utf8 2026-09-29 11:57:11.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/quickreference.html.zh-cn.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -34,7 +34,6 @@  tr  |  zh-cn 

    -
    此翻译�能过期。�了解最近的更改,请阅读英文版。

    指令快速索引显示指令的用法,默认值,状�和上下文。�获得更多信�,请�� �述指令的术语。

    @@ -711,7 +710,7 @@ MDCertificateAuthority url letsencrypt sXThe URL(s) of the ACME Certificate Authority to use. MDCertificateCheck name urlsXSet name and URL pattern for a certificate monitoring site. MDCertificateFile path-to-pem-filesXSpecify a static certificate file for the MD. -MDCertificateKeyFile path-to-filesXSpecify a static private key for for the static cerrtificate. +MDCertificateKeyFile path-to-filesXSpecify a static private key for the static certificate. MDCertificateMonitor name url crt.sh https://crt. +sXThe URL of a certificate log monitor. MDCertificateProtocol protocol ACME sXThe protocol to use with the Certificate Authority. MDCertificateStatus on|off on sXExposes public certificate information in JSON. @@ -722,500 +721,501 @@ MDDriveMode always|auto|manual auto sXformer name of MDRenewMode. MDExternalAccountBinding key-id hmac-64 | none | file none sXSet the external account binding keyid and hmac values to use at CA MDHttpProxy urlsXDefine a proxy for outgoing connections. -MDInitialDelay duration 0s sXHow long to delay the first certificate check. -MDMatchNames all|servernames all sXDetermines how DNS names are matched to vhosts -MDMember hostnamesXAdditional hostname for the managed domain. -MDMembers auto|manual auto sXControl if the alias domain names are automatically added. -MDMessageCmd path-to-cmd optional-argssXHandle events for Manage Domains -MDMustStaple on|off off sXControl if new certificates carry the OCSP Must Staple flag. -MDNotifyCmd path [ args ]sXRun a program when a Managed Domain is ready. -MDomain dns-name [ other-dns-name... ] [auto|manual]sXDefine list of domain names that belong to one group. -<MDomainSet dns-name [ other-dns-name... ]>...</MDomainSet>sXContainer for directives applied to the same managed domains. -MDPortMap map1 [ map2 ] http:80 https:443 sXMap external to internal ports for domain ownership verification. -MDPrivateKeys type [ params... ] RSA 2048 sXSet type and size of the private keys generated. -MDProfile namesXUse a specific ACME profile from the CA -MDProfileMandatory on|off off sXControl if an MDProfile is mandatory. -MDRenewMode always|auto|manual auto sXControls if certificates shall be renewed. -MDRenewViaARI on|off on sXusage of the ACME ARI extension (rfc9773). -MDRenewWindow duration 33% sXControl when a certificate will be renewed. -MDRequireHttps off|temporary|permanent off sXRedirects http: traffic to https: for Managed Domains. -MDRetryDelay duration 30s sXTime length for first retry, doubled on every consecutive error. -MDRetryFailover number 13 sXThe number of errors before a failover to another CA is triggered -MDServerStatus on|off off sXControl if Managed Domain information is added to server-status. -MDStapleOthers on|off on sXEnable stapling for certificates not managed by mod_md. -MDStapling on|off off sXEnable stapling for all or a particular MDomain. -MDStaplingKeepResponse duration 7d sXControls when old responses should be removed. -MDStaplingRenewWindow duration 33% sXControl when the stapling responses will be renewed. -MDStoreDir path md sXPath on the local file system to store the Managed Domains data. -MDStoreLocks on|off|duration off sXConfigure locking of store for updates -MDWarnWindow duration 10% sXDefine the time window when you want to be warned about an expiring certificate. -MemcacheConnTTL num[units] 15s svEKeepalive time for idle connections -MergeSlashes ON|OFF ON svCControls whether the server merges consecutive slashes in URLs. +MDHttpProxyCACertificateFile path-to-pem-file none sXSets the root (CA) certificates to use for TLS connections to the http-proxy. +MDInitialDelay duration 0s sXHow long to delay the first certificate check. +MDMatchNames all|servernames all sXDetermines how DNS names are matched to vhosts +MDMember hostnamesXAdditional hostname for the managed domain. +MDMembers auto|manual auto sXControl if the alias domain names are automatically added. +MDMessageCmd path-to-cmd optional-argssXHandle events for Manage Domains +MDMustStaple on|off off sXControl if new certificates carry the OCSP Must Staple flag. +MDNotifyCmd path [ args ]sXRun a program when a Managed Domain is ready. +MDomain dns-name [ other-dns-name... ] [auto|manual]sXDefine list of domain names that belong to one group. +<MDomainSet dns-name [ other-dns-name... ]>...</MDomainSet>sXContainer for directives applied to the same managed domains. +MDPortMap map1 [ map2 ] http:80 https:443 sXMap external to internal ports for domain ownership verification. +MDPrivateKeys type [ params... ] RSA 2048 sXSet type and size of the private keys generated. +MDProfile namesXUse a specific ACME profile from the CA +MDProfileMandatory on|off off sXControl if an MDProfile is mandatory. +MDRenewMode always|auto|manual auto sXControls if certificates shall be renewed. +MDRenewViaARI on|off on sXusage of the ACME ARI extension (rfc9773). +MDRenewWindow duration 33% sXControl when a certificate will be renewed. +MDRequireHttps off|temporary|permanent off sXRedirects http: traffic to https: for Managed Domains. +MDRetryDelay duration 30s sXTime length for first retry, doubled on every consecutive error. +MDRetryFailover number 13 sXThe number of errors before a failover to another CA is triggered +MDServerStatus on|off off sXControl if Managed Domain information is added to server-status. +MDStapleOthers on|off on sXEnable stapling for certificates not managed by mod_md. +MDStapling on|off off sXEnable stapling for all or a particular MDomain. +MDStaplingKeepResponse duration 7d sXControls when old responses should be removed. +MDStaplingRenewWindow duration 33% sXControl when the stapling responses will be renewed. +MDStoreDir path md sXPath on the local file system to store the Managed Domains data. +MDStoreLocks on|off|duration off sXConfigure locking of store for updates +MDWarnWindow duration 10% sXDefine the time window when you want to be warned about an expiring certificate. +MemcacheConnTTL num[units] 15s svEKeepalive time for idle connections +MergeSlashes ON|OFF ON svCControls whether the server merges consecutive slashes in URLs. -MergeTrailers [on|off] off svCDetermines whether trailers are merged into headers -MetaDir directory .web svdhName of the directory to find CERN-style meta information +MergeTrailers [on|off] off svCDetermines whether trailers are merged into headers +MetaDir directory .web svdhName of the directory to find CERN-style meta information files -MetaFiles on|off off svdhActivates CERN meta-file processing -MetaSuffix suffix .meta svdhFile name suffix for the file containing CERN-style +MetaFiles on|off off svdhActivates CERN meta-file processing +MetaSuffix suffix .meta svdhFile name suffix for the file containing CERN-style meta information -MimeMagicFile file-pathsvEEnable MIME-type determination based on file contents +MimeMagicFile file-pathsvEEnable MIME-type determination based on file contents using the specified magic file -MinSpareServers number 5 sMMinimum number of idle child server processes -MinSpareThreads numbersMMinimum number of idle threads available to handle request +MinSpareServers number 5 sMMinimum number of idle child server processes +MinSpareThreads numbersMMinimum number of idle threads available to handle request spikes -MMapFile file-path [file-path] ...sXMap a list of files into memory at startup time -ModemStandard V.21|V.26bis|V.32|V.34|V.92dXModem standard to simulate -ModMimeUsePathInfo On|Off Off dBTells mod_mime to treat path_info +MMapFile file-path [file-path] ...sXMap a list of files into memory at startup time +ModemStandard V.21|V.26bis|V.32|V.34|V.92dXModem standard to simulate +ModMimeUsePathInfo On|Off Off dBTells mod_mime to treat path_info components as part of the filename -MultiviewsMatch Any|NegotiatedOnly|Filters|Handlers -[Handlers|Filters] NegotiatedOnly svdhBThe types of files that will be included when searching for +MultiviewsMatch Any|NegotiatedOnly|Filters|Handlers +[Handlers|Filters] NegotiatedOnly svdhBThe types of files that will be included when searching for a matching file with MultiViews -Mutex mechanism [default|mutex-name] ... [OmitPID] default sCConfigures mutex mechanism and lock file directory for all +Mutex mechanism [default|mutex-name] ... [OmitPID] default sCConfigures mutex mechanism and lock file directory for all or specified mutexes -NameVirtualHost addr[:port]sCDEPRECATED: Designates an IP address for name-virtual +NameVirtualHost addr[:port]sCDEPRECATED: Designates an IP address for name-virtual hosting -NoProxy host [host] ...svEHosts, domains, or networks that will be connected to +NoProxy host [host] ...svEHosts, domains, or networks that will be connected to directly -NWSSLTrustedCerts filename [filename] ...sBList of additional client certificates -NWSSLUpgradeable [IP-address:]portnumbersBAllows a connection to be upgraded to an SSL connection upon request -Options - [+|-]option [[+|-]option] ... FollowSymlinks svdhCConfigures what features are available in a particular +NWSSLTrustedCerts filename [filename] ...sBList of additional client certificates +NWSSLUpgradeable [IP-address:]portnumbersBAllows a connection to be upgraded to an SSL connection upon request +Options + [+|-]option [[+|-]option] ... FollowSymlinks svdhCConfigures what features are available in a particular directory - Order ordering Deny,Allow dhControls the default access state and the order in which + Order ordering Deny,Allow dhControls the default access state and the order in which Allow and Deny are evaluated. -OutputSed sed-commanddhXSed command for filtering response content -PassEnv env-variable [env-variable] -...svdhBPasses environment variables from the shell -PidFile filename logs/httpd.pid sMFile where the server records the process ID +OutputSed sed-commanddhXSed command for filtering response content +PassEnv env-variable [env-variable] +...svdhBPasses environment variables from the shell +PidFile filename logs/httpd.pid sMFile where the server records the process ID of the daemon -PrivilegesMode FAST|SECURE|SELECTIVE FAST svdTrade off processing speed and efficiency vs security against +PrivilegesMode FAST|SECURE|SELECTIVE FAST svdTrade off processing speed and efficiency vs security against malicious privileges-aware code. -Protocol protocolsvCProtocol for a listening socket -ProtocolEcho On|Off Off svXTurn the echo server on or off -Protocols protocol ... http/1.1 svCProtocols available for a server/virtual host -ProtocolsHonorOrder On|Off On svCDetermines if order of Protocols determines precedence during negotiation -<Proxy wildcard-url> ...</Proxy>svEContainer for directives applied to proxied resources -Proxy100Continue Off|On On svdEForward 100-continue expectation to the origin server -ProxyAddHeaders Off|On On svdEAdd proxy information in X-Forwarded-* headers -ProxyBadHeader IsError|Ignore|StartBody IsError svEDetermines how to handle bad header lines in a +Protocol protocolsvCProtocol for a listening socket +ProtocolEcho On|Off Off svXTurn the echo server on or off +Protocols protocol ... http/1.1 svCProtocols available for a server/virtual host +ProtocolsHonorOrder On|Off On svCDetermines if order of Protocols determines precedence during negotiation +<Proxy wildcard-url> ...</Proxy>svEContainer for directives applied to proxied resources +Proxy100Continue Off|On On svdEForward 100-continue expectation to the origin server +ProxyAddHeaders Off|On On svdEAdd proxy information in X-Forwarded-* headers +ProxyBadHeader IsError|Ignore|StartBody IsError svEDetermines how to handle bad header lines in a response -ProxyBlock *|word|host|domain -[word|host|domain] ...svEWords, hosts, or domains that are banned from being +ProxyBlock *|word|host|domain +[word|host|domain] ...svEWords, hosts, or domains that are banned from being proxied -ProxyDomain DomainsvEDefault domain name for proxied requests -ProxyErrorOverride Off|On [code ...] Off svdEOverride error pages for proxied content -ProxyExpressDBMFile pathnamesvEPathname to DBM file. -ProxyExpressDBMType type default svEDBM type of file. -ProxyExpressEnable on|off off svEEnable the module functionality. -ProxyFCGIBackendType FPM|GENERIC FPM svdhESpecify the type of backend FastCGI application -ProxyFCGISetEnvIf conditional-expression +ProxyDomain DomainsvEDefault domain name for proxied requests +ProxyErrorOverride Off|On [code ...] Off svdEOverride error pages for proxied content +ProxyExpressDBMFile pathnamesvEPathname to DBM file. +ProxyExpressDBMType type default svEDBM type of file. +ProxyExpressEnable on|off off svEEnable the module functionality. +ProxyFCGIBackendType FPM|GENERIC FPM svdhESpecify the type of backend FastCGI application +ProxyFCGISetEnvIf conditional-expression [!]environment-variable-name - [value-expression]svdhEAllow variables sent to FastCGI servers to be fixed up -ProxyFtpDirCharset character_set ISO-8859-1 svdEDefine the character set for proxied FTP listings -ProxyFtpEscapeWildcards on|off on svdEWhether wildcards in requested filenames are escaped when sent to the FTP server -ProxyFtpListOnWildcard on|off on svdEWhether wildcards in requested filenames trigger a file listing -ProxyHCExpr name {ap_expr expression}svECreates a named condition expression to use to determine health of the backend based on its response -ProxyHCTemplate name parameter=setting [...]svECreates a named template for setting various health check parameters -ProxyHCTPsize size 16 sESets the total server-wide size of the threadpool used for the health check workers -ProxyHTMLBufSize bytes 8192 svdBSets the buffer size increment for buffering inline scripts and + [value-expression]svdhEAllow variables sent to FastCGI servers to be fixed up +ProxyFtpDirCharset character_set ISO-8859-1 svdEDefine the character set for proxied FTP listings +ProxyFtpEscapeWildcards on|off on svdEWhether wildcards in requested filenames are escaped when sent to the FTP server +ProxyFtpListOnWildcard on|off on svdEWhether wildcards in requested filenames trigger a file listing +ProxyHCExpr name {ap_expr expression}svECreates a named condition expression to use to determine health of the backend based on its response +ProxyHCTemplate name parameter=setting [...]svECreates a named template for setting various health check parameters +ProxyHCTPsize size 16 sESets the total server-wide size of the threadpool used for the health check workers +ProxyHTMLBufSize bytes 8192 svdBSets the buffer size increment for buffering inline scripts and stylesheets. -ProxyHTMLCharsetOut Charset | * UTF-8 svdBSpecify a charset for mod_proxy_html output. -ProxyHTMLDocType HTML|XHTML [Legacy]
    OR -
    ProxyHTMLDocType fpi [SGML|XML]
    svdBSets an HTML or XHTML document type declaration. -ProxyHTMLEnable On|Off Off svdBTurns the proxy_html filter on or off. -ProxyHTMLEvents attribute [attribute ...]svdBSpecify attributes to treat as scripting events. -ProxyHTMLExtended On|Off Off svdBDetermines whether to fix links in inline scripts, stylesheets, +ProxyHTMLCharsetOut Charset | * UTF-8 svdBSpecify a charset for mod_proxy_html output. +ProxyHTMLDocType HTML|XHTML [Legacy]
    OR +
    ProxyHTMLDocType fpi [SGML|XML]
    svdBSets an HTML or XHTML document type declaration. +ProxyHTMLEnable On|Off Off svdBTurns the proxy_html filter on or off. +ProxyHTMLEvents attribute [attribute ...]svdBSpecify attributes to treat as scripting events. +ProxyHTMLExtended On|Off Off svdBDetermines whether to fix links in inline scripts, stylesheets, and scripting events. -ProxyHTMLFixups [lowercase] [dospath] [reset] none svdBFixes for simple HTML errors. -ProxyHTMLInterp On|Off Off svdBEnables per-request interpolation of +ProxyHTMLFixups [lowercase] [dospath] [reset] none svdBFixes for simple HTML errors. +ProxyHTMLInterp On|Off Off svdBEnables per-request interpolation of ProxyHTMLURLMap rules. -ProxyHTMLLinks element attribute [attribute2 ...]svdBSpecify HTML elements that have URL attributes to be rewritten. -ProxyHTMLMeta On|Off Off svdBTurns on or off extra pre-parsing of metadata in HTML +ProxyHTMLLinks element attribute [attribute2 ...]svdBSpecify HTML elements that have URL attributes to be rewritten. +ProxyHTMLMeta On|Off Off svdBTurns on or off extra pre-parsing of metadata in HTML <head> sections. -ProxyHTMLStripComments On|Off Off svdBDetermines whether to strip HTML comments. -ProxyHTMLURLMap from-pattern to-pattern [flags] [cond]svdBDefines a rule to rewrite HTML links -ProxyIOBufferSize bytes 8192 svEDetermine size of internal data throughput buffer -<ProxyMatch regex> ...</ProxyMatch>svEContainer for directives applied to regular-expression-matched +ProxyHTMLStripComments On|Off Off svdBDetermines whether to strip HTML comments. +ProxyHTMLURLMap from-pattern to-pattern [flags] [cond]svdBDefines a rule to rewrite HTML links +ProxyIOBufferSize bytes 8192 svEDetermine size of internal data throughput buffer +<ProxyMatch regex> ...</ProxyMatch>svEContainer for directives applied to regular-expression-matched proxied resources -ProxyMaxForwards number -1 svEMaximum number of proxies that a request can be forwarded +ProxyMaxForwards number -1 svEMaximum number of proxies that a request can be forwarded through -ProxyPass [path] !|url [key=value - [key=value ...]] [nocanon] [interpolate] [noquery]svdEMaps remote servers into the local server URL-space -ProxyPassInherit On|Off On svEInherit ProxyPass directives defined from the main server -ProxyPassInterpolateEnv On|Off Off svdEEnable Environment Variable interpolation in Reverse Proxy configurations -ProxyPassMatch [regex] !|url [key=value - [key=value ...]]svdEMaps remote servers into the local server URL-space using regular expressions -ProxyPassReverse [path] url -[interpolate]svdEAdjusts the URL in HTTP response headers sent from a reverse +ProxyPass [path] !|url [key=value + [key=value ...]] [nocanon] [interpolate] [noquery]svdEMaps remote servers into the local server URL-space +ProxyPassInherit On|Off On svEInherit ProxyPass directives defined from the main server +ProxyPassInterpolateEnv On|Off Off svdEEnable Environment Variable interpolation in Reverse Proxy configurations +ProxyPassMatch [regex] !|url [key=value + [key=value ...]]svdEMaps remote servers into the local server URL-space using regular expressions +ProxyPassReverse [path] url +[interpolate]svdEAdjusts the URL in HTTP response headers sent from a reverse proxied server -ProxyPassReverseCookieDomain internal-domain -public-domain [interpolate]svdEAdjusts the Domain string in Set-Cookie headers from a reverse- +ProxyPassReverseCookieDomain internal-domain +public-domain [interpolate]svdEAdjusts the Domain string in Set-Cookie headers from a reverse- proxied server -ProxyPassReverseCookiePath internal-path -public-path [interpolate]svdEAdjusts the Path string in Set-Cookie headers from a reverse- +ProxyPassReverseCookiePath internal-path +public-path [interpolate]svdEAdjusts the Path string in Set-Cookie headers from a reverse- proxied server -ProxyPreserveHost On|Off Off svdEUse incoming Host HTTP request header for proxy +ProxyPreserveHost On|Off Off svdEUse incoming Host HTTP request header for proxy request -ProxyReceiveBufferSize bytes 0 svENetwork buffer size for proxied HTTP and FTP +ProxyReceiveBufferSize bytes 0 svENetwork buffer size for proxied HTTP and FTP connections -ProxyRemote match remote-server [username:password]svERemote proxy used to handle certain requests -ProxyRemoteMatch regex remote-server [username:password]svERemote proxy used to handle requests matched by regular +ProxyRemote match remote-server [username:password]svERemote proxy used to handle certain requests +ProxyRemoteMatch regex remote-server [username:password]svERemote proxy used to handle requests matched by regular expressions -ProxyRequests On|Off Off svEEnables forward (standard) proxy requests -ProxySCGIInternalRedirect On|Off|Headername On svdEEnable or disable internal redirect responses from the +ProxyRequests On|Off Off svEEnables forward (standard) proxy requests +ProxySCGIInternalRedirect On|Off|Headername On svdEEnable or disable internal redirect responses from the backend -ProxySCGISendfile On|Off|Headername Off svdEEnable evaluation of X-Sendfile pseudo response +ProxySCGISendfile On|Off|Headername Off svdEEnable evaluation of X-Sendfile pseudo response header -ProxySet url key=value [key=value ...]svdESet various Proxy balancer or member parameters -ProxySourceAddress addresssvESet local IP address for outgoing proxy connections -ProxyStatus Off|On|Full Off svEShow Proxy LoadBalancer status in mod_status -ProxyTimeout secondssvENetwork timeout for proxied requests -ProxyVia On|Off|Full|Block Off svEInformation provided in the Via HTTP response +ProxySet url key=value [key=value ...]svdESet various Proxy balancer or member parameters +ProxySourceAddress addresssvESet local IP address for outgoing proxy connections +ProxyStatus Off|On|Full Off svEShow Proxy LoadBalancer status in mod_status +ProxyTimeout secondssvENetwork timeout for proxied requests +ProxyVia On|Off|Full|Block Off svEInformation provided in the Via HTTP response header for proxied requests -ProxyWebsocketFallbackToProxyHttp On|Off On svInstructs this module to let mod_proxy_http handle the request -QualifyRedirectURL On|Off Off svdCControls whether the REDIRECT_URL environment variable is +ProxyWebsocketFallbackToProxyHttp On|Off On svInstructs this module to let mod_proxy_http handle the request +QualifyRedirectURL On|Off Off svdCControls whether the REDIRECT_URL environment variable is fully qualified -ReadBufferSize bytes 8192 svdCSize of the buffers used to read data -ReadmeName filenamesvdhBName of the file that will be inserted at the end +ReadBufferSize bytes 8192 svdCSize of the buffers used to read data +ReadmeName filenamesvdhBName of the file that will be inserted at the end of the index listing -ReceiveBufferSize bytes 0 sMTCP receive buffer size -Redirect [status] [URL-path] -URLsvdhBSends an external redirect asking the client to fetch +ReceiveBufferSize bytes 0 sMTCP receive buffer size +Redirect [status] [URL-path] +URLsvdhBSends an external redirect asking the client to fetch a different URL -RedirectMatch [status] regex -URLsvdhBSends an external redirect based on a regular expression match +RedirectMatch [status] regex +URLsvdhBSends an external redirect based on a regular expression match of the current URL -RedirectPermanent URL-path URLsvdhBSends an external permanent redirect asking the client to fetch +RedirectPermanent URL-path URLsvdhBSends an external permanent redirect asking the client to fetch a different URL -RedirectRelative On|Off Off svdBAllows relative redirect targets. -RedirectTemp URL-path URLsvdhBSends an external temporary redirect asking the client to fetch +RedirectRelative On|Off Off svdBAllows relative redirect targets. +RedirectTemp URL-path URLsvdhBSends an external temporary redirect asking the client to fetch a different URL -RedisConnPoolTTL num[units] 15s svETTL used for the connection pool with the Redis server(s) -RedisTimeout num[units] 5s svER/W timeout used for the connection with the Redis server(s) -ReflectorHeader inputheader [outputheader]svdhBReflect an input header to the output headers -RegexDefaultOptions [none] [+|-]option [[+|-]option] ... DOTALL DOLLAR_ENDON +sCAllow to configure global/default options for regexes -RegisterHttpMethod method [method [...]]sCRegister non-standard HTTP methods -RemoteIPHeader header-fieldsvBDeclare the header field which should be parsed for useragent IP addresses -RemoteIPInternalProxy proxy-ip|proxy-ip/subnet|hostname ...svBDeclare client intranet IP addresses trusted to present the RemoteIPHeader value -RemoteIPInternalProxyList filenamesvBDeclare client intranet IP addresses trusted to present the RemoteIPHeader value -RemoteIPProxiesHeader HeaderFieldNamesvBDeclare the header field which will record all intermediate IP addresses -RemoteIPProxyProtocol On|OffsvBEnable or disable PROXY protocol handling -RemoteIPProxyProtocolExceptions host|range [host|range] [host|range]svBDisable processing of PROXY header for certain hosts or networks -RemoteIPTrustedProxy proxy-ip|proxy-ip/subnet|hostname ...svBDeclare client intranet IP addresses trusted to present the RemoteIPHeader value -RemoteIPTrustedProxyList filenamesvBDeclare client intranet IP addresses trusted to present the RemoteIPHeader value -RemoveCharset extension [extension] -...vdhBRemoves any character set associations for a set of file +RedisConnPoolTTL num[units] 15s svETTL used for the connection pool with the Redis server(s) +RedisTimeout num[units] 5s svER/W timeout used for the connection with the Redis server(s) +ReflectorHeader inputheader [outputheader]svdhBReflect an input header to the output headers +RegexDefaultOptions [none] [+|-]option [[+|-]option] ... DOTALL DOLLAR_ENDON +sCAllow to configure global/default options for regexes +RegisterHttpMethod method [method [...]]sCRegister non-standard HTTP methods +RemoteIPHeader header-fieldsvBDeclare the header field which should be parsed for useragent IP addresses +RemoteIPInternalProxy proxy-ip|proxy-ip/subnet|hostname ...svBDeclare client intranet IP addresses trusted to present the RemoteIPHeader value +RemoteIPInternalProxyList filenamesvBDeclare client intranet IP addresses trusted to present the RemoteIPHeader value +RemoteIPProxiesHeader HeaderFieldNamesvBDeclare the header field which will record all intermediate IP addresses +RemoteIPProxyProtocol On|OffsvBEnable or disable PROXY protocol handling +RemoteIPProxyProtocolExceptions host|range [host|range] [host|range]svBDisable processing of PROXY header for certain hosts or networks +RemoteIPTrustedProxy proxy-ip|proxy-ip/subnet|hostname ...svBDeclare client intranet IP addresses trusted to present the RemoteIPHeader value +RemoteIPTrustedProxyList filenamesvBDeclare client intranet IP addresses trusted to present the RemoteIPHeader value +RemoveCharset extension [extension] +...vdhBRemoves any character set associations for a set of file extensions -RemoveEncoding extension [extension] -...vdhBRemoves any content encoding associations for a set of file +RemoveEncoding extension [extension] +...vdhBRemoves any content encoding associations for a set of file extensions -RemoveHandler extension [extension] -...vdhBRemoves any handler associations for a set of file +RemoveHandler extension [extension] +...vdhBRemoves any handler associations for a set of file extensions -RemoveInputFilter extension [extension] -...vdhBRemoves any input filter associations for a set of file +RemoveInputFilter extension [extension] +...vdhBRemoves any input filter associations for a set of file extensions -RemoveLanguage extension [extension] -...vdhBRemoves any language associations for a set of file +RemoveLanguage extension [extension] +...vdhBRemoves any language associations for a set of file extensions -RemoveOutputFilter extension [extension] -...vdhBRemoves any output filter associations for a set of file +RemoveOutputFilter extension [extension] +...vdhBRemoves any output filter associations for a set of file extensions -RemoveType extension [extension] -...vdhBRemoves any content type associations for a set of file +RemoveType extension [extension] +...vdhBRemoves any content type associations for a set of file extensions -RequestHeader add|append|edit|edit*|merge|set|setifempty|unset +RequestHeader add|append|edit|edit*|merge|set|setifempty|unset header [[expr=]value [replacement] [early|env=[!]varname|expr=expression]] -svdhEConfigure HTTP request headers -RequestReadTimeout +svdhEConfigure HTTP request headers +RequestReadTimeout [handshake=timeout[-maxtimeout][,MinRate=rate] [header=timeout[-maxtimeout][,MinRate=rate] [body=timeout[-maxtimeout][,MinRate=rate] - handshake=0 header= +svESet timeout values for completing the TLS handshake, receiving + handshake=0 header= +svESet timeout values for completing the TLS handshake, receiving the request headers and/or body from client. -Require [not] entity-name - [entity-name] ...dhBTests whether an authenticated user is authorized by +Require [not] entity-name + [entity-name] ...dhBTests whether an authenticated user is authorized by an authorization provider. -<RequireAll> ... </RequireAll>dhBEnclose a group of authorization directives of which none +<RequireAll> ... </RequireAll>dhBEnclose a group of authorization directives of which none must fail and at least one must succeed for the enclosing directive to succeed. -<RequireAny> ... </RequireAny>dhBEnclose a group of authorization directives of which one +<RequireAny> ... </RequireAny>dhBEnclose a group of authorization directives of which one must succeed for the enclosing directive to succeed. -<RequireNone> ... </RequireNone>dhBEnclose a group of authorization directives of which none +<RequireNone> ... </RequireNone>dhBEnclose a group of authorization directives of which none must succeed for the enclosing directive to not fail. -RewriteBase URL-pathdhESets the base URL for per-directory rewrites - RewriteCond - TestString [!]CondPattern [flags]svdhEDefines a condition under which rewriting will take place +RewriteBase URL-pathdhESets the base URL for per-directory rewrites + RewriteCond + TestString [!]CondPattern [flags]svdhEDefines a condition under which rewriting will take place -RewriteEngine on|off off svdhEEnables or disables runtime rewriting engine -RewriteMap MapName MapType:MapSource +RewriteEngine on|off off svdhEEnables or disables runtime rewriting engine +RewriteMap MapName MapType:MapSource [MapTypeOptions] -svEDefines a mapping function for key-lookup -RewriteOptions OptionssvdhESets some special options for the rewrite engine -RewriteRule - [!]Pattern Substitution [flags]svdhEDefines rules for the rewriting engine -RLimitCPU seconds|max [seconds|max]svdhCLimits the CPU consumption of processes launched +svEDefines a mapping function for key-lookup +RewriteOptions OptionssvdhESets some special options for the rewrite engine +RewriteRule + [!]Pattern Substitution [flags]svdhEDefines rules for the rewriting engine +RLimitCPU seconds|max [seconds|max]svdhCLimits the CPU consumption of processes launched by Apache httpd children -RLimitMEM bytes|max [bytes|max]svdhCLimits the memory consumption of processes launched +RLimitMEM bytes|max [bytes|max]svdhCLimits the memory consumption of processes launched by Apache httpd children -RLimitNPROC number|max [number|max]svdhCLimits the number of processes that can be launched by +RLimitNPROC number|max [number|max]svdhCLimits the number of processes that can be launched by processes launched by Apache httpd children -Satisfy Any|All All dhInteraction between host-level access control and +Satisfy Any|All All dhInteraction between host-level access control and user authentication -ScoreBoardFile file-path logs/apache_runtime +sMLocation of the file used to store coordination data for +ScoreBoardFile file-path logs/apache_runtime +sMLocation of the file used to store coordination data for the child processes -Script method cgi-scriptsvdBActivates a CGI script for a particular request +Script method cgi-scriptsvdBActivates a CGI script for a particular request method. -ScriptAlias [URL-path] -file-path|directory-pathsvdBMaps a URL to a filesystem location and designates the +ScriptAlias [URL-path] +file-path|directory-pathsvdBMaps a URL to a filesystem location and designates the target as a CGI script -ScriptAliasMatch regex -file-path|directory-pathsvBMaps a URL to a filesystem location using a regular expression +ScriptAliasMatch regex +file-path|directory-pathsvBMaps a URL to a filesystem location using a regular expression and designates the target as a CGI script -ScriptInterpreterSource Registry|Registry-Strict|Script Script svdhCTechnique for locating the interpreter for CGI +ScriptInterpreterSource Registry|Registry-Strict|Script Script svdhCTechnique for locating the interpreter for CGI scripts -ScriptLog file-pathsvBLocation of the CGI script error logfile -ScriptLogBuffer bytes 1024 svBMaximum amount of PUT or POST requests that will be recorded +ScriptLog file-pathsvBLocation of the CGI script error logfile +ScriptLogBuffer bytes 1024 svBMaximum amount of PUT or POST requests that will be recorded in the scriptlog -ScriptLogLength bytes 10385760 svBSize limit of the CGI script logfile -ScriptSock file-path cgisock sBThe filename prefix of the socket to use for communication with +ScriptLogLength bytes 10385760 svBSize limit of the CGI script logfile +ScriptSock file-path cgisock sBThe filename prefix of the socket to use for communication with the cgi daemon -SecureListen [IP-address:]portnumber -Certificate-Name [MUTUAL]sBEnables SSL encryption for the specified port -SeeRequestTail On|Off Off sCDetermine if mod_status displays the first 63 characters +SecureListen [IP-address:]portnumber +Certificate-Name [MUTUAL]sBEnables SSL encryption for the specified port +SeeRequestTail On|Off Off sCDetermine if mod_status displays the first 63 characters of a request or the last 63, assuming the request itself is greater than 63 chars. -SendBufferSize bytes 0 sMTCP buffer size -ServerAdmin email-address|URLsvCEmail address that the server includes in error +SendBufferSize bytes 0 sMTCP buffer size +ServerAdmin email-address|URLsvCEmail address that the server includes in error messages sent to the client -ServerAlias hostname [hostname] ...vCAlternate names for a host used when matching requests +ServerAlias hostname [hostname] ...vCAlternate names for a host used when matching requests to name-virtual hosts -ServerLimit numbersMUpper limit on configurable number of processes -ServerName [scheme://]domain-name|ip-address[:port]svCHostname and port that the server uses to identify +ServerLimit numbersMUpper limit on configurable number of processes +ServerName [scheme://]domain-name|ip-address[:port]svCHostname and port that the server uses to identify itself -ServerPath URL-pathvCLegacy URL pathname for a name-based virtual host that +ServerPath URL-pathvCLegacy URL pathname for a name-based virtual host that is accessed by an incompatible browser -ServerRoot directory-path /usr/local/apache sCBase directory for the server installation -ServerSignature On|Off|EMail Off svdhCConfigures the footer on server-generated documents -ServerTokens Major|Minor|Min[imal]|Prod[uctOnly]|OS|Full Full sCConfigures the Server HTTP response +ServerRoot directory-path /usr/local/apache sCBase directory for the server installation +ServerSignature On|Off|EMail Off svdhCConfigures the footer on server-generated documents +ServerTokens Major|Minor|Min[imal]|Prod[uctOnly]|OS|Full Full sCConfigures the Server HTTP response header -Session On|Off Off svdhEEnables a session for the current directory or location -SessionCookieName name attributessvdhEName and attributes for the RFC2109 cookie storing the session -SessionCookieName2 name attributessvdhEName and attributes for the RFC2965 cookie storing the session -SessionCookieRemove On|Off Off svdhEControl for whether session cookies should be removed from incoming HTTP headers -SessionCryptoCipher name aes256 svdhXThe crypto cipher to be used to encrypt the session -SessionCryptoDriver name [param[=value]]sXThe crypto driver to be used to encrypt the session -SessionCryptoPassphrase secret [ secret ... ] svdhXThe key used to encrypt the session -SessionCryptoPassphraseFile filenamesvdXFile containing keys used to encrypt the session -SessionDBDCookieName name attributessvdhEName and attributes for the RFC2109 cookie storing the session ID -SessionDBDCookieName2 name attributessvdhEName and attributes for the RFC2965 cookie storing the session ID -SessionDBDCookieRemove On|Off On svdhEControl for whether session ID cookies should be removed from incoming HTTP headers -SessionDBDDeleteLabel label deletesession svdhEThe SQL query to use to remove sessions from the database -SessionDBDInsertLabel label insertsession svdhEThe SQL query to use to insert sessions into the database -SessionDBDPerUser On|Off Off svdhEEnable a per user session -SessionDBDSelectLabel label selectsession svdhEThe SQL query to use to select sessions from the database -SessionDBDUpdateLabel label updatesession svdhEThe SQL query to use to update existing sessions in the database -SessionEnv On|Off Off svdhEControl whether the contents of the session are written to the +Session On|Off Off svdhEEnables a session for the current directory or location +SessionCookieName name attributessvdhEName and attributes for the RFC2109 cookie storing the session +SessionCookieName2 name attributessvdhEName and attributes for the RFC2965 cookie storing the session +SessionCookieRemove On|Off Off svdhEControl for whether session cookies should be removed from incoming HTTP headers +SessionCryptoCipher name aes256 svdhXThe crypto cipher to be used to encrypt the session +SessionCryptoDriver name [param[=value]]sXThe crypto driver to be used to encrypt the session +SessionCryptoPassphrase secret [ secret ... ] svdhXThe key used to encrypt the session +SessionCryptoPassphraseFile filenamesvdXFile containing keys used to encrypt the session +SessionDBDCookieName name attributessvdhEName and attributes for the RFC2109 cookie storing the session ID +SessionDBDCookieName2 name attributessvdhEName and attributes for the RFC2965 cookie storing the session ID +SessionDBDCookieRemove On|Off On svdhEControl for whether session ID cookies should be removed from incoming HTTP headers +SessionDBDDeleteLabel label deletesession svdhEThe SQL query to use to remove sessions from the database +SessionDBDInsertLabel label insertsession svdhEThe SQL query to use to insert sessions into the database +SessionDBDPerUser On|Off Off svdhEEnable a per user session +SessionDBDSelectLabel label selectsession svdhEThe SQL query to use to select sessions from the database +SessionDBDUpdateLabel label updatesession svdhEThe SQL query to use to update existing sessions in the database +SessionEnv On|Off Off svdhEControl whether the contents of the session are written to the HTTP_SESSION environment variable -SessionExclude pathsvdhEDefine URL prefixes for which a session is ignored -SessionExpiryUpdateInterval interval 0 (always update) svdhEDefine the number of seconds a session's expiry may change without +SessionExclude pathsvdhEDefine URL prefixes for which a session is ignored +SessionExpiryUpdateInterval interval 0 (always update) svdhEDefine the number of seconds a session's expiry may change without the session being updated -SessionHeader headersvdhEImport session updates from a given HTTP response header -SessionInclude pathsvdhEDefine URL prefixes for which a session is valid -SessionMaxAge maxage 0 svdhEDefine a maximum age in seconds for a session -SetEnv env-variable [value]svdhBSets environment variables -SetEnvIf attribute +SessionHeader headersvdhEImport session updates from a given HTTP response header +SessionInclude pathsvdhEDefine URL prefixes for which a session is valid +SessionMaxAge maxage 0 svdhEDefine a maximum age in seconds for a session +SetEnv env-variable [value]svdhBSets environment variables +SetEnvIf attribute regex [!]env-variable[=value] - [[!]env-variable[=value]] ...svdhBSets environment variables based on attributes of the request + [[!]env-variable[=value]] ...svdhBSets environment variables based on attributes of the request -SetEnvIfExpr expr +SetEnvIfExpr expr [!]env-variable[=value] - [[!]env-variable[=value]] ...svdhBSets environment variables based on an ap_expr expression -SetEnvIfNoCase attribute regex + [[!]env-variable[=value]] ...svdhBSets environment variables based on an ap_expr expression +SetEnvIfNoCase attribute regex [!]env-variable[=value] - [[!]env-variable[=value]] ...svdhBSets environment variables based on attributes of the request + [[!]env-variable[=value]] ...svdhBSets environment variables based on attributes of the request without respect to case -SetHandler handler-name|none|expressionsvdhCForces all matching files to be processed by a +SetHandler handler-name|none|expressionsvdhCForces all matching files to be processed by a handler -SetInputFilter filter[;filter...]svdhCSets the filters that will process client requests and POST +SetInputFilter filter[;filter...]svdhCSets the filters that will process client requests and POST input -SetOutputFilter filter[;filter...]svdhCSets the filters that will process responses from the +SetOutputFilter filter[;filter...]svdhCSets the filters that will process responses from the server -SSIEndTag tag "-->" svBString that ends an include element -SSIErrorMsg message "[an error occurred +svdhBError message displayed when there is an SSI +SSIEndTag tag "-->" svBString that ends an include element +SSIErrorMsg message "[an error occurred +svdhBError message displayed when there is an SSI error -SSIETag on|off off dhBControls whether ETags are generated by the server. -SSILastModified on|off off dhBControls whether Last-Modified headers are generated by the +SSIETag on|off off dhBControls whether ETags are generated by the server. +SSILastModified on|off off dhBControls whether Last-Modified headers are generated by the server. -SSILegacyExprParser on|off off dhBEnable compatibility mode for conditional expressions. -SSIStartTag tag "<!--#" svBString that starts an include element -SSITimeFormat formatstring "%A, %d-%b-%Y %H:%M +svdhBConfigures the format in which date strings are +SSILegacyExprParser on|off off dhBEnable compatibility mode for conditional expressions. +SSIStartTag tag "<!--#" svBString that starts an include element +SSITimeFormat formatstring "%A, %d-%b-%Y %H:%M +svdhBConfigures the format in which date strings are displayed -SSIUndefinedEcho string "(none)" svdhBString displayed when an unset variable is echoed -SSLCACertificateFile file-pathsvEFile of concatenated PEM-encoded CA Certificates +SSIUndefinedEcho string "(none)" svdhBString displayed when an unset variable is echoed +SSLCACertificateFile file-pathsvEFile of concatenated PEM-encoded CA Certificates for Client Auth -SSLCACertificatePath directory-pathsvEDirectory of PEM-encoded CA Certificates for +SSLCACertificatePath directory-pathsvEDirectory of PEM-encoded CA Certificates for Client Auth -SSLCADNRequestFile file-pathsvEFile of concatenated PEM-encoded CA Certificates +SSLCADNRequestFile file-pathsvEFile of concatenated PEM-encoded CA Certificates for defining acceptable CA names -SSLCADNRequestPath directory-pathsvEDirectory of PEM-encoded CA Certificates for +SSLCADNRequestPath directory-pathsvEDirectory of PEM-encoded CA Certificates for defining acceptable CA names -SSLCARevocationCheck chain|leaf|none [flags ...] none svEEnable CRL-based revocation checking -SSLCARevocationFile file-pathsvEFile of concatenated PEM-encoded CA CRLs for +SSLCARevocationCheck chain|leaf|none [flags ...] none svEEnable CRL-based revocation checking +SSLCARevocationFile file-pathsvEFile of concatenated PEM-encoded CA CRLs for Client Auth -SSLCARevocationPath directory-pathsvEDirectory of PEM-encoded CA CRLs for +SSLCARevocationPath directory-pathsvEDirectory of PEM-encoded CA CRLs for Client Auth -SSLCertificateChainFile file-pathsvEFile of PEM-encoded Server CA Certificates -SSLCertificateFile file-path|certidsvEServer PEM-encoded X.509 certificate data file or token identifier -SSLCertificateKeyFile file-path|keyidsvEServer PEM-encoded private key file -SSLCipherSuite [protocol] cipher-spec DEFAULT (depends on +svdhECipher Suite available for negotiation in SSL +SSLCertificateChainFile file-pathsvEFile of PEM-encoded Server CA Certificates +SSLCertificateFile file-path|certidsvEServer PEM-encoded X.509 certificate data file or token identifier +SSLCertificateKeyFile file-path|keyidsvEServer PEM-encoded private key file +SSLCipherSuite [protocol] cipher-spec DEFAULT (depends on +svdhECipher Suite available for negotiation in SSL handshake -SSLCompression on|off off svEEnable compression on the SSL level -SSLCryptoDevice engine builtin sEEnable use of a cryptographic hardware accelerator -SSLEngine on|off off svESSL Engine Operation Switch -SSLFIPS on|off off sESSL FIPS mode Switch -SSLHonorCipherOrder on|off off svEOption to prefer the server's cipher preference order -SSLInsecureRenegotiation on|off off svEOption to enable support for insecure renegotiation -SSLOCSPDefaultResponder urisvESet the default responder URI for OCSP validation -SSLOCSPEnable on|leaf|off [flags] off svEEnable OCSP validation of the client certificate chain -SSLOCSPNoverify on|off off svEskip the OCSP responder certificates verification -SSLOCSPOverrideResponder on|off off svEForce use of the default responder URI for OCSP validation -SSLOCSPProxyURL urlsvEProxy URL to use for OCSP requests -SSLOCSPResponderCertificateFile filesvESet of trusted PEM encoded OCSP responder certificates -SSLOCSPResponderTimeout seconds 10 svETimeout for OCSP queries -SSLOCSPResponseMaxAge seconds -1 svEMaximum allowable age for OCSP responses -SSLOCSPResponseTimeSkew seconds 300 svEMaximum allowable time skew for OCSP response validation -SSLOCSPUseRequestNonce on|off on svEUse a nonce within OCSP queries -SSLOpenSSLConfCmd command-name command-valuesvEConfigure OpenSSL parameters through its SSL_CONF API -SSLOptions [+|-]option ...svdhEConfigure various SSL engine run-time options -SSLPassPhraseDialog type builtin sEType of pass phrase dialog for encrypted private +SSLCompression on|off off svEEnable compression on the SSL level +SSLCryptoDevice engine builtin sEEnable use of a cryptographic hardware accelerator +SSLEngine on|off off svESSL Engine Operation Switch +SSLFIPS on|off off sESSL FIPS mode Switch +SSLHonorCipherOrder on|off off svEOption to prefer the server's cipher preference order +SSLInsecureRenegotiation on|off off svEOption to enable support for insecure renegotiation +SSLOCSPDefaultResponder urisvESet the default responder URI for OCSP validation +SSLOCSPEnable on|leaf|off [flags] off svEEnable OCSP validation of the client certificate chain +SSLOCSPNoverify on|off off svEskip the OCSP responder certificates verification +SSLOCSPOverrideResponder on|off off svEForce use of the default responder URI for OCSP validation +SSLOCSPProxyURL urlsvEProxy URL to use for OCSP requests +SSLOCSPResponderCertificateFile filesvESet of trusted PEM encoded OCSP responder certificates +SSLOCSPResponderTimeout seconds 10 svETimeout for OCSP queries +SSLOCSPResponseMaxAge seconds -1 svEMaximum allowable age for OCSP responses +SSLOCSPResponseTimeSkew seconds 300 svEMaximum allowable time skew for OCSP response validation +SSLOCSPUseRequestNonce on|off on svEUse a nonce within OCSP queries +SSLOpenSSLConfCmd command-name command-valuesvEConfigure OpenSSL parameters through its SSL_CONF API +SSLOptions [+|-]option ...svdhEConfigure various SSL engine run-time options +SSLPassPhraseDialog type builtin sEType of pass phrase dialog for encrypted private keys -SSLProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svEConfigure usable SSL/TLS protocol versions -SSLProxyCACertificateFile file-pathsvEFile of concatenated PEM-encoded CA Certificates +SSLProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svEConfigure usable SSL/TLS protocol versions +SSLProxyCACertificateFile file-pathsvEFile of concatenated PEM-encoded CA Certificates for Remote Server Auth -SSLProxyCACertificatePath directory-pathsvEDirectory of PEM-encoded CA Certificates for +SSLProxyCACertificatePath directory-pathsvEDirectory of PEM-encoded CA Certificates for Remote Server Auth -SSLProxyCARevocationCheck chain|leaf|none none svEEnable CRL-based revocation checking for Remote Server Auth -SSLProxyCARevocationFile file-pathsvEFile of concatenated PEM-encoded CA CRLs for +SSLProxyCARevocationCheck chain|leaf|none none svEEnable CRL-based revocation checking for Remote Server Auth +SSLProxyCARevocationFile file-pathsvEFile of concatenated PEM-encoded CA CRLs for Remote Server Auth -SSLProxyCARevocationPath directory-pathsvEDirectory of PEM-encoded CA CRLs for +SSLProxyCARevocationPath directory-pathsvEDirectory of PEM-encoded CA CRLs for Remote Server Auth -SSLProxyCheckPeerCN on|off on svEWhether to check the remote server certificate's CN field +SSLProxyCheckPeerCN on|off on svEWhether to check the remote server certificate's CN field -SSLProxyCheckPeerExpire on|off on svEWhether to check if remote server certificate is expired +SSLProxyCheckPeerExpire on|off on svEWhether to check if remote server certificate is expired -SSLProxyCheckPeerName on|off on svEConfigure host name checking for remote server certificates +SSLProxyCheckPeerName on|off on svEConfigure host name checking for remote server certificates -SSLProxyCipherSuite [protocol] cipher-spec ALL:!ADH:RC4+RSA:+H +svECipher Suite available for negotiation in SSL +SSLProxyCipherSuite [protocol] cipher-spec ALL:!ADH:RC4+RSA:+H +svECipher Suite available for negotiation in SSL proxy handshake -SSLProxyEngine on|off off svESSL Proxy Engine Operation Switch -SSLProxyMachineCertificateChainFile filenamesvEFile of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate -SSLProxyMachineCertificateFile filenamesvEFile of concatenated PEM-encoded client certificates and keys to be used by the proxy -SSLProxyMachineCertificatePath directorysvEDirectory of PEM-encoded client certificates and keys to be used by the proxy -SSLProxyProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svEConfigure usable SSL protocol flavors for proxy usage -SSLProxyVerify level none svEType of remote server Certificate verification -SSLProxyVerifyDepth number 1 svEMaximum depth of CA Certificates in Remote Server +SSLProxyEngine on|off off svESSL Proxy Engine Operation Switch +SSLProxyMachineCertificateChainFile filenamesvEFile of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate +SSLProxyMachineCertificateFile filenamesvEFile of concatenated PEM-encoded client certificates and keys to be used by the proxy +SSLProxyMachineCertificatePath directorysvEDirectory of PEM-encoded client certificates and keys to be used by the proxy +SSLProxyProtocol [+|-]protocol ... all -SSLv3 (up to 2 +svEConfigure usable SSL protocol flavors for proxy usage +SSLProxyVerify level none svEType of remote server Certificate verification +SSLProxyVerifyDepth number 1 svEMaximum depth of CA Certificates in Remote Server Certificate verification -SSLRandomSeed context source -[bytes]sEPseudo Random Number Generator (PRNG) seeding +SSLRandomSeed context source +[bytes]sEPseudo Random Number Generator (PRNG) seeding source -SSLRenegBufferSize bytes 131072 dhESet the size for the SSL renegotiation buffer -SSLRequire expressiondhEAllow access only when an arbitrarily complex +SSLRenegBufferSize bytes 131072 dhESet the size for the SSL renegotiation buffer +SSLRequire expressiondhEAllow access only when an arbitrarily complex boolean expression is true -SSLRequireSSLdhEDeny access when SSL is not used for the +SSLRequireSSLdhEDeny access when SSL is not used for the HTTP request -SSLSessionCache type none sEType of the global/inter-process SSL Session +SSLSessionCache type none sEType of the global/inter-process SSL Session Cache -SSLSessionCacheTimeout seconds 300 svENumber of seconds before an SSL session expires +SSLSessionCacheTimeout seconds 300 svENumber of seconds before an SSL session expires in the Session Cache -SSLSessionTicketKeyFile file-pathsvEPersistent encryption/decryption key for TLS session tickets -SSLSessionTickets on|off on svEEnable or disable use of TLS session tickets -SSLSRPUnknownUserSeed secret-stringsvESRP unknown user seed -SSLSRPVerifierFile file-pathsvEPath to SRP verifier file -SSLStaplingCache typesEConfigures the OCSP stapling cache -SSLStaplingErrorCacheTimeout seconds 600 svENumber of seconds before expiring invalid responses in the OCSP stapling cache -SSLStaplingFakeTryLater on|off on svESynthesize "tryLater" responses for failed OCSP stapling queries -SSLStaplingForceURL urisvEOverride the OCSP responder URI specified in the certificate's AIA extension -SSLStaplingResponderTimeout seconds 10 svETimeout for OCSP stapling queries -SSLStaplingResponseMaxAge seconds -1 svEMaximum allowable age for OCSP stapling responses -SSLStaplingResponseTimeSkew seconds 300 svEMaximum allowable time skew for OCSP stapling response validation -SSLStaplingReturnResponderErrors on|off on svEPass stapling related OCSP errors on to client -SSLStaplingStandardCacheTimeout seconds 3600 svENumber of seconds before expiring responses in the OCSP stapling cache -SSLStrictSNIVHostCheck on|off off svEWhether to allow non-SNI clients to access a name-based virtual +SSLSessionTicketKeyFile file-pathsvEPersistent encryption/decryption key for TLS session tickets +SSLSessionTickets on|off on svEEnable or disable use of TLS session tickets +SSLSRPUnknownUserSeed secret-stringsvESRP unknown user seed +SSLSRPVerifierFile file-pathsvEPath to SRP verifier file +SSLStaplingCache typesEConfigures the OCSP stapling cache +SSLStaplingErrorCacheTimeout seconds 600 svENumber of seconds before expiring invalid responses in the OCSP stapling cache +SSLStaplingFakeTryLater on|off on svESynthesize "tryLater" responses for failed OCSP stapling queries +SSLStaplingForceURL urisvEOverride the OCSP responder URI specified in the certificate's AIA extension +SSLStaplingResponderTimeout seconds 10 svETimeout for OCSP stapling queries +SSLStaplingResponseMaxAge seconds -1 svEMaximum allowable age for OCSP stapling responses +SSLStaplingResponseTimeSkew seconds 300 svEMaximum allowable time skew for OCSP stapling response validation +SSLStaplingReturnResponderErrors on|off on svEPass stapling related OCSP errors on to client +SSLStaplingStandardCacheTimeout seconds 3600 svENumber of seconds before expiring responses in the OCSP stapling cache +SSLStrictSNIVHostCheck on|off off svEWhether to allow non-SNI clients to access a name-based virtual host. -SSLUserName varnamesdhEVariable name to determine user name -SSLUseStapling on|off off svEEnable stapling of OCSP responses in the TLS handshake -SSLVerifyClient level none svdhEType of Client Certificate verification -SSLVerifyDepth number 1 svdhEMaximum depth of CA Certificates in Client +SSLUserName varnamesdhEVariable name to determine user name +SSLUseStapling on|off off svEEnable stapling of OCSP responses in the TLS handshake +SSLVerifyClient level none svdhEType of Client Certificate verification +SSLVerifyDepth number 1 svdhEMaximum depth of CA Certificates in Client Certificate verification -SSLVHostSNIPolicy strict|secure|authonly|insecure secure sESet compatibility policy for SNI client access to virtual hosts. -StartServers numbersMNumber of child server processes created at startup -StartThreads numbersMNumber of threads created on startup -StrictHostCheck ON|OFF OFF svCControls whether the server requires the requested hostname be +SSLVHostSNIPolicy strict|secure|authonly|insecure secure sESet compatibility policy for SNI client access to virtual hosts. +StartServers numbersMNumber of child server processes created at startup +StartThreads numbersMNumber of threads created on startup +StrictHostCheck ON|OFF OFF svCControls whether the server requires the requested hostname be listed enumerated in the virtual host handling the request -Substitute s/pattern/substitution/[infq]dhEPattern to filter the response content -SubstituteInheritBefore on|off off dhEChange the merge order of inherited patterns -SubstituteMaxLineLength bytes(b|B|k|K|m|M|g|G) 1m dhESet the maximum line size -Suexec On|OffsBEnable or disable the suEXEC feature -SuexecUserGroup User GroupsvEUser and group for CGI programs to run as -ThreadLimit numbersMSets the upper limit on the configurable number of threads +Substitute s/pattern/substitution/[infq]dhEPattern to filter the response content +SubstituteInheritBefore on|off off dhEChange the merge order of inherited patterns +SubstituteMaxLineLength bytes(b|B|k|K|m|M|g|G) 1m dhESet the maximum line size +Suexec On|OffsBEnable or disable the suEXEC feature +SuexecUserGroup User GroupsvEUser and group for CGI programs to run as +ThreadLimit numbersMSets the upper limit on the configurable number of threads per child process -ThreadsPerChild numbersMNumber of threads created by each child process -ThreadStackSize sizesMThe size in bytes of the stack used by threads handling +ThreadsPerChild numbersMNumber of threads created by each child process +ThreadStackSize sizesMThe size in bytes of the stack used by threads handling client connections -TimeOut seconds 60 svCAmount of time the server will wait for +TimeOut seconds 60 svCAmount of time the server will wait for certain events before failing a request -TraceEnable [on|off|extended] on svCDetermines the behavior on TRACE requests -TransferLog file|pipesvBSpecify location of a log file -TypesConfig file-path conf/mime.types sBThe location of the mime.types file -UNCList hostname [hostname...]sCControls what UNC host names can be accessed by the server +TraceEnable [on|off|extended] on svCDetermines the behavior on TRACE requests +TransferLog file|pipesvBSpecify location of a log file +TypesConfig file-path conf/mime.types sBThe location of the mime.types file +UNCList hostname [hostname...]sCControls what UNC host names can be accessed by the server -UnDefine parameter-namesCUndefine the existence of a variable -UndefMacro namesvdBUndefine a macro -UnsetEnv env-variable [env-variable] -...svdhBRemoves variables from the environment -Use name [value1 ... valueN] -svdBUse a macro -UseCanonicalName On|Off|DNS Off svdCConfigures how the server determines its own name and +UnDefine parameter-namesCUndefine the existence of a variable +UndefMacro namesvdBUndefine a macro +UnsetEnv env-variable [env-variable] +...svdhBRemoves variables from the environment +Use name [value1 ... valueN] +svdBUse a macro +UseCanonicalName On|Off|DNS Off svdCConfigures how the server determines its own name and port -UseCanonicalPhysicalPort On|Off Off svdCConfigures how the server determines its own port -User unix-userid #-1 sBThe userid under which the server will answer +UseCanonicalPhysicalPort On|Off Off svdCConfigures how the server determines its own port +User unix-userid #-1 sBThe userid under which the server will answer requests -UserDir directory-filename [directory-filename] ... -svBLocation of the user-specific directories -VHostCGIMode On|Off|Secure On vDetermines whether the virtualhost can run +UserDir directory-filename [directory-filename] ... +svBLocation of the user-specific directories +VHostCGIMode On|Off|Secure On vDetermines whether the virtualhost can run subprocesses, and the privileges available to subprocesses. -VHostCGIPrivs [+-]?privilege-name [[+-]?privilege-name] ...vAssign arbitrary privileges to subprocesses created +VHostCGIPrivs [+-]?privilege-name [[+-]?privilege-name] ...vAssign arbitrary privileges to subprocesses created by a virtual host. -VHostGroup unix-groupidvSets the Group ID under which a virtual host runs. -VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vAssign arbitrary privileges to a virtual host. -VHostSecure On|Off On vDetermines whether the server runs with enhanced security +VHostGroup unix-groupidvSets the Group ID under which a virtual host runs. +VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vAssign arbitrary privileges to a virtual host. +VHostSecure On|Off On vDetermines whether the server runs with enhanced security for the virtualhost. -VHostUser unix-useridvSets the User ID under which a virtual host runs. -VirtualDocumentRoot interpolated-directory|none none svEDynamically configure the location of the document root +VHostUser unix-useridvSets the User ID under which a virtual host runs. +VirtualDocumentRoot interpolated-directory|none none svEDynamically configure the location of the document root for a given virtual host -VirtualDocumentRootIP interpolated-directory|none none svEDynamically configure the location of the document root +VirtualDocumentRootIP interpolated-directory|none none svEDynamically configure the location of the document root for a given virtual host -<VirtualHost +<VirtualHost addr[:port] [addr[:port]] - ...> ... </VirtualHost>sCContains directives that apply only to a specific + ...> ... </VirtualHost>sCContains directives that apply only to a specific hostname or IP address -VirtualScriptAlias interpolated-directory|none none svEDynamically configure the location of the CGI directory for +VirtualScriptAlias interpolated-directory|none none svEDynamically configure the location of the CGI directory for a given virtual host -VirtualScriptAliasIP interpolated-directory|none none svEDynamically configure the location of the CGI directory for +VirtualScriptAliasIP interpolated-directory|none none svEDynamically configure the location of the CGI directory for a given virtual host -WatchdogInterval time-interval[s] 1 sBWatchdog interval in seconds -XBitHack on|off|full off svdhBParse SSI directives in files with the execute bit +WatchdogInterval time-interval[s] 1 sBWatchdog interval in seconds +XBitHack on|off|full off svdhBParse SSI directives in files with the execute bit set -xml2EncAlias charset alias [alias ...]sBRecognize Aliases for encoding values -xml2EncDefault namesvdhBSets a default encoding to assume when absolutely no information +xml2EncAlias charset alias [alias ...]sBRecognize Aliases for encoding values +xml2EncDefault namesvdhBSets a default encoding to assume when absolutely no information can be automatically detected -xml2StartParse element [element ...]svdhBAdvise the parser to skip leading junk. +xml2StartParse element [element ...]svdhBAdvise the parser to skip leading junk.

    å�¯ç”¨è¯­è¨€:  de  | diff -Nru apache2-2.4.69/docs/manual/mod/worker.html.de apache2-2.4.71/docs/manual/mod/worker.html.de --- apache2-2.4.69/docs/manual/mod/worker.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/worker.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -32,9 +32,6 @@  ja  |  tr 

    -
    Diese Übersetzung ist möglicherweise - nicht mehr aktuell. Bitte prüfen Sie die englische Version auf - die neuesten Änderungen.
    diff -Nru apache2-2.4.69/docs/manual/mod/worker.html.ja.utf8 apache2-2.4.71/docs/manual/mod/worker.html.ja.utf8 --- apache2-2.4.69/docs/manual/mod/worker.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/worker.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  ja  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
    Beschreibung:Multi-Processing-Modul, das einen Hybrid-Webserver mit Multi-Thread und Multi-Prozess-Unterstützung implementiert
    Status:MPM
    diff -Nru apache2-2.4.69/docs/manual/mod/worker.html.tr.utf8 apache2-2.4.71/docs/manual/mod/worker.html.tr.utf8 --- apache2-2.4.69/docs/manual/mod/worker.html.tr.utf8 2026-06-16 16:46:03.000000000 +0000 +++ apache2-2.4.71/docs/manual/mod/worker.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,7 +32,6 @@  ja  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.
    説明:マル�スレッド�マル�プロセス��イブリッド型 ウェブサー�を実装��マル�プロセッシングモジュール
    ステータス:MPM
    diff -Nru apache2-2.4.69/docs/manual/mpm.html.de apache2-2.4.71/docs/manual/mpm.html.de --- apache2-2.4.69/docs/manual/mpm.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mpm.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -32,9 +32,6 @@  tr  |  zh-cn 

    -
    Diese Übersetzung ist möglicherweise - nicht mehr aktuell. Bitte prüfen Sie die englische Version auf - die neuesten Änderungen.

    Das Dokument beschreibt, was ein Multi-Processing-Modul ist und wie solche Module beim Apache HTTP Server verwendet werden.

    diff -Nru apache2-2.4.69/docs/manual/mpm.html.en apache2-2.4.71/docs/manual/mpm.html.en --- apache2-2.4.69/docs/manual/mpm.html.en 2026-06-16 16:46:03.000000000 +0000 +++ apache2-2.4.71/docs/manual/mpm.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -95,7 +95,7 @@ systems. This will be the MPM selected if you do not make another choice at compile-time.

    -
    Açıklama:Çok evreli ve çok süreçli melez bir HTTP sunucusu oluÅŸturan çok süreçlilik modülü.
    Durum:MPM
    +
    Netwarempm_netware
    diff -Nru apache2-2.4.69/docs/manual/mpm.html.es apache2-2.4.71/docs/manual/mpm.html.es --- apache2-2.4.69/docs/manual/mpm.html.es 2026-06-16 16:46:03.000000000 +0000 +++ apache2-2.4.71/docs/manual/mpm.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  tr  |  zh-cn 

    -
    Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

    Este documento describe que es un Módulo de Multiprocesamiento y como los usa Apache.

    diff -Nru apache2-2.4.69/docs/manual/mpm.html.ja.utf8 apache2-2.4.71/docs/manual/mpm.html.ja.utf8 --- apache2-2.4.69/docs/manual/mpm.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mpm.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,10 +32,6 @@  tr  |  zh-cn 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

    ��文書��マル�プロセッシングモジュール���よ��も��� Apache HTTP サー����よ��使用�れる�����解説�����。

    diff -Nru apache2-2.4.69/docs/manual/mpm.html.ko.euc-kr apache2-2.4.71/docs/manual/mpm.html.ko.euc-kr --- apache2-2.4.69/docs/manual/mpm.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mpm.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -32,8 +32,6 @@  tr  |  zh-cn 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    ÀÌ ¹®¼­´Â ´ÙÁßó¸® ¸ðµâ (Multi-Processing Module)ÀÌ ¹«¾ùÀ̸ç, ¾ÆÆÄÄ¡ À¥¼­¹ö°¡ À̸¦ ¾î¶»°Ô »ç¿ëÇÏ´ÂÁö ¼³¸íÇÑ´Ù.

    diff -Nru apache2-2.4.69/docs/manual/mpm.html.tr.utf8 apache2-2.4.71/docs/manual/mpm.html.tr.utf8 --- apache2-2.4.69/docs/manual/mpm.html.tr.utf8 2026-06-16 16:46:03.000000000 +0000 +++ apache2-2.4.71/docs/manual/mpm.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,7 +32,6 @@  tr  |  zh-cn 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    Bu belgede Çok Süreçlilik Modülü denince ne anlaşıldığı ve bunların Apache HTTP Sunucusu tarafından nasıl kullanıldıkları açıklanmıştır.

    diff -Nru apache2-2.4.69/docs/manual/mpm.html.zh-cn.utf8 apache2-2.4.71/docs/manual/mpm.html.zh-cn.utf8 --- apache2-2.4.69/docs/manual/mpm.html.zh-cn.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/mpm.html.zh-cn.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -32,7 +32,6 @@  tr  |  zh-cn 

    -
    此翻译�能过期。�了解最近的更改,请阅读英文版。

    本文档介�了什么是多处�模�,以� Apache HTTP �务器如何使用它们。

    diff -Nru apache2-2.4.69/docs/manual/new_features_2_0.html.ja.utf8 apache2-2.4.71/docs/manual/new_features_2_0.html.ja.utf8 --- apache2-2.4.69/docs/manual/new_features_2_0.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/new_features_2_0.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  pt-br  |  tr 

    -
    ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

    ��文書���Apache HTTP サー��ージョン 1.3 � 2.0 �主�������記述�����。

    diff -Nru apache2-2.4.69/docs/manual/new_features_2_0.html.ko.euc-kr apache2-2.4.71/docs/manual/new_features_2_0.html.ko.euc-kr --- apache2-2.4.69/docs/manual/new_features_2_0.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/new_features_2_0.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  pt-br  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    ÀÌ ¹®¼­´Â ¾ÆÆÄÄ¡ À¥¼­¹ö 1.3 ¹öÀü°ú 2.0 ¹öÀü°£ÀÇ ÁÖµÈ Â÷ÀÌÁ¡À» ¼³¸íÇÑ´Ù.

    diff -Nru apache2-2.4.69/docs/manual/new_features_2_2.html.en apache2-2.4.71/docs/manual/new_features_2_2.html.en --- apache2-2.4.69/docs/manual/new_features_2_2.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/new_features_2_2.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -64,7 +64,7 @@
    Configuration
    The default configuration layout has been simplified and - modularised. Configuration snippets which can be used to + modularized. Configuration snippets which can be used to enable commonly-used features are now bundled with Apache, and can be easily added to the main server config.
    diff -Nru apache2-2.4.69/docs/manual/new_features_2_2.html.ko.euc-kr apache2-2.4.71/docs/manual/new_features_2_2.html.ko.euc-kr --- apache2-2.4.69/docs/manual/new_features_2_2.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/new_features_2_2.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -29,8 +29,6 @@  pt-br  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    ÀÌ ¹®¼­´Â ¾ÆÆÄÄ¡ À¥¼­¹ö 2.0 ¹öÀü°ú 2.2 ¹öÀü°£ÀÇ ÁÖµÈ Â÷ÀÌÁ¡À» ¼³¸íÇÑ´Ù. 1.3 ¹öÀü ÀÌÈÄÀÇ »õ·Î¿î ±â´ÉÀº 2.0 »õ·Î¿î ±â´É ¹®¼­¸¦ diff -Nru apache2-2.4.69/docs/manual/new_features_2_2.html.tr.utf8 apache2-2.4.71/docs/manual/new_features_2_2.html.tr.utf8 --- apache2-2.4.69/docs/manual/new_features_2_2.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/new_features_2_2.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,7 +29,6 @@  pt-br  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    Bu belgede Apache HTTP Sunucusunun 2.0 ve 2.2 sürümleri arasındaki baÅŸlıca farklara deÄŸinilmiÅŸtir. 1.3 sürümüne göre yeni özellikler için Apache 2.0'da Yeni olan Özellikler diff -Nru apache2-2.4.69/docs/manual/new_features_2_4.html.en apache2-2.4.71/docs/manual/new_features_2_4.html.en --- apache2-2.4.69/docs/manual/new_features_2_4.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/new_features_2_4.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -178,7 +178,7 @@

    Provides a Slot-based shared memory provider (ala the scoreboard).
    mod_xml2enc
    -
    Formerly a third-party module, this supports internationalisation +
    Formerly a third-party module, this supports internationalization in libxml2-based (markup-aware) filter modules.
    mod_macro (available since 2.4.5)
    @@ -198,13 +198,13 @@
    HTTP/2 Protocol backend for mod_proxy
    mod_proxy_hcheck (available since 2.4.21)
    -
    Support independent dynamic health checks for remote proxiy backend servers.
    +
    Support independent dynamic health checks for remote proxy backend servers.
    mod_brotli (available since 2.4.26)
    Support the Brotli compression algorithm.
    mod_md (available since 2.4.30)
    -
    Support the ACME protocol to automate certificate provisionning.
    +
    Support the ACME protocol to automate certificate provisioning.
    mod_proxy_uwsgi (available since 2.4.30)
    UWSGI gateway module for mod_proxy.
    @@ -387,7 +387,7 @@ to properly distinguish between the RFC2616 HTTP/1.1 caching features provided by mod_cache, and the generic key/value caching provided by the socache - interface, as well as to cover specialised caching provided by + interface, as well as to cover specialized caching provided by mechanisms such as mod_file_cache. diff -Nru apache2-2.4.69/docs/manual/new_features_2_4.html.tr.utf8 apache2-2.4.71/docs/manual/new_features_2_4.html.tr.utf8 --- apache2-2.4.69/docs/manual/new_features_2_4.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/new_features_2_4.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  pt-br  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    Bu belgede Apache HTTP Sunucusunun 2.2 ve 2.4 sürümleri arasındaki baÅŸlıca farklara deÄŸinilmiÅŸtir. 2.0 sürümüne göre yeni özellikler için Apache 2.2'de Yeni olan Özellikler diff -Nru apache2-2.4.69/docs/manual/platform/ebcdic.html.ko.euc-kr apache2-2.4.71/docs/manual/platform/ebcdic.html.ko.euc-kr --- apache2-2.4.69/docs/manual/platform/ebcdic.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/platform/ebcdic.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -26,8 +26,6 @@

    °¡´ÉÇÑ ¾ð¾î:  en  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    ÀÌ ¹®¼­´Â ¾ÆÆÄÄ¡ À¥¼­¹ö 2.0 ¹öÀü¿¡¼­ diff -Nru apache2-2.4.69/docs/manual/platform/index.html.en apache2-2.4.71/docs/manual/platform/index.html.en --- apache2-2.4.69/docs/manual/platform/index.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/platform/index.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -66,7 +66,7 @@
    -
    RPM Based Systems (Redhat / CentOS / Fedora)
    +
    RPM Based Systems (Red Hat / CentOS / Fedora)

    This document explains how to build, install, and run Apache 2.4 on systems supporting the RPM packaging format.

    diff -Nru apache2-2.4.69/docs/manual/platform/index.html.ko.euc-kr apache2-2.4.71/docs/manual/platform/index.html.ko.euc-kr --- apache2-2.4.69/docs/manual/platform/index.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/platform/index.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -28,8 +28,6 @@  ko  |  zh-cn 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
    • Microsoft Windows
    • ±âŸ Ç÷¡Æû
    • diff -Nru apache2-2.4.69/docs/manual/platform/index.html.zh-cn.utf8 apache2-2.4.71/docs/manual/platform/index.html.zh-cn.utf8 --- apache2-2.4.69/docs/manual/platform/index.html.zh-cn.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/platform/index.html.zh-cn.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  ko  |  zh-cn 

    -
    此翻译�能过期。�了解最近的更改,请阅读英文版。
    • Microsoft Windows
    • 其它平å�°
    • diff -Nru apache2-2.4.69/docs/manual/platform/netware.html.ko.euc-kr apache2-2.4.71/docs/manual/platform/netware.html.ko.euc-kr --- apache2-2.4.69/docs/manual/platform/netware.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/platform/netware.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -27,8 +27,6 @@  fr  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    ÀÌ ¹®¼­´Â Novell NetWare 6.0 À̻󿡼­ ¾ÆÆÄÄ¡ 2.0À» ¼³Ä¡, diff -Nru apache2-2.4.69/docs/manual/platform/rpm.html.en apache2-2.4.71/docs/manual/platform/rpm.html.en --- apache2-2.4.69/docs/manual/platform/rpm.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/platform/rpm.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -6,7 +6,7 @@ This file is generated from xml source: DO NOT EDIT XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --> -Using Apache With RPM Based Systems (Redhat / CentOS / Fedora) - Apache HTTP Server Version 2.4 +Using Apache With RPM Based Systems (Red Hat / CentOS / Fedora) - Apache HTTP Server Version 2.4 @@ -20,7 +20,7 @@

    <-

    Using Apache With RPM Based Systems (Redhat / CentOS / Fedora)

    +Apache > HTTP Server > Documentation > Version 2.4 > Platform Specific Notes

    Using Apache With RPM Based Systems (Red Hat / CentOS / Fedora)

    Available Languages:  en  | diff -Nru apache2-2.4.69/docs/manual/platform/win_compiling.html.en apache2-2.4.71/docs/manual/platform/win_compiling.html.en --- apache2-2.4.69/docs/manual/platform/win_compiling.html.en 2026-06-11 20:15:48.000000000 +0000 +++ apache2-2.4.71/docs/manual/platform/win_compiling.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -156,9 +156,9 @@ awk.exe is really a symlink to the gawk.exe file. The Windows command shell does not recognize symlinks, and because of this building InstallBin will fail. A workaround is - to delete awk.exe from the cygwin installation and + to delete awk.exe from the Cygwin installation and copy gawk.exe to awk.exe. Also note the - cygwin/mingw ports of gawk 3.0.x were buggy, please upgrade to 3.1.x + Cygwin/MinGW ports of gawk 3.0.x were buggy, please upgrade to 3.1.x before attempting to use any gawk port.

    @@ -227,7 +227,7 @@ odbc dbd providers are compiled unconditionally.

    The dbd support includes the Oracle instantclient package, MySQL, - PostgreSQL and sqlite. To build these all, for example, set up the + PostgreSQL and SQLite. To build these all, for example, set up the LIB to include the library path, INCLUDE to include the headers path, and PATH to include the dll bin path of all four SDK's, and set the DBD_LIST environment variable to inform the build which client driver diff -Nru apache2-2.4.69/docs/manual/platform/win_compiling.html.ko.euc-kr apache2-2.4.71/docs/manual/platform/win_compiling.html.ko.euc-kr --- apache2-2.4.69/docs/manual/platform/win_compiling.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/platform/win_compiling.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -27,8 +27,6 @@  fr  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    ¾ÆÆÄÄ¡¸¦ ÄÄÆÄÀÏÇϱâ Àü¿¡ ÁÖÀÇÇÒ Á¡ÀÌ ¸¹´Ù. ¹Ì¸® Microsoft Windows¿¡¼­ ¾ÆÆÄÄ¡ »ç¿ë¹ýÀ» diff -Nru apache2-2.4.69/docs/manual/platform/windows.html.en apache2-2.4.71/docs/manual/platform/windows.html.en --- apache2-2.4.69/docs/manual/platform/windows.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/platform/windows.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -318,11 +318,7 @@ memorize its password.

  • Grant the newly-created user a privilege of Log on - as a service and Act as part of the operating - system. On Windows NT 4.0 these privileges are granted via - User Manager for Domains, but on Windows 2000 and XP you probably - want to use Group Policy for propagating these settings. You can - also manually set these via the Local Security Policy MMC snap-in. + as a service.
  • Confirm that the created account is a member of the Users diff -Nru apache2-2.4.69/docs/manual/platform/windows.html.fr.utf8 apache2-2.4.71/docs/manual/platform/windows.html.fr.utf8 --- apache2-2.4.69/docs/manual/platform/windows.html.fr.utf8 2026-06-08 12:25:13.000000000 +0000 +++ apache2-2.4.71/docs/manual/platform/windows.html.fr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -344,13 +344,7 @@ assurez-vous de retenir son mot de passe.
  • Accordez à l'utilisateur nouvellement créé les privilèges - Log on as a service et Act as part of the - operating system. Sous Windows NT 4.0, ces privilèges sont - accordés via le Gestionnaire des utilisateurs du Domaine, mais - sous Windows 2000 et XP, vous aurez plutôt intérêt à utiliser une - GPO pour propager ces configurations. Vous pouvez aussi effectuer - ces réglages via la Politique de Sécurité Locale intégrée à la - MMC.
  • + Log on as a service.
  • Vérifiez que le compte nouvellement créé est membre du groupe Utilisateurs
  • diff -Nru apache2-2.4.69/docs/manual/platform/windows.html.ko.euc-kr apache2-2.4.71/docs/manual/platform/windows.html.ko.euc-kr --- apache2-2.4.69/docs/manual/platform/windows.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/platform/windows.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -27,8 +27,6 @@  fr  |  ko 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    ÀÌ ¹®¼­´Â Microsoft Windows¿¡¼­ ¾ÆÆÄÄ¡ 2.0À» ¼³Ä¡, ¼³Á¤, diff -Nru apache2-2.4.69/docs/manual/programs/ab.html.ko.euc-kr apache2-2.4.71/docs/manual/programs/ab.html.ko.euc-kr --- apache2-2.4.69/docs/manual/programs/ab.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/ab.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -28,8 +28,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    ab´Â ¾ÆÆÄÄ¡ ÇÏÀÌÆÛÅØ½ºÆ® Àü¼Û ÇÁ·ÎÅäÄÝ (HTTP) ¼­¹öÀÇ ¼º´ÉÀ» °Ë»çÇÏ´Â(benchmarking) µµ±¸ÀÌ´Ù. ÇöÀç ¾ÆÆÄÄ¡°¡ diff -Nru apache2-2.4.69/docs/manual/programs/ab.html.tr.utf8 apache2-2.4.71/docs/manual/programs/ab.html.tr.utf8 --- apache2-2.4.69/docs/manual/programs/ab.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/ab.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    ab Apache Hiper Metin Aktarım Protokolü (HTTP) sunucunuzun baÅŸarımını ölçmek amacıyla kullanabileceÄŸiniz bir diff -Nru apache2-2.4.69/docs/manual/programs/apachectl.html.ko.euc-kr apache2-2.4.71/docs/manual/programs/apachectl.html.ko.euc-kr --- apache2-2.4.69/docs/manual/programs/apachectl.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/apachectl.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -28,8 +28,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    apachectlÀº ¾ÆÆÄÄ¡ ÇÏÀÌÆÛÅØ½ºÆ® Àü¼Û ÇÁ·ÎÅäÄÝ (HTTP) ¼­¹öÀÇ ¾Õ´ÜÀÌ´Ù. ÀÌ ÇÁ·Î±×·¥Àº °ü¸®ÀÚ°¡ diff -Nru apache2-2.4.69/docs/manual/programs/apachectl.html.tr.utf8 apache2-2.4.71/docs/manual/programs/apachectl.html.tr.utf8 --- apache2-2.4.69/docs/manual/programs/apachectl.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/apachectl.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    apachectl Apache Hiper Metin Aktarım Protokolü (HTTP) sunucusu için bir denetim aracıdır. Sistem diff -Nru apache2-2.4.69/docs/manual/programs/apxs.html.ko.euc-kr apache2-2.4.71/docs/manual/programs/apxs.html.ko.euc-kr --- apache2-2.4.69/docs/manual/programs/apxs.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/apxs.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -28,8 +28,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    apxs´Â ¾ÆÆÄÄ¡ ÇÏÀÌÆÛÅØ½ºÆ® Àü¼Û ÇÁ·ÎÅäÄÝ (HTTP) ¼­¹öÀÇ È®Àå¸ðµâÀ» ÄÄÆÄÀÏÇÏ°í ¼³Ä¡ÇÏ´Â µµ±¸ÀÌ´Ù. ÀÌ diff -Nru apache2-2.4.69/docs/manual/programs/apxs.html.tr.utf8 apache2-2.4.71/docs/manual/programs/apxs.html.tr.utf8 --- apache2-2.4.69/docs/manual/programs/apxs.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/apxs.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    apxs, Apache Hiper Metin Aktarım Protokolü (HTTP) sunucusu için ek modül derleme ve kurulum aracıdır. Bu diff -Nru apache2-2.4.69/docs/manual/programs/configure.html.ko.euc-kr apache2-2.4.71/docs/manual/programs/configure.html.ko.euc-kr --- apache2-2.4.69/docs/manual/programs/configure.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/configure.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -28,8 +28,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    configure ½ºÅ©¸³Æ®´Â ƯÁ¤ Ç÷¡Æû¿¡¼­ ¾ÆÆÄÄ¡ À¥¼­¹ö¸¦ ÄÄÆÄÀÏÇÏ°í ¼³Ä¡ÇϱâÀ§ÇØ ¼Ò½º Æ®¸®¸¦ ±¸¼ºÇÑ´Ù. ¿©·¯ diff -Nru apache2-2.4.69/docs/manual/programs/configure.html.tr.utf8 apache2-2.4.71/docs/manual/programs/configure.html.tr.utf8 --- apache2-2.4.69/docs/manual/programs/configure.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/configure.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    configure betiÄŸi, Apache HTTP Sunucusunun kaynak kodlarını belli bir platform için yapılandırmakta ve derlemekte kullanılır. diff -Nru apache2-2.4.69/docs/manual/programs/dbmmanage.html.en apache2-2.4.71/docs/manual/programs/dbmmanage.html.en --- apache2-2.4.69/docs/manual/programs/dbmmanage.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/dbmmanage.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -99,10 +99,10 @@

    Encodings

    -d
    -
    crypt hashing (default, except on Win32, Netware)
    +
    crypt hashing (default, except on Win32, NetWare)
    -m
    -
    MD5 hashing (default on Win32, Netware)
    +
    MD5 hashing (default on Win32, NetWare)
    -s
    SHA1 hashing
    diff -Nru apache2-2.4.69/docs/manual/programs/dbmmanage.html.ko.euc-kr apache2-2.4.71/docs/manual/programs/dbmmanage.html.ko.euc-kr --- apache2-2.4.69/docs/manual/programs/dbmmanage.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/dbmmanage.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -28,8 +28,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    dbmmanage´Â HTTP basic authentication¿¡ »ç¿ëÇÒ »ç¿ëÀÚ¸í°ú ¾ÏÈ£¸¦ ÀúÀåÇÏ´Â DBMÇü½ÄÀÇ ÆÄÀÏÀ» ¸¸µé°í diff -Nru apache2-2.4.69/docs/manual/programs/dbmmanage.html.tr.utf8 apache2-2.4.71/docs/manual/programs/dbmmanage.html.tr.utf8 --- apache2-2.4.69/docs/manual/programs/dbmmanage.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/dbmmanage.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    dbmmanage, mod_authn_dbm üzerinden HTTP kullanıcılarının temel diff -Nru apache2-2.4.69/docs/manual/programs/htcacheclean.html.ko.euc-kr apache2-2.4.71/docs/manual/programs/htcacheclean.html.ko.euc-kr --- apache2-2.4.69/docs/manual/programs/htcacheclean.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/htcacheclean.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -28,8 +28,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    htcachecleanÀº mod_cache_disk°¡ »ç¿ëÇÏ´Â ÀúÀå¼Ò ¿ë·®À» ÀÏÁ¤ Çѵµ·Î À¯ÁöÇÑ´Ù. ÀÌ µµ±¸´Â Á÷Á¢ diff -Nru apache2-2.4.69/docs/manual/programs/htcacheclean.html.tr.utf8 apache2-2.4.71/docs/manual/programs/htcacheclean.html.tr.utf8 --- apache2-2.4.69/docs/manual/programs/htcacheclean.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/htcacheclean.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    htcacheclean, mod_cache_disk deposunun boyutlarını belli sınırlar diff -Nru apache2-2.4.69/docs/manual/programs/htdbm.html.en apache2-2.4.71/docs/manual/programs/htdbm.html.en --- apache2-2.4.69/docs/manual/programs/htdbm.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/htdbm.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -156,7 +156,7 @@ cannot be combined with the -c option.

    -m
    -
    Use MD5 hashing for passwords. On Windows and Netware, this is +
    Use MD5 hashing for passwords. On Windows and NetWare, this is the default.
    -B
    @@ -170,9 +170,9 @@
    -d
    Use crypt() hashing for passwords. The default on all - platforms but Windows and Netware. Though possibly supported by + platforms but Windows and NetWare. Though possibly supported by htdbm on all platforms, it is not supported by the - httpd server on Windows and Netware. + httpd server on Windows and NetWare. This algorithm is insecure by today's standards.
    -s
    @@ -183,7 +183,7 @@
    -p
    Use plaintext passwords. Though htdbm will support creation on all platforms, the httpd daemon will - only accept plain text passwords on Windows and Netware.
    + only accept plain text passwords on Windows and NetWare.
    -l
    Print each of the usernames and comments from the database on diff -Nru apache2-2.4.69/docs/manual/programs/htdbm.html.tr.utf8 apache2-2.4.71/docs/manual/programs/htdbm.html.tr.utf8 --- apache2-2.4.69/docs/manual/programs/htdbm.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/htdbm.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -27,7 +27,6 @@  fr  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    htdbm, mod_authn_dbm üzerinden HTTP kullanıcılarının temel diff -Nru apache2-2.4.69/docs/manual/programs/htdigest.html.ko.euc-kr apache2-2.4.71/docs/manual/programs/htdigest.html.ko.euc-kr --- apache2-2.4.69/docs/manual/programs/htdigest.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/htdigest.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    htdigest´Â HTTP »ç¿ëÀÚÀÇ digest authentication¿¡ »ç¿ëÇÒ »ç¿ëÀÚ¸í, ¿µ¿ª, ¾ÏÈ£¸¦ ÀúÀåÇÏ´Â ÀÏ¹ÝÆÄÀÏÀ» ¸¸µé°í diff -Nru apache2-2.4.69/docs/manual/programs/htdigest.html.tr.utf8 apache2-2.4.71/docs/manual/programs/htdigest.html.tr.utf8 --- apache2-2.4.69/docs/manual/programs/htdigest.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/htdigest.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    htdigest, HTTP kullanıcılarının digest türü kimlik doÄŸrulaması için kullanıcı isimlerinin ve parolalarının diff -Nru apache2-2.4.69/docs/manual/programs/htpasswd.html.en apache2-2.4.71/docs/manual/programs/htpasswd.html.en --- apache2-2.4.69/docs/manual/programs/htpasswd.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/htpasswd.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -177,7 +177,7 @@

    -d
    Use crypt() hashing for passwords. This is not supported by the httpd server on Windows and - Netware. This algorithm limits the password length to 8 characters. + NetWare. This algorithm limits the password length to 8 characters. This algorithm is insecure by today's standards. It used to be the default algorithm until version 2.2.17.
    @@ -190,7 +190,7 @@
    -p
    Use plaintext passwords. Though htpasswd will support creation on all platforms, the httpd daemon will - only accept plain text passwords on Windows and Netware.
    + only accept plain text passwords on Windows and NetWare.
    -D
    Delete user. If the username exists in the specified htpasswd file, it diff -Nru apache2-2.4.69/docs/manual/programs/htpasswd.html.ko.euc-kr apache2-2.4.71/docs/manual/programs/htpasswd.html.ko.euc-kr --- apache2-2.4.69/docs/manual/programs/htpasswd.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/htpasswd.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    htpasswd´Â HTTP basic authentication¿¡ »ç¿ëÇÒ »ç¿ëÀÚ¸í°ú ¾ÏÈ£¸¦ ÀúÀåÇÏ´Â ÀÏ¹ÝÆÄÀÏÀ» »ý¼ºÇÏ°í ¼öÁ¤ÇÑ´Ù. diff -Nru apache2-2.4.69/docs/manual/programs/htpasswd.html.tr.utf8 apache2-2.4.71/docs/manual/programs/htpasswd.html.tr.utf8 --- apache2-2.4.69/docs/manual/programs/htpasswd.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/htpasswd.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    htpasswd, HTTP kullanıcılarının temel kimlik doÄŸrulaması için kullanıcı isimlerinin ve parolalarının diff -Nru apache2-2.4.69/docs/manual/programs/httpd.html.ko.euc-kr apache2-2.4.71/docs/manual/programs/httpd.html.ko.euc-kr --- apache2-2.4.69/docs/manual/programs/httpd.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/httpd.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -28,8 +28,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    httpd´Â ¾ÆÆÄÄ¡ ÇÏÀÌÆÛÅØ½ºÆ® Àü¼Û ÇÁ·ÎÅäÄÝ (HTTP) ¼­¹ö ÇÁ·Î±×·¥ÀÌ´Ù. ÀÚü(standalone) µ¥¸ó ÇÁ·Î¼¼½º·Î diff -Nru apache2-2.4.69/docs/manual/programs/httpd.html.tr.utf8 apache2-2.4.71/docs/manual/programs/httpd.html.tr.utf8 --- apache2-2.4.69/docs/manual/programs/httpd.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/httpd.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    httpd, Apache Hiper Metin Aktarım Protokolü (HTTP) sunucusu programıdır. Tek başına çalışan bir artalan diff -Nru apache2-2.4.69/docs/manual/programs/httxt2dbm.html.tr.utf8 apache2-2.4.71/docs/manual/programs/httxt2dbm.html.tr.utf8 --- apache2-2.4.69/docs/manual/programs/httxt2dbm.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/httxt2dbm.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -27,7 +27,6 @@  fr  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    httxt2dbm, RewriteMap ile kullanmak için düz metin dosyalardan DBM dosyaları üretir.

    diff -Nru apache2-2.4.69/docs/manual/programs/index.html.ko.euc-kr apache2-2.4.71/docs/manual/programs/index.html.ko.euc-kr --- apache2-2.4.69/docs/manual/programs/index.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/index.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  tr  |  zh-cn 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    ¾ÆÆÄÄ¡ À¥¼­¹ö¿¡ Æ÷ÇÔµÈ ÇÁ·Î±×·¥µéÀÌ´Ù.

    diff -Nru apache2-2.4.69/docs/manual/programs/index.html.zh-cn.utf8 apache2-2.4.71/docs/manual/programs/index.html.zh-cn.utf8 --- apache2-2.4.69/docs/manual/programs/index.html.zh-cn.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/index.html.zh-cn.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,7 +30,6 @@  tr  |  zh-cn 

    -
    此翻译�能过期。�了解最近的更改,请阅读英文版。

    本页�述了 Apache HTTP �务器包�的所有�执行程�。

    diff -Nru apache2-2.4.69/docs/manual/programs/logresolve.html.ko.euc-kr apache2-2.4.71/docs/manual/programs/logresolve.html.ko.euc-kr --- apache2-2.4.69/docs/manual/programs/logresolve.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/logresolve.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    logresolve´Â ¾ÆÆÄÄ¡ Á¢±Ù·Î±×ÆÄÀÏ¿¡ ÀÖ´Â IP-ÁÖ¼Ò¸¦ ã´Â ÈÄó¸® ÇÁ·Î±×·¥ÀÌ´Ù. ³×ÀÓ¼­¹öÀÇ ºÎÇϸ¦ diff -Nru apache2-2.4.69/docs/manual/programs/logresolve.html.tr.utf8 apache2-2.4.71/docs/manual/programs/logresolve.html.tr.utf8 --- apache2-2.4.69/docs/manual/programs/logresolve.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/logresolve.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,7 +30,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    logresolve, Apache'nin eriÅŸim günlüklerindeki IP adreslerini çözümlemek için bir ardıl iÅŸlem diff -Nru apache2-2.4.69/docs/manual/programs/other.html.ko.euc-kr apache2-2.4.71/docs/manual/programs/other.html.ko.euc-kr --- apache2-2.4.69/docs/manual/programs/other.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/other.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -28,8 +28,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    ´ÙÀ½Àº °£´ÜÇÏ¿© manpage°¡ ¾ø´Â, ¾ÆÆÄÄ¡ À¥¼­¹ö¿¡ Æ÷ÇÔµÈ Áö¿ø ÇÁ·Î±×·¥µéÀÌ´Ù. ÀÚµ¿À¸·Î À̵éÀ» ¼³Ä¡ÇÏÁö ¾Ê´Â´Ù. diff -Nru apache2-2.4.69/docs/manual/programs/rotatelogs.html.ko.euc-kr apache2-2.4.71/docs/manual/programs/rotatelogs.html.ko.euc-kr --- apache2-2.4.69/docs/manual/programs/rotatelogs.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/rotatelogs.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    rotatelogs´Â ¾ÆÆÄÄ¡ÀÇ ÆÄÀÌÇÁ ·Î±×ÆÄÀÏ ±â´ÉÀ» À§ÇÑ °£´ÜÇÑ ÇÁ·Î±×·¥ÀÌ´Ù. ¿¹¸¦ µé¾î:

    diff -Nru apache2-2.4.69/docs/manual/programs/rotatelogs.html.tr.utf8 apache2-2.4.71/docs/manual/programs/rotatelogs.html.tr.utf8 --- apache2-2.4.69/docs/manual/programs/rotatelogs.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/rotatelogs.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,7 +30,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    rotatelogs, Apache'nin borulu günlük dosyaları özelliÄŸi ile birlikte kullanmak için tasarlanmış basit bir diff -Nru apache2-2.4.69/docs/manual/programs/suexec.html.ko.euc-kr apache2-2.4.71/docs/manual/programs/suexec.html.ko.euc-kr --- apache2-2.4.69/docs/manual/programs/suexec.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/suexec.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -28,8 +28,6 @@  ko  |  tr 

    -
    ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

    ¾ÆÆÄÄ¡ À¥¼­¹ö´Â CGI ÇÁ·Î±×·¥À» ½ÇÇàÇϱâ Àü¿¡ ´Ù¸¥ »ç¿ëÀÚ·Î ÀüȯÇϱâÀ§ÇØ suexec¸¦ »ç¿ëÇÑ´Ù. À̸¦ »ç¿ëÇÏ·Á¸é diff -Nru apache2-2.4.69/docs/manual/programs/suexec.html.tr.utf8 apache2-2.4.71/docs/manual/programs/suexec.html.tr.utf8 --- apache2-2.4.69/docs/manual/programs/suexec.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/programs/suexec.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  ko  |  tr 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    suexec, CGI programlarını çalıştırmadan önce Apache HTTP Sunucusu tarafından kullanıcı deÄŸiÅŸtirmek için kullanılır. diff -Nru apache2-2.4.69/docs/manual/rewrite/avoid.html.en apache2-2.4.71/docs/manual/rewrite/avoid.html.en --- apache2-2.4.69/docs/manual/rewrite/avoid.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/rewrite/avoid.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -469,7 +469,7 @@

    Modules such as mod_macro are also useful for creating a large number of virtual hosts dynamically.

    -

    Using mod_rewrite for vitualhost creation may be +

    Using mod_rewrite for virtual host creation may be appropriate if you are using a hosting service that does not provide you access to the server configuration files, and you are therefore restricted to configuration using .htaccess files.

    diff -Nru apache2-2.4.69/docs/manual/rewrite/flags.html.en apache2-2.4.71/docs/manual/rewrite/flags.html.en --- apache2-2.4.69/docs/manual/rewrite/flags.html.en 2026-07-25 13:10:55.000000000 +0000 +++ apache2-2.4.71/docs/manual/rewrite/flags.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -355,7 +355,7 @@

    Security Warning

    Exercise care when constructing the argument from backreferences or other variable expansion. If any part of the argument is derived from user input, -a malicious request may include delimeters or other unexpected values.

    +a malicious request may include delimiters or other unexpected values.

    If a literal ':' character is needed in any of the cookie fields, an diff -Nru apache2-2.4.69/docs/manual/rewrite/index.html.tr.utf8 apache2-2.4.71/docs/manual/rewrite/index.html.tr.utf8 --- apache2-2.4.69/docs/manual/rewrite/index.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/rewrite/index.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  tr  |  zh-cn 

    -
    Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

    mod_rewrite modülü gelen URL isteklerinde deÄŸiÅŸiklik yapabilmek için düzenli ifade kurallarına diff -Nru apache2-2.4.69/docs/manual/rewrite/index.html.zh-cn.utf8 apache2-2.4.71/docs/manual/rewrite/index.html.zh-cn.utf8 --- apache2-2.4.69/docs/manual/rewrite/index.html.zh-cn.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/rewrite/index.html.zh-cn.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  tr  |  zh-cn 

    -
    此翻译�能过期。�了解最近的更改,请阅读英文版。

    mod_rewrite �供了基于正则表达�规则动�修改传入的请求的 URL 的方法。 这�许你以自己喜欢的任�方法映射任� URL 到你的内部 URL 结构。

    diff -Nru apache2-2.4.69/docs/manual/rewrite/remapping.html.en apache2-2.4.71/docs/manual/rewrite/remapping.html.en --- apache2-2.4.69/docs/manual/rewrite/remapping.html.en 2026-06-08 12:25:13.000000000 +0000 +++ apache2-2.4.71/docs/manual/rewrite/remapping.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -711,7 +711,7 @@

    Many of the solutions in this section will all use the same condition, which leaves the matched value in the %2 backreference. %1 is the beginning -of the query string (up to the key of intererest), and %3 is the remainder. This +of the query string (up to the key of interest), and %3 is the remainder. This condition is a bit complex for flexibility and to avoid double '&&' in the substitutions.

      diff -Nru apache2-2.4.69/docs/manual/sections.html.ja.utf8 apache2-2.4.71/docs/manual/sections.html.ja.utf8 --- apache2-2.4.69/docs/manual/sections.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/sections.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,10 +29,6 @@  ko  |  tr 

      -
      ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

      設定ファイル中ã�®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒ†ã‚£ãƒ–ã�¯ サーãƒ�全体ã�«é�©ç”¨ã�•れã�Ÿã‚Šã€�特定ã�®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚„ファイルã€�ホストã€�URL ã�«ã�®ã�¿ é�©ç”¨ã�•れるよã�†ã�«åˆ¶é™�ã�—ã�Ÿã‚Šã�™ã‚‹ã�“ã�¨ã�Œã�§ã��ã�¾ã�™ã€‚ã�“ã�®æ–‡æ›¸ã�¯è¨­å®šç”¨ã�®ã‚»ã‚¯ã‚·ãƒ§ãƒ³ã�® diff -Nru apache2-2.4.69/docs/manual/sections.html.ko.euc-kr apache2-2.4.71/docs/manual/sections.html.ko.euc-kr --- apache2-2.4.69/docs/manual/sections.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/sections.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -29,8 +29,6 @@  ko  |  tr 

      -
      ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

      ¼³Á¤ÆÄÀÏ¿¡ ÀÖ´Â Áö½Ã¾î´Â ¼­¹ö Àüü¿¡ Àû¿ëµÇ°Å³ª, ƯÁ¤ µð·ºÅ丮, ÆÄÀÏ, È£½ºÆ®, URL¿¡¸¸ Àû¿ëµÉ ¼ö ÀÖ´Ù. ÀÌ ¹®¼­´Â ´Ù¸¥ Áö½Ã¾îÀÇ Àû¿ë¹üÀ§¸¦ diff -Nru apache2-2.4.69/docs/manual/sections.html.tr.utf8 apache2-2.4.71/docs/manual/sections.html.tr.utf8 --- apache2-2.4.69/docs/manual/sections.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/sections.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,7 +29,6 @@  ko  |  tr 

      -
      Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

      Yapılandırma dosyalarındaki yönergeler sunucunun tamamına uygulanacağı gibi sadece belli dizinler, diff -Nru apache2-2.4.69/docs/manual/server-wide.html.ja.utf8 apache2-2.4.71/docs/manual/server-wide.html.ja.utf8 --- apache2-2.4.69/docs/manual/server-wide.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/server-wide.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,10 +29,6 @@  ko  |  tr 

      -
      ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

      ã�“ã�®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã�§ã�¯core サーãƒ�ã�®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒ†ã‚£ãƒ–ã�®ä¸­ã�§ã€� diff -Nru apache2-2.4.69/docs/manual/server-wide.html.ko.euc-kr apache2-2.4.71/docs/manual/server-wide.html.ko.euc-kr --- apache2-2.4.69/docs/manual/server-wide.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/server-wide.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -29,8 +29,6 @@  ko  |  tr 

      -
      ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

      ÀÌ ¹®¼­´Â core ¼­¹ö°¡ ¼­¹öÀÇ ±âº» ÇൿÀ» ¼³Á¤ÇϱâÀ§ÇØ Á¦°øÇÏ´Â Áö½Ã¾îÁß ÀϺθ¦ ¼³¸íÇÑ´Ù.

      diff -Nru apache2-2.4.69/docs/manual/sitemap.html.de apache2-2.4.71/docs/manual/sitemap.html.de --- apache2-2.4.69/docs/manual/sitemap.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/sitemap.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -34,9 +34,6 @@  tr  |  zh-cn 

      -
      Diese Übersetzung ist möglicherweise - nicht mehr aktuell. Bitte prüfen Sie die englische Version auf - die neuesten Änderungen.

      Diese Seite verzeichnet die zur Zeit verfügbaren Dokumente der Dokumentation zum Apache HTTP Server Version diff -Nru apache2-2.4.69/docs/manual/sitemap.html.es apache2-2.4.71/docs/manual/sitemap.html.es --- apache2-2.4.69/docs/manual/sitemap.html.es 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/sitemap.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -34,10 +34,6 @@  tr  |  zh-cn 

      -
      Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

      Esta página contiene la lista con los documentos actualmente disponibles de la Versión 2.4 de la diff -Nru apache2-2.4.69/docs/manual/sitemap.html.ja.utf8 apache2-2.4.71/docs/manual/sitemap.html.ja.utf8 --- apache2-2.4.69/docs/manual/sitemap.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/sitemap.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -34,10 +34,6 @@  tr  |  zh-cn 

      -
      ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

      ã�“ã�®ãƒšãƒ¼ã‚¸ã�¯ç�¾æ™‚点ã�§åˆ©ç”¨å�¯èƒ½ã�ª Apache HTTP サーãƒ�ãƒ�ージョン 2.4 ã�®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒ†ãƒ¼ã‚·ãƒ§ãƒ³ diff -Nru apache2-2.4.69/docs/manual/sitemap.html.ko.euc-kr apache2-2.4.71/docs/manual/sitemap.html.ko.euc-kr --- apache2-2.4.69/docs/manual/sitemap.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/sitemap.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -34,8 +34,6 @@  tr  |  zh-cn 

      -
      ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

      ÀÌ ÆäÀÌÁö´Â ÇöÀç Apache HTTP Server Version 2.4 ¹®¼­ ¸ñ·ÏÀ» diff -Nru apache2-2.4.69/docs/manual/sitemap.html.tr.utf8 apache2-2.4.71/docs/manual/sitemap.html.tr.utf8 --- apache2-2.4.69/docs/manual/sitemap.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/sitemap.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -34,7 +34,6 @@  tr  |  zh-cn 

      -
      Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

      Bu sayfada Apache HTTP Sunucusu Sürüm 2.4 Belgelerinin tamamı listelenmiÅŸtir.

      diff -Nru apache2-2.4.69/docs/manual/sitemap.html.zh-cn.utf8 apache2-2.4.71/docs/manual/sitemap.html.zh-cn.utf8 --- apache2-2.4.69/docs/manual/sitemap.html.zh-cn.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/sitemap.html.zh-cn.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -34,7 +34,6 @@  tr  |  zh-cn 

      -
      此翻译�能过期。�了解最近的更改,请阅读英文版。

      本页列出了 Apache HTTP æœ�务器 2.4 diff -Nru apache2-2.4.69/docs/manual/ssl/index.html.zh-cn.utf8 apache2-2.4.71/docs/manual/ssl/index.html.zh-cn.utf8 --- apache2-2.4.69/docs/manual/ssl/index.html.zh-cn.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/ssl/index.html.zh-cn.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,7 +29,6 @@  tr  |  zh-cn 

      -
      此翻译�能过期。�了解最近的更改,请阅读英文版。

      Apache HTTP æœ�务器模å�— mod_ssl æ��供了与 OpenSSL diff -Nru apache2-2.4.69/docs/manual/ssl/ssl_intro.html.ja.utf8 apache2-2.4.71/docs/manual/ssl/ssl_intro.html.ja.utf8 --- apache2-2.4.69/docs/manual/ssl/ssl_intro.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/ssl/ssl_intro.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -27,10 +27,6 @@  fr  |  ja 

      -
      ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

      標準è¦�æ ¼ã�®è‰¯ã�„所ã�¯ã€�ã�Ÿã��ã�•ã‚“ã�®è¦�æ ¼ã�‹ã‚‰é�¸ã�¹ã‚‹ã�¨ã�„ã�†ã�“ã�¨ã� ã€‚ diff -Nru apache2-2.4.69/docs/manual/stopping.html.de apache2-2.4.71/docs/manual/stopping.html.de --- apache2-2.4.69/docs/manual/stopping.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/stopping.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -31,9 +31,6 @@  ko  |  tr 

      -
      Diese Übersetzung ist möglicherweise - nicht mehr aktuell. Bitte prüfen Sie die englische Version auf - die neuesten Änderungen.

      Dieses Dokument umfasst das Beenden und Neustarten des Apache auf Unix-ähnlichen Systemen. Anwender von Windows NT, 2000 diff -Nru apache2-2.4.69/docs/manual/stopping.html.en apache2-2.4.71/docs/manual/stopping.html.en --- apache2-2.4.69/docs/manual/stopping.html.en 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/stopping.html.en 2026-11-09 03:07:05.000000000 +0000 @@ -198,7 +198,7 @@ their current request (or to exit immediately if they're not serving anything). The parent will then remove its PidFile and cease listening on all ports. The parent will continue to run, and monitor children - which are handling requests. Once all children have finalised + which are handling requests. Once all children have finalized and exited or the timeout specified by the GracefulShutdownTimeout has been reached, the parent will also exit. If the timeout is reached, any remaining children will be sent the TERM signal diff -Nru apache2-2.4.69/docs/manual/stopping.html.es apache2-2.4.71/docs/manual/stopping.html.es --- apache2-2.4.69/docs/manual/stopping.html.es 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/stopping.html.es 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ko  |  tr 

      -
      Esta traducción podría estar - obsoleta. Consulte la versión en inglés de la - documentación para comprobar si se han producido cambios - recientemente.

      Este documento explica como iniciar y parar el servidor Apache en sistemas tipo Unix. Los usuarios de Windows NT, 2000 y XP diff -Nru apache2-2.4.69/docs/manual/stopping.html.ja.utf8 apache2-2.4.71/docs/manual/stopping.html.ja.utf8 --- apache2-2.4.69/docs/manual/stopping.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/stopping.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  ko  |  tr 

      -
      ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

      ã�“ã�®æ–‡æ›¸ã�§ã�¯ Unix ã�«é¡žä¼¼ã�—ã�Ÿã‚·ã‚¹ãƒ†ãƒ ã�§ã�® Apache HTTP Serverã�®å�œæ­¢ã�¨å†�èµ·å‹•ã�«ã�¤ã�„ã�¦æ‰±ã�£ã�¦ã�„ã�¾ã�™ã€‚ diff -Nru apache2-2.4.69/docs/manual/stopping.html.ko.euc-kr apache2-2.4.71/docs/manual/stopping.html.ko.euc-kr --- apache2-2.4.69/docs/manual/stopping.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/stopping.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  ko  |  tr 

      -
      ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

      ÀÌ ¹®¼­´Â À¯´Ð½º·ù ½Ã½ºÅÛ¿¡¼­ ¾ÆÆÄÄ¡¸¦ Áß´ÜÇϰí Àç½ÃÀÛÇÏ´Â ³»¿ëÀ» ´ã°íÀÖ´Ù. À©µµ¿ìÁî NT, 2000, XP »ç¿ëÀÚ´Â ¼­ºñ½º·Î ¾ÆÆÄÄ¡ diff -Nru apache2-2.4.69/docs/manual/stopping.html.tr.utf8 apache2-2.4.71/docs/manual/stopping.html.tr.utf8 --- apache2-2.4.69/docs/manual/stopping.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/stopping.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,7 +31,6 @@  ko  |  tr 

      -
      Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

      Bu belge Apache HTTP Sunucusunun Unix benzeri sistemlerde durdurulması ve yeniden baÅŸlatılması konularını kapsar. Windows NT, 2000 ve XP diff -Nru apache2-2.4.69/docs/manual/style/version.ent apache2-2.4.71/docs/manual/style/version.ent --- apache2-2.4.69/docs/manual/style/version.ent 2026-06-08 12:25:13.000000000 +0000 +++ apache2-2.4.71/docs/manual/style/version.ent 2026-11-09 03:07:05.000000000 +0000 @@ -19,6 +19,6 @@ - + diff -Nru apache2-2.4.69/docs/manual/suexec.html.ja.utf8 apache2-2.4.71/docs/manual/suexec.html.ja.utf8 --- apache2-2.4.69/docs/manual/suexec.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/suexec.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,10 +29,6 @@  ko  |  tr 

      -
      ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

      suEXEC 機能ã�«ã‚ˆã‚Šã€�Apache ユーザã�¯ Web サーãƒ�を実行ã�—ã�¦ã�„るユーザ ID ã�¨ã�¯ diff -Nru apache2-2.4.69/docs/manual/suexec.html.ko.euc-kr apache2-2.4.71/docs/manual/suexec.html.ko.euc-kr --- apache2-2.4.69/docs/manual/suexec.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/suexec.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -29,8 +29,6 @@  ko  |  tr 

      -
      ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

      suEXEC ±â´ÉÀº ¾ÆÆÄÄ¡°¡ CGI¿Í SSI ÇÁ·Î±×·¥À» À¥¼­¹ö¸¦ ½ÇÇàÇÑ »ç¿ëÀÚ ID°¡ diff -Nru apache2-2.4.69/docs/manual/suexec.html.tr.utf8 apache2-2.4.71/docs/manual/suexec.html.tr.utf8 --- apache2-2.4.69/docs/manual/suexec.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/suexec.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,7 +29,6 @@  ko  |  tr 

      -
      Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

      SuEXEC özelliÄŸi, Apache HTTP Sunucusu kullanıcılarına CGI ve SSI programlarını sunucunun diff -Nru apache2-2.4.69/docs/manual/urlmapping.html.ja.utf8 apache2-2.4.71/docs/manual/urlmapping.html.ja.utf8 --- apache2-2.4.69/docs/manual/urlmapping.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/urlmapping.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,10 +29,6 @@  ko  |  tr 

      -
      ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

      ��文書� Apache �リクエスト� URL �ら�信�るファイル� ファイルシステム上��置を決定�る方法を説明���。

      diff -Nru apache2-2.4.69/docs/manual/urlmapping.html.ko.euc-kr apache2-2.4.71/docs/manual/urlmapping.html.ko.euc-kr --- apache2-2.4.69/docs/manual/urlmapping.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/urlmapping.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -29,8 +29,6 @@  ko  |  tr 

      -
      ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

      ÀÌ ¹®¼­´Â ¿äûÀÇ URLÀ» °¡Áö°í ¾ÆÆÄÄ¡°¡ ¾î¶»°Ô ¼­ºñ½ºÇÒ ÆÄÀÏÀÇ ÆÄÀϽýºÅÛ»ó À§Ä¡¸¦ ã´ÂÁö ¼³¸íÇÑ´Ù.

      diff -Nru apache2-2.4.69/docs/manual/urlmapping.html.tr.utf8 apache2-2.4.71/docs/manual/urlmapping.html.tr.utf8 --- apache2-2.4.69/docs/manual/urlmapping.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/urlmapping.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,7 +29,6 @@  ko  |  tr 

      -
      Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

      Bu belgede, bir istekte belirtilen URL'nin sunulacak dosyanın dosya sistemindeki yerini bulmak için Apache HTTP Sunucusu tarafından nasıl diff -Nru apache2-2.4.69/docs/manual/vhosts/details.html.ko.euc-kr apache2-2.4.71/docs/manual/vhosts/details.html.ko.euc-kr --- apache2-2.4.69/docs/manual/vhosts/details.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/vhosts/details.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -28,8 +28,6 @@  ko  |  tr 

      -
      ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

      °¡»óÈ£½ºÆ® ÄÚµå´Â ¾ÆÆÄÄ¡ 1.3¿¡¼­ °ÅÀÇ ´Ù½Ã diff -Nru apache2-2.4.69/docs/manual/vhosts/details.html.tr.utf8 apache2-2.4.71/docs/manual/vhosts/details.html.tr.utf8 --- apache2-2.4.69/docs/manual/vhosts/details.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/vhosts/details.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  ko  |  tr 

      -
      Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

      Bu belgede, bir istek aldığında Apache'nin hangi sanal konak diff -Nru apache2-2.4.69/docs/manual/vhosts/examples.html.ja.utf8 apache2-2.4.71/docs/manual/vhosts/examples.html.ja.utf8 --- apache2-2.4.69/docs/manual/vhosts/examples.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/vhosts/examples.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,10 +29,6 @@  ko  |  tr 

      -
      ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

      ã�“ã�®æ–‡æ›¸ã�¯ã€�ãƒ�ーãƒ�ャルホストã�®è¨­å®šã�®éš›ã�« diff -Nru apache2-2.4.69/docs/manual/vhosts/examples.html.ko.euc-kr apache2-2.4.71/docs/manual/vhosts/examples.html.ko.euc-kr --- apache2-2.4.69/docs/manual/vhosts/examples.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/vhosts/examples.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -29,8 +29,6 @@  ko  |  tr 

      -
      ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

      ÀÌ ¹®¼­´Â ÀÚÁÖ ¹®ÀǵǴ °¡»óÈ£½ºÆ® diff -Nru apache2-2.4.69/docs/manual/vhosts/fd-limits.html.ja.utf8 apache2-2.4.71/docs/manual/vhosts/fd-limits.html.ja.utf8 --- apache2-2.4.69/docs/manual/vhosts/fd-limits.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/vhosts/fd-limits.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,10 +29,6 @@  ko  |  tr 

      -
      ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

      ã�Ÿã��ã�•ã‚“ã�®ãƒ�ーãƒ�ャルホストをé�‹ç”¨ã�™ã‚‹å ´å�ˆã€�ã‚‚ã�—ã€� diff -Nru apache2-2.4.69/docs/manual/vhosts/fd-limits.html.ko.euc-kr apache2-2.4.71/docs/manual/vhosts/fd-limits.html.ko.euc-kr --- apache2-2.4.69/docs/manual/vhosts/fd-limits.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/vhosts/fd-limits.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -29,8 +29,6 @@  ko  |  tr 

      -
      ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

      °¡»óÈ£½ºÆ®¸¦ ¸¹ÀÌ »ç¿ëÇÏ°í °¢ °¡»óÈ£½ºÆ®¿¡ ¼­·Î ´Ù¸¥ diff -Nru apache2-2.4.69/docs/manual/vhosts/index.html.de apache2-2.4.71/docs/manual/vhosts/index.html.de --- apache2-2.4.69/docs/manual/vhosts/index.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/vhosts/index.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -31,9 +31,6 @@  tr  |  zh-cn 

      -
      Diese Übersetzung ist möglicherweise - nicht mehr aktuell. Bitte prüfen Sie die englische Version auf - die neuesten Änderungen.

      Der Begriff virtueller Host (Anm.d.Ü.: engl. 'virtual host') bezieht sich auf die Praxis, mehr als ein Webangebot diff -Nru apache2-2.4.69/docs/manual/vhosts/index.html.ja.utf8 apache2-2.4.71/docs/manual/vhosts/index.html.ja.utf8 --- apache2-2.4.69/docs/manual/vhosts/index.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/vhosts/index.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,10 +31,6 @@  tr  |  zh-cn 

      -
      ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

      ãƒ�ーãƒ�ャルホストã�¨ã�„ã�†ç”¨èªžã�¯ã€�1 å�°ã�®ãƒžã‚·ãƒ³ä¸Šã�§ diff -Nru apache2-2.4.69/docs/manual/vhosts/index.html.ko.euc-kr apache2-2.4.71/docs/manual/vhosts/index.html.ko.euc-kr --- apache2-2.4.69/docs/manual/vhosts/index.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/vhosts/index.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -31,8 +31,6 @@  tr  |  zh-cn 

      -
      ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

      °¡»óÈ£½ºÆ® (Virtual Host)´Â ÇÑ ÄÄÇ»ÅÍ¿¡¼­ diff -Nru apache2-2.4.69/docs/manual/vhosts/index.html.zh-cn.utf8 apache2-2.4.71/docs/manual/vhosts/index.html.zh-cn.utf8 --- apache2-2.4.69/docs/manual/vhosts/index.html.zh-cn.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/vhosts/index.html.zh-cn.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -31,7 +31,6 @@  tr  |  zh-cn 

      -
      此翻译�能过期。�了解最近的更改,请阅读英文版。

      术语虚拟主机指的是在å�•一机器上è¿�行多个网站 diff -Nru apache2-2.4.69/docs/manual/vhosts/ip-based.html.ja.utf8 apache2-2.4.71/docs/manual/vhosts/ip-based.html.ja.utf8 --- apache2-2.4.69/docs/manual/vhosts/ip-based.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/vhosts/ip-based.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -29,10 +29,6 @@  ko  |  tr 

      -
      ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -
      -
      ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.
      • ½Ã½ºÅÛ ¿ä±¸»çÇ×
      • ¾ÆÆÄÄ¡ ¼³Á¤¹æ¹ý
      • diff -Nru apache2-2.4.69/docs/manual/vhosts/mass.html.ko.euc-kr apache2-2.4.71/docs/manual/vhosts/mass.html.ko.euc-kr --- apache2-2.4.69/docs/manual/vhosts/mass.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/vhosts/mass.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -28,8 +28,6 @@  ko  |  tr 

      -
      ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

      ÀÌ ¹®¼­´Â ¾ÆÆÄÄ¡ 1.3¿¡¼­ ´ë·®ÀÇ °¡»óÈ£½ºÆ®¸¦ È¿À²ÀûÀ¸·Î diff -Nru apache2-2.4.69/docs/manual/vhosts/mass.html.tr.utf8 apache2-2.4.71/docs/manual/vhosts/mass.html.tr.utf8 --- apache2-2.4.69/docs/manual/vhosts/mass.html.tr.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/vhosts/mass.html.tr.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -28,7 +28,6 @@  ko  |  tr 

      -
      Bu çeviri güncel olmayabilir. Son deÄŸiÅŸiklikler için İngilizce sürüm geçerlidir.

      Bu belgede sanal konakların sonu belirsiz bir ÅŸekilde artışı karşısında diff -Nru apache2-2.4.69/docs/manual/vhosts/name-based.html.de apache2-2.4.71/docs/manual/vhosts/name-based.html.de --- apache2-2.4.69/docs/manual/vhosts/name-based.html.de 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/vhosts/name-based.html.de 2026-11-09 03:07:05.000000000 +0000 @@ -30,9 +30,6 @@  ko  |  tr 

      -
      Diese Übersetzung ist möglicherweise - nicht mehr aktuell. Bitte prüfen Sie die englische Version auf - die neuesten Änderungen.

      Das Dokument beschreibt, wann und wie namensbasierte virtuelle Hosts zu verwenden sind.

      diff -Nru apache2-2.4.69/docs/manual/vhosts/name-based.html.ja.utf8 apache2-2.4.71/docs/manual/vhosts/name-based.html.ja.utf8 --- apache2-2.4.69/docs/manual/vhosts/name-based.html.ja.utf8 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/vhosts/name-based.html.ja.utf8 2026-11-09 03:07:05.000000000 +0000 @@ -30,10 +30,6 @@  ko  |  tr 

      -
      ��日本語訳����������る - �能性��り��。 - 最近更新�れ�内容を見る��英語版を�覧下��。 -

      ��文書����ベース��ー�ャルホストを�ん���� ��や��使��を説明���。

      diff -Nru apache2-2.4.69/docs/manual/vhosts/name-based.html.ko.euc-kr apache2-2.4.71/docs/manual/vhosts/name-based.html.ko.euc-kr --- apache2-2.4.69/docs/manual/vhosts/name-based.html.ko.euc-kr 2026-05-31 13:57:20.000000000 +0000 +++ apache2-2.4.71/docs/manual/vhosts/name-based.html.ko.euc-kr 2026-11-09 03:07:05.000000000 +0000 @@ -30,8 +30,6 @@  ko  |  tr 

      -
      ÀÌ ¹®¼­´Â ÃÖ½ÅÆÇ ¹ø¿ªÀÌ ¾Æ´Õ´Ï´Ù. - ÃÖ±Ù¿¡ º¯°æµÈ ³»¿ëÀº ¿µ¾î ¹®¼­¸¦ Âü°íÇϼ¼¿ä.

      ÀÌ ¹®¼­´Â À̸§±â¹Ý °¡»óÈ£½ºÆ®¸¦ »ç¿ëÇÏ´Â °æ¿ì¿Í ¹æ¹ýÀ» ¼³¸íÇÑ´Ù.

      diff -Nru apache2-2.4.69/httpd.spec apache2-2.4.71/httpd.spec --- apache2-2.4.69/httpd.spec 2026-09-29 12:44:25.000000000 +0000 +++ apache2-2.4.71/httpd.spec 2026-11-09 03:18:45.000000000 +0000 @@ -4,7 +4,7 @@ Summary: Apache HTTP Server Name: httpd -Version: 2.4.69 +Version: 2.4.71 Release: 1 URL: http://httpd.apache.org/ Vendor: Apache Software Foundation diff -Nru apache2-2.4.69/include/ap_release.h apache2-2.4.71/include/ap_release.h --- apache2-2.4.69/include/ap_release.h 2026-09-29 11:57:11.000000000 +0000 +++ apache2-2.4.71/include/ap_release.h 2026-11-09 03:07:05.000000000 +0000 @@ -43,7 +43,7 @@ #define AP_SERVER_MAJORVERSION_NUMBER 2 #define AP_SERVER_MINORVERSION_NUMBER 4 -#define AP_SERVER_PATCHLEVEL_NUMBER 69 +#define AP_SERVER_PATCHLEVEL_NUMBER 71 #define AP_SERVER_DEVBUILD_BOOLEAN 0 /* Synchronize the above with docs/manual/style/version.ent */ diff -Nru apache2-2.4.69/modules/aaa/mod_authnz_ldap.c apache2-2.4.71/modules/aaa/mod_authnz_ldap.c --- apache2-2.4.69/modules/aaa/mod_authnz_ldap.c 2026-09-28 13:16:28.000000000 +0000 +++ apache2-2.4.71/modules/aaa/mod_authnz_ldap.c 2026-11-09 03:07:05.000000000 +0000 @@ -369,7 +369,10 @@ static apr_status_t authnz_ldap_cleanup_connection_close(void *param) { util_ldap_connection_t *ldc = param; - ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, ldc->r, "Release ldc %pp", ldc); + /* NULL ldc->r implies some kind of mismanagement/reuse but the close is tolerant */ + if (ldc->r) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, ldc->r, "Release ldc %pp", ldc); + } util_ldap_connection_close(ldc); return APR_SUCCESS; } diff -Nru apache2-2.4.69/modules/cache/cache_storage.c apache2-2.4.71/modules/cache/cache_storage.c --- apache2-2.4.69/modules/cache/cache_storage.c 2019-07-18 13:57:04.000000000 +0000 +++ apache2-2.4.71/modules/cache/cache_storage.c 2026-11-09 03:07:05.000000000 +0000 @@ -282,6 +282,19 @@ const char *h1, *h2; /* + * RFC 9111 4.1: a stored response with a Vary header field + * value containing a member "*" always fails to match, as + * "*" is not the name of a real request header to compare. + */ + if (vary[0] == '*' && vary[1] == '\0') { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, + r, APLOGNO(10620) "cache_select(): Vary header " + "contains '*', cannot match stored response."); + mismatch = 1; + break; + } + + /* * is this header in the request and the header in the cached * request identical? If not, we give up and do a straight get */ diff -Nru apache2-2.4.69/modules/cache/mod_cache.c apache2-2.4.71/modules/cache/mod_cache.c --- apache2-2.4.69/modules/cache/mod_cache.c 2026-07-06 11:55:01.000000000 +0000 +++ apache2-2.4.71/modules/cache/mod_cache.c 2026-11-09 03:07:05.000000000 +0000 @@ -1115,7 +1115,12 @@ */ reason = "Authorization required"; } - else if (ap_find_token(NULL, apr_table_get(r->headers_out, "Vary"), "*")) { + else if (ap_find_token(NULL, apr_table_get(r->headers_out, "Vary"), "*") + || ap_find_token(NULL, apr_table_get(r->err_headers_out, "Vary"), "*")) { + /* err_headers_out carries a Vary set via "Header always", which + * is merged into headers_out only when the response is actually + * stored (see cache_merge_headers_out()); check it here too so + * that decision matches what gets stored. */ reason = "Vary header contains '*'"; } else if (apr_table_get(r->subprocess_env, "no-cache") != NULL) { diff -Nru apache2-2.4.69/modules/dav/lock/locks.c apache2-2.4.71/modules/dav/lock/locks.c --- apache2-2.4.69/modules/dav/lock/locks.c 2026-04-26 16:04:35.000000000 +0000 +++ apache2-2.4.71/modules/dav/lock/locks.c 2026-11-09 03:07:05.000000000 +0000 @@ -155,7 +155,7 @@ /* Stored indirect lock info - lock token and apr_datum_t */ #define dav_size_indirect(a) (1 + sizeof(apr_uuid_t) \ + sizeof(time_t) \ - + sizeof(int) + (a)->key.dsize) + + sizeof((a)->key.dsize) + (a)->key.dsize) /* * The lockdb structure. @@ -667,7 +667,7 @@ memcpy(&ip->timeout, val.dptr + offset, sizeof(ip->timeout)); offset += sizeof(ip->timeout); /* length of datum */ - ip->key.dsize = *((int *) (val.dptr + offset)); + memcpy(&ip->key.dsize, val.dptr + offset, sizeof(ip->key.dsize)); offset += sizeof(ip->key.dsize); ip->key.dptr = apr_pmemdup(p, val.dptr + offset, ip->key.dsize); offset += ip->key.dsize; diff -Nru apache2-2.4.69/modules/dav/main/util.c apache2-2.4.71/modules/dav/main/util.c --- apache2-2.4.69/modules/dav/main/util.c 2026-09-29 08:18:27.000000000 +0000 +++ apache2-2.4.71/modules/dav/main/util.c 2026-11-09 03:07:05.000000000 +0000 @@ -531,6 +531,10 @@ ** */ +/* largest value this platform's (signed) time_t can hold */ +#define DAV_TIME_T_MAX \ + ((time_t) (((apr_uint64_t) 1 << (sizeof(time_t) * 8 - 1)) - 1)) + /* dav_get_timeout: If the Timeout: header exists, return a time_t * when this lock is expected to expire. Otherwise, return * a time_t of DAV_TIMEOUT_INFINITE. @@ -559,12 +563,50 @@ } if (!strncmp(val, "Second-", 7)) { + char *endp; + apr_int64_t sec; + val += 7; - /* ### We need to handle overflow better: - * ### timeout will be <= 2^32 - 1 + + /* atol() has no overflow detection: a long enough digit + * string overflows it, and "now + expires" below can then + * overflow time_t too, yielding a nonsensical (e.g. + * negative, already-expired) stored timeout instead of + * the large-but-finite one the client asked for. Use an + * overflow-checked parse, and clamp to the RFC 4918 + * TimeOut maximum of 2^32 - 1 instead of propagating the + * overflow. */ - expires = atol(val); - now = time(NULL); + errno = 0; + sec = apr_strtoi64(val, &endp, 10); + if (endp == val) { + /* not a number at all -- same as atol(), treat as 0 + * (an immediately-expired lock), not as Infinite + */ + sec = 0; + } + else if (errno == ERANGE || sec > APR_UINT32_MAX) { + sec = APR_UINT32_MAX; + } + else if (sec < 0) { + sec = 0; + } + + now = time(NULL); + + /* The RFC 4918 maximum of 2^32 - 1 does not necessarily + * fit a time_t on platforms where time_t is 32 bits. + * Cap "sec" so "now + sec" cannot itself overflow time_t + * -- undefined behavior for a signed type, and in + * practice a silent wraparound to an already-expired + * value, i.e. exactly the bug this change fixes, + * reintroduced through the cast below. + */ + if (sec > (apr_int64_t) (DAV_TIME_T_MAX - now)) { + sec = (apr_int64_t) (DAV_TIME_T_MAX - now); + } + + expires = (time_t) sec; return now + expires; } } diff -Nru apache2-2.4.69/modules/filters/mod_deflate.c apache2-2.4.71/modules/filters/mod_deflate.c --- apache2-2.4.69/modules/filters/mod_deflate.c 2024-03-26 15:00:06.000000000 +0000 +++ apache2-2.4.71/modules/filters/mod_deflate.c 2026-11-09 03:07:05.000000000 +0000 @@ -1053,7 +1053,7 @@ if (!*len) { return APR_INCOMPLETE; } - ctx->consume_len = (unsigned int)**data; + ctx->consume_len = (unsigned int)(unsigned char)**data; ctx->consume_pos++; ++*data; --*len; @@ -1062,7 +1062,7 @@ if (!*len) { return APR_INCOMPLETE; } - ctx->consume_len += ((unsigned int)**data) << 8; + ctx->consume_len += ((unsigned int)(unsigned char)**data) << 8; ctx->consume_pos++; ++*data; --*len; diff -Nru apache2-2.4.69/modules/filters/mod_substitute.c apache2-2.4.71/modules/filters/mod_substitute.c --- apache2-2.4.69/modules/filters/mod_substitute.c 2026-09-01 10:27:29.000000000 +0000 +++ apache2-2.4.71/modules/filters/mod_substitute.c 2026-11-09 03:07:05.000000000 +0000 @@ -275,7 +275,7 @@ } } else if (script->regexp) { - int left = bytes; + apr_size_t left = bytes; const char *pos = buff; char *repl; apr_size_t space_left = cfg->max_line_length; diff -Nru apache2-2.4.69/modules/generators/mod_cgi.c apache2-2.4.71/modules/generators/mod_cgi.c --- apache2-2.4.69/modules/generators/mod_cgi.c 2024-07-02 13:07:17.000000000 +0000 +++ apache2-2.4.71/modules/generators/mod_cgi.c 2026-11-09 03:07:05.000000000 +0000 @@ -625,7 +625,8 @@ #if APR_FILES_AS_SOCKETS b = cgi_bucket_create(r, dc->timeout, script_in, script_err, c->bucket_alloc); if (b == NULL) - return HTTP_INTERNAL_SERVER_ERROR; + return log_scripterror(r, conf, HTTP_SERVICE_UNAVAILABLE, 0, NULL, + "could not create CGI bucket"); #else b = apr_bucket_pipe_create(script_in, c->bucket_alloc); #endif diff -Nru apache2-2.4.69/modules/generators/mod_cgid.c apache2-2.4.71/modules/generators/mod_cgid.c --- apache2-2.4.69/modules/generators/mod_cgid.c 2026-09-01 10:27:29.000000000 +0000 +++ apache2-2.4.71/modules/generators/mod_cgid.c 2026-11-09 03:07:05.000000000 +0000 @@ -192,7 +192,9 @@ } cgid_rlimit_t; #endif -#define ENV_COUNT_MAX (256) +/* Sanity bound on the length of one environment variable value + * received by the daemon. */ +#define ENV_VAL_MAX (128 * 1024) typedef struct { int req_type; /* request type (CGI_REQ, SSI_REQ, etc.) */ @@ -504,7 +506,8 @@ return APR_SUCCESS; } -static apr_status_t get_req(int fd, request_rec *r, char **argv0, char ***env, +static apr_status_t get_req(int fd, server_rec *main_server, request_rec *r, + char **argv0, char ***env, int *errfd, cgid_req_t *req) { int i; @@ -532,11 +535,11 @@ } /* Sanity check the structure received. */ - if (req->env_count > ENV_COUNT_MAX + if (req->env_count > APACHE_ARG_MAX || req->filename_len == 0 || req->filename_len > APR_PATH_MAX || req->argv0_len == 0 || req->argv0_len > APR_PATH_MAX - || req->uri_len == 0 || req->uri_len > APR_PATH_MAX - || req->args_len > APR_PATH_MAX) { + || req->uri_len == 0 || req->uri_len > main_server->limit_req_line + || req->args_len > main_server->limit_req_line) { return APR_EINVAL; } @@ -573,7 +576,7 @@ if ((stat = sock_read(fd, &curlen, sizeof(curlen))) != APR_SUCCESS) { return stat; } - if (curlen > APR_PATH_MAX) { + if (curlen > ENV_VAL_MAX) { return APR_EINVAL; } environ[i] = apr_pcalloc(r->pool, curlen + 1); @@ -614,6 +617,9 @@ for (req.env_count = 0; env[req.env_count]; req.env_count++) { continue; } + if (req.env_count > APACHE_ARG_MAX) { + return APR_ENOSPC; + } req.filename_len = strlen(r->filename); req.argv0_len = strlen(argv0); req.uri_len = strlen(r->uri); @@ -801,6 +807,10 @@ return rc; } + ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, main_server, + "cgid daemon listening on %s, pid %" APR_PID_T_FMT, + sockname, getpid()); + while (!daemon_should_exit) { int errfileno; char *argv0 = NULL; @@ -842,7 +852,8 @@ r = apr_pcalloc(ptrans, sizeof(request_rec)); procnew = apr_pcalloc(ptrans, sizeof(*procnew)); r->pool = ptrans; - stat = get_req(sd2, r, &argv0, &env, &errfileno, &cgid_req); + stat = get_req(sd2, main_server, r, + &argv0, &env, &errfileno, &cgid_req); if (stat != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, stat, main_server, APLOGNO(01248) @@ -1077,6 +1088,11 @@ parent_pid = getpid(); tmp_sockname = ap_runtime_dir_relative(p, sockname); + if (!tmp_sockname) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, APLOGNO(10423) + "Invalid socket path %s", sockname); + return DECLINED; + } if (strlen(tmp_sockname) > sizeof(server_addr->sun_path) - 1) { tmp_sockname[sizeof(server_addr->sun_path) - 1] = '\0'; ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, APLOGNO(01254) @@ -1644,7 +1660,8 @@ #ifdef HAVE_CGID_FDPASSING b = cgi_bucket_create(r, dc->timeout, tempsock, script_err, c->bucket_alloc); if (b == NULL) - return HTTP_INTERNAL_SERVER_ERROR; /* should call log_scripterror() w/ _UNAVAILABLE? */ + return log_scripterror(r, conf, HTTP_SERVICE_UNAVAILABLE, 0, NULL, + "could not create CGI bucket"); #else b = apr_bucket_pipe_create(tempsock, c->bucket_alloc); #endif diff -Nru apache2-2.4.69/modules/http2/h2.h apache2-2.4.71/modules/http2/h2.h --- apache2-2.4.69/modules/http2/h2.h 2023-10-16 13:21:37.000000000 +0000 +++ apache2-2.4.71/modules/http2/h2.h 2026-11-09 03:07:05.000000000 +0000 @@ -53,7 +53,7 @@ * The magic PRIamble of RFC 7540 that is always sent when starting * a h2 communication. */ -extern const char *H2_MAGIC_TOKEN; +extern const char *const H2_MAGIC_TOKEN; #define H2_ERR_NO_ERROR (0x00) #define H2_ERR_PROTOCOL_ERROR (0x01) diff -Nru apache2-2.4.69/modules/http2/h2_protocol.c apache2-2.4.71/modules/http2/h2_protocol.c --- apache2-2.4.69/modules/http2/h2_protocol.c 2026-07-02 19:54:30.000000000 +0000 +++ apache2-2.4.71/modules/http2/h2_protocol.c 2026-11-09 03:07:05.000000000 +0000 @@ -45,15 +45,15 @@ #include "h2_protocol.h" #include "mod_http2.h" -const char *h2_protocol_ids_tls[] = { +const char *const h2_protocol_ids_tls[] = { "h2", NULL }; -const char *h2_protocol_ids_clear[] = { +const char *const h2_protocol_ids_clear[] = { "h2c", NULL }; -const char *H2_MAGIC_TOKEN = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; +const char *const H2_MAGIC_TOKEN = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; /******************************************************************************* * HTTP/2 error stuff diff -Nru apache2-2.4.69/modules/http2/h2_protocol.h apache2-2.4.71/modules/http2/h2_protocol.h --- apache2-2.4.69/modules/http2/h2_protocol.h 2023-01-09 07:35:18.000000000 +0000 +++ apache2-2.4.71/modules/http2/h2_protocol.h 2026-11-09 03:07:05.000000000 +0000 @@ -21,13 +21,13 @@ * List of protocol identifiers that we support in cleartext * negotiations. NULL terminated. */ -extern const char *h2_protocol_ids_clear[]; +extern const char *const h2_protocol_ids_clear[]; /** * List of protocol identifiers that we support in TLS encrypted * negotiations (ALPN). NULL terminated. */ -extern const char *h2_protocol_ids_tls[]; +extern const char *const h2_protocol_ids_tls[]; /** * Provide a user readable description of the HTTP/2 error code- diff -Nru apache2-2.4.69/modules/http2/h2_push.c apache2-2.4.71/modules/http2/h2_push.c --- apache2-2.4.69/modules/http2/h2_push.c 2023-08-29 07:23:10.000000000 +0000 +++ apache2-2.4.71/modules/http2/h2_push.c 2026-11-09 03:07:05.000000000 +0000 @@ -699,179 +699,3 @@ pushes = h2_push_collect(stream->pool, req, stream->push_policy, res); return h2_push_diary_update(stream->session, pushes); } - -typedef struct { - h2_push_diary *diary; - unsigned char log2p; - int mask_bits; - int delta_bits; - int fixed_bits; - apr_uint64_t fixed_mask; - apr_pool_t *pool; - unsigned char *data; - apr_size_t datalen; - apr_size_t offset; - unsigned int bit; - apr_uint64_t last; -} gset_encoder; - -static int cmp_puint64(const void *p1, const void *p2) -{ - const apr_uint64_t *pu1 = p1, *pu2 = p2; - return (*pu1 > *pu2)? 1 : ((*pu1 == *pu2)? 0 : -1); -} - -/* in golomb bit stream encoding, bit 0 is the 8th of the first char, or - * more generally: - * char(bit/8) & cbit_mask[(bit % 8)] - */ -static unsigned char cbit_mask[] = { - 0x80u, - 0x40u, - 0x20u, - 0x10u, - 0x08u, - 0x04u, - 0x02u, - 0x01u, -}; - -static apr_status_t gset_encode_bit(gset_encoder *encoder, int bit) -{ - if (++encoder->bit >= 8) { - if (++encoder->offset >= encoder->datalen) { - apr_size_t nlen = encoder->datalen*2; - unsigned char *ndata = apr_pcalloc(encoder->pool, nlen); - if (!ndata) { - return APR_ENOMEM; - } - memcpy(ndata, encoder->data, encoder->datalen); - encoder->data = ndata; - encoder->datalen = nlen; - } - encoder->bit = 0; - encoder->data[encoder->offset] = 0xffu; - } - if (!bit) { - encoder->data[encoder->offset] &= ~cbit_mask[encoder->bit]; - } - return APR_SUCCESS; -} - -static apr_status_t gset_encode_next(gset_encoder *encoder, apr_uint64_t pval) -{ - apr_uint64_t delta, flex_bits; - apr_status_t status = APR_SUCCESS; - int i; - - delta = pval - encoder->last; - encoder->last = pval; - flex_bits = (delta >> encoder->fixed_bits); - /* Intentional no APLOGNO */ - ap_log_perror(APLOG_MARK, GCSLOG_LEVEL, 0, encoder->pool, - "h2_push_diary_enc: val=%"APR_UINT64_T_HEX_FMT", delta=%" - APR_UINT64_T_HEX_FMT" flex_bits=%"APR_UINT64_T_FMT", " - ", fixed_bits=%d, fixed_val=%"APR_UINT64_T_HEX_FMT, - pval, delta, flex_bits, encoder->fixed_bits, delta&encoder->fixed_mask); - for (; flex_bits != 0; --flex_bits) { - status = gset_encode_bit(encoder, 1); - if (status != APR_SUCCESS) { - return status; - } - } - status = gset_encode_bit(encoder, 0); - if (status != APR_SUCCESS) { - return status; - } - - for (i = encoder->fixed_bits-1; i >= 0; --i) { - status = gset_encode_bit(encoder, (delta >> i) & 1); - if (status != APR_SUCCESS) { - return status; - } - } - return APR_SUCCESS; -} - -/** - * Get a cache digest as described in - * https://datatracker.ietf.org/doc/draft-kazuho-h2-cache-digest/ - * from the contents of the push diary. - * - * @param diary the diary to calculdate the digest from - * @param p the pool to use - * @param pdata on successful return, the binary cache digest - * @param plen on successful return, the length of the binary data - */ -apr_status_t h2_push_diary_digest_get(h2_push_diary *diary, apr_pool_t *pool, - int maxP, const char *authority, - const char **pdata, apr_size_t *plen) -{ - int nelts, N; - unsigned char log2n, log2pmax; - gset_encoder encoder; - apr_uint64_t *hashes; - apr_size_t hash_count, i; - - nelts = diary->entries->nelts; - N = ceil_power_of_2(nelts); - log2n = h2_log2(N); - - /* Now log2p is the max number of relevant bits, so that - * log2p + log2n == mask_bits. We can use a lower log2p - * and have a shorter set encoding... - */ - log2pmax = h2_log2(ceil_power_of_2(maxP)); - - memset(&encoder, 0, sizeof(encoder)); - encoder.diary = diary; - encoder.log2p = H2MIN(diary->mask_bits - log2n, log2pmax); - encoder.mask_bits = log2n + encoder.log2p; - encoder.delta_bits = diary->mask_bits - encoder.mask_bits; - encoder.fixed_bits = encoder.log2p; - encoder.fixed_mask = 1; - encoder.fixed_mask = (encoder.fixed_mask << encoder.fixed_bits) - 1; - encoder.pool = pool; - encoder.datalen = 512; - encoder.data = apr_pcalloc(encoder.pool, encoder.datalen); - - encoder.data[0] = log2n; - encoder.data[1] = encoder.log2p; - encoder.offset = 1; - encoder.bit = 8; - encoder.last = 0; - - /* Intentional no APLOGNO */ - ap_log_perror(APLOG_MARK, GCSLOG_LEVEL, 0, pool, - "h2_push_diary_digest_get: %d entries, N=%d, log2n=%d, " - "mask_bits=%d, enc.mask_bits=%d, delta_bits=%d, enc.log2p=%d, authority=%s", - (int)nelts, (int)N, (int)log2n, diary->mask_bits, - (int)encoder.mask_bits, (int)encoder.delta_bits, - (int)encoder.log2p, authority); - - if (!authority || !diary->authority - || !strcmp("*", authority) || !strcmp(diary->authority, authority)) { - hash_count = diary->entries->nelts; - hashes = apr_pcalloc(encoder.pool, hash_count); - for (i = 0; i < hash_count; ++i) { - hashes[i] = ((&APR_ARRAY_IDX(diary->entries, i, h2_push_diary_entry))->hash - >> encoder.delta_bits); - } - - qsort(hashes, hash_count, sizeof(apr_uint64_t), cmp_puint64); - for (i = 0; i < hash_count; ++i) { - if (!i || (hashes[i] != hashes[i-1])) { - gset_encode_next(&encoder, hashes[i]); - } - } - /* Intentional no APLOGNO */ - ap_log_perror(APLOG_MARK, GCSLOG_LEVEL, 0, pool, - "h2_push_diary_digest_get: golomb compressed hashes, %d bytes", - (int)encoder.offset + 1); - } - *pdata = (const char *)encoder.data; - *plen = encoder.offset + 1; - - return APR_SUCCESS; -} - diff -Nru apache2-2.4.69/modules/http2/h2_push.h apache2-2.4.71/modules/http2/h2_push.h --- apache2-2.4.69/modules/http2/h2_push.h 2023-01-09 07:35:18.000000000 +0000 +++ apache2-2.4.71/modules/http2/h2_push.h 2026-11-09 03:07:05.000000000 +0000 @@ -140,19 +140,4 @@ const struct h2_headers *res); #endif -/** - * Get a cache digest as described in - * https://datatracker.ietf.org/doc/draft-kazuho-h2-cache-digest/ - * from the contents of the push diary. - * - * @param diary the diary to calculdate the digest from - * @param p the pool to use - * @param authority the authority to get the data for, use NULL/"*" for all - * @param pdata on successful return, the binary cache digest - * @param plen on successful return, the length of the binary data - */ -apr_status_t h2_push_diary_digest_get(h2_push_diary *diary, apr_pool_t *p, - int maxP, const char *authority, - const char **pdata, apr_size_t *plen); - #endif /* defined(__mod_h2__h2_push__) */ diff -Nru apache2-2.4.69/modules/http2/h2_request.c apache2-2.4.71/modules/http2/h2_request.c --- apache2-2.4.69/modules/http2/h2_request.c 2025-07-07 12:12:49.000000000 +0000 +++ apache2-2.4.71/modules/http2/h2_request.c 2026-11-09 03:07:05.000000000 +0000 @@ -350,7 +350,7 @@ /* If we have a content-type, but already seen eos, no more * data will come. Signal a zero content length explicitly. */ - apr_table_setn(req->headers, "Content-Length", "0"); + apr_table_setn(r->headers_in, "Content-Length", "0"); } } #if !AP_HAS_RESPONSE_BUCKETS diff -Nru apache2-2.4.69/modules/http2/h2_session.c apache2-2.4.71/modules/http2/h2_session.c --- apache2-2.4.69/modules/http2/h2_session.c 2026-09-29 08:28:40.000000000 +0000 +++ apache2-2.4.71/modules/http2/h2_session.c 2026-11-09 03:07:05.000000000 +0000 @@ -1065,8 +1065,8 @@ (int)session->max_stream_mem, session->mplx->processing_limit, session->mplx->processing_max, - session->push_diary->dtype, - (int)session->push_diary->N, + session->push_diary ? session->push_diary->dtype : 0, + session->push_diary ? (int)session->push_diary->N : 0, (int)session->max_data_frame_len, session->max_stream_errors); } diff -Nru apache2-2.4.69/modules/http2/h2_stream.c apache2-2.4.71/modules/http2/h2_stream.c --- apache2-2.4.69/modules/http2/h2_stream.c 2026-09-28 18:58:27.000000000 +0000 +++ apache2-2.4.71/modules/http2/h2_stream.c 2026-11-09 03:07:05.000000000 +0000 @@ -763,7 +763,7 @@ if (name[0] == ':') { if (vlen > APR_INT32_MAX || (int)vlen > session->s->limit_req_line) { /* pseudo header: approximation of request line size check */ - if (!h2_stream_is_ready(stream)) { + if (!stream->request_headers_failed) { ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, session->c1, H2_STRM_LOG(APLOGNO(10178), stream, "Request pseudo header exceeds " @@ -810,7 +810,7 @@ if (APR_EINVAL == status) { /* header too long */ - if (!h2_stream_is_ready(stream) && !stream->request_headers_failed) { + if (!stream->request_headers_failed) { ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, session->c1, H2_STRM_LOG(APLOGNO(10180), stream, "Request header exceeds LimitRequestFieldSize(%d): %.*s"), @@ -829,7 +829,7 @@ h2_stream_rst(stream, H2_ERR_ENHANCE_YOUR_CALM); return APR_ECONNRESET; } - if (!h2_stream_is_ready(stream)) { + if (!stream->request_headers_failed) { ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, session->c1, H2_STRM_LOG(APLOGNO(10181), stream, "Number of request headers " "exceeds LimitRequestFields(%d)"), @@ -889,7 +889,7 @@ ctx.failed_key = NULL; apr_table_do(table_check_val_len, &ctx, req->headers, NULL); if (ctx.failed_key) { - if (!h2_stream_is_ready(stream)) { + if (!stream->request_headers_failed) { ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, stream->session->c1, H2_STRM_LOG(APLOGNO(10230), stream,"Request header exceeds " "LimitRequestFieldSize: %.*s"), @@ -969,10 +969,13 @@ } } - if (req->scheme && (req->path && req->path[0] != '/')) { + if (req->scheme && (req->path && req->path[0] != '/') + && !(is_http_or_https && !strcmp(req->path, "*") + && !strcmp(req->method, "OPTIONS"))) { /* We still have a scheme, which means we need to pass an absolute URI into * our HTTP protocol handling and the missing '/' at the start will prevent - * us from doing so (as it then confuses path and authority). */ + * us from doing so (as it then confuses path and authority). + * The asterisk-form of OPTIONS is no URI path and passed as is. */ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, stream->session->c1, H2_STRM_LOG(APLOGNO(10379), stream, "Request :scheme '%s' and " "path '%s' do not allow creating an absolute URL. Failing " diff -Nru apache2-2.4.69/modules/http2/h2_switch.c apache2-2.4.71/modules/http2/h2_switch.c --- apache2-2.4.69/modules/http2/h2_switch.c 2023-06-30 07:22:40.000000000 +0000 +++ apache2-2.4.71/modules/http2/h2_switch.c 2026-11-09 03:07:05.000000000 +0000 @@ -56,7 +56,7 @@ { int proposed = 0; int is_tls = ap_ssl_conn_is_ssl(c); - const char **protos = is_tls? h2_protocol_ids_tls : h2_protocol_ids_clear; + const char *const *protos = is_tls? h2_protocol_ids_tls : h2_protocol_ids_clear; if (!h2_mpm_supported()) { return DECLINED; @@ -155,8 +155,8 @@ const char *protocol) { int found = 0; - const char **protos = ap_ssl_conn_is_ssl(c)? h2_protocol_ids_tls : h2_protocol_ids_clear; - const char **p = protos; + const char *const *protos = ap_ssl_conn_is_ssl(c)? h2_protocol_ids_tls : h2_protocol_ids_clear; + const char *const *p = protos; (void)s; if (!h2_mpm_supported()) { diff -Nru apache2-2.4.69/modules/ldap/util_ldap.c apache2-2.4.71/modules/ldap/util_ldap.c --- apache2-2.4.69/modules/ldap/util_ldap.c 2026-06-03 17:09:40.000000000 +0000 +++ apache2-2.4.71/modules/ldap/util_ldap.c 2026-11-09 03:07:05.000000000 +0000 @@ -878,7 +878,12 @@ l->st = st; #if APR_HAS_THREADS - apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, l->pool); + /* uldap_connection_find() relies on apr_thread_mutex_trylock() failing + * for a connection this thread already holds. APR_THREAD_MUTEX_DEFAULT + * is a recursive critical section on Windows, which would hand the same + * connection back to a nested caller, so ask for a non-recursive lock. + */ + apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_UNNESTED, l->pool); apr_thread_mutex_lock(l->lock); #endif l->bound = 0; diff -Nru apache2-2.4.69/modules/mappers/mod_vhost_alias.c apache2-2.4.71/modules/mappers/mod_vhost_alias.c --- apache2-2.4.69/modules/mappers/mod_vhost_alias.c 2026-09-29 08:44:30.000000000 +0000 +++ apache2-2.4.71/modules/mappers/mod_vhost_alias.c 2026-11-09 03:07:05.000000000 +0000 @@ -244,7 +244,7 @@ "VirtualDocumentRoot expansion too large"); return HTTP_REQUEST_URI_TOO_LARGE; } - if (*pdest + size > buf + HUGE_STRING_LEN) { + if (*pdest + size >= buf + HUGE_STRING_LEN) { **pdest = '\0'; if (r->filename) { r->filename = apr_pstrcat(r->pool, r->filename, buf, NULL); diff -Nru apache2-2.4.69/modules/md/md.h apache2-2.4.71/modules/md/md.h --- apache2-2.4.69/modules/md/md.h 2025-11-04 14:51:48.000000000 +0000 +++ apache2-2.4.71/modules/md/md.h 2026-11-09 03:07:05.000000000 +0000 @@ -100,7 +100,11 @@ struct apr_array_header_t *acme_tls_1_domains; /* domains supporting "acme-tls/1" protocol */ const char *dns01_cmd; /* DNS challenge command, override global command */ - + const char *proxy_url; /* Proxy URL, override global command */ + const char *ca_certs; /* root certificates to use for connections, + override global command */ + const char *proxy_ca_certs; /* root certificates to use for proxy connections, + override global command */ const struct md_srv_conf_t *sc; /* server config where it was defined or NULL */ const char *defn_name; /* config file this MD was defined */ unsigned defn_line_number; /* line number of definition */ @@ -125,6 +129,7 @@ #define MD_KEY_AUTHORIZATIONS "authorizations" #define MD_KEY_BITS "bits" #define MD_KEY_CA "ca" +#define MD_KEY_CA_CERTS "ca-certs" #define MD_KEY_CA_URL "ca-url" #define MD_KEY_CERT "cert" #define MD_KEY_CERT_FILES "cert-files" @@ -185,6 +190,8 @@ #define MD_KEY_PROFILE "profile" #define MD_KEY_PROFILE_MANDATORY "profile-mandatory" #define MD_KEY_PROTO "proto" +#define MD_KEY_PROXY_CA_CERTS "proxy-ca-certs" +#define MD_KEY_PROXY_URL "proxy-url" #define MD_KEY_READY "ready" #define MD_KEY_REGISTRATION "registration" #define MD_KEY_RENEW "renew" diff -Nru apache2-2.4.69/modules/md/md_acme.c apache2-2.4.71/modules/md/md_acme.c --- apache2-2.4.69/modules/md/md_acme.c 2026-07-06 11:55:01.000000000 +0000 +++ apache2-2.4.71/modules/md/md_acme.c 2026-11-09 03:07:05.000000000 +0000 @@ -624,7 +624,8 @@ /* ACME setup */ apr_status_t md_acme_create(md_acme_t **pacme, apr_pool_t *p, const char *url, - const char *proxy_url, const char *ca_file) + const char *proxy_url, const char *ca_file, + const char *proxy_ca_file) { md_acme_t *acme; const char *err = NULL; @@ -647,9 +648,10 @@ acme->p = p; acme->user_agent = apr_psprintf(p, "%s mod_md/%s", base_product, MOD_MD_VERSION); - acme->proxy_url = proxy_url? apr_pstrdup(p, proxy_url) : NULL; - acme->max_retries = 99; + acme->proxy_url = apr_pstrdup(p, proxy_url); + acme->max_retries = 9; acme->ca_file = ca_file; + acme->proxy_ca_file = proxy_ca_file; if (APR_SUCCESS != (rv = apr_uri_parse(p, url, &uri_parsed))) { md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "parsing ACME uri: %s", url); @@ -802,6 +804,7 @@ md_http_set_connect_timeout_default(acme->http, apr_time_from_sec(30)); md_http_set_stalling_default(acme->http, 10, apr_time_from_sec(30)); md_http_set_ca_file(acme->http, acme->ca_file); + md_http_set_proxy_ca_file(acme->http, acme->proxy_ca_file); md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, acme->p, "get directory from %s", acme->url); diff -Nru apache2-2.4.69/modules/md/md_acme.h apache2-2.4.71/modules/md/md_acme.h --- apache2-2.4.69/modules/md/md_acme.h 2025-11-04 14:51:48.000000000 +0000 +++ apache2-2.4.71/modules/md/md_acme.h 2026-11-09 03:07:05.000000000 +0000 @@ -98,6 +98,7 @@ const char *user_agent; const char *proxy_url; const char *ca_file; + const char *proxy_ca_file; const char *acct_id; /* local storage id account was loaded from or NULL */ struct md_acme_acct_t *acct; /* account at ACME server to use for requests */ @@ -151,9 +152,12 @@ * @param p pool to used * @param url url of the server, optional if known at path * @param proxy_url optional url of a HTTP(S) proxy to use + * @param ca_file optional CA trust anchor file to use + * @param proxy_ca_file optional CA trust anchor file to use for the HTTP proxy */ apr_status_t md_acme_create(md_acme_t **pacme, apr_pool_t *p, const char *url, - const char *proxy_url, const char *ca_file); + const char *proxy_url, const char *ca_file, + const char *proxy_ca_file); /** * Contact the ACME server and retrieve its directory information. diff -Nru apache2-2.4.69/modules/md/md_acme_authz.c apache2-2.4.71/modules/md/md_acme_authz.c --- apache2-2.4.69/modules/md/md_acme_authz.c 2025-11-04 14:51:48.000000000 +0000 +++ apache2-2.4.71/modules/md/md_acme_authz.c 2026-11-09 03:07:05.000000000 +0000 @@ -385,7 +385,7 @@ rv = md_store_save(store, p, MD_SG_CHALLENGES, authz->domain, cfn, MD_SV_CERT, (void*)cha_cert, 0); } - ++notify_server; + notify_server = 1; } } diff -Nru apache2-2.4.69/modules/md/md_acme_drive.c apache2-2.4.71/modules/md/md_acme_drive.c --- apache2-2.4.69/modules/md/md_acme_drive.c 2025-11-04 14:51:48.000000000 +0000 +++ apache2-2.4.71/modules/md/md_acme_drive.c 2026-11-09 03:07:05.000000000 +0000 @@ -771,7 +771,9 @@ md_result_activity_printf(result, "Contacting ACME server for %s at %s", d->md->name, ca_effective); if (APR_SUCCESS != (rv = md_acme_create(&ad->acme, d->p, ca_effective, - d->proxy_url, d->ca_file))) { + ad->md->proxy_url ? ad->md->proxy_url : d->proxy_url, + ad->md->ca_certs ? ad->md->ca_certs : d->ca_certs, + ad->md->proxy_ca_certs ? ad->md->proxy_ca_certs : d->proxy_ca_certs))) { md_result_printf(result, rv, "setup ACME communications"); md_result_log(result, MD_LOG_ERR); goto out; @@ -1033,7 +1035,9 @@ } if (APR_SUCCESS != (rv = md_acme_create(&acme, d->p, md->ca_effective, - d->proxy_url, d->ca_file))) { + d->md->proxy_url ? d->md->proxy_url : d->proxy_url, + d->md->ca_certs ? d->md->ca_certs : d->ca_certs, + d->md->proxy_ca_certs ? d->md->proxy_ca_certs : d->proxy_ca_certs))) { md_result_set(result, rv, "error setting up acme"); goto leave; } @@ -1142,7 +1146,9 @@ } if (APR_SUCCESS != (rv = md_acme_create(&ad->acme, d->p, ca_effective, - d->proxy_url, d->ca_file))) { + d->md->proxy_url ? d->md->proxy_url : d->proxy_url, + d->md->ca_certs ? d->md->ca_certs : d->ca_certs, + d->md->proxy_ca_certs ? d->md->proxy_ca_certs : d->proxy_ca_certs))) { md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, d->p, "create ACME communications"); goto out; diff -Nru apache2-2.4.69/modules/md/md_core.c apache2-2.4.71/modules/md/md_core.c --- apache2-2.4.69/modules/md/md_core.c 2025-11-04 14:51:48.000000000 +0000 +++ apache2-2.4.71/modules/md/md_core.c 2026-11-09 03:07:05.000000000 +0000 @@ -165,6 +165,9 @@ md_t *md_get_by_domain(struct apr_array_header_t *mds, const char *domain) { int i; + if (!domain) { + return NULL; + } for (i = 0; i < mds->nelts; ++i) { md_t *md = APR_ARRAY_IDX(mds, i, md_t *); if (md_contains(md, domain, 0)) { @@ -258,6 +261,9 @@ md->acme_tls_1_domains = md_array_str_compact(p, src->acme_tls_1_domains, 0); md->stapling = src->stapling; if (src->dns01_cmd) md->dns01_cmd = apr_pstrdup(p, src->dns01_cmd); + if (src->proxy_url) md->proxy_url = apr_pstrdup(p, src->proxy_url); + if (src->ca_certs) md->ca_certs = apr_pstrdup(p, src->ca_certs); + if (src->proxy_ca_certs) md->proxy_ca_certs = apr_pstrdup(p, src->proxy_ca_certs); if (src->cert_files) md->cert_files = md_array_str_clone(p, src->cert_files); if (src->pkey_files) md->pkey_files = md_array_str_clone(p, src->pkey_files); } @@ -315,6 +321,9 @@ if (md->pkey_files) md_json_setsa(md->pkey_files, json, MD_KEY_PKEY_FILES, NULL); md_json_setb(md->stapling > 0, json, MD_KEY_STAPLING, NULL); if (md->dns01_cmd) md_json_sets(md->dns01_cmd, json, MD_KEY_CMD_DNS01, NULL); + if (md->proxy_url) md_json_sets(md->proxy_url, json, MD_KEY_PROXY_URL, NULL); + if (md->ca_certs) md_json_sets(md->ca_certs, json, MD_KEY_CA_CERTS, NULL); + if (md->proxy_ca_certs) md_json_sets(md->proxy_ca_certs, json, MD_KEY_PROXY_CA_CERTS, NULL); if (md->ca_eab_kid && strcmp("none", md->ca_eab_kid)) { md_json_sets(md->ca_eab_kid, json, MD_KEY_EAB, MD_KEY_KID, NULL); if (md->ca_eab_hmac) md_json_sets(md->ca_eab_hmac, json, MD_KEY_EAB, MD_KEY_HMAC, NULL); @@ -384,6 +393,9 @@ } md->stapling = (int)md_json_getb(json, MD_KEY_STAPLING, NULL); md->dns01_cmd = md_json_dups(p, json, MD_KEY_CMD_DNS01, NULL); + md->proxy_url = md_json_dups(p, json, MD_KEY_PROXY_URL, NULL); + md->ca_certs = md_json_dups(p, json, MD_KEY_CA_CERTS, NULL); + md->proxy_ca_certs = md_json_dups(p, json, MD_KEY_PROXY_CA_CERTS, NULL); if (md_json_has_key(json, MD_KEY_EAB, NULL)) { md->ca_eab_kid = md_json_dups(p, json, MD_KEY_EAB, MD_KEY_KID, NULL); md->ca_eab_hmac = md_json_dups(p, json, MD_KEY_EAB, MD_KEY_HMAC, NULL); diff -Nru apache2-2.4.69/modules/md/md_crypt.c apache2-2.4.71/modules/md/md_crypt.c --- apache2-2.4.69/modules/md/md_crypt.c 2026-09-01 10:27:29.000000000 +0000 +++ apache2-2.4.71/modules/md/md_crypt.c 2026-11-09 03:07:05.000000000 +0000 @@ -1256,13 +1256,25 @@ int md_cert_is_valid_now(const md_cert_t *cert) { +#if OPENSSL_VERSION_NUMBER >= 0x40000000L + return X509_check_certificate_times(NULL, cert->x509, NULL) == 1; +#else return ((X509_cmp_current_time(X509_get_notBefore(cert->x509)) < 0) && (X509_cmp_current_time(X509_get_notAfter(cert->x509)) > 0)); +#endif } int md_cert_has_expired(const md_cert_t *cert) { +#if OPENSSL_VERSION_NUMBER >= 0x40000000L + /* Report expiry only (not a not-yet-valid notBefore), matching the + * legacy notAfter-only check below. */ + int error = 0; + X509_check_certificate_times(NULL, cert->x509, &error); + return error == X509_V_ERR_CERT_HAS_EXPIRED; +#else return (X509_cmp_current_time(X509_get_notAfter(cert->x509)) <= 0); +#endif } apr_time_t md_cert_get_not_after(const md_cert_t *cert) diff -Nru apache2-2.4.69/modules/md/md_curl.c apache2-2.4.71/modules/md/md_curl.c --- apache2-2.4.69/modules/md/md_curl.c 2026-07-06 11:55:01.000000000 +0000 +++ apache2-2.4.71/modules/md/md_curl.c 2026-11-09 03:07:05.000000000 +0000 @@ -248,6 +248,7 @@ CURL *curl; apr_status_t rv = APR_SUCCESS; long ssl_options = 0; + long proxy_ssl_options = 0; curl = md_http_get_impl_data(req->http); if (!curl) { @@ -315,6 +316,16 @@ ssl_options |= CURLSSLOPT_NO_REVOKE; #endif } + if (req->proxy_ca_file) { + curl_easy_setopt(curl, CURLOPT_PROXY_CAINFO, req->proxy_ca_file); + /* for a custom CA, allow certificates checking to ignore the + * Schannel error CRYPT_E_NO_REVOCATION_CHECK (could be a missing OCSP + * responder URL in the certs???). See issue #361 */ +#ifdef CURLSSLOPT_NO_REVOKE + proxy_ssl_options |= CURLSSLOPT_NO_REVOKE; +#endif + } + if (req->unix_socket_path) { curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, req->unix_socket_path); } @@ -356,6 +367,9 @@ if (ssl_options) curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, ssl_options); + if (proxy_ssl_options) + curl_easy_setopt(curl, CURLOPT_PROXY_SSL_OPTIONS, proxy_ssl_options); + leave: req->internals = (APR_SUCCESS == rv)? internals : NULL; return rv; diff -Nru apache2-2.4.69/modules/md/md_http.c apache2-2.4.71/modules/md/md_http.c --- apache2-2.4.69/modules/md/md_http.c 2025-11-04 14:51:48.000000000 +0000 +++ apache2-2.4.71/modules/md/md_http.c 2026-11-09 03:07:05.000000000 +0000 @@ -36,6 +36,7 @@ const char *unix_socket_path; md_http_timeouts_t timeout; const char *ca_file; + const char *proxy_ca_file; }; static md_http_impl_t *cur_impl; @@ -82,7 +83,7 @@ http->pool = p; http->impl = cur_impl; http->user_agent = apr_pstrdup(p, user_agent); - http->proxy_url = proxy_url? apr_pstrdup(p, proxy_url) : NULL; + http->proxy_url = apr_pstrdup(p, proxy_url); http->bucket_alloc = apr_bucket_alloc_create(p); if (!http->bucket_alloc) { return APR_EGENERAL; @@ -107,6 +108,9 @@ if (source_http->ca_file) { (*phttp)->ca_file = apr_pstrdup(p, source_http->ca_file); } + if (source_http->proxy_ca_file) { + (*phttp)->proxy_ca_file = apr_pstrdup(p, source_http->proxy_ca_file); + } } return rv; } @@ -163,6 +167,11 @@ http->ca_file = ca_file; } +void md_http_set_proxy_ca_file(md_http_t *http, const char *ca_file) +{ + http->proxy_ca_file = ca_file; +} + void md_http_set_unix_socket_path(md_http_t *http, const char *path) { http->unix_socket_path = path; @@ -235,6 +244,7 @@ req->proxy_url = http->proxy_url; req->timeout = http->timeout; req->ca_file = http->ca_file; + req->proxy_ca_file = http->proxy_ca_file; req->unix_socket_path = http->unix_socket_path; *preq = req; return rv; diff -Nru apache2-2.4.69/modules/md/md_http.h apache2-2.4.71/modules/md/md_http.h --- apache2-2.4.69/modules/md/md_http.h 2022-08-25 14:11:44.000000000 +0000 +++ apache2-2.4.71/modules/md/md_http.h 2026-11-09 03:07:05.000000000 +0000 @@ -65,6 +65,7 @@ const char *user_agent; const char *proxy_url; const char *ca_file; + const char *proxy_ca_file; const char *unix_socket_path; apr_table_t *headers; struct apr_bucket_brigade *body; @@ -119,13 +120,20 @@ void md_http_set_stalling(md_http_request_t *req, long bytes_per_sec, apr_time_t timeout); /** - * Set a CA file (in PERM format) to use for root certificates when + * Set a CA file (in PEM format) to use for root certificates when * verifying SSL connections. If not set (or set to NULL), the systems * certificate store will be used. */ void md_http_set_ca_file(md_http_t *http, const char *ca_file); /** + * Set a CA file (in PEM format) to use for root certificates when + * verifying SSL connections to the HTTP proxy. If not set (or set to NULL), + * the systems certificate store will be used. + */ +void md_http_set_proxy_ca_file(md_http_t *http, const char *ca_file); + +/** * Set the path of a unix domain socket for use instead of TCP * in a connection. Disable by providing NULL as path. */ diff -Nru apache2-2.4.69/modules/md/md_reg.c apache2-2.4.71/modules/md/md_reg.c --- apache2-2.4.69/modules/md/md_reg.c 2025-11-04 14:51:48.000000000 +0000 +++ apache2-2.4.71/modules/md/md_reg.c 2026-11-09 03:07:05.000000000 +0000 @@ -47,7 +47,8 @@ int can_http; int can_https; const char *proxy_url; - const char *ca_file; + const char *ca_certs; + const char *proxy_ca_certs; int domains_frozen; md_timeslice_t *renew_window; md_timeslice_t *warn_window; @@ -96,9 +97,10 @@ } apr_status_t md_reg_create(md_reg_t **preg, apr_pool_t *p, struct md_store_t *store, - const char *proxy_url, const char *ca_file, - apr_time_t min_delay, int retry_failover, - int use_store_locks, apr_time_t lock_wait_timeout) + const char *proxy_url, const char *ca_certs, + const char *proxy_ca_certs, apr_time_t min_delay, + int retry_failover, int use_store_locks, + apr_time_t lock_wait_timeout) { md_reg_t *reg; apr_status_t rv; @@ -110,9 +112,11 @@ reg->certs = apr_hash_make(p); reg->can_http = 1; reg->can_https = 1; - reg->proxy_url = proxy_url? apr_pstrdup(p, proxy_url) : NULL; - reg->ca_file = (ca_file && apr_cstr_casecmp("none", ca_file))? - apr_pstrdup(p, ca_file) : NULL; + reg->proxy_url = apr_pstrdup(p, proxy_url); + reg->ca_certs = (ca_certs && apr_cstr_casecmp("none", ca_certs))? + apr_pstrdup(p, ca_certs) : NULL; + reg->proxy_ca_certs = (proxy_ca_certs && apr_cstr_casecmp("none", proxy_ca_certs))? + apr_pstrdup(p, proxy_ca_certs) : NULL; reg->min_delay = min_delay; reg->retry_failover = retry_failover; reg->use_store_locks = use_store_locks; @@ -1109,7 +1113,8 @@ driver->reg = reg; driver->store = md_reg_store_get(reg); driver->proxy_url = reg->proxy_url; - driver->ca_file = reg->ca_file; + driver->ca_certs = reg->ca_certs; + driver->proxy_ca_certs = reg->proxy_ca_certs; driver->md = md; driver->can_http = reg->can_http; driver->can_https = reg->can_https; diff -Nru apache2-2.4.69/modules/md/md_reg.h apache2-2.4.71/modules/md/md_reg.h --- apache2-2.4.69/modules/md/md_reg.h 2025-11-04 14:51:48.000000000 +0000 +++ apache2-2.4.71/modules/md/md_reg.h 2026-11-09 03:07:05.000000000 +0000 @@ -39,14 +39,16 @@ * @param pm memory pool to use for creation * @param store the store to base on * @param proxy_url optional URL of a proxy to use for requests - * @param ca_file optioinal CA trust anchor file to use + * @param ca_certs optional CA trust anchor file to use + * @param proxy_ca_certs optional CA trust anchor file to use for the HTTP proxy * @param min_delay minimum delay between renewal attempts for a domain - * @param retry_failover numer of failed renewals attempt to fail over to alternate ACME ca + * @param retry_failover number of failed renewals attempt to fail over to alternate ACME ca */ apr_status_t md_reg_create(md_reg_t **preg, apr_pool_t *pm, md_store_t *store, - const char *proxy_url, const char *ca_file, - apr_time_t min_delay, int retry_failover, - int use_store_locks, apr_time_t lock_wait_timeout); + const char *proxy_url, const char *ca_certs, + const char *proxy_ca_certs, apr_time_t min_delay, + int retry_failover, int use_store_locks, + apr_time_t lock_wait_timeout); md_store_t *md_reg_store_get(md_reg_t *reg); @@ -224,7 +226,8 @@ md_reg_t *reg; md_store_t *store; const char *proxy_url; - const char *ca_file; + const char *ca_certs; + const char *proxy_ca_certs; const md_t *md; int can_http; diff -Nru apache2-2.4.69/modules/md/mod_md.c apache2-2.4.71/modules/md/mod_md.c --- apache2-2.4.69/modules/md/mod_md.c 2025-11-04 14:51:48.000000000 +0000 +++ apache2-2.4.71/modules/md/mod_md.c 2026-11-09 03:07:05.000000000 +0000 @@ -855,6 +855,9 @@ apr_status_t rv = APR_SUCCESS; int dry_run = 0, log_level = APLOG_DEBUG; md_store_t *store; + const char *proxy_url; + const char *ca_certs; + const char *proxy_ca_certs; apr_pool_userdata_get(&data, mod_md_init_key, s->process->pool); if (data == NULL) { @@ -893,7 +896,11 @@ rv = setup_store(&store, mc, p, s); if (APR_SUCCESS != rv) goto leave; - rv = md_reg_create(&mc->reg, p, store, mc->proxy_url, mc->ca_certs, + proxy_url = apr_table_get(mc->env, MD_KEY_PROXY_URL); + ca_certs = apr_table_get(mc->env, MD_KEY_CA_CERTS); + proxy_ca_certs = apr_table_get(mc->env, MD_KEY_PROXY_CA_CERTS); + + rv = md_reg_create(&mc->reg, p, store, proxy_url, ca_certs, proxy_ca_certs, mc->min_delay, mc->retry_failover, mc->use_store_locks, mc->lock_wait_timeout); if (APR_SUCCESS != rv) { @@ -903,7 +910,7 @@ /* renew on 30% remaining /*/ rv = md_ocsp_reg_make(&mc->ocsp, p, store, mc->ocsp_renew_window, - AP_SERVER_BASEVERSION, mc->proxy_url, + AP_SERVER_BASEVERSION, proxy_url, mc->min_delay); if (APR_SUCCESS != rv) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10196) "setup ocsp registry"); @@ -1412,6 +1419,11 @@ && !strncmp(ACME_CHALLENGE_PREFIX, r->parsed_uri.path, sizeof(ACME_CHALLENGE_PREFIX)-1)) { sc = ap_get_module_config(r->server->module_config, &md_module); if (sc && sc->mc) { + if (!r->hostname) { + /* No Host: (e.g. an HTTP/1.0 request without one) means no + * domain to look up; leave the request to normal handling. */ + return DECLINED; + } ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "access inside /.well-known/acme-challenge for %s%s", r->hostname, r->parsed_uri.path); diff -Nru apache2-2.4.69/modules/md/mod_md_config.c apache2-2.4.71/modules/md/mod_md_config.c --- apache2-2.4.69/modules/md/mod_md_config.c 2026-07-16 14:35:44.000000000 +0000 +++ apache2-2.4.71/modules/md/mod_md_config.c 2026-11-09 03:07:05.000000000 +0000 @@ -61,7 +61,6 @@ #else MD_DEFAULT_BASE_DIR, #endif - NULL, /* proxy url for outgoing http */ NULL, /* md_reg_t */ NULL, /* md_ocsp_reg_t */ 80, /* local http: port */ @@ -83,7 +82,6 @@ &def_ocsp_renew_window, /* default time to renew ocsp responses */ "crt.sh", /* default cert checker site name */ "https://crt.sh?q=", /* default cert checker site url */ - NULL, /* CA cert file to use */ APR_TIME_C(0), /* initial cert check delay */ apr_time_from_sec(MD_SECS_PER_DAY/2), /* default time between cert checks */ apr_time_from_sec(30), /* minimum delay for retries */ @@ -127,6 +125,9 @@ 1, /* staple others */ 1, /* ACME ARI renewals */ NULL, /* dns01_cmd */ + NULL, /* proxy URL */ + NULL, /* CA cert file to use */ + NULL, /* CA cert file to use for proxy */ NULL, /* currently defined md */ NULL, /* assigned md, post config */ 0, /* is_ssl, set during mod_ssl post_config */ @@ -185,6 +186,9 @@ sc->staple_others = DEF_VAL; sc->ari_renewals = DEF_VAL; sc->dns01_cmd = NULL; + sc->proxy_url = NULL; + sc->ca_certs = NULL; + sc->proxy_ca_certs = NULL; } static void srv_conf_props_copy(md_srv_conf_t *to, const md_srv_conf_t *from) @@ -209,6 +213,9 @@ to->staple_others = from->staple_others; to->ari_renewals = from->ari_renewals; to->dns01_cmd = from->dns01_cmd; + to->proxy_url = from->proxy_url; + to->ca_certs = from->ca_certs; + to->proxy_ca_certs = from->proxy_ca_certs; } static void srv_conf_props_apply(md_t *md, const md_srv_conf_t *from, apr_pool_t *p) @@ -236,6 +243,9 @@ if (from->ari_renewals != DEF_VAL) md->ari_renewals = from->ari_renewals; if (from->stapling != DEF_VAL) md->stapling = from->stapling; if (from->dns01_cmd) md->dns01_cmd = from->dns01_cmd; + if (from->proxy_url) md->proxy_url = from->proxy_url; + if (from->ca_certs) md->ca_certs = from->ca_certs; + if (from->proxy_ca_certs) md->proxy_ca_certs = from->proxy_ca_certs; } void *md_config_create_svr(apr_pool_t *pool, server_rec *s) @@ -285,6 +295,9 @@ nsc->staple_others = (add->staple_others != DEF_VAL)? add->staple_others : base->staple_others; nsc->ari_renewals = (add->ari_renewals != DEF_VAL)? add->ari_renewals : base->ari_renewals; nsc->dns01_cmd = (add->dns01_cmd)? add->dns01_cmd : base->dns01_cmd; + nsc->proxy_url = (add->proxy_url)? add->proxy_url : base->proxy_url; + nsc->ca_certs = (add->ca_certs)? add->ca_certs : base->ca_certs; + nsc->proxy_ca_certs = (add->proxy_ca_certs)? add->proxy_ca_certs : base->proxy_ca_certs; nsc->current = NULL; return nsc; @@ -865,14 +878,20 @@ md_srv_conf_t *sc = md_config_get(cmd->server); const char *err; - if ((err = md_conf_check_location(cmd, MD_LOC_NOT_MD))) { + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { return err; } md_util_abs_http_uri_check(cmd->pool, value, &err); if (err) { return err; } - sc->mc->proxy_url = value; + + if (inside_md_section(cmd)) { + sc->proxy_url = value; + } else { + apr_table_set(sc->mc->env, MD_KEY_PROXY_URL, value); + } + (void)arg; return NULL; } @@ -1240,12 +1259,41 @@ return NULL; } -static const char *md_config_set_ca_certs(cmd_parms *cmd, void *dc, const char *path) +static const char *md_config_set_ca_certs(cmd_parms *cmd, void *arg, const char *value) { md_srv_conf_t *sc = md_config_get(cmd->server); + const char *err; - (void)dc; - sc->mc->ca_certs = path; + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { + return err; + } + + if (inside_md_section(cmd)) { + sc->ca_certs = value; + } else { + apr_table_set(sc->mc->env, MD_KEY_CA_CERTS, value); + } + + (void)arg; + return NULL; +} + +static const char *md_config_set_proxy_ca_certs(cmd_parms *cmd, void *arg, const char *value) +{ + md_srv_conf_t *sc = md_config_get(cmd->server); + const char *err; + + if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) { + return err; + } + + if (inside_md_section(cmd)) { + sc->proxy_ca_certs = value; + } else { + apr_table_set(sc->mc->env, MD_KEY_PROXY_CA_CERTS, value); + } + + (void)arg; return NULL; } @@ -1387,6 +1435,8 @@ "How long to delay activation of new certificates"), AP_INIT_TAKE1("MDCACertificateFile", md_config_set_ca_certs, NULL, RSRC_CONF, "Set the CA file to use for connections"), + AP_INIT_TAKE1("MDHttpProxyCACertificateFile", md_config_set_proxy_ca_certs, NULL, RSRC_CONF, + "Set the CA file to use for connections to the HTTP(S) proxy"), AP_INIT_TAKE12("MDExternalAccountBinding", md_config_set_eab, NULL, RSRC_CONF, "Set the external account binding keyid and hmac values to use at CA"), AP_INIT_TAKE1("MDRetryDelay", md_config_set_min_delay, NULL, RSRC_CONF, @@ -1471,8 +1521,6 @@ return sc->ca_proto? sc->ca_proto : defconf.ca_proto; case MD_CONFIG_BASE_DIR: return sc->mc->base_dir; - case MD_CONFIG_PROXY: - return sc->mc->proxy_url; case MD_CONFIG_CA_AGREEMENT: return sc->ca_agreement? sc->ca_agreement : defconf.ca_agreement; case MD_CONFIG_NOTIFY_CMD: diff -Nru apache2-2.4.69/modules/md/mod_md_config.h apache2-2.4.71/modules/md/mod_md_config.h --- apache2-2.4.69/modules/md/mod_md_config.h 2025-11-20 13:00:23.000000000 +0000 +++ apache2-2.4.71/modules/md/mod_md_config.h 2026-11-09 03:07:05.000000000 +0000 @@ -32,7 +32,6 @@ MD_CONFIG_RENEW_WINDOW, MD_CONFIG_WARN_WINDOW, MD_CONFIG_TRANSITIVE, - MD_CONFIG_PROXY, MD_CONFIG_REQUIRE_HTTPS, MD_CONFIG_MUST_STAPLE, MD_CONFIG_NOTIFY_CMD, @@ -53,7 +52,6 @@ struct md_mod_conf_t { apr_array_header_t *mds; /* all md_t* defined in the config, shared */ const char *base_dir; /* base dir for store */ - const char *proxy_url; /* proxy url to use (or NULL) */ struct md_reg_t *reg; /* md registry instance */ struct md_ocsp_reg_t *ocsp; /* ocsp status registry */ @@ -77,7 +75,6 @@ md_timeslice_t *ocsp_renew_window; /* time before exp. that we start renewing ocsp resp. */ const char *cert_check_name; /* name of the linked certificate check site */ const char *cert_check_url; /* url "template for" checking a certificate */ - const char *ca_certs; /* root certificates to use for connections */ apr_time_t initial_delay; /* how long to delay the first cert renewal check */ apr_time_t check_interval; /* duration between cert renewal checks */ apr_time_t min_delay; /* minimum delay for retries */ @@ -115,6 +112,11 @@ int ari_renewals; /* ACME ARI extension enabled */ const char *dns01_cmd; /* DNS challenge command, override global command */ + const char *proxy_url; /* Proxy URL, override global command */ + const char *ca_certs; /* root certificates to use for connections, + override global command */ + const char *proxy_ca_certs; /* root certificates to use for proxy connections, + override global command */ md_t *current; /* md currently defined in section */ struct apr_array_header_t *assigned; /* post_config: MDs that apply to this server */ diff -Nru apache2-2.4.69/modules/md/mod_md_status.c apache2-2.4.71/modules/md/mod_md_status.c --- apache2-2.4.69/modules/md/mod_md_status.c 2025-11-04 14:51:48.000000000 +0000 +++ apache2-2.4.71/modules/md/mod_md_status.c 2026-11-09 03:07:05.000000000 +0000 @@ -77,6 +77,7 @@ /* We are looking for information about a staged certificate */ sc = ap_get_module_config(r->server->module_config, &md_module); if (!sc || !sc->mc || !sc->mc->reg || !sc->mc->certificate_status_enabled) return DECLINED; + if (!r->hostname) return DECLINED; md = md_get_by_domain(sc->mc->mds, r->hostname); if (!md) return DECLINED; diff -Nru apache2-2.4.69/modules/metadata/mod_remoteip.c apache2-2.4.71/modules/metadata/mod_remoteip.c --- apache2-2.4.69/modules/metadata/mod_remoteip.c 2020-01-14 17:39:25.000000000 +0000 +++ apache2-2.4.71/modules/metadata/mod_remoteip.c 2026-11-09 03:07:05.000000000 +0000 @@ -176,9 +176,18 @@ config->proxies_header_name = server->proxies_header_name ? server->proxies_header_name : global->proxies_header_name; - config->proxymatch_ip = server->proxymatch_ip - ? server->proxymatch_ip - : global->proxymatch_ip; + if (server->proxymatch_ip && global->proxymatch_ip) { + /* Both have entries: merge them. remoteip_modify_request() uses the + * first match, so the more specific entries of the vhost go first. + */ + config->proxymatch_ip = apr_array_append(p, server->proxymatch_ip, + global->proxymatch_ip); + } + else { + config->proxymatch_ip = server->proxymatch_ip + ? server->proxymatch_ip + : global->proxymatch_ip; + } return config; } @@ -493,14 +502,14 @@ } static int remoteip_hook_post_config(apr_pool_t *pconf, apr_pool_t *plog, - apr_pool_t *ptemp, server_rec *s) + apr_pool_t *ptemp, server_rec *s) { - remoteip_config_t *conf; + remoteip_config_t *conf, *vconf; remoteip_addr_info *info; + server_rec *vs; char buf[INET6_ADDRSTRLEN]; - conf = ap_get_module_config(ap_server_conf->module_config, - &remoteip_module); + conf = ap_get_module_config(s->module_config, &remoteip_module); for (info = conf->proxy_protocol_enabled; info; info = info->next) { apr_sockaddr_ip_getbuf(buf, sizeof(buf), info->addr); @@ -513,6 +522,24 @@ "RemoteIPProxyProtocol: disabled on %s:%hu", buf, info->addr->port); } + /* A vhost with entries of its own got a new array in the config + * merge, otherwise it shares the one of the main server. Warn for + * this case (temporarily) since the behaviour has changed with + * the fix for PR 70207. */ + if (conf->proxymatch_ip) { + for (vs = s->next; vs; vs = vs->next) { + vconf = ap_get_module_config(vs->module_config, &remoteip_module); + if (vconf->proxymatch_ip + && vconf->proxymatch_ip != conf->proxymatch_ip) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, vs, APLOGNO(10633) + "RemoteIP proxy entries are configured in both " + "the main server and the virtual host defined " + "at %s:%u, they are merged", + vs->defn_name, vs->defn_line_number); + } + } + } + return OK; } @@ -616,12 +643,12 @@ ++parse_remote; } - eos = parse_remote + strlen(parse_remote) - 1; - while (eos >= parse_remote && *eos == ' ') { - *(eos--) = '\0'; + eos = parse_remote + strlen(parse_remote); + while (eos > parse_remote && eos[-1] == ' ') { + *--eos = '\0'; } - if (eos < parse_remote) { + if (eos == parse_remote) { if (remote) { *(remote + strlen(remote)) = ','; } @@ -813,7 +840,7 @@ else if (strcmp(word, "TCP6") == 0) { #if APR_HAVE_IPV6 family = APR_INET6; - valid_addr_chars = "0123456789abcdefABCDEF:"; + valid_addr_chars = "0123456789abcdefABCDEF:."; #else ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(03498) "RemoteIPProxyProtocol: Unable to parse v6 address - APR is not compiled with IPv6 support"); @@ -931,6 +958,12 @@ return OK; } +/** Return length for a v2 protocol header. */ +static apr_size_t remoteip_get_v2_len(proxy_header *hdr) +{ + return ntohs(hdr->v2.len); +} + /* Binary format: * * sig = \x0D \x0A \x0D \x0A \x00 \x0D \x0A \x51 \x55 \x49 \x54 \x0A @@ -948,9 +981,21 @@ apr_status_t ret; switch (hdr->v2.ver_cmd & 0xF) { + case 0x00: /* LOCAL command */ + /* keep local connection address for LOCAL */ + conn_conf->client_addr = c->client_addr; + conn_conf->client_ip = c->client_ip; + return HDR_DONE; case 0x01: /* PROXY command */ switch (hdr->v2.fam) { case 0x11: /* TCPv4 */ + if (remoteip_get_v2_len(hdr) < sizeof(hdr->v2.addr.ip4)) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(10597) + "RemoteIPProxyProtocol: address length " + "%" APR_SIZE_T_FMT " too short for TCPv4", + remoteip_get_v2_len(hdr)); + return HDR_ERROR; + } ret = apr_sockaddr_info_get(&conn_conf->client_addr, NULL, APR_INET, ntohs(hdr->v2.addr.ip4.src_port), @@ -968,6 +1013,13 @@ case 0x21: /* TCPv6 */ #if APR_HAVE_IPV6 + if (remoteip_get_v2_len(hdr) < sizeof(hdr->v2.addr.ip6)) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(10598) + "RemoteIPProxyProtocol: address length " + "%" APR_SIZE_T_FMT " too short for TCPv6", + remoteip_get_v2_len(hdr)); + return HDR_ERROR; + } ret = apr_sockaddr_info_get(&conn_conf->client_addr, NULL, APR_INET6, ntohs(hdr->v2.addr.ip6.src_port), @@ -1014,12 +1066,6 @@ return HDR_DONE; } -/** Return length for a v2 protocol header. */ -static apr_size_t remoteip_get_v2_len(proxy_header *hdr) -{ - return ntohs(hdr->v2.len); -} - /** Determine if this is a v1 or v2 PROXY header. */ static int remoteip_determine_version(conn_rec *c, const char *ptr) @@ -1088,6 +1134,14 @@ apr_off_t got, want = ctx->need - ctx->rcvd; ret = ap_get_brigade(f->next, ctx->bb, ctx->mode, block, want); + if (APR_STATUS_IS_EOF(ret)) { + /* The peer went away before sending a whole header, as a + * health check may. */ + ap_log_cerror(APLOG_MARK, APLOG_INFO, ret, f->c, APLOGNO(10634) + "RemoteIPProxyProtocol: connection closed " + "before a complete header was received"); + return ret; + } if (ret != APR_SUCCESS) { ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, f->c, APLOGNO(10184) "failed reading input"); @@ -1111,6 +1165,13 @@ while (!ctx->done && !APR_BRIGADE_EMPTY(ctx->bb)) { b = APR_BRIGADE_FIRST(ctx->bb); + if (APR_BUCKET_IS_METADATA(b)) { + /* Nothing to copy from EOS or FLUSH; EOF is reported by + * the next read. */ + apr_bucket_delete(b); + continue; + } + ret = apr_bucket_read(b, &ptr, &len, block); if (APR_STATUS_IS_EAGAIN(ret) && block == APR_NONBLOCK_READ) { return APR_SUCCESS; @@ -1228,11 +1289,11 @@ "Specifies one or more internal (transparent) proxies " "which are trusted to present IP headers"), AP_INIT_TAKE1("RemoteIPTrustedProxyList", proxylist_read, 0, - RSRC_CONF | EXEC_ON_READ, + RSRC_CONF, "The filename to read the list of trusted proxies, " "see the RemoteIPTrustedProxy directive"), AP_INIT_TAKE1("RemoteIPInternalProxyList", proxylist_read, (void*)1, - RSRC_CONF | EXEC_ON_READ, + RSRC_CONF, "The filename to read the list of internal proxies, " "see the RemoteIPInternalProxy directive"), AP_INIT_FLAG("RemoteIPProxyProtocol", remoteip_enable_proxy_protocol, NULL, diff -Nru apache2-2.4.69/support/ab.c apache2-2.4.71/support/ab.c --- apache2-2.4.69/support/ab.c 2026-06-04 08:50:44.000000000 +0000 +++ apache2-2.4.71/support/ab.c 2026-11-09 03:07:05.000000000 +0000 @@ -2128,14 +2128,14 @@ static void copyright(void) { if (!use_html) { - printf("This is ApacheBench, Version %s\n", AP_AB_BASEREVISION " <$Revision: 1934973 $>"); + printf("This is ApacheBench, Version %s\n", AP_AB_BASEREVISION " <$Revision$>"); printf("Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/\n"); printf("Licensed to The Apache Software Foundation, http://www.apache.org/\n"); printf("\n"); } else { printf("

      \n"); - printf(" This is ApacheBench, Version %s <%s>
      \n", AP_AB_BASEREVISION, "$Revision: 1934973 $"); + printf(" This is ApacheBench, Version %s <%s>
      \n", AP_AB_BASEREVISION, "$Revision$"); printf(" Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
      \n"); printf(" Licensed to The Apache Software Foundation, http://www.apache.org/
      \n"); printf("

      \n

      \n"); diff -Nru apache2-2.4.69/test/.gitignore apache2-2.4.71/test/.gitignore --- apache2-2.4.69/test/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/.gitignore 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,2 @@ +config.ini +gen diff -Nru apache2-2.4.69/test/GitHub-CI-Emu.md apache2-2.4.71/test/GitHub-CI-Emu.md --- apache2-2.4.69/test/GitHub-CI-Emu.md 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/GitHub-CI-Emu.md 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,296 @@ +# GitHub CI emulation for Apache httpd (act + Docker) + +Run the Linux jobs of httpd's GitHub Actions CI workflow +(`.github/workflows/linux.yml`) on your own machine, for any commit of trunk or +2.4.x. Use it to check changes to the workflow, `test/travis_run_linux.sh`, +`Makefile.in` or `test/` before pushing, to reproduce a CI failure with a +shell in the same environment, or when the hosted Linux queue is long. + +You need `act`, Docker and a git checkout of httpd. It was written and tested +against an arm64 Linux Docker engine. The scripts pick the container +architecture from `uname -m`, but nothing has been run on x86_64, Windows or +WSL. On Windows, work from a WSL shell. + +## How it works + +* [act](https://github.com/nektos/act) reads the workflow and runs one job in a + Docker container built from an image that imitates GitHub's `ubuntu-24.04` + runner. +* `prepare.sh` exports a committed ref of your checkout (`git archive`) into a + scratch tree, `$CI_EMU/src`, and patches that copy in four small ways so the + workflow runs under act (see "Deviations"). Your checkout is not touched. + Uncommitted changes are not tested; commit them first, on a throw-away branch + if you like. +* `run-job.sh ""` runs one entry of the `build` job's matrix with + the packages, `configure` flags, environment and `test/travis_run_linux.sh` + from the workflow. + +Not covered: the other workflows (`windows.yml`, `sanity.yml`) and the GitHub +caches (APR and httpd are built from scratch each run). `upload-artifact` fails +under act with "Unable to get the ACTIONS_RUNTIME_TOKEN env variable"; ignore +it. + +## Differences from the hosted runner + +| | GitHub runner | Here | +|---|---|---| +| CPU | x86_64; arm64 only for the "64-bit ARM" entries | the architecture of your Docker engine | +| User | `runner`, with sudo | the same (created in the image) | +| PID 1 | init | `docker --init` | +| `$GITHUB_ENV` in step `if:` | honoured | not honoured (deviation 2) | +| `go`, for the pebble step | present | absent unless you add it (see Dockerfile) | + +Matrix entries with `os: ubuntu-24.04-arm` are not mapped by +`-P ubuntu-latest=...`, so run the entry of the same name without `, 64-bit +ARM`; the container runs on your engine's architecture anyway. + +## Prerequisites + +* Docker (Engine or Desktop) with the daemon running; `docker info` must work + for your user. +* `act`: from your package manager, a release binary from + , or `gh extension install + https://github.com/nektos/gh-act`. Tested with 0.2.90. +* `git`, `tar`, `perl`, a POSIX shell. `gh` is optional (comparing with real + runs). +* Network access from the container (apt, GitHub, Go modules). +* About 2 GB for the image, plus the container and the scratch tree. + +## Setting it up + +Pick a directory for the harness. The scripts read both paths from the +environment: + + export CI_EMU=$HOME/ci-emul # scripts, scratch tree, logs + export REPO=$HOME/src/httpd # the httpd git checkout + mkdir -p "$CI_EMU/out" && cd "$CI_EMU" + +### 1. The runner image + + docker pull ghcr.io/catthehacker/ubuntu:act-24.04 + +The image is on ghcr.io (not Docker Hub) and has amd64 and arm64 variants. If +`DOCKER_DEFAULT_PLATFORM` is set in your environment to another architecture, +unset it or add `--platform` to the pull. + +`Dockerfile.act` adds what the hosted image has and the act one lacks: + +```dockerfile +FROM ghcr.io/catthehacker/ubuntu:act-24.04 +# The hosted image has apt assume "yes"; this one does not. +RUN echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/90assumeyes +# linux.yml picks /usr/sbin/php-fpm*, which the hosted image ships. +RUN apt-get update && apt-get install -y php8.3-fpm golang-go && rm -rf /var/lib/apt/lists/* +# httpd's default "User #-1" fails as root, and the hosted runner is not root. +RUN useradd -m -u 1001 -s /bin/bash runner \ + && echo 'runner ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/runner +``` + +`golang-go` is for the "Install pebble" step of the `TEST_MD` jobs. Without +`go` that step only warns ("pebble install failed; modules/md tests will be +skipped") and every `modules/md` test skips. The distribution's Go is older +than the Go 1.24 that pebble v2 needs; `go install` fetches the toolchain +itself. Installing `golang-go` by hand in a running container worked. The +Dockerfile line has not been built. + + docker build -f Dockerfile.act -t act-ubuntu-24.04-ci:local . + +### 2. `prepare.sh` + +```sh +#!/bin/sh +# usage: prepare.sh [ref] (default HEAD of $REPO; a branch, tag or sha) +set -e +REPO=${REPO:?set REPO to the httpd git checkout} +CI_EMU=${CI_EMU:-$(cd "$(dirname "$0")" && pwd)} +ref=${1:-HEAD} +src=$CI_EMU/src +git -C "$REPO" rev-parse --verify -q "$ref^{commit}" >/dev/null \ + || { echo "prepare.sh: no such commit in $REPO: $ref" >&2; exit 2; } +/bin/rm -rf "$src"; mkdir -p "$src" +git -C "$REPO" archive "$ref" | tar -x -C "$src" +cd "$src" + +# 1: /etc/hosts is a bind mount in Docker; "sed -i" cannot rename over it +perl -pi -e 's|^(\s*)sudo sed -i "/ip6-/d" /etc/hosts|$1sudo sh -c "sed /ip6-/d /etc/hosts > /tmp/hosts.new \&\& cat /tmp/hosts.new > /etc/hosts"|' \ + test/travis_before_linux.sh +grep -q 'hosts.new' test/travis_before_linux.sh || { echo "fix 1 did not apply" >&2; exit 1; } + +# 2: act does not feed $GITHUB_ENV into step "if:" expressions +perl -pi -e 's/if: \$\{\{ ! \( env\.SKIP_TESTING \|\| env\.NO_TEST_FRAMEWORK \) \}\}/if: \${{ ! ( contains(matrix.env, \x27SKIP_TESTING\x27) || contains(matrix.env, \x27NO_TEST_FRAMEWORK\x27) ) }}/' \ + .github/workflows/linux.yml +grep -q "contains(matrix.env, 'NO_TEST_FRAMEWORK')" .github/workflows/linux.yml \ + || { echo "fix 2 did not apply" >&2; exit 1; } + +# 3: act's $GITHUB_ENV parser rejects the trailing blank line of a multi-line matrix.env +perl -pi -e 's/echo "\$\{\{ matrix\.env \}\}" >> \$GITHUB_ENV/echo "\${{ matrix.env }}" | sed "\/^\$\/d" >> \$GITHUB_ENV/' \ + .github/workflows/linux.yml +grep -q 'sed "/^\$/d" >> \$GITHUB_ENV' .github/workflows/linux.yml \ + || { echo "fix 3 did not apply" >&2; exit 1; } + +# 4: -f matters; .gitignore lists /build/PrintPath but it is tracked upstream +git init -q; git add -Af >/dev/null +git -c user.name=ci -c user.email=ci@example.com commit -qm "emulation snapshot of $ref" +echo "prepared $ref in $src" +``` + +### 3. `run-job.sh` + +```sh +#!/bin/sh +# usage: run-job.sh 'Python pytest test suites' +CI_EMU=${CI_EMU:-$(cd "$(dirname "$0")" && pwd)} +cd "$CI_EMU/src" || exit 1 +case $(uname -m) in + x86_64|amd64) arch=linux/amd64 ;; + *) arch=linux/arm64 ;; +esac +# Keep the previous run's container (as -prev) rather than deleting it. +if docker inspect httpd-github-ci >/dev/null 2>&1; then + docker rm -f httpd-github-ci-prev >/dev/null 2>&1 + docker rename httpd-github-ci httpd-github-ci-prev +fi +act push -W .github/workflows/linux.yml -j build \ + --matrix "name:${1:-Python pytest test suites}" \ + -P ubuntu-latest=act-ubuntu-24.04-ci:local --pull=false \ + --container-architecture "$arch" \ + --container-options "--init --user runner" --reuse +rc=$? +# act cannot name the container, so rename it for later inspection. +c=$(docker ps -a --format '{{.Names}}' | grep '^act-' | head -1) +[ -n "$c" ] && docker rename "$c" httpd-github-ci && echo "container renamed: $c -> httpd-github-ci" +exit $rc +``` + +* `-P ubuntu-latest=...` maps the runner label to the local image; + `--pull=false` stops act looking for it on a registry. +* `--init` gives the container a real PID 1, so orphaned CGI processes are + reaped; `--user runner` runs the job as the non-root user from the image. +* `--reuse` keeps the container after a successful run (a failed job's + container is kept as well, unless `--rm` is given). + +`chmod +x prepare.sh run-job.sh`. + +## Running a job + + cd "$CI_EMU" + ./prepare.sh origin/2.4.x # or trunk, a branch, a tag, a sha + ./run-job.sh 'Python pytest test suites' > out/job.log 2>&1 + +* The argument is the `name:` of a matrix entry of the `build` job; list them: + `grep -n '^ - name:' src/.github/workflows/linux.yml`. +* Re-run `prepare.sh` after every commit you want tested; the job sees the + last snapshot only. +* A full pytest job takes roughly 15 to 30 minutes (dependencies, APR, httpd, + the suites). `tail -f out/job.log` shows progress; a log that stops growing + while no `act` process is running means the job died. + +### Narrowing a run + +`travis_run_linux.sh` passes `PYTEST_ARGS` to `make check-all-pytest`: test +paths relative to `test/` and any pytest options (see `test/README.ci`). To use +it, after `prepare.sh` add a line to the `env:` block of the matrix entry in +`$CI_EMU/src/.github/workflows/linux.yml`, for example after `TEST_MD=1`: + + PYTEST_ARGS=modules/md/test_310_conf_store.py -v + +The value may contain spaces. `prepare.sh` regenerates the file, so this is +not permanent. The build still runs in full; for fast repeat runs of the same +build use the kept container, below. + +### Reading the result + + grep -n -E '�|�|Job succeeded|Job failed|[0-9]+ passed|[0-9]+ failed|error in' out/job.log + +The `Build and test` step's pytest summary (`N passed, M skipped, ...`) is the +line to compare with GitHub. Check that suites you care about did not skip: a +`::warning::pebble install failed` line means `modules/md` did not run. + +## Inspecting and debugging in the kept container + +The container is kept as `httpd-github-ci`, the run before it as +`httpd-github-ci-prev`. Its workspace is a Docker volume that act filled with a +copy of `$CI_EMU/src`, mounted at the same absolute path. The build (`check/`) +is in it, and editing files on the host does not change it. If Docker was +restarted, run `docker start httpd-github-ci` first. + + docker exec -it -u runner -w "$CI_EMU/src" httpd-github-ci bash + +Run the pytest suites again against the existing build, without rebuilding: + + docker exec -u runner -w "$CI_EMU/src" httpd-github-ci bash -c ' + export PATH=$HOME/go/bin:$HOME/.local/bin:$PATH \ + PYHTTPD_CONFIG='"$CI_EMU"'/src/check/config.ini + test/runtests.sh --apxs='"$CI_EMU"'/src/check/bin/apxs \ + --php-fpm=/usr/sbin/php-fpm8.3 modules/md/test_310_conf_store.py -q' + +Tools missing from the image can be installed in the container with `sudo +apt-get install` (for example `gdb`, to take `thread apply all bt` of a hung +httpd child); they are lost when the container is removed. `docker cp` puts a +changed file into the container. + +## Deviations, and why + +`prepare.sh` applies these to the scratch copy only. It stops if one does not +apply, which is how a workflow change that breaks one gets noticed. + +1. `/etc/hosts` is a bind mount in Docker, so `sed -i`, which renames a new + file over it, fails. It is rewritten in place instead. +2. act does not feed `$GITHUB_ENV` into step `if:` conditions, so a step that + GitHub skips for `NO_TEST_FRAMEWORK` jobs would run. The condition tests + `matrix.env` instead. +3. act's `$GITHUB_ENV` parser rejects the blank line a multi-line `matrix.env` + ends with and drops all of the job's variables, `TEST_*` included. Blank + lines are filtered out of the write. +4. act copies the workspace honouring `.gitignore`, which lists + `/build/PrintPath` although the file is tracked. Without it `configure` + finds no perl and `apxs` gets a placeholder shebang. `git add -Af` in the + snapshot commit includes it. + +The image, not `prepare.sh`, supplies php-fpm, the non-root `runner` user and +(optionally) Go. + +## Pitfalls + +* `Cannot connect to the Docker daemon` / `failed to connect to the docker + API`: the daemon is not running or your user may not use it. +* `No such image` for `catthehacker/ubuntu`: pull from `ghcr.io/...`, not Docker + Hub. An architecture mismatch error from act: see `DOCKER_DEFAULT_PLATFORM` + above, and `--container-architecture` in `run-job.sh`. +* Every test fails with connection refused, or httpd exits at start: the job ran + as root. Keep `--user runner`. +* `modules/md` tests all skip: no `go`, so no pebble. +* Jobs that do not use `NO_TEST_FRAMEWORK` check out the Perl test framework + from GitHub and need network access. +* A pytest run that was killed can leave httpd, php-fpm or CGI processes behind + in the kept container; the next run then fails on busy ports. Kill them or + start from a fresh container. + +## Comparing with real CI + + gh run list --repo apache/httpd --workflow Linux --branch trunk --limit 5 + gh run view --repo apache/httpd --json jobs \ + --jq '.jobs[] | select(.conclusion=="failure") | [.databaseId,.name] | @tsv' + gh run view --repo apache/httpd --job --log-failed + +On a failed job the workflow uploads an `error_log-` artifact (httpd error +logs and `config.log`): + + gh api repos/apache/httpd/actions/runs//artifacts --jq '.artifacts[]|[.id,.name]|@tsv' + gh api repos/apache/httpd/actions/artifacts//zip > a.zip + +Unzip it into an empty directory of its own. The `modules/` suites reuse one +log file that is truncated at every server start, so it holds only the last +server instance; run the test in the kept container to see the others. + +The hosted result is the reference. A difference that the CPU architecture does +not explain is worth chasing. + +## Files + + $CI_EMU/ + Dockerfile.act runner image additions + prepare.sh export a ref + deviations + run-job.sh act invocation, container rename + src/ the scratch tree act runs (regenerated) + out/ logs diff -Nru apache2-2.4.69/test/README apache2-2.4.71/test/README --- apache2-2.4.69/test/README 2026-07-18 15:32:22.000000000 +0000 +++ apache2-2.4.71/test/README 2026-11-09 03:07:05.000000000 +0000 @@ -6,91 +6,89 @@ Python / pytest test suites =========================== -There are two complementary Python (pytest) test suites in this directory. -Both run against an already-built httpd; they are independent of each other. +There are two complementary Python (pytest) test suites in this directory, +run as one pytest project against an already-built httpd. pyhttpd / modules/ httpd's own test framework. Covers HTTP/2, mod_md (ACME), HTTP/1, proxy and core behaviour. Drives the - server with external clients (curl, nghttp, h2load) and - is configured via pyhttpd/config.ini (generated by - httpd's configure). Framework code lives in pyhttpd/; - tests live in modules/{core,http1,http2,md,proxy}/. + server with external clients (curl, nghttp, h2load); + each test package writes the configuration it needs and + restarts the server. Framework code lives in pyhttpd/; + tests live in modules/{core,http1,http2,md,proxy,...}/. pytest_suite/ A self-contained port of the classic Perl Apache::Test suite (core HTTP, per-module tests, security/CVE - regressions, SSL/TLS, PHP). Drives the server with an - in-process Python HTTP client (httpx) and raw sockets. - It is fully self-contained: it ships its own conf - templates, document root, C test modules and helper - framework under pytest_suite/, and reads nothing outside - that directory. See pytest_suite/README.md for details, + regressions, SSL/TLS, PHP). One server with a static + configuration, driven by an in-process Python HTTP + client (httpx) and raw sockets. It ships its own conf + templates, document root and C test modules under + pytest_suite/. See pytest_suite/README.md for details, including how to add new tests. -The two suites coexist: pytest_suite/ has its own conftest.py and -pyproject.toml, so pytest treats it as its own rootdir and does NOT pick up -this directory's pyhttpd conftest.py. Run pytest_suite from inside -pytest_suite/ (or via the unified runner below) -- do not point pytest at the -top-level test/ directory expecting to run both at once. - - -Unified runner: run-all-tests.sh ---------------------------------- - -run-all-tests.sh runs BOTH suites against the same built httpd and reports a -combined result. pytest_suite runs first, then the pyhttpd modules/ tests. - - ./run-all-tests.sh # run both suites (pytest_suite first) - ./run-all-tests.sh --only=pysuite # only the pytest_suite tests - ./run-all-tests.sh --only=pyhttpd # only the pyhttpd modules/ tests - ./run-all-tests.sh --apxs=/path/to/apxs # point at a specific httpd build - ./run-all-tests.sh -k status -v # flags pass through to BOTH suites - -How it finds the httpd build: - * pytest_suite is located via apxs -- from --apxs, the $APXS environment - variable, the "prefix" in pyhttpd/config.ini, or apxs on $PATH. - * the pyhttpd tests use pyhttpd/config.ini (built by httpd's configure), via - the system "pytest". - -Selecting tests: - * Flag arguments (-v, -k NAME, -x, ...) are passed to both suites. - * Positional test paths are passed only to pytest_suite (they are - pytest_suite paths). Select pyhttpd tests with the PYHTTPD_TARGETS - environment variable, e.g. - PYHTTPD_TARGETS="modules/http2" ./run-all-tests.sh --only=pyhttpd - By default the pyhttpd side runs every modules/* directory whose conftest - imports successfully in your environment; a directory whose optional - dependency is missing (e.g. modules/md needs pyOpenSSL) is skipped with a - note rather than aborting the run. - -Environment overrides: - APXS path to apxs (default: pyhttpd/config.ini prefix, else PATH) - PHP_FPM path to a php-fpm binary; enables pytest_suite's PHP tests - PYHTTPD_TARGETS pyhttpd test path(s) to run (default: auto-detected modules/*) - -The runner exits non-zero if either suite has failures. - - -Running a suite directly ------------------------- - -Both runtests.sh scripts create their own virtualenv on first run and can be -invoked from any directory -- the paths below are just the convenient way to -type them. The venv is (re)built automatically whenever it is missing or its -pyproject.toml has changed, using `uv sync` (uv reads pyproject.toml + uv.lock). -uv (https://docs.astral.sh/uv/) is required. To force a clean rebuild yourself, -`rm -rf /.venv`. - -pytest_suite (self-contained; the venv holds only pytest + httpx): - - ./pytest_suite/runtests.sh --apxs=/path/to/apxs # all tests - ./pytest_suite/runtests.sh --php-fpm=/path/to/php-fpm tests/t/php # PHP tests - ./pytest_suite/runtests.sh -k rewrite -v # any pytest args pass through - -pyhttpd tests (need pyhttpd/config.ini from httpd's configure, plus curl, -nghttp2/h2load, and -- for modules/md -- pyOpenSSL and an ACME test server): - - ./pyhttpd/runtests.sh modules/http2 # all HTTP/2 tests - ./pyhttpd/runtests.sh modules/core -k test_001 # a subset +Both use the same framework underneath (pyhttpd/: finding and probing the +build, marker-based skipping, starting and stopping httpd, ports, test +certificates, C test modules). The framework in pytest_suite/apache_pytest/ +is a thin layer over it, so that the classic test files stay identical to +their upstream copy in the httpd-tests repository. + +One project, one environment: test/pyproject.toml holds the dependencies and +the pytest configuration, test/uv.lock pins the versions, and test/.venv is +the virtualenv. + + +Running the tests: runtests.sh +------------------------------- + +runtests.sh (runtests.bat on Windows) creates or updates the venv, finds apxs +and php-fpm, and runs pytest from this directory. It can be invoked from any +directory. Without a path it runs both suites. + + ./runtests.sh # both suites + ./runtests.sh modules # only the modules/ suites + ./runtests.sh pytest_suite/tests # only the classic suite + ./runtests.sh modules/http2 -k 003 -v # a subset; any pytest option passes through + ./runtests.sh --apxs=/path/to/apxs # a specific httpd build + ./runtests.sh tests/t/php --php-fpm=/path/to/php-fpm + +Paths are relative to this directory; a path relative to pytest_suite/ +(tests/t/php) is accepted as well. ./run-one-test.sh PATH[::nodeid] re-runs a +single test from a path as printed in a test run, and lists all tests when +called without arguments. + +The venv is (re)built automatically when it is missing or pyproject.toml or +uv.lock has changed, with `uv sync`; uv (https://docs.astral.sh/uv/) is required. To force +a clean rebuild, `rm -rf test/.venv`. You can also run +`.venv/bin/pytest --apxs=/path/to/apxs` yourself from test/. + +How the httpd build is found: from --apxs (or $APXS); else from --httpd with +--conf (or $HTTPD); else from the config.ini that httpd's configure generates +in pyhttpd/ (or $PYHTTPD_CONFIG); runtests.sh only falls back to an apxs on +$PATH without that. It is found once, before the tests are collected, and +both suites, and every skip and expected failure, use that one build; a +build named with --apxs or --httpd that is not usable stops the run. The +modules/ suites +need curl and, for HTTP/2, nghttp2 and h2load; modules/md needs an ACME test +server (pebble) and skips without it. A suite whose optional dependency is +missing skips (and fails under --strict-optional). + +Ports: the modules/ suites listen on free ports picked for each test +environment. --fixed-ports uses the ports of config.ini (5001 ...) instead, +--base-port N uses N to N+4. + +Error log: the modules/ suites fail a test that makes httpd log an unexpected +error or warning. The classic suite does the same; what it expects is listed +in pyhttpd/classic_errorlog.py (--classic-elog-report=FILE lists what each +test caused instead of failing). + +The C modules the tests load (mod_aptest, mod_h1test, mod_h2test, the +c-modules/ of pytest_suite) are built with apxs, once: next to each .so a +.stamp records the sources, defines and the httpd it was built for, and a +module is rebuilt when one of them changes, e.g. when you point --apxs at +another build. --clean-modules removes all of them first. + +From a build tree, `make check-all-pytest` builds the check/ tree and runs the +same thing; pass paths and options as PYTEST_ARGS, for example +`make check-all-pytest PYTEST_ARGS=modules/http2`. Other contents -------------- diff -Nru apache2-2.4.69/test/README.ci apache2-2.4.71/test/README.ci --- apache2-2.4.69/test/README.ci 2023-03-02 09:58:30.000000000 +0000 +++ apache2-2.4.71/test/README.ci 2026-11-09 03:07:05.000000000 +0000 @@ -1,4 +1,3 @@ - Variables --------- @@ -23,61 +22,172 @@ * BUILDCONFIG - arguments to pass when running httpd's ./buildconf script -* MFLAGS - arguments to pass when running "make" for httpd. +* MFLAGS - arguments to pass when running "make", both for httpd and + for any dependency built from source. The Linux workflow defaults + this to "-j"; a matrix entry can override it, e.g. + setting it empty to force a serial build. + +* SKIP_TESTING - if set, no testing is done at all -* SKIP_TESTING - if set, the Perl test framework is not run for the - build. +* NO_TEST_FRAMEWORK - if set, the Perl-based test framework and + libcheck-based unit tests are skipped + +* TEST_LOGNO - if set, checks for APLOGNO() macro errors (duplicates + or empty arguments) and exits; no build or testing is done. * TEST_UBSAN - set for job using UBSan ("Undefined Behaviour Sanitizer") -* TEST_MALLOC - set for job using enhanced malloc debugging. +* TEST_ASAN - set for job using ASan ("Address Sanitizer") + +* TEST_MALLOC - set for job using enhanced malloc debugging + (sets MALLOC_PERTURB_, MALLOC_CHECK_ and LIBC_FATAL_STDERR_). * TEST_INSTALL - set for job testing "make install" -* TEST_VPATH - set for job testing srcdir!=builddir +* TEST_VPATH - set for job testing srcdir!=builddir (VPATH build) * TEST_LDAP - set for job with slapd, running LDAP tests -* TEST_SSL - set for job with SSL/TLS testing variants +* TEST_SSL - set for job with SSL/TLS testing variants (encrypted + private keys, session cache backends via redis/memcache/shmcb, etc.) + +* TEST_OPENSSL3 - if set to a version string (e.g. "3.4.7"), that + version of OpenSSL is built from source and used for mod_ssl. + Requires non-system APR/APR-util (APR_VERSION must also be set). + +* TEST_OPENSSL3_BRANCH - if set together with TEST_OPENSSL3, the + OpenSSL source is cloned from the named git branch instead of + downloading a release tarball. + +* OPENSSL_CONFIG - extra arguments passed to OpenSSL's ./Configure + when building from source (e.g. "no-engine"). + +* TEST_PYTEST - if set, runs all available pytest-based test suites + via "make check-all-pytest" (pytest_suite/ and test/modules/*/ + suites, in one pytest session). Replaces the old per-suite TEST_CORE + / TEST_PROXY / TEST_H2 / TEST_MD flags. A suite whose dependencies + are missing skips itself: modules/md without `pebble` on PATH, + modules/http2 when mod_http2 was not built. + +* PYTEST_ARGS - with TEST_PYTEST, test paths (relative to test/) and + options passed through to pytest, to narrow the run: "modules/http2", + "modules" (skip pytest_suite/), "pytest_suite/tests" (skip modules/), + "-k expr", "-m expr". + +* TEST_NGHTTP2 - if set to a version string (e.g. "1.68.0"), that + release of nghttp2 is built from its GitHub tag (library only) and + used for mod_http2/mod_proxy_http2 via --with-nghttp2. Used by the + HTTP/2 workflow to test against nghttp2 versions other than the one + packaged for the runner image. + +* TEST_MD - set together with TEST_PYTEST; installs pebble first so + that modules/md runs instead of skipping. See the + "Python pytest test suites" job in .github/workflows/linux.yml. + +* TEST_APR_TARBALL - if set, APR/APR-util source is fetched as a + release tarball from archive.apache.org rather than cloned from git. + (This is necessary for old releases where running ./buildconf from + a tag may now be difficult.) * TESTS - a list of Perl framework tests to run * TEST_ARGS - arguments to pass to ./t/TEST in the Perl test framework -* CLEAR_CACHE - if set, the cached $HOME/root is removed before each build +* LITMUS - if set, runs the litmus WebDAV test suite against mod_dav. + +* PHP_FPM - path to the php-fpm binary. If set, PHP tests are + included in the pytest test suites. If not set by the matrix + entry, the workflow sets it to the newest /usr/sbin/php-fpm* found + in the runner image (e.g. "/usr/sbin/php-fpm8.3"), failing the job + if there is none. -Caching -- NOTE, BROKEN IN GITHUB ACTIONS -- +Caching ------- -Perl modules installed in $HOME/perl5 are cached. +Two directories are cached. -Anything installed into the $HOME/root directory is cached - notably, -versions of APR/APR-util are installed here and cached across httpd -build jobs without needing to be rebuilt every time. - -The cached installs of APR/APR-util are refreshed if the -last-changed-revision of the build is stale. - -If APR_VERSION and APU_VERSION are both set to 1.x versions, then -CLEAR_CACHE should also be set to disable APR* caching. APR-util can -only be rebuilt if an APR checkout is present, so a APR-util cannot be -built from source alone. (i.e. the scripts do not handle the case of -cached, fresh APR plus a cached but stale APR-util) +~/root (key "cache-libs----$ROOTID") holds the +APR, APR-util and OpenSSL installs. gha-resolve-deps.sh computes +$ROOTID before the cache is restored, hashing only what determines +those installs: the versions, their configure arguments and $CC. The +job's own configuration is deliberately not included, so jobs building +the same dependencies share one entry however differently they +configure or test httpd - the five pool-debug jobs, for instance. A job +which builds no dependencies gets no $ROOTID and does not use the cache +at all. + +For anything built from a branch rather than a tag - APR_VERSION=trunk +or 1.7.x, and TEST_OPENSSL3_BRANCH - the version alone does not identify +what gets built, so the branch is resolved to a commit and that goes +into $ROOTID too. The commit is exported as +$APR_COMMIT/$APU_COMMIT/$OPENSSL_COMMIT so that the build uses exactly +what the key names rather than resolving the branch a second time, +minutes later, by which point it may have moved. + +It is restored before the dependencies are built and saved afterwards, +only if they built successfully - a failed job would otherwise write a +half-built tree to a key which can never be updated. + +~/perl5 (key "cache-cpan--$PERLID--") holds the +CPAN modules. Nothing in it varies per job: the module list is fixed +and the modules are always built with CC=gcc, so the content depends +only on the perl ABI and on the module list itself, hashed from +travis_before_linux.sh. One cache is therefore shared by every job. + +$PERLID is a hash of the "perl -V" output, computed by +gha-resolve-deps.sh, which also logs that output so a change to it can +be seen. The image name is deliberately not used: the runner image is +rebuilt regularly and its name does not change when perl does, so the +key outlived a perl upgrade which the modules did not. The key has no +restore-keys fallback, so the modules restored always match the perl +running the job. + +The cached CPAN modules still become gradually stale as versions in +CPAN move on from whatever was installed for the first build. Bump +the serial in the key ("cache-cpan-1-" -> "cache-cpan-2-") to discard +them and rebuild against current CPAN. + +APR and APR-util must stay in a single cache entry. APR-util's +buildconf needs APR's *source* tree, which lives in $HOME/build and is +not cached, so APR-util can only be built in a job which also builds +APR. $ROOTID covers both, and the entry is saved only once both have +built, so a restored entry never holds one without the other. + +Artifacts +--------- + +A job which fails uploads its config.log, and the error_log from any +test run, as artifacts named "config.log-$JOBID" and "error_log-$JOBID". + +$JOBID is the matrix index of the job followed by its name, with every +character upload-artifact rejects - "/" among them - replaced, so job +zero uploads "error_log-0-Shared-MPMs-event-pool-debug-SSL-TLS-variants". + +The index is what makes the name unique. It cannot collide however the +matrix changes, including for an entry which sets no name at all, +whereas hashing a hand-picked subset of the job configuration - as this +used to do - collides silently as soon as two entries differ only in a +field which the hash omits. Two entries already differ only in "os", +which was never hashed: they stayed apart only because $ImageOS, which +was, happens to differ on an ARM runner. The job name is appended +purely so that an artifact can be matched to the job it came from +without counting matrix entries; it is not needed for uniqueness, and +an entry which sets none is still named unambiguously. + +The name reaches the shell as $MATRIX_NAME through the step's env +rather than being interpolated into the script, so that no job name can +be written which breaks the shell. + +The value computed for a job can be read from its log without waiting +for a failure: it is listed in the environment which the runner prints +under the "Run" group of every step after the one which sets it. Travis to Github Actions Migration TODO --------------------------------------- -* better path filtering so e.g. CHANGES changes don't trigger CI -* support branch conditionals again (some tests are 2.4.x only, some trunk only) -* make caching work properly for APR + CPAN modules - - this is using the wrong model at the moment - - the cache key needs to be based off (source code, job configuration) - - rather than done on the fly in test/travis_before_linux.sh - - pebble + Rustls builds should also be cached -* turn on failure notifications? * test across different Ubuntu versions again - and test against OpenSSL 1.x since we're now ONLY building against 3.x -* update the docs below for testing from PRs/feature branches +* container testing for non-Ubuntu hosts * introduce some job ordering rather than having a flat/concurrent set, if the default "./configure && make && test" works *then* start jobs doing 200 different variations on ./configure --enable-XXX @@ -86,33 +196,23 @@ TODO list --------- -* non-x86 builds * MacOS build -* Windows build * clang-on-Linux build * Use containers for non-Ubuntu-based Linux testing Known Failures -------------- -Some known failures: +- A few mod_http2 Python tests sometimes fail spuriously, e.g. + FAILED modules/http2/test_106_shutdown.py::TestShutdown::test_h2_106_02 - AssertionError: failed on 2. request: * Added test1.tests.httpd.apache.org:5001:127.0.0.1 to DNS cache -* prefork, and more rarely non-prefork testing sometimes catches child - segfaults under pool-debug from assert()s in (e.g.) - __pthread_tpp_change_priority where one child is destroying threads - which another is waiting for, or iterating through via - apr_pool_walk_tree(). - - See dev@httpd threads: - msg <5f4abde1b5789_13fde2ecacb40795a1@travis-tasks-5b566d48fc-drkb9.mail> - msg <73060f7b-df7f-ad3c-a9fa-dd666a59b31e@kippdata.de> and - https://bz.apache.org/bugzilla/show_bug.cgi?id=63098 - https://bz.apache.org/bugzilla/show_bug.cgi?id=46185 +- mod_md tests can deadlock pebble v2.10.1 which hangs the test suite, + hopefully fixed for a future release. See e.g. + https://github.com/letsencrypt/pebble/pull/555 - Not clear if there is a real bug here which can be reproduced - outside of pool-debug. +- ERROR at teardown of TestConf.test_md_300_022 - some race condition? -Testing from a Feature Branch [*** N/A FOR GITHUB ACTIONS ***] +Testing from a Feature Branch ----------------------------- An SVN branch off trunk should be mirrored to github, and will be @@ -120,11 +220,7 @@ available for those familiar with using Subversion and the standard ASF/httpd repository layout. -Tested branches are listed at: https://travis-ci.com/github/apache/httpd/branches - -Travis will also run the tests for a PR filed against the httpd Github -repository at https://github.com/apache/httpd or from a fork of this -repository if enabled for the Travis user. +Test runs are listed at: https://github.com/apache/httpd/actions A workflow to enable testing would be as follows, substituting $USERNAME for your github username: @@ -137,18 +233,12 @@ $ git commit ... $ git push -u $USERNAME my-feature:my-feature -To enable testing for a fork, visit the settings page at -https://travis-ci.com/$USERNAME/httpd/settings - you may need to sync -your account via https://travis-ci.com/account/repositories for a -freshly created fork. - To create a Pull Request, go to the URL produced in the "git push" command output when pushing to your fork, which is something like: https://github.com/apache/httpd/compare/trunk...$USERNAME:trunk -Once a PR has been created, travis will run the tests and link the -results from a PR comment. All tested PRs are listed here: -https://travis-ci.com/github/apache/httpd/pull_requests +Once a PR has been created, the tests will run and test results +displayed directly in the PR. To merge from github back to SVN trunk, create a patch from e.g.: @@ -161,3 +251,47 @@ $ git rebase -i origin/trunk and follow the standard rebase steps. + +An alternative way to apply a PR into an SVN checkout is to use the +apply_trunk_pr.sh script: +https://svn.apache.org/repos/asf/httpd/dev-tools/github/apply_trunk_pr.sh + +If you use keywords in the SVN commit message, the PR will be closed +when the commit is mirrored to Github, e.g. a commit message like: + + mod_foo: Fix the baz feature. + + PR: 12345 + Submitted by: Jane Doe + GitHub: closes #1 + +will close PR #1 when it's merged. + +2.4.x +----- + +The 2.4.x workflow runs the pytest jobs of trunk's, and TEST_PYTEST and +"make check-all-pytest" work the same on both branches, so a test +backported from trunk needs no change in how it is run. A job that +configures an MPM which the branch does not have yet (MotorZ, for now) +succeeds without doing anything. travis_before_linux.sh and +travis_run_linux.sh are the same on both branches and do what the +workflow leaves to them: + +* CLEAR_CACHE - if set, the cached $HOME/root and $HOME/perl5 are + removed before the build. + +* MFLAGS - the 2.4.x workflow does not set it; the build script then + uses "-j2". + +* TEST_MOD_TLS - with RUSTLS_VERSION, builds that tag of rustls-ffi + into $HOME/root/rustls and configures httpd with --with-tls and + --with-rustls. + +When the workflow does not set $ROOTID, its cache key does not say what +was built, so the scripts check for themselves. APR and APR-util are +rebuilt unless $HOME/root/.key-apr (.key-apr-util) names the same +version, commit, configuration and $CC; OpenSSL is rebuilt unless +$HOME/root/openssl-is-$TEST_OPENSSL3 exists and, for a branch, names its +current commit; the CPAN modules are rebuilt when the output of +"perl -V" differs from the one saved in $HOME/perl5/.perlver. diff -Nru apache2-2.4.69/test/README.pytest apache2-2.4.71/test/README.pytest --- apache2-2.4.69/test/README.pytest 2026-07-18 15:32:22.000000000 +0000 +++ apache2-2.4.71/test/README.pytest 2026-11-09 03:07:05.000000000 +0000 @@ -6,9 +6,10 @@ Install ------- The Python dependencies (pytest, pyOpenSSL, etc.) are managed with uv -(https://docs.astral.sh/uv/), which reads pyproject.toml + uv.lock and creates -a local .venv. Install uv, then let pyhttpd/runtests.sh build the venv on first -run (it invokes `uv sync` for you); or create it yourself: +(https://docs.astral.sh/uv/), which reads test/pyproject.toml + test/uv.lock and +creates test/.venv, shared with pytest_suite. Install uv, then let +runtests.sh build the venv on first run (it invokes `uv sync` for you); +or create it yourself, from test/: > uv sync And for 'h2load': @@ -35,6 +36,50 @@ > pytest -vvv -k test_h2_004_01 run the specific test with mod_http2 at log level TRACE2. +There is an option to archive the results across different +modules and httpd versions. The archiving will preserve +error_log and access_log (cumulative for all tests in the module), +and save a separate config file for each test case. +> pytest test/modules/core --archive=/path/to/archive + +If you don't provide any specific module, pytest will execute all of them +and add a new folder per module to the existing structure. +> pytest --archive=/path/to/archive + +Always use --archive= (with =, not a space) to avoid pytest rootdir issues. + +The archive folder is named after the httpd version and the git commit hash +so results from different code versions are kept separate. +Running the same module again with the same commit updates its folder in place. + +What gets archived: +- error_log and access_log for the module run +- per-test configs: test.conf snapshot after each test +- module config: modules.conf +- shared configs and infrastructure in _shared/ to avoid duplication + +Example archive structure with 2 httpd versions: + ├── /path/to/archive + │ ├── 2.4.65-a3f8c1d + │ │ ├── _shared + │ │ │ ├── conf + │ │ │ │ ├── httpd.conf + │ │ │ │ ├── mime.types + │ │ │ │ └── stop.conf + │ │ │ ├── ca + │ │ ├── core + │ │ │ ├── conf + │ │ │ │ ├── modules.conf + │ │ │ │ ├── test_core_001_01.conf + │ │ │ └── logs + │ │ │ ├── error_log + │ │ │ └── access_log + │ │ ├── http1 + │ │ └── http2 + │ └── 2.4.66-b7d2e4f + │ ├── _shared + │ └── core + By default, test cases will configure httpd with mpm_event. You can change that with the invocation: > MPM=worker pytest test/modules/http2 @@ -49,6 +94,52 @@ of 'pebble', an ACME test server, and look for it in $PATH. +Strict optional dependencies +---------------------------- +A skip is convenient locally and dangerous in CI: a run that was meant +to cover SSL, HTTP/2 and mod_md stays green while quietly covering none +of them, because the machine was misconfigured. Strict mode turns that +silence into a failure. + +> pytest --strict-optional +> PYHTTPD_STRICT_OPTIONAL=1 pytest + +Both are off by default, so nothing changes unless you ask for it. The +env var is the one to set in CI; the flag is the one to use by hand. + +Not every skip becomes a failure. Skips come in two kinds: + + - "does not apply here" -- wrong MPM, httpd too old, STRESS_TEST not + set, $SSL selects a different TLS module. These always skip, in + strict mode too, and a test gated on both kinds skips rather than + fails. + + - "something we expected to be installed is missing" -- curl, nghttp, + h2load, the 'websockets' and 'multipart' python packages, mod_ssl, + mod_tls, mod_md, a2md, the ACME test server, an OCSP responder. + These fail under strict mode. + +The OCSP responder is the one judgement call in that split. pebble +serves no OCSP, boulder does, so with ACME=pebble the stapling tests +have nothing to talk to. That is a property of the environment rather +than of this machine's packages, and a pipeline that is meant to cover +OCSP stapling and runs pebble has lost that coverage silently -- which +is the thing strict mode exists to catch. So it fails, and the message +names ACME=pebble as what was detected. Run ACME=boulder to cover +stapling, or leave strict mode off. + +A strict failure is raised during test setup, so pytest reports it as +ERROR rather than FAILED; the exit code is non-zero either way. The +message is prefixed '[strict-optional]' so it greps cleanly out of a CI +log, and names the dependency, what was expected, what was found +instead, and how to switch strict mode back off. + +curl is the one dependency gated for the suite as a whole rather than +test by test: apache_restart() proves the server came up by making a +curl request, so without curl nothing under test/modules/ can run. That +gate lives in the root conftest.py. + + Workings -------- All tests start httpd on their own and try hard to shut it down @@ -124,11 +215,44 @@ other modules. 'test/modules/md' checks and starts external processes (an ACME test server). +If your test needs something that may not be installed, say +so with the decorator from 'pyhttpd.depends'. It takes the +condition itself, so it works on a method, a class or a +module-level 'pytestmark': + + from pyhttpd.depends import needs_dependency + + @needs_dependency("a2md", MDTestEnv.has_a2md(), + reason="no a2md available") + class TestStore: + +For the tools the suite gates on most often there are ready +made versions of that decorator, which probe the tool once +and cache the answer: + + @H2TestEnv.needs_nghttp() # and (assets=True) + @H2TestEnv.needs_h2load("1.41.0") + @HttpdTestEnv.needs_multipart() + +For a check that can only be made once the test is running, +'env.require()' does the same job from inside the body: + + env.require(env.curl_is_at_least('8.0.0'), + 'need at least curl v8.0.0 for this', + dep='curl', + detected=f"curl {env.curl_version_str()}") + +Both skip normally and fail under strict mode; see "Strict +optional dependencies" above. When the reason is instead +"this test does not apply here", keep using a plain +'pytest.mark.skipif' or 'pytest.skip()' -- those never +become failures. + Infrastructure -------------- The test cases rely on the classes provided in `test/pyhttpd/` (where -`test/pyhttpd/runtests.sh` runs the suite) for common code in managing +`test/runtests.sh` runs the suite) for common code in managing a httpd test instance and do provide some base setups: - a small set of virtual hosts with some files - access to paths and definitions (env fixture) @@ -137,3 +261,38 @@ - create certificates for your hosts and make curl use the root certs (so no --insecure calls by curl). + +Tests of changes missing from 2.4.x +----------------------------------- +This directory is the same on trunk and on 2.4.x: a test is written on trunk, +then `test/` is copied to 2.4.x as it is, and `check-test-sync.sh` fails if +the two branches differ. A test covering a trunk change that 2.4.x does not +have yet carries a mark from `pyhttpd/backport.py` (or, in `pytest_suite/`, +`apache_pytest.not_backported`) naming the trunk revision: + + from pyhttpd.backport import not_backported, skip_not_backported + + @not_backported("r1939033", "nonce-count validation") + def test_digest_001(self, env): ... + +`not_backported` makes the test an expected failure on a server older than +2.5.0, `skip_not_backported` skips it where failing would hang or crash the +server or leave errors in the error log. Neither does anything on trunk. +Only a failure of the test function itself is expected: an error in the +setup or teardown of a fixture (a server that does not start, the error log +check) stays an error. Mark only the tests, or the parameters (with +`pytest.param(..., marks=not_backported(...))`), that fail without the +change; on 2.4.x a mark on a test that passes there would turn a regression +into an expected failure. +Both marks are applied when the tests are collected, by `pyhttpd/gating.py`, +against the httpd that `pyhttpd/build.py` found (`--apxs`, `--httpd`, or the +build's `config.ini`); the `need_*` markers of `pytest_suite/` are evaluated +there too, and a missing module or PHP/SSL support is a missing dependency: +skipped, or failed under `--strict-optional`. + +When the change is backported, add the 2.4.x release it first appears in: + + @not_backported("r1939033", "nonce-count validation", backported_in="2.4.71") + +on trunk, then copy `test/` to 2.4.x again. Older 2.4.x servers keep the +expectation. `platform="win32"` limits it to one `sys.platform`. diff -Nru apache2-2.4.69/test/check-test-sync.sh apache2-2.4.71/test/check-test-sync.sh --- apache2-2.4.69/test/check-test-sync.sh 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/check-test-sync.sh 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,28 @@ +#!/bin/sh +# Fail if test/ differs between trunk and 2.4.x. +# +# usage: check-test-sync.sh [trunk-ref [2.4.x-ref]] +# +# Run from anywhere in a git checkout of apache/httpd that has both refs; +# they default to origin/trunk and origin/2.4.x. The comparison is of the +# committed trees, so uncommitted changes do not count. +set -e + +a=${1:-origin/trunk} +b=${2:-origin/2.4.x} + +for ref in "$a" "$b"; do + if ! git rev-parse --verify --quiet "$ref^{commit}" >/dev/null; then + echo "check-test-sync: no such ref: $ref" >&2 + exit 2 + fi +done + +top=$(git rev-parse --show-toplevel) +if git -C "$top" diff --quiet "$a" "$b" -- test; then + echo "test/ is identical on $a and $b" +else + echo "test/ differs between $a and $b:" >&2 + git -C "$top" diff --stat "$a" "$b" -- test >&2 + exit 1 +fi diff -Nru apache2-2.4.69/test/clients/h2ws.c apache2-2.4.71/test/clients/h2ws.c --- apache2-2.4.69/test/clients/h2ws.c 2026-03-09 15:27:35.000000000 +0000 +++ apache2-2.4.71/test/clients/h2ws.c 2026-11-09 03:07:05.000000000 +0000 @@ -141,14 +141,13 @@ int *pipv6, size_t *pconsumed, const char *s, size_t len, uint16_t def_port) { - size_t i, offset; + size_t i, offset=0; char *host = NULL; int port = 0; int rv = 1, ipv6 = 0; if (!len) goto leave; - offset = 0; if (s[offset] == '[') { ipv6 = 1; for (i = offset++; i < len; ++i) { @@ -490,7 +489,7 @@ const nghttp2_frame *frame, void *user_data) { - struct h2_session *session = user_data; + (void)ngh2; switch (frame->hd.type) { case NGHTTP2_HEADERS: @@ -511,8 +510,19 @@ break; case NGHTTP2_GOAWAY: log_infof("frame recv", "FRAME[GOAWAY]"); - fprintf(stdout, "[%d] GOAWAY\n", frame->hd.stream_id); - session->aborted = 1; + { + struct h2_session *session = user_data; + struct h2_stream *s; + while ((s = session->streams) != NULL) { + s->error_code = frame->goaway.error_code; + s->closed = 1; + s->reset = 1; + fprintf(stdout, "[%d] RST\n", s->id); + h2_session_stream_remove(session, s); + if (s->on_close) + s->on_close(s); + } + } break; } return 0; @@ -746,9 +756,6 @@ int want_read, want_write; struct h2_stream *stream; - if(session->aborted) - return 0; - want_read = (nghttp2_session_want_read(session->ngh2) || session->want_io == IO_WANT_READ); want_write = (nghttp2_session_want_write(session->ngh2) || @@ -807,6 +814,14 @@ static void h2_session_close(struct h2_session *session) { + if (session->ngh2) { + nghttp2_session_del(session->ngh2); + session->ngh2 = NULL; + } + if (session->fd >= 0) { + close(session->fd); + session->fd = -1; + } log_infof(cmd, "closed session to %s:%u", session->connect_host, session->connect_port); } @@ -958,7 +973,7 @@ struct uri uri; struct h2_session session; struct ws_stream *stream; - char ch; + int ch; cmd = argv[0]; while((ch = getopt(argc, argv, "c:vh")) != -1) { @@ -1111,5 +1126,11 @@ h2_session_run(&session); h2_session_close(&session); + free(stream); + free((void *)uri.host); + if (uri.authority != uri.host) + free((void *)uri.authority); + free((void *)uri.path); + free((void *)host); return 0; } diff -Nru apache2-2.4.69/test/conftest.py apache2-2.4.71/test/conftest.py --- apache2-2.4.69/test/conftest.py 2026-07-17 16:04:01.000000000 +0000 +++ apache2-2.4.71/test/conftest.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,5 +1,7 @@ import sys import os +import re +import subprocess import pytest @@ -7,14 +9,178 @@ from pyhttpd.env import HttpdTestEnv +# Imported here before pytest_plugins loads it, so register it for +# assertion rewriting first, to avoid PytestAssertRewriteWarning. +pytest.register_assert_rewrite("pyhttpd.depends") +from pyhttpd.depends import apply_gate, strict_optional_enabled + +# pyhttpd.plugin registers the command-line options of both suites; +# pyhttpd.gating evaluates the need_* and backport markers of both; +# pyhttpd.depends provides --strict-optional and the needs_dependency marker. +# pytest_plugins is only honoured in the rootdir conftest, and there is no +# pytest.ini in this tree, so this is the only place it can be declared. +pytest_plugins = ["pyhttpd.plugin", "pyhttpd.gating", "pyhttpd.depends", + "pyhttpd.classic_errorlog", + # pytester: the self-tests of these plugins + "pytester"] + +# Everything below test/modules/ drives a real httpd. +MODULES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'modules') + + def pytest_report_header(config, start_path): + from pyhttpd.build import config_ini_path, resolve_build + info = resolve_build(config) + if info is None: + # no httpd found (neither --apxs/--httpd nor a build generated + # config.ini); nothing to report + return None + line = f"[apache httpd: {info.version_str}, mpm: {info.mpm}, {info.prefix}, via {info.source}]" + if info.source != "config.ini" and config_ini_path().is_file(): + # the modules/ suite tests the same httpd, but still takes its + # ports and client tools from here + line += f"\n[modules/ suite: ports and clients from {config_ini_path()}]" + return line + + +def _generate_config_ini(config): + """Generate a config.ini from --httpd/--conf when no config.ini exists.""" + httpd_opt = config.getoption("--httpd") + conf_opt = config.getoption("--conf") + if not httpd_opt: + return + + httpd_path = os.path.abspath(httpd_opt) + prefix = os.path.dirname(os.path.dirname(httpd_path)) + bindir = os.path.join(prefix, "bin") + libexecdir = os.path.join(prefix, "modules") + is_win = sys.platform == "win32" + + # version from httpd -v + version = "2.5.0" + try: + p = subprocess.run([httpd_path, "-v"], capture_output=True, text=True) + m = re.search(r"Apache/(\S+)", p.stdout) + if m: + version = m.group(1) + except Exception: + pass + + # modules and mpm from conf file + dso_modules = [] + mpm_modules = [] + if conf_opt: + conf_path = os.path.abspath(conf_opt) + try: + with open(conf_path) as f: + for line in f: + line = line.strip() + if line.startswith("#"): + continue + m = re.match(r"LoadModule\s+(\S+)_module", line) + if m: + name = m.group(1) + if name.startswith("mpm_"): + mpm_modules.append(name) + else: + dso_modules.append(name) + except Exception: + pass + + if not mpm_modules: + mpm_modules = ["mpm_winnt"] if is_win else ["mpm_event"] + if is_win and "cgid" in dso_modules and "cgi" not in dso_modules: + dso_modules = ["cgi" if m == "cgid" else m for m in dso_modules] + + apachectl = httpd_path if is_win else os.path.join(bindir, "apachectl") + if is_win: + prefix_curl = os.path.join(prefix, "bin", "curl.exe") + curl_bin = prefix_curl if os.path.isfile(prefix_curl) else "curl.exe" + else: + curl_bin = "curl" + test_dir = os.path.dirname(os.path.abspath(__file__)) + gen_dir = os.path.join(test_dir, "gen") + + ini_content = f"""[global] +prefix = {prefix} +exec_prefix = ${{prefix}} +bindir = ${{prefix}}/bin +sbindir = ${{exec_prefix}}/sbin +libdir = ${{exec_prefix}}/lib +libexecdir = ${{exec_prefix}}/modules + +apr_bindir = ${{prefix}} +apxs = ${{prefix}}/bin/apxs +apachectl = {apachectl} + +curl_bin = {curl_bin} +nghttp = nghttp +h2load = h2load + +pre_built_test_binaries_dir = ${{libexecdir}} + +[httpd] +version = {version} +name = httpd +dso_modules = {' '.join(dso_modules)} +mpm_modules = {' '.join(mpm_modules)} + +[test] +gen_dir = {gen_dir} +http_port = 5002 +https_port = 5001 +proxy_port = 5003 +http_port2 = 5004 +ws_port = 5100 +http_tld = tests.httpd.apache.org +test_dir = {test_dir} +test_src_dir = {test_dir} +""" + ini_path = os.path.join(test_dir, "pyhttpd", "config.ini") + with open(ini_path, "w") as f: + f.write(ini_content) + + +def pytest_configure(config): + if config.getoption("--clean-modules", default=False): + # the c-modules/ of pytest_suite are cleaned by its own fixture + from pyhttpd.cmodules import clean_test_modules + clean_test_modules(os.path.dirname(os.path.abspath(__file__))) + httpd_opt = config.getoption("--httpd", default=None) + if not httpd_opt: + return + ini_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), + "pyhttpd", "config.ini") + if not os.path.isfile(ini_path): + # No build-generated config.ini (typical on Windows without + # autoconf) — generate one from --httpd/--conf so the framework + # has something to read. + _generate_config_ini(config) + + +def pytest_collection_modifyitems(config, items): + """Gate the integration suite on the clients the framework itself needs. + + curl is not an optional dependency of the individual test cases that + call curl_get(): apache_restart() proves the server came up with + is_live(), and is_live() is a curl request. Without curl every test + that starts an httpd dies in fixture setup, which is neither a skip nor + a useful failure. So it is gated once here for the whole of + test/modules/ rather than test by test. + + Tests outside that directory - the strict-mode self-test - run no httpd + and are left alone. + """ + if not any(str(item.path).startswith(MODULES_DIR) for item in items): + return env = HttpdTestEnv() - return f"[apache httpd: {env.get_httpd_version()}, mpm: {env.mpm_module}, {env.prefix}]" - -def pytest_addoption(parser): - parser.addoption("--repeat", action="store", type=int, default=1, - help='Number of times to repeat each test') - parser.addoption("--all", action="store_true") + if env.has_curl(): + return + missing = [("curl", "no curl available", f"tried to run {env.curl}")] + strict = strict_optional_enabled(config) + for item in items: + if str(item.path).startswith(MODULES_DIR): + apply_gate(item, missing, strict) def pytest_generate_tests(metafunc): @@ -22,26 +188,3 @@ count = int(metafunc.config.getoption("repeat")) metafunc.fixturenames.append('tmp_ct') metafunc.parametrize('repeat', range(count)) - - -@pytest.fixture(autouse=True, scope="function") -def _function_scope(env, request): - env.set_current_test_name(request.node.name) - yield - env.check_error_log() - env.set_current_test_name(None) - - -@pytest.fixture(autouse=True, scope="module") -def _module_scope(env): - yield - env.check_error_log() - - -@pytest.fixture(autouse=True, scope="package") -def _package_scope(env): - env.httpd_error_log.clear_ignored_matches() - env.httpd_error_log.clear_ignored_lognos() - yield - assert env.apache_stop() == 0 - env.check_error_log() diff -Nru apache2-2.4.69/test/gha-docs-check.sh apache2-2.4.71/test/gha-docs-check.sh --- apache2-2.4.69/test/gha-docs-check.sh 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/gha-docs-check.sh 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,46 @@ +#!/bin/sh +# +# Check the XML sources of the manual: every file must be well-formed, +# and every file with a DOCTYPE must be valid against its DTD. Pass +# file names to check only those, otherwise all of docs/ is checked. +# +# The DTDs pull in the XHTML entity sets by public identifier, so these +# must be resolvable from an XML catalog (on Debian/Ubuntu, install +# w3c-sgml-lib; on Fedora, xhtml1-dtds, or point XML_CATALOG_FILES at +# a catalog); nothing is fetched from the network. + +cd "`dirname "$0"`/.." || exit 1 + +if ! command -v xmllint >/dev/null; then + echo "FAIL: xmllint not found" + exit 1 +fi + +if [ $# -eq 0 ]; then + set -- `find docs -name '*.xml' -o -name '*.xml.*' | sort` +fi + +rv=0 +count=0 + +for f in "$@"; do + count=$((count + 1)) + # The language files use lang.dtd only to define entities, and + # declare no elements, so can only be checked for well-formedness. + if grep -q '&1` \ + || echo "$out" | grep -q 'failed to load external entity'; then + echo "$out" + echo "FAIL: $f" + rv=1 + fi +done + +[ $rv -eq 0 ] && echo "PASS: $count files checked" + +exit $rv diff -Nru apache2-2.4.69/test/gha-resolve-deps.sh apache2-2.4.71/test/gha-resolve-deps.sh --- apache2-2.4.69/test/gha-resolve-deps.sh 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/gha-resolve-deps.sh 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,93 @@ +#!/bin/bash -e +# +# Work out what identifies the contents of the cached directories and +# write it to $GITHUB_ENV for the cache keys to use: $ROOTID for ~/root, +# $PERLID for ~/perl5. +# +# ~/root holds only the dependencies built from source, so the key +# covers those alone - not the job's own configuration. Jobs which +# build the same dependencies then share one cache entry even where they +# configure or test httpd differently. $ROOTID is left unset for a job +# which builds no dependencies at all, and the cache is skipped. +# +# Anything built from a branch is resolved to a commit here and the +# commit exported, so that the build uses exactly what the key names; +# resolving the branch again at build time would race with it moving. +# Tags and release tarballs are immutable, so the version is enough. + +root= + +# $1 = name, $2 = version, $3 = configure arguments, $4 = variable to +# export the resolved commit into. +function add_apx() { + local name=$1 + local version=$2 + local config=$3 + local var=$4 + local sha + + if test -z "$version"; then + return 0 + fi + + case "$version" in + trunk|*.x) + sha=`git ls-remote https://github.com/apache/${name}.git refs/heads/${version} | cut -f1` + if test -z "$sha"; then + : Could not resolve ${name} branch ${version} - check branch is valid? + exit 1 + fi + echo "${var}=${sha}" >> $GITHUB_ENV ;; + *) sha=${version} ;; + esac + + root="${root} ${name}-${version}-${sha}-${config}" +} + +add_apx apr "${APR_VERSION-}" "${APR_CONFIG-}" APR_COMMIT +add_apx apr-util "${APU_VERSION-}" "${APU_CONFIG-}" APU_COMMIT + +if test -n "${TEST_OPENSSL3-}"; then + if test -n "${TEST_OPENSSL3_BRANCH-}"; then + sha=`git ls-remote https://github.com/openssl/openssl refs/heads/${TEST_OPENSSL3_BRANCH} | cut -f1` + if test -z "$sha"; then + : Could not resolve openssl branch ${TEST_OPENSSL3_BRANCH} - check branch is valid? + exit 1 + fi + echo "OPENSSL_COMMIT=${sha}" >> $GITHUB_ENV + root="${root} openssl-${TEST_OPENSSL3_BRANCH}-${sha}-${OPENSSL_CONFIG-}" + else + root="${root} openssl-${TEST_OPENSSL3}-${OPENSSL_CONFIG-}" + fi +fi + +if test -n "${TEST_NGHTTP2-}"; then + root="${root} nghttp2-${TEST_NGHTTP2}" +fi + +if test -n "$root"; then + # The compiler and every flag which reaches the build are baked into + # the libraries, NOTEST_* included: APR adds those to the build too, + # it just does not use them for ./configure. + root="${root} CC=${CC-} CFLAGS=${CFLAGS-} CPPFLAGS=${CPPFLAGS-}" + root="${root} LDFLAGS=${LDFLAGS-} LIBS=${LIBS-}" + root="${root} NOTEST_CFLAGS=${NOTEST_CFLAGS-} NOTEST_CPPFLAGS=${NOTEST_CPPFLAGS-}" + root="${root} NOTEST_LDFLAGS=${NOTEST_LDFLAGS-} NOTEST_LIBS=${NOTEST_LIBS-}" + root="${root}${TEST_APR_TARBALL+ tarball}" + : Dependencies:${root} + echo ROOTID=`echo "$root" | md5sum - | sed 's/ .*//'` >> $GITHUB_ENV +else + : No dependencies are built from source, so ~/root is not cached. +fi + +# The CPAN modules in ~/perl5 are XS builds tied to the system perl, so +# key that cache on the "perl -V" output rather than on the image name. +# The image is rebuilt regularly and its name does not change when perl +# does, so the key stayed valid across a perl upgrade and restored +# modules built for a different perl. The output is logged because it +# is otherwise impossible to see what changed. +perlv=`perl -V` +echo "::group::perl -V" +echo "$perlv" +echo "::endgroup::" +echo PERLID=`echo "$perlv" | md5sum - | sed 's/ .*//'` >> $GITHUB_ENV diff -Nru apache2-2.4.69/test/gha-sanity-check.sh apache2-2.4.71/test/gha-sanity-check.sh --- apache2-2.4.69/test/gha-sanity-check.sh 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/gha-sanity-check.sh 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,34 @@ +#!/bin/sh +# +# Sanity-check the CI inputs: the GitHub Actions workflow files must +# parse as YAML, and the scripts those workflows run must pass a shell +# syntax check. Neither is exercised by the build, so run this before +# pushing a change to either. + +cd "`dirname "$0"`/.." || exit 1 + +rv=0 + +if python3 -c 'import yaml' 2>/dev/null; then + for f in .github/workflows/*.yml; do + if python3 -c 'import sys, yaml; yaml.safe_load(open(sys.argv[1]))' "$f"; then + echo "PASS: $f" + else + echo "FAIL: $f" + rv=1 + fi + done +else + echo "SKIP: .github/workflows/*.yml, python3 has no yaml module" +fi + +for f in test/travis*.sh test/gha*.sh; do + if bash -n "$f"; then + echo "PASS: $f" + else + echo "FAIL: $f" + rv=1 + fi +done + +exit $rv diff -Nru apache2-2.4.69/test/httpdunit.c apache2-2.4.71/test/httpdunit.c --- apache2-2.4.69/test/httpdunit.c 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/httpdunit.c 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,53 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +#include "httpdunit.h" + +#include "apr_general.h" + +static Suite *main_test_suite(void) +{ + Suite *suite = suite_create("main"); + + /* The list of test cases is automatically generated from the test/unit + * directory by the build system. */ +# include "test/httpdunit.cases" + + return suite; +} + +int main(int argc, const char * const argv[]) +{ + SRunner *runner; + int failed; + + /* Initialize APR and create our test runner. */ + apr_app_initialize(&argc, &argv, NULL); + runner = srunner_create(main_test_suite()); + + /* Log TAP to stdout. */ + srunner_set_tap(runner, "-"); + + /* Run the tests and collect failures. */ + srunner_run_all(runner, CK_SILENT /* output only TAP */); + failed = srunner_ntests_failed(runner); + + /* Clean up. */ + srunner_free(runner); + apr_terminate(); + + return failed ? 1 : 0; +} diff -Nru apache2-2.4.69/test/httpdunit.h apache2-2.4.71/test/httpdunit.h --- apache2-2.4.69/test/httpdunit.h 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/httpdunit.h 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,120 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/* + * httpdunit.h: a collection of test helper macros designed to reduce test + * boilerplate and help the build system autogenerate test case definitions. + * + * Here's how the magic works: + * + * Every test case under test/unit declares test functions using Check's + * START_TEST or the HTTPD_START_LOOP_TEST macro. The build system searches each + * source file for those macros and generates an appropriate stub, called + * test/unit/.tests, that will add each test function to the test + * case. This stub is then pulled in by three lines of boilerplate at the end of + * every test case's source file (see the HTTPD_BEGIN_TEST_CASE documentation, + * below). + * + * The build system uses that same three-line boilerplate to determine the names + * of all the test cases, and adds them automatically to the main test suite + * using a similar generate-stub-and-include process. + */ + +#include "apr.h" /* for pid_t on Windows, needed by Check */ + +#if defined(__GNUC__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#pragma GCC diagnostic push +#endif +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#elif defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wstrict-prototypes" +#endif + +#include "check.h" + +#if defined(__GNUC__) +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#pragma GCC diagnostic pop +#endif +#elif defined(__clang__) +#pragma clang diagnostic pop +#endif + +/* + * Boilerplate Macros + */ + +/** + * Declares a test case. The build system uses this macro to generate a stub, + * which will automatically pull the new test case into the main suite. + * + * After beginning a test case, you must #include the autogenerated testcase + * stub ("test/unit/.tests") and then end the test case with + * HTTPD_END_TEST_CASE. For example, for a test case called foobar.c: + * + * HTTPD_BEGIN_TEST_CASE(foobar) + * #include "test/unit/foobar.tests" + * HTTPD_END_TEST_CASE + * + * The NAME identifying the test case must be globally unique within the test + * suite; if in doubt, just use the filename (minus extension). + */ +#define HTTPD_BEGIN_TEST_CASE(NAME) \ +TCase * NAME##_test_case(void); \ +TCase * NAME##_test_case(void) \ +{ \ + TCase *testcase = tcase_create(#NAME); + +/** + * Like HTTPD_BEGIN_TEST_CASE, but additionally declares a pair of setup and + * teardown functions for the test case. The setup function is run once before + * every test function, and the teardown function is run directly after. Keep in + * mind that expensive fixtures will heavily impact test runtime. + * + * Both setup and teardown take no parameters and return void. + * + * These functions are passed to Check's tcase_add_checked_fixture(). See the + * Check documentation for details; the gist is that checked fixures run after + * Check forks the test process, and therefore they cannot influence each other + * even if something smashes the heap. + */ +#define HTTPD_BEGIN_TEST_CASE_WITH_FIXTURE(NAME, SETUP, TEARDOWN) \ +HTTPD_BEGIN_TEST_CASE(NAME) \ + tcase_add_checked_fixture(testcase, (SETUP), (TEARDOWN)); + +/** + * Bookend for HTTPD_BEGIN_TEST_CASE[_WITH_FIXTURE], which finishes the test + * case function. + */ +#define HTTPD_END_TEST_CASE \ + return testcase; \ +} + +/* + * Test Declaration Macros + */ + +/** + * Use this macro instead of Check's START_TEST when you want to run a test + * function multiple times. The build system will add the test function to the + * test case using Check's tcase_add_loop_test(). + * + * The loop index is available to the test function as the special variable _i. + * _i will start at 0 and increment to a maximum of (NUM_ITERATIONS - 1). + */ +#define HTTPD_START_LOOP_TEST(NAME, NUM_ITERATIONS) START_TEST(NAME) diff -Nru apache2-2.4.69/test/modules/aaa/__init__.py apache2-2.4.71/test/modules/aaa/__init__.py --- apache2-2.4.69/test/modules/aaa/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/__init__.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru apache2-2.4.69/test/modules/aaa/conftest.py apache2-2.4.71/test/modules/aaa/conftest.py --- apache2-2.4.69/test/modules/aaa/conftest.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/conftest.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,128 @@ +import logging +import os +import sys + +import pytest + +from .env import AAATestEnv +from pyhttpd.conf import HttpdConf + +sys.path.append(os.path.join(os.path.dirname(__file__), '../..')) + + +def pytest_report_header(config, start_path): + env = AAATestEnv() + return f"mod_auth_digest [apache: {env.get_httpd_version()}, mpm: {env.mpm_module}, {env.prefix}]" + + +def _digest_dir(docs, path, extra_lines): + lines = [ + f'', + ' AuthType Digest', + f' AuthName "{AAATestEnv.REALM}"', + ] + lines.extend(f" {l}" for l in extra_lines) + lines.append(' Require valid-user') + lines.append('') + return lines + + +@pytest.fixture(scope="package") +def env(pytestconfig) -> AAATestEnv: + level = logging.INFO + console = logging.StreamHandler() + console.setLevel(level) + console.setFormatter(logging.Formatter('%(levelname)s: %(message)s')) + logging.getLogger('').addHandler(console) + logging.getLogger('').setLevel(level=level) + env = AAATestEnv(pytestconfig=pytestconfig) + env.setup_httpd() + env.apache_access_log_clear() + env.httpd_error_log.clear_log() + + docs = env.server_docs_dir + pwfile = env.digest_pwfile + conf = HttpdConf(env) + # Pin the client table to its historical size, ~12 entries, rather than + # the current default of ~140: the tests which need an entry to be + # garbage collected (085) drive that by filling the table with bare + # requests, and a table an order of magnitude larger makes them an order + # of magnitude slower for nothing. + conf.add('AuthDigestShmemSize 1000') + conf.add(_digest_dir(docs, "default", [ + 'AuthDigestProvider file', + f'AuthUserFile "{pwfile}"', + ])) + conf.add(_digest_dir(docs, "nccheck", [ + 'AuthDigestProvider file', + f'AuthUserFile "{pwfile}"', + 'AuthDigestNcCheck On', + ])) + conf.add(_digest_dir(docs, "nccheck-shortlife", [ + 'AuthDigestProvider file', + f'AuthUserFile "{pwfile}"', + 'AuthDigestNcCheck On', + 'AuthDigestNonceLifetime 2', + ])) + conf.add(_digest_dir(docs, "shortlife", [ + 'AuthDigestProvider file', + f'AuthUserFile "{pwfile}"', + 'AuthDigestNonceLifetime 2', + ])) + conf.add(_digest_dir(docs, "neverexpire", [ + 'AuthDigestProvider file', + f'AuthUserFile "{pwfile}"', + 'AuthDigestNonceLifetime -1', + ])) + conf.add(_digest_dir(docs, "onetime", [ + 'AuthDigestProvider file', + f'AuthUserFile "{pwfile}"', + 'AuthDigestNonceLifetime 0', + ])) + conf.add(_digest_dir(docs, "onetime-nccheck", [ + 'AuthDigestProvider file', + f'AuthUserFile "{pwfile}"', + 'AuthDigestNonceLifetime 0', + 'AuthDigestNcCheck On', + ])) + conf.add(_digest_dir(docs, "domain", [ + 'AuthDigestProvider file', + f'AuthUserFile "{pwfile}"', + 'AuthDigestDomain "/digest/domain/" "https://mirror.example.org/other/"', + ])) + conf.add(_digest_dir(docs, "noprovider", [ + # AuthDigestProvider intentionally omitted: falls back to "file". + f'AuthUserFile "{pwfile}"', + ])) + # A request which fails header validation (e.g. no Host on HTTP/1.1) + # is answered before the Digest module's post_read_request runs, so it + # has no per-request Digest record; an ErrorDocument redirecting it into + # a Digest-protected location must not then dereference that record. + # Only configured where that is fixed (r1939032): earlier code does + # dereference the missing record. + if env.httpd_is_at_least("2.5.0"): + conf.add('ErrorDocument 400 /digest/default/secret.txt') + # A reverse-proxied location which authenticates with Digest at the + # proxy (so the backend is never reached and need not be reachable). + # The challenge is an origin-style WWW-Authenticate, so AuthDigestDomain + # applies to it just as for a non-proxied location. + conf.add([ + 'ProxyPass "/pxdomain/" "http://127.0.0.1:1/unused/"', + '', + ' AuthType Digest', + f' AuthName "{AAATestEnv.REALM}"', + ' AuthDigestProvider file', + f' AuthUserFile "{pwfile}"', + ' AuthDigestDomain "/pxdomain/"', + ' Require valid-user', + '', + ]) + conf.install() + assert env.apache_restart() == 0 + return env + + +@pytest.fixture(autouse=True, scope="package") +def _stop_package_scope(env): + yield + assert env.apache_stop() == 0 diff -Nru apache2-2.4.69/test/modules/aaa/digest_client.py apache2-2.4.71/test/modules/aaa/digest_client.py --- apache2-2.4.69/test/modules/aaa/digest_client.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/digest_client.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,134 @@ +"""Minimal hand-rolled RFC 2617 Digest auth client. + +curl's own `--digest` handles the challenge/response handshake transparently, +which is no good for testing edge cases (tampered nonces, replayed +nonce-counts, wrong realms, bad algorithm tokens, ...). This module lets +tests parse a WWW-Authenticate challenge, compute the expected response by +hand, and build a (possibly deliberately broken) Authorization header. + +mod_auth_digest here only implements qop="auth" (see modules/aaa/mod_auth_digest.c +Open Issues: "MD5-sess and auth-int are not yet implemented"), so this client +only implements the qop=auth request-digest/response-auth formulas from +RFC 2617 section 3.2.2. +""" + +import hashlib +import re +from dataclasses import dataclass +from typing import Dict, List, Optional + +_PARAM_RE = re.compile(r'(\w+)=(?:"([^"]*)"|([^\s,]+))\s*,?\s*') + + +def _md5hex(s: str) -> str: + return hashlib.md5(s.encode('utf-8')).hexdigest() + + +def parse_params(value: str) -> Dict[str, str]: + """Parse a comma-separated key=value / key="value" list, as used by + both WWW-Authenticate and Authentication-Info header values.""" + params = {} + for m in _PARAM_RE.finditer(value): + key = m.group(1) + val = m.group(2) if m.group(2) is not None else m.group(3) + params[key.lower()] = val + return params + + +@dataclass +class DigestChallenge: + realm: Optional[str] + nonce: Optional[str] + algorithm: Optional[str] = None + opaque: Optional[str] = None + domain: Optional[str] = None + qop: Optional[str] = None + stale: bool = False + raw: str = "" + + @staticmethod + def parse(www_authenticate: str) -> 'DigestChallenge': + assert www_authenticate.startswith("Digest "), \ + f"not a Digest challenge: {www_authenticate}" + params = parse_params(www_authenticate[len("Digest "):]) + return DigestChallenge( + realm=params.get('realm'), + nonce=params.get('nonce'), + algorithm=params.get('algorithm'), + opaque=params.get('opaque'), + domain=params.get('domain'), + qop=params.get('qop'), + stale=params.get('stale', '').lower() == 'true', + raw=www_authenticate, + ) + + def domain_list(self) -> List[str]: + return self.domain.split() if self.domain else [] + + +def ha1(username: str, realm: str, password: str) -> str: + return _md5hex(f"{username}:{realm}:{password}") + + +def ha2(method: str, uri: str) -> str: + return _md5hex(f"{method}:{uri}") + + +def request_digest(ha1_hex: str, nonce: str, nc: str, cnonce: str, + qop: str, ha2_hex: str) -> str: + return _md5hex(f"{ha1_hex}:{nonce}:{nc}:{cnonce}:{qop}:{ha2_hex}") + + +def rspauth_digest(ha1_hex: str, nonce: str, nc: str, cnonce: str, + qop: str, uri: str) -> str: + """Authentication-Info's rspauth uses A2 = ':' + uri (no method).""" + ha2_hex = _md5hex(f":{uri}") + return _md5hex(f"{ha1_hex}:{nonce}:{nc}:{cnonce}:{qop}:{ha2_hex}") + + +def build_authorization(username: str, challenge: DigestChallenge, password: str, + method: str, uri: str, nc: str = "00000001", + cnonce: str = "0a4f113b3c2e7a1d", qop: Optional[str] = "auth", + realm: Optional[str] = None, nonce_val: Optional[str] = None, + algorithm: Optional[str] = None, response: Optional[str] = None, + opaque: Optional[str] = None, include_opaque: bool = True, + include_qop_fields: bool = True, extra: Optional[List[str]] = None + ) -> str: + """Build a Digest Authorization header value. + + By default this builds a *correct* response for the given challenge and + credentials. Any of realm=/nonce_val=/algorithm=/response=/opaque= can be + overridden to construct deliberately invalid headers, and qop=None with + include_qop_fields=False builds a legacy RFC 2069-style header (no qop, + cnonce, or nc) to prove that path is rejected. + """ + eff_realm = challenge.realm if realm is None else realm + eff_nonce = challenge.nonce if nonce_val is None else nonce_val + if response is None: + h1 = ha1(username, eff_realm, password) + h2 = ha2(method, uri) + if qop: + response = request_digest(h1, eff_nonce, nc, cnonce, qop, h2) + else: + # legacy RFC 2069: MD5(HA1:nonce:HA2), no qop/cnonce/nc + response = _md5hex(f"{h1}:{eff_nonce}:{h2}") + + parts = [ + f'username="{username}"', + f'realm="{eff_realm}"', + f'nonce="{eff_nonce}"', + f'uri="{uri}"', + f'response="{response}"', + ] + if algorithm is not None: + parts.append(f'algorithm={algorithm}') + if qop and include_qop_fields: + parts.append(f'qop={qop}') + parts.append(f'nc={nc}') + parts.append(f'cnonce="{cnonce}"') + eff_opaque = challenge.opaque if (opaque is None and include_opaque) else opaque + if eff_opaque: + parts.append(f'opaque="{eff_opaque}"') + if extra: + parts.extend(extra) + return "Digest " + ", ".join(parts) diff -Nru apache2-2.4.69/test/modules/aaa/env.py apache2-2.4.71/test/modules/aaa/env.py --- apache2-2.4.69/test/modules/aaa/env.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/env.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,79 @@ +import hashlib +import inspect +import logging +import os +from typing import List, Optional + +from pyhttpd.env import HttpdTestEnv, HttpdTestSetup +from pyhttpd.result import ExecResult + +log = logging.getLogger(__name__) + + +class AAATestSetup(HttpdTestSetup): + + def __init__(self, env: 'HttpdTestEnv'): + super().__init__(env=env) + self.add_source_dir(os.path.dirname(inspect.getfile(AAATestSetup))) + self.add_modules(["auth_digest", "authn_file", "authn_core", + "authz_core", "authz_user", "proxy", "proxy_http"]) + + +class AAATestEnv(HttpdTestEnv): + + REALM = "AAA Digest Realm" + DIGEST_USER = "digestuser" + DIGEST_PASSWORD = "digestpass2617" + DIGEST_USER2 = "otheruser" + DIGEST_PASSWORD2 = "otherpass2617" + + def __init__(self, pytestconfig=None): + super().__init__(pytestconfig=pytestconfig) + self.add_httpd_log_modules(["auth_digest", "authn_file", "authz_core"]) + self._digest_pwfile = os.path.join(self.server_dir, "digest.passwd") + + def setup_httpd(self, setup: HttpdTestSetup = None): + super().setup_httpd(setup=AAATestSetup(env=self)) + self._write_digest_pwfile() + + def _write_digest_pwfile(self): + def ha1(user, password): + return hashlib.md5( + f"{user}:{self.REALM}:{password}".encode()).hexdigest() + + with open(self._digest_pwfile, 'w') as fd: + fd.write(f"{self.DIGEST_USER}:{self.REALM}:" + f"{ha1(self.DIGEST_USER, self.DIGEST_PASSWORD)}\n") + fd.write(f"{self.DIGEST_USER2}:{self.REALM}:" + f"{ha1(self.DIGEST_USER2, self.DIGEST_PASSWORD2)}\n") + + @property + def digest_pwfile(self) -> str: + return self._digest_pwfile + + def configtest(self, directory_lines: List[str], extra_top_lines: Optional[List[str]] = None + ) -> ExecResult: + """Run `httpd -t` against a minimal, standalone config built from the + already-generated modules.conf plus `directory_lines` wrapped in a + block over the shared docroot. Used to test directives + that are rejected at config-check time (e.g. AuthDigestQop values + other than 'auth') without touching the package's running server. + """ + conf_path = os.path.join(self.gen_dir, "digest-configtest.conf") + modules_conf = os.path.join(self.server_conf_dir, "modules.conf") + lines = [ + f'ServerRoot "{self.server_dir}"', + f'Include "{modules_conf}"', + f'DocumentRoot "{self.server_docs_dir}"', + f'Listen {self.http_port2}', + ] + if extra_top_lines: + lines.extend(extra_top_lines) + lines.append(f'') + lines.extend(f" {l}" for l in directory_lines) + lines.append('') + with open(conf_path, 'w') as fd: + fd.write('\n'.join(lines)) + fd.write('\n') + httpd_bin = os.path.join(self.bin_dir, 'httpd') + return self.run([httpd_bin, '-t', '-f', conf_path]) diff -Nru apache2-2.4.69/test/modules/aaa/htdocs/digest/default/secret.txt apache2-2.4.71/test/modules/aaa/htdocs/digest/default/secret.txt --- apache2-2.4.69/test/modules/aaa/htdocs/digest/default/secret.txt 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/htdocs/digest/default/secret.txt 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +digest-default-secret diff -Nru apache2-2.4.69/test/modules/aaa/htdocs/digest/domain/nested/secret.txt apache2-2.4.71/test/modules/aaa/htdocs/digest/domain/nested/secret.txt --- apache2-2.4.69/test/modules/aaa/htdocs/digest/domain/nested/secret.txt 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/htdocs/digest/domain/nested/secret.txt 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +digest-domain-nested-secret diff -Nru apache2-2.4.69/test/modules/aaa/htdocs/digest/domain/secret.txt apache2-2.4.71/test/modules/aaa/htdocs/digest/domain/secret.txt --- apache2-2.4.69/test/modules/aaa/htdocs/digest/domain/secret.txt 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/htdocs/digest/domain/secret.txt 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +digest-domain-secret diff -Nru apache2-2.4.69/test/modules/aaa/htdocs/digest/nccheck/secret.txt apache2-2.4.71/test/modules/aaa/htdocs/digest/nccheck/secret.txt --- apache2-2.4.69/test/modules/aaa/htdocs/digest/nccheck/secret.txt 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/htdocs/digest/nccheck/secret.txt 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +digest-nccheck-secret diff -Nru apache2-2.4.69/test/modules/aaa/htdocs/digest/nccheck-shortlife/secret.txt apache2-2.4.71/test/modules/aaa/htdocs/digest/nccheck-shortlife/secret.txt --- apache2-2.4.69/test/modules/aaa/htdocs/digest/nccheck-shortlife/secret.txt 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/htdocs/digest/nccheck-shortlife/secret.txt 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +digest-nccheck-secret diff -Nru apache2-2.4.69/test/modules/aaa/htdocs/digest/neverexpire/secret.txt apache2-2.4.71/test/modules/aaa/htdocs/digest/neverexpire/secret.txt --- apache2-2.4.69/test/modules/aaa/htdocs/digest/neverexpire/secret.txt 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/htdocs/digest/neverexpire/secret.txt 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +digest-neverexpire-secret diff -Nru apache2-2.4.69/test/modules/aaa/htdocs/digest/noprovider/secret.txt apache2-2.4.71/test/modules/aaa/htdocs/digest/noprovider/secret.txt --- apache2-2.4.69/test/modules/aaa/htdocs/digest/noprovider/secret.txt 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/htdocs/digest/noprovider/secret.txt 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +digest-noprovider-secret diff -Nru apache2-2.4.69/test/modules/aaa/htdocs/digest/onetime/secret.txt apache2-2.4.71/test/modules/aaa/htdocs/digest/onetime/secret.txt --- apache2-2.4.69/test/modules/aaa/htdocs/digest/onetime/secret.txt 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/htdocs/digest/onetime/secret.txt 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +digest-onetime-secret diff -Nru apache2-2.4.69/test/modules/aaa/htdocs/digest/onetime-nccheck/secret.txt apache2-2.4.71/test/modules/aaa/htdocs/digest/onetime-nccheck/secret.txt --- apache2-2.4.69/test/modules/aaa/htdocs/digest/onetime-nccheck/secret.txt 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/htdocs/digest/onetime-nccheck/secret.txt 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +digest-onetime-secret diff -Nru apache2-2.4.69/test/modules/aaa/htdocs/digest/shortlife/secret.txt apache2-2.4.71/test/modules/aaa/htdocs/digest/shortlife/secret.txt --- apache2-2.4.69/test/modules/aaa/htdocs/digest/shortlife/secret.txt 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/htdocs/digest/shortlife/secret.txt 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +digest-shortlife-secret diff -Nru apache2-2.4.69/test/modules/aaa/test_001_challenge_response.py apache2-2.4.71/test/modules/aaa/test_001_challenge_response.py --- apache2-2.4.69/test/modules/aaa/test_001_challenge_response.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/test_001_challenge_response.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,199 @@ +"""RFC 2617 Digest challenge/response scenarios against mod_auth_digest's +default configuration (AuthDigestProvider file, AuthDigestQop auth (the only +supported value), AuthDigestNonceLifetime 300, no AuthDigestDomain). +""" + +from . import digest_client as dc +from pyhttpd.backport import not_backported +from .env import AAATestEnv + + +class TestDigestChallengeResponse: + + def url(self, env, path="secret.txt", location="default"): + return env.mkurl("http", "aaa", f"/digest/{location}/{path}") + + def challenge(self, env, location="default"): + r = env.curl_get(self.url(env, location=location)) + assert r.response["status"] == 401 + return dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + + def test_digest_001_no_credentials(self, env): + # No Authorization header at all -> 401 with a well-formed challenge. + r = env.curl_get(self.url(env)) + assert r.response["status"] == 401 + auth = r.response["header"]["www-authenticate"] + challenge = dc.DigestChallenge.parse(auth) + assert challenge.realm == AAATestEnv.REALM + assert challenge.algorithm == "MD5" + assert challenge.qop == "auth" + assert challenge.stale is False + # no AuthDigestDomain configured for this Location -> no domain= + assert challenge.domain is None + # nonce-count checking is off and lifetime isn't 0 here, so the + # server has no reason to track this client -> no opaque= + assert challenge.opaque is None + + def test_digest_002_success(self, env): + challenge = self.challenge(env) + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri="/digest/default/secret.txt") + r = env.curl_get(self.url(env), options=["-H", f"Authorization: {auth}"]) + assert r.response["status"] == 200 + assert r.response["body"].decode() == "digest-default-secret\n" + + def test_digest_003_rspauth(self, env): + # Authentication-Info's rspauth= must match what we independently + # compute from the same HA1 -- proves the server round-trips the + # session parameters (nonce/nc/cnonce/qop) correctly. + challenge = self.challenge(env) + nc = "00000001" + cnonce = "test-cnonce-rspauth" + uri = "/digest/default/secret.txt" + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri=uri, nc=nc, cnonce=cnonce) + r = env.curl_get(self.url(env), options=["-H", f"Authorization: {auth}"]) + assert r.response["status"] == 200 + ai = dc.parse_params(r.response["header"]["authentication-info"]) + h1 = dc.ha1(AAATestEnv.DIGEST_USER, challenge.realm, AAATestEnv.DIGEST_PASSWORD) + expected = dc.rspauth_digest(h1, challenge.nonce, nc, cnonce, "auth", uri) + assert ai["rspauth"] == expected + assert ai["qop"] == "auth" + assert ai["nc"] == nc + assert ai["cnonce"] == cnonce + + def test_digest_004_wrong_password(self, env): + challenge = self.challenge(env) + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, "not-the-password", + method="GET", uri="/digest/default/secret.txt") + r = env.curl_get(self.url(env), options=["-H", f"Authorization: {auth}"]) + assert r.response["status"] == 401 + env.httpd_error_log.ignore_recent(lognos=["AH01794"]) + + def test_digest_005_unknown_user(self, env): + challenge = self.challenge(env) + auth = dc.build_authorization( + "no-such-user", challenge, "whatever", + method="GET", uri="/digest/default/secret.txt") + r = env.curl_get(self.url(env), options=["-H", f"Authorization: {auth}"]) + assert r.response["status"] == 401 + env.httpd_error_log.ignore_recent(lognos=["AH01790"]) + + def test_digest_006_second_user(self, env): + # a distinct user in the same password file also works + challenge = self.challenge(env) + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER2, challenge, AAATestEnv.DIGEST_PASSWORD2, + method="GET", uri="/digest/default/secret.txt") + r = env.curl_get(self.url(env), options=["-H", f"Authorization: {auth}"]) + assert r.response["status"] == 200 + + def test_digest_007_wrong_realm(self, env): + challenge = self.challenge(env) + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri="/digest/default/secret.txt", + realm="Some Other Realm") + r = env.curl_get(self.url(env), options=["-H", f"Authorization: {auth}"]) + assert r.response["status"] == 401 + env.httpd_error_log.ignore_recent(lognos=["AH01788"]) + + def test_digest_008_bad_algorithm_token(self, env): + # a client claiming an algorithm other than MD5 is rejected outright, + # even though the response hash below is computed correctly for MD5. + challenge = self.challenge(env) + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri="/digest/default/secret.txt", + algorithm="MD5-sess") + r = env.curl_get(self.url(env), options=["-H", f"Authorization: {auth}"]) + assert r.response["status"] == 401 + env.httpd_error_log.ignore_recent(lognos=["AH01789"]) + + def test_digest_009_legacy_no_qop_rejected(self, env): + # RFC 2069-style digest (no qop/cnonce/nc) is syntactically valid but + # explicitly no longer supported by this module. + challenge = self.challenge(env) + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri="/digest/default/secret.txt", + qop=None, include_qop_fields=False) + r = env.curl_get(self.url(env), options=["-H", f"Authorization: {auth}"]) + assert r.response["status"] == 401 + env.httpd_error_log.ignore_recent(lognos=["AH10560"]) + + def test_digest_010_malformed_header_missing_field(self, env): + # missing "uri" entirely -> header is syntactically INVALID, so the + # server issues a fresh (non-stale) challenge rather than evaluating + # the (nonexistent) response hash. + challenge = self.challenge(env) + h1 = dc.ha1(AAATestEnv.DIGEST_USER, challenge.realm, AAATestEnv.DIGEST_PASSWORD) + auth = ('Digest username="digestuser", ' + f'realm="{challenge.realm}", nonce="{challenge.nonce}", ' + f'response="{h1}", qop=auth, nc=00000001, cnonce="x"') + r = env.curl_get(self.url(env), options=["-H", f"Authorization: {auth}"]) + assert r.response["status"] == 401 + new_challenge = dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + assert new_challenge.stale is False + env.httpd_error_log.ignore_recent(lognos=["AH01782"]) + + def test_digest_011_wrong_scheme(self, env): + r = env.curl_get(self.url(env), options=[ + "-H", "Authorization: Basic ZGlnZXN0dXNlcjpkaWdlc3RwYXNz"]) + assert r.response["status"] == 401 + env.httpd_error_log.ignore_recent(lognos=["AH01781"]) + + def test_digest_012_uri_mismatch(self, env): + # The Authorization uri= must match the actual request-target; a + # self-consistent response computed for a *different* uri than the + # one actually requested is rejected as a bad request, before the + # hash is even checked. + challenge = self.challenge(env) + other_uri = "/digest/default/other-secret.txt" + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri=other_uri) + r = env.curl_get(self.url(env), options=["-H", f"Authorization: {auth}"]) + assert r.response["status"] == 400 + env.httpd_error_log.ignore_recent(lognos=["AH01786"]) + + def test_digest_013_invalid_opaque(self, env): + challenge = self.challenge(env) + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri="/digest/default/secret.txt", + opaque="not-a-hex-number") + r = env.curl_get(self.url(env), options=["-H", f"Authorization: {auth}"]) + assert r.response["status"] == 401 + env.httpd_error_log.ignore_recent(lognos=["AH01782", "AH01787"]) + + def test_digest_014_tampered_response_hash(self, env): + challenge = self.challenge(env) + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri="/digest/default/secret.txt", + response="0" * 32) + r = env.curl_get(self.url(env), options=["-H", f"Authorization: {auth}"]) + assert r.response["status"] == 401 + env.httpd_error_log.ignore_recent(lognos=["AH01794"]) + + @not_backported("r1939034", "rejecting an Authorization uri which fails to unescape") + def test_digest_015_uri_bad_escaping_rejected(self, env): + # The Authorization uri= is compared against the request-target after + # %-decoding both. A uri= that fails to decode -- an encoded NUL or + # slash -- must be rejected, not silently truncated at the bad octet + # so that its prefix matches the request-target. + challenge = self.challenge(env) + for bad_uri in ["/digest/default/secret.txt%00ignored", + "/digest/default%2fsecret.txt"]: + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri=bad_uri) + r = env.curl_get(self.url(env), + options=["-H", f"Authorization: {auth}"]) + assert r.response["status"] == 400, \ + f"uri={bad_uri!r} was not rejected" + env.httpd_error_log.ignore_recent(lognos=["AH01783"]) diff -Nru apache2-2.4.69/test/modules/aaa/test_002_nonce.py apache2-2.4.71/test/modules/aaa/test_002_nonce.py --- apache2-2.4.69/test/modules/aaa/test_002_nonce.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/test_002_nonce.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,134 @@ +"""Nonce lifecycle scenarios: tampered nonces, AuthDigestNonceLifetime +expiry/reissue, a never-expiring nonce, and the one-time-nonce +(AuthDigestNonceLifetime 0) case. +""" + +import time + +from . import digest_client as dc +from .env import AAATestEnv + + +class TestDigestNonce: + + def url(self, env, location, path="secret.txt"): + return env.mkurl("http", "aaa", f"/digest/{location}/{path}") + + def challenge(self, env, location): + r = env.curl_get(self.url(env, location)) + assert r.response["status"] == 401 + return dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + + def authenticate(self, env, location, challenge, nc="00000001", + cnonce="nonce-test-cnonce", uri=None): + uri = uri or f"/digest/{location}/secret.txt" + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri=uri, nc=nc, cnonce=cnonce) + return env.curl_get(self.url(env, location), options=["-H", f"Authorization: {auth}"]) + + def test_digest_020_tampered_nonce_is_stale(self, env): + challenge = self.challenge(env, "default") + # flip a character in the middle of the opaque nonce blob: it stays + # the right length but its embedded hash no longer verifies. + bad = list(challenge.nonce) + mid = len(bad) // 2 + bad[mid] = 'x' if bad[mid] != 'x' else 'y' + challenge.nonce = ''.join(bad) + r = self.authenticate(env, "default", challenge) + assert r.response["status"] == 401 + new_challenge = dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + assert new_challenge.stale is True + env.httpd_error_log.ignore_recent(lognos=["AH01776"]) + + def test_digest_021_garbage_nonce_hash_is_stale(self, env): + # A nonce must still look like "b64(time)+hex(hash)" (VALID_NONCE in + # mod_auth_digest.c checks length and the '=' padding boundary) to + # even be considered for a hash check; something that doesn't match + # that shape is instead rejected as a malformed header (see + # test_digest_010). Here we keep the genuine time-prefix (so the + # shape is valid) but replace the whole hash suffix with garbage, to + # hit check_nonce()'s "hash is not %s" path distinctly from + # test_digest_020's single-flipped-character tamper. + # + # The hash length depends on which keyed hash the module was built + # with, so take it from the nonce rather than assuming: the time is + # base64 of 8 bytes and so ends with the only '=' in the string. + challenge = self.challenge(env, "default") + time_prefix = challenge.nonce[:challenge.nonce.index('=') + 1] + hash_len = len(challenge.nonce) - len(time_prefix) + challenge.nonce = time_prefix + ("f" * hash_len) + r = self.authenticate(env, "default", challenge) + assert r.response["status"] == 401 + new_challenge = dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + assert new_challenge.stale is True + env.httpd_error_log.ignore_recent(lognos=["AH01776"]) + + def test_digest_022_short_lifetime_expires(self, env): + # AuthDigestNonceLifetime 2 for this location. + challenge = self.challenge(env, "shortlife") + r = self.authenticate(env, "shortlife", challenge) + assert r.response["status"] == 200 + + time.sleep(3) + # same nonce, now past its lifetime -> 401 stale=true + r = self.authenticate(env, "shortlife", challenge) + assert r.response["status"] == 401 + stale_challenge = dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + assert stale_challenge.stale is True + env.httpd_error_log.ignore_recent(lognos=["AH01776"]) + + # the fresh nonce from the stale challenge works again + r = self.authenticate(env, "shortlife", stale_challenge) + assert r.response["status"] == 200 + + def test_digest_023_never_expiring_nonce(self, env): + # AuthDigestNonceLifetime -1 for this location: no NcCheck is + # configured, so the identical Authorization line can simply be + # replayed after a delay and must still succeed both times. + challenge = self.challenge(env, "neverexpire") + r1 = self.authenticate(env, "neverexpire", challenge) + assert r1.response["status"] == 200 + + time.sleep(3) + r2 = self.authenticate(env, "neverexpire", challenge) + assert r2.response["status"] == 200 + + def test_digest_024_one_time_nonce_rejects_reuse(self, env): + # AuthDigestNonceLifetime 0: a successful request immediately + # supersedes its nonce (the tracked "last_nonce" moves on to the + # nextnonce from Authentication-Info), so replaying the very same + # nonce right afterwards must fail as stale. Each request against + # this client (success OR failure) advances the tracked nonce again, + # so this test does exactly one success followed by exactly one + # reuse -- no longer chain that would need to account for that. + challenge = self.challenge(env, "onetime") + assert challenge.opaque is not None, \ + "one-time-nonce tracking requires an opaque to identify the client" + + r1 = self.authenticate(env, "onetime", challenge) + assert r1.response["status"] == 200 + ai1 = dc.parse_params(r1.response["header"]["authentication-info"]) + assert "nextnonce" in ai1 + assert ai1["nextnonce"] != challenge.nonce + + # reusing the exact same (now superseded) nonce fails as stale + r2 = self.authenticate(env, "onetime", challenge) + assert r2.response["status"] == 401 + stale_challenge = dc.DigestChallenge.parse(r2.response["header"]["www-authenticate"]) + assert stale_challenge.stale is True + env.httpd_error_log.ignore_recent(lognos=["AH01776"]) + + def test_digest_025_one_time_nonce_chain_continues(self, env): + # Following the nextnonce handed out on a successful response lets + # the client keep authenticating, one hop at a time. + challenge = self.challenge(env, "onetime") + r1 = self.authenticate(env, "onetime", challenge) + assert r1.response["status"] == 200 + ai1 = dc.parse_params(r1.response["header"]["authentication-info"]) + + challenge.nonce = ai1["nextnonce"] + r2 = self.authenticate(env, "onetime", challenge) + assert r2.response["status"] == 200 + ai2 = dc.parse_params(r2.response["header"]["authentication-info"]) + assert ai2["nextnonce"] != ai1["nextnonce"] diff -Nru apache2-2.4.69/test/modules/aaa/test_003_nccheck.py apache2-2.4.71/test/modules/aaa/test_003_nccheck.py --- apache2-2.4.69/test/modules/aaa/test_003_nccheck.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/test_003_nccheck.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,180 @@ +"""AuthDigestNcCheck replay-detection scenarios. + +The semantics are those of RFC 7616 3.4.3: the nonce-count is counted by +the client per-nonce, so the server tracks a count per (client, nonce) pair +and requires it to strictly increase. Within one nonce, an nc which has +already been seen is a replay and is rejected; a *higher* nc than expected +is not, since the client also counts the requests it sends to URIs in the +protection space which turn out not to need authentication, and the server +never sees those. Moving to a newer nonce starts a fresh count, and a nonce +the client has already moved on from is rejected. + +The tracked count is only ever updated for a fully verified request, so a +failed request cannot disturb the count of the client whose opaque it +quotes; test_007_replay.py covers that property directly. +""" + +from . import digest_client as dc +from pyhttpd.backport import not_backported +from .env import AAATestEnv + + +class TestDigestNcCheck: + + def url(self, env, location, path="secret.txt"): + return env.mkurl("http", "aaa", f"/digest/{location}/{path}") + + def challenge(self, env, location): + r = env.curl_get(self.url(env, location)) + assert r.response["status"] == 401 + return dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + + def authenticate(self, env, location, challenge, nc, cnonce="ncc-test-cnonce", + include_opaque=True): + uri = f"/digest/{location}/secret.txt" + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri=uri, nc=nc, cnonce=cnonce, + include_opaque=include_opaque) + return env.curl_get(self.url(env, location), options=["-H", f"Authorization: {auth}"]) + + def test_digest_030_nccheck_requires_opaque(self, env): + # with AuthDigestNcCheck on, the server cannot verify nc without + # having tracked this client via its opaque -- omitting the opaque + # therefore fails, even with nc=00000001. It is rejected before the + # nc check is even reached: the nonce hash is computed over the + # opaque (gen_nonce_hash()), so a nonce quoted without the opaque it + # was issued with does not verify, and that is reported as stale. + challenge = self.challenge(env, "nccheck") + assert challenge.opaque is not None + r = self.authenticate(env, "nccheck", challenge, nc="00000001", include_opaque=False) + assert r.response["status"] == 401 + new_challenge = dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + assert new_challenge.stale is True + env.httpd_error_log.ignore_recent(lognos=["AH01776"]) + + def test_digest_031_nccheck_sequential_ok(self, env): + challenge = self.challenge(env, "nccheck") + r1 = self.authenticate(env, "nccheck", challenge, nc="00000001") + assert r1.response["status"] == 200 + r2 = self.authenticate(env, "nccheck", challenge, nc="00000002") + assert r2.response["status"] == 200 + r3 = self.authenticate(env, "nccheck", challenge, nc="00000003") + assert r3.response["status"] == 200 + + def test_digest_032_nccheck_replay_rejected(self, env): + challenge = self.challenge(env, "nccheck") + r1 = self.authenticate(env, "nccheck", challenge, nc="00000001") + assert r1.response["status"] == 200 + r2 = self.authenticate(env, "nccheck", challenge, nc="00000002") + assert r2.response["status"] == 200 + + # replay an already-used nc -> rejected, and NOT reported as stale + # (this is a distinct failure mode from an invalid/expired nonce). + r3 = self.authenticate(env, "nccheck", challenge, nc="00000001") + assert r3.response["status"] == 401 + new_challenge = dc.DigestChallenge.parse(r3.response["header"]["www-authenticate"]) + assert new_challenge.stale is False + env.httpd_error_log.ignore_recent(lognos=["AH01774"]) + + # recovery: the rejected attempt handed out a fresh challenge for + # this client, and following it -- new nonce, so the count starts + # over at 00000001 -- authenticates again. + r4 = self.authenticate(env, "nccheck", new_challenge, nc="00000001") + assert r4.response["status"] == 200 + + # the superseded nonce is not usable any more, at any nc. + r5 = self.authenticate(env, "nccheck", challenge, nc="00000003") + assert r5.response["status"] == 401 + env.httpd_error_log.ignore_recent(lognos=["AH01774"]) + + def test_digest_033_nccheck_skip_ahead_allowed(self, env): + challenge = self.challenge(env, "nccheck") + r1 = self.authenticate(env, "nccheck", challenge, nc="00000001") + assert r1.response["status"] == 200 + + # skipping ahead is allowed: nc only has to be higher than the + # highest already seen for this nonce, not exactly one more. A + # client legitimately produces gaps by sending counted requests to + # URIs in the protection space which don't need authentication, and + # a higher nc is not a replay in any case. + r2 = self.authenticate(env, "nccheck", challenge, nc="00000009") + assert r2.response["status"] == 200 + + # ...and the skipped-over counts are spent: they are no longer + # accepted afterwards. + r3 = self.authenticate(env, "nccheck", challenge, nc="00000005") + assert r3.response["status"] == 401 + env.httpd_error_log.ignore_recent(lognos=["AH01774"]) + + def test_digest_034_no_nccheck_allows_replay(self, env): + # the "default" location has no AuthDigestNcCheck (Off by default), + # so replaying the exact same nc is not detected or rejected. + challenge = self.challenge(env, "default") + r1 = self.authenticate(env, "default", challenge, nc="00000001") + assert r1.response["status"] == 200 + r2 = self.authenticate(env, "default", challenge, nc="00000001") + assert r2.response["status"] == 200 + + def test_digest_035_out_of_range_opaque_is_not_truncated(self, env): + # The opaque is a 32- or 64-bit client id. A value which would + # truncate onto a live id must not select that client. This is + # observable in the challenge which comes back: a client the server + # still knows is re-challenged with its own opaque, whereas an unknown + # one is given a freshly minted opaque and stale=true. + challenge = self.challenge(env, "nccheck") + assert self.authenticate(env, "nccheck", challenge, + nc="00000001").response["status"] == 200 + + # 2^bits + the live id, which truncates to the live id in that many + # bits; one of the two is out of range whichever width is in use + for bits in (32, 64): + crafted = "%x" % ((1 << bits) + int(challenge.opaque, 16)) + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri="/digest/nccheck/secret.txt", nc="00000002", + cnonce="trunc-cnonce", opaque=crafted) + r = env.curl_get(env.mkurl("http", "aaa", "/digest/nccheck/secret.txt"), + options=["-H", f"Authorization: {auth}"]) + # AH01782 (AH01787 before 64-bit ids) for an opaque out of range, + # AH01776 (nonce hash) for one in range but not the opaque the + # nonce was issued with + env.httpd_error_log.ignore_recent(lognos=["AH01782", "AH01787", + "AH01776"]) + assert r.response["status"] == 401 + new_challenge = dc.DigestChallenge.parse( + r.response["header"]["www-authenticate"]) + assert new_challenge.opaque != crafted, \ + f"an opaque 2^{bits} above a live client id was truncated onto it" + + @not_backported("r1939033", "nonce-count validation") + def test_digest_036_malformed_nc_rejected(self, env): + # The nonce-count is 8 hex digits. A value which is not -- a negative + # number, non-hex, or the wrong length -- must be rejected rather + # than parsed leniently into a wrapped count. + challenge = self.challenge(env, "nccheck") + for bad in ["-0000001", "zzzzzzzz", "000000001", "1", "0000 001"]: + r = self.authenticate(env, "nccheck", challenge, nc=bad, + cnonce=f"nc-{len(bad)}") + assert r.response["status"] == 401, f"nc={bad!r} was accepted" + env.httpd_error_log.ignore_recent(lognos=["AH01782"]) + + @not_backported("r1939033", "nonce-count validation") + def test_digest_037_malformed_nc_does_not_poison_client(self, env): + # A correctly-signed request carrying nc="-0000001" must not be + # accepted: before the count was validated it was read as a huge + # unsigned value and stored, locking the client out of every later + # (smaller) in-sequence count. + challenge = self.challenge(env, "nccheck") + assert self.authenticate(env, "nccheck", challenge, + nc="00000001").response["status"] == 200 + + r = self.authenticate(env, "nccheck", challenge, nc="-0000001", + cnonce="poison-cnonce") + assert r.response["status"] == 401, "a negative nc was accepted" + + # the legitimate client carries on with its next in-sequence count + assert self.authenticate(env, "nccheck", challenge, + nc="00000002").response["status"] == 200, \ + "a malformed nc locked the client out" + env.httpd_error_log.ignore_recent(lognos=["AH01782"]) diff -Nru apache2-2.4.69/test/modules/aaa/test_004_domain.py apache2-2.4.71/test/modules/aaa/test_004_domain.py --- apache2-2.4.69/test/modules/aaa/test_004_domain.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/test_004_domain.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,56 @@ +"""AuthDigestDomain: presence, format, and inheritance of the domain= +attribute in the WWW-Authenticate challenge. +""" + +from . import digest_client as dc +from .env import AAATestEnv + + +class TestDigestDomain: + + def url(self, env, path): + return env.mkurl("http", "aaa", path) + + def test_digest_040_domain_attribute_present(self, env): + r = env.curl_get(self.url(env, "/digest/domain/secret.txt")) + assert r.response["status"] == 401 + challenge = dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + # set_uri_list() (mod_auth_digest.c) builds a single quoted, + # space-separated list from the configured AuthDigestDomain URIs. + assert challenge.domain == "/digest/domain/ https://mirror.example.org/other/" + assert challenge.domain_list() == [ + "/digest/domain/", "https://mirror.example.org/other/"] + + def test_digest_041_no_domain_configured_omits_attribute(self, env): + r = env.curl_get(self.url(env, "/digest/default/secret.txt")) + assert r.response["status"] == 401 + challenge = dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + assert challenge.domain is None + + def test_digest_042_domain_location_still_authenticates(self, env): + r = env.curl_get(self.url(env, "/digest/domain/secret.txt")) + challenge = dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri="/digest/domain/secret.txt") + r = env.curl_get(self.url(env, "/digest/domain/secret.txt"), + options=["-H", f"Authorization: {auth}"]) + assert r.response["status"] == 200 + assert r.response["body"].decode() == "digest-domain-secret\n" + + def test_digest_043_domain_inherited_by_nested_path(self, env): + # AuthDigestDomain is set on /digest/domain/; a path nested below it + # inherits the same directory config (same realm/credentials/domain). + r = env.curl_get(self.url(env, "/digest/domain/nested/secret.txt")) + assert r.response["status"] == 401 + challenge = dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + assert challenge.realm == AAATestEnv.REALM + assert challenge.domain == "/digest/domain/ https://mirror.example.org/other/" + + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri="/digest/domain/nested/secret.txt") + r = env.curl_get(self.url(env, "/digest/domain/nested/secret.txt"), + options=["-H", f"Authorization: {auth}"]) + assert r.response["status"] == 200 + assert r.response["body"].decode() == "digest-domain-nested-secret\n" diff -Nru apache2-2.4.69/test/modules/aaa/test_005_provider.py apache2-2.4.71/test/modules/aaa/test_005_provider.py --- apache2-2.4.69/test/modules/aaa/test_005_provider.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/test_005_provider.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,37 @@ +"""AuthDigestProvider scenarios.""" + +from . import digest_client as dc +from .env import AAATestEnv + + +class TestDigestProvider: + + def url(self, env, path): + return env.mkurl("http", "aaa", path) + + def test_digest_050_omitted_provider_defaults_to_file(self, env): + # /digest/noprovider/ has no AuthDigestProvider directive at all; + # mod_auth_digest falls back to the "file" provider (mod_authn_file) + # by default (see get_hash() / AUTHN_DEFAULT_PROVIDER in mod_auth.h). + path = "/digest/noprovider/secret.txt" + r = env.curl_get(self.url(env, path)) + assert r.response["status"] == 401 + challenge = dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri=path) + r = env.curl_get(self.url(env, path), options=["-H", f"Authorization: {auth}"]) + assert r.response["status"] == 200 + assert r.response["body"].decode() == "digest-noprovider-secret\n" + + def test_digest_051_unknown_provider_rejected_at_config_time(self, env): + r = env.configtest([ + 'AuthType Digest', + f'AuthName "{AAATestEnv.REALM}"', + 'AuthDigestProvider no-such-provider', + f'AuthUserFile "{env.digest_pwfile}"', + 'Require valid-user', + ]) + assert r.exit_code != 0 + assert "Unknown Authn provider" in r.stderr diff -Nru apache2-2.4.69/test/modules/aaa/test_006_config_errors.py apache2-2.4.71/test/modules/aaa/test_006_config_errors.py --- apache2-2.4.69/test/modules/aaa/test_006_config_errors.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/test_006_config_errors.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,107 @@ +"""Config-time validation for directives whose *documented* syntax (see +docs/manual/mod/mod_auth_digest.xml) is broader than what this build's +mod_auth_digest.c actually implements: AuthDigestQop only accepts "auth" +(qop=none/auth-int are rejected -- the "Open Issues" comment in the source +notes MD5-sess and auth-int were removed as incomplete), AuthDigestAlgorithm +only accepts "MD5", and AuthDigestShmemSize enforces a minimum size. These +are all checked with `httpd -t` against a throwaway config so the shared +package server is never disturbed. +""" + +from pyhttpd.backport import not_backported +from .env import AAATestEnv + + +class TestDigestConfigErrors: + + def test_digest_060_qop_none_rejected(self, env): + r = env.configtest([ + 'AuthType Digest', + f'AuthName "{AAATestEnv.REALM}"', + 'AuthDigestProvider file', + f'AuthUserFile "{env.digest_pwfile}"', + 'AuthDigestQop none', + 'Require valid-user', + ]) + assert r.exit_code != 0 + assert "AuthDigestQop" in r.stderr + + def test_digest_061_qop_auth_int_rejected(self, env): + r = env.configtest([ + 'AuthType Digest', + f'AuthName "{AAATestEnv.REALM}"', + 'AuthDigestProvider file', + f'AuthUserFile "{env.digest_pwfile}"', + 'AuthDigestQop auth-int', + 'Require valid-user', + ]) + assert r.exit_code != 0 + assert "AuthDigestQop" in r.stderr + + def test_digest_062_qop_auth_accepted(self, env): + # the only value actually supported must still work. + r = env.configtest([ + 'AuthType Digest', + f'AuthName "{AAATestEnv.REALM}"', + 'AuthDigestProvider file', + f'AuthUserFile "{env.digest_pwfile}"', + 'AuthDigestQop auth', + 'Require valid-user', + ]) + assert r.exit_code == 0 + + def test_digest_063_algorithm_md5_sess_rejected(self, env): + r = env.configtest([ + 'AuthType Digest', + f'AuthName "{AAATestEnv.REALM}"', + 'AuthDigestProvider file', + f'AuthUserFile "{env.digest_pwfile}"', + 'AuthDigestAlgorithm MD5-sess', + 'Require valid-user', + ]) + assert r.exit_code != 0 + assert "Unsupported algorithm" in r.stderr + + def test_digest_064_algorithm_md5_accepted(self, env): + r = env.configtest([ + 'AuthType Digest', + f'AuthName "{AAATestEnv.REALM}"', + 'AuthDigestProvider file', + f'AuthUserFile "{env.digest_pwfile}"', + 'AuthDigestAlgorithm MD5', + 'Require valid-user', + ]) + assert r.exit_code == 0 + + def test_digest_065_shmemsize_too_small_rejected(self, env): + r = env.configtest([], extra_top_lines=["AuthDigestShmemSize 10"]) + assert r.exit_code != 0 + assert "AuthDigestShmemSize" in r.stderr + + def test_digest_066_shmemsize_valid_accepted(self, env): + r = env.configtest([], extra_top_lines=["AuthDigestShmemSize 1000"]) + assert r.exit_code == 0 + + def test_digest_067_shmemsize_units_accepted(self, env): + r = env.configtest([], extra_top_lines=["AuthDigestShmemSize 64K"]) + assert r.exit_code == 0 + + @not_backported("r1939036", "AuthDigestShmemSize validation") + def test_digest_068_shmemsize_trailing_junk_rejected(self, env): + # Junk after the unit character must be rejected, not silently + # ignored (which read "64Kfoo" as 64K). + r = env.configtest([], extra_top_lines=["AuthDigestShmemSize 64Kfoo"]) + assert r.exit_code != 0 + assert "AuthDigestShmemSize" in r.stderr + + @not_backported("r1939036", "AuthDigestShmemSize validation") + def test_digest_069_shmemsize_no_room_for_entry_rejected(self, env): + # A size which would hold the table and an entry by their bare + # struct sizes, but not once the per-allocation rmm overhead is + # counted, must be rejected: otherwise every request needing an + # entry gets a 503 the config check did not warn about. 120 bytes is + # above those struct sizes but below the overhead-aware minimum on + # both 32- and 64-bit. + r = env.configtest([], extra_top_lines=["AuthDigestShmemSize 120"]) + assert r.exit_code != 0 + assert "AuthDigestShmemSize" in r.stderr diff -Nru apache2-2.4.69/test/modules/aaa/test_007_replay.py apache2-2.4.71/test/modules/aaa/test_007_replay.py --- apache2-2.4.69/test/modules/aaa/test_007_replay.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/test_007_replay.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,237 @@ +"""Replay-attack scenarios against AuthDigestNcCheck. + +AuthDigestNcCheck exists to detect replayed requests: the server tracks the +highest nonce-count it has accepted from a client (identified by its opaque) +for the nonce that client is using, and requires each request to raise it. + +The security property under test here is not just "the replayed request is +rejected", but that rejecting it must not damage the legitimate client: + + With nonce-count checking enabled, a replay attack MUST NOT affect the + original (legitimate) client by resetting its nonce count. + +It used to. On a failed authentication mod_auth_digest issues a fresh +challenge via note_digest_auth_failure(), and for an already-known +(opaque-identified) client that path reset client->nonce_count to 0, while +the post_read_request hook re-incremented the count from 0 on the next +request carrying that opaque. An attacker who could make *any* request fail +for the victim's opaque therefore rewound the victim's counter, with two +consequences: + + * the legitimate client's next in-sequence nc no longer matched, so it + was locked out (denial of service against the victim), and + * the attacker's replayed request lined up with the rewound counter and + was accepted -- 200, 401, 200, 401, ... for one captured header, or + every time if the attacker rewound the counter deliberately first. + +The count is now tracked per (client, nonce) and updated only for a request +which has been fully verified, so a request which fails to authenticate +leaves the victim's state untouched. +""" + +import time + +from . import digest_client as dc +from .env import AAATestEnv + +# See the note in test_003_nccheck.py: a failed nc check is not reported as +# stale, since it is a distinct failure mode from an invalid/expired nonce. +NC_FAILED = "AH01774" +NONCE_HASH_INVALID = "AH01776" +PASSWORD_MISMATCH = "AH01794" + + +class TestDigestReplay: + + LOCATION = "nccheck" + + def url(self, env, path="secret.txt"): + return env.mkurl("http", "aaa", f"/digest/{self.LOCATION}/{path}") + + @property + def uri(self): + return f"/digest/{self.LOCATION}/secret.txt" + + def challenge(self, env): + r = env.curl_get(self.url(env)) + assert r.response["status"] == 401 + return dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + + def victim_header(self, challenge, nc, cnonce="victim-cnonce"): + """A correct Authorization header from the legitimate client.""" + return dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri=self.uri, nc=nc, cnonce=cnonce) + + def attacker_header(self, challenge, nc="00000001", cnonce="attacker-cnonce"): + """A well-formed Digest header carrying the victim's opaque and nonce + but a bogus response digest. An attacker who has merely *seen* one of + the victim's requests can build this; no credentials are needed.""" + return dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, "not-the-password", + method="GET", uri=self.uri, nc=nc, cnonce=cnonce, + response="0" * 32) + + def send(self, env, auth): + return env.curl_get(self.url(env), options=["-H", f"Authorization: {auth}"]) + + def test_digest_070_replay_does_not_lock_out_legit_client(self, env): + # The legitimate client authenticates a few times, in sequence. + challenge = self.challenge(env) + for nc in ["00000001", "00000002", "00000003"]: + assert self.send(env, self.victim_header(challenge, nc)).response["status"] == 200 + + # An attacker replays a request captured earlier in that sequence. + # Rejecting it is correct... + replayed = self.victim_header(challenge, "00000002") + replay_status = self.send(env, replayed).response["status"] + + # ...but it must not disturb the legitimate client, which knows + # nothing of the replay and simply carries on with its next nc. + r = self.send(env, self.victim_header(challenge, "00000004")) + env.httpd_error_log.ignore_recent(lognos=[NC_FAILED]) + assert replay_status == 401 + assert r.response["status"] == 200, \ + "the replay reset the victim's nonce-count and locked it out" + + def test_digest_071_bogus_request_does_not_lock_out_legit_client(self, env): + # Same property, but the attacker does not even need to have captured + # a complete valid request: any well-formed Digest header quoting the + # victim's opaque is enough to rewind the victim's counter. + challenge = self.challenge(env) + for nc in ["00000001", "00000002"]: + assert self.send(env, self.victim_header(challenge, nc)).response["status"] == 200 + + bogus_status = self.send(env, self.attacker_header(challenge)).response["status"] + + r = self.send(env, self.victim_header(challenge, "00000003")) + env.httpd_error_log.ignore_recent(lognos=[NC_FAILED, PASSWORD_MISMATCH]) + assert bogus_status == 401 + assert r.response["status"] == 200, \ + "a bogus request reset the victim's nonce-count and locked it out" + + def test_digest_072_captured_request_is_never_accepted_twice(self, env): + # The flip side of the same defect. One captured Authorization header + # is replayed verbatim; the first send is the genuine request, so it + # succeeds, and every later send must be rejected. Before the fix the + # rejection rewound the counter, so the replay after it lined up + # again: the observed pattern was 200, 401, 200, 401, ... + challenge = self.challenge(env) + captured = self.victim_header(challenge, "00000001", cnonce="captured-cnonce") + + assert self.send(env, captured).response["status"] == 200 + statuses = [self.send(env, captured).response["status"] for _ in range(4)] + env.httpd_error_log.ignore_recent(lognos=[NC_FAILED]) + assert statuses == [401, 401, 401, 401], \ + f"replayed request was accepted again: {statuses}" + + def test_digest_073_attacker_cannot_force_replay_to_succeed(self, env): + # Severity check: the attacker must not be able to line the counter + # up on demand. Before the fix, sending a bogus request first rewound + # the counter to 0, so the replay that followed succeeded every + # single time. + challenge = self.challenge(env) + captured = self.victim_header(challenge, "00000001", cnonce="captured-cnonce") + assert self.send(env, captured).response["status"] == 200 + + statuses = [] + for _ in range(3): + self.send(env, self.attacker_header(challenge)) + statuses.append(self.send(env, captured).response["status"]) + env.httpd_error_log.ignore_recent(lognos=[NC_FAILED, PASSWORD_MISMATCH]) + assert statuses == [401, 401, 401], \ + f"attacker replayed at will by forcing a counter reset: {statuses}" + + def test_digest_074_legit_client_recovers_via_fresh_challenge(self, env): + # Invariant: a client whose nc is rejected is handed a fresh + # challenge, and following that challenge -- new nonce, so the count + # starts over at 1 -- gets it working again. Simply never resetting + # the count, without tying it to the nonce it was counted for, would + # break this. + challenge = self.challenge(env) + assert self.send(env, self.victim_header(challenge, "00000001")).response["status"] == 200 + + # provoke the rejection with a replay of that first request + r = self.send(env, self.victim_header(challenge, "00000001")) + assert r.response["status"] == 401 + env.httpd_error_log.ignore_recent(lognos=[NC_FAILED]) + fresh = dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + assert fresh.stale is False + assert fresh.opaque == challenge.opaque, \ + "the client keeps its identity across a re-challenge" + assert fresh.nonce != challenge.nonce + + r = self.send(env, self.victim_header(fresh, "00000001")) + assert r.response["status"] == 200 + + def test_digest_075_nonce_is_bound_to_opaque(self, env): + # A captured header cannot be re-pointed at a *different* client + # session to dodge that session's nonce-count: the nonce hash is + # computed over the opaque (gen_nonce_hash()), so quoting one + # client's nonce under another client's opaque fails the hash check + # outright, and is reported as stale. + victim = self.challenge(env) + captured = self.victim_header(victim, "00000001", cnonce="captured-cnonce") + assert self.send(env, captured).response["status"] == 200 + + attacker = self.challenge(env) + assert attacker.opaque != victim.opaque + spliced = dc.build_authorization( + AAATestEnv.DIGEST_USER, victim, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri=self.uri, nc="00000001", cnonce="captured-cnonce", + opaque=attacker.opaque) + r = self.send(env, spliced) + env.httpd_error_log.ignore_recent(lognos=[NONCE_HASH_INVALID]) + assert r.response["status"] == 401 + assert dc.DigestChallenge.parse( + r.response["header"]["www-authenticate"]).stale is True + + +class TestDigestNcCheckExpiry: + """AuthDigestNcCheck combined with an expiring nonce. + + The nonce is checked before the nonce-count, so that an expired nonce + still produces a "stale=true" challenge rather than being reported as a + replay -- the client then retries silently against the fresh nonce, with + its count restarted at 1. + """ + + LOCATION = "nccheck-shortlife" # AuthDigestNcCheck On, lifetime 2s + + def url(self, env): + return env.mkurl("http", "aaa", f"/digest/{self.LOCATION}/secret.txt") + + def challenge(self, env): + r = env.curl_get(self.url(env)) + assert r.response["status"] == 401 + return dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + + def send(self, env, challenge, nc): + auth = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri=f"/digest/{self.LOCATION}/secret.txt", nc=nc, + cnonce="expiry-cnonce") + return env.curl_get(self.url(env), options=["-H", f"Authorization: {auth}"]) + + def test_digest_076_expired_nonce_restarts_the_count(self, env): + challenge = self.challenge(env) + assert self.send(env, challenge, "00000001").response["status"] == 200 + assert self.send(env, challenge, "00000002").response["status"] == 200 + + time.sleep(3) + + # past its lifetime: reported as stale, not as a nonce-count failure + r = self.send(env, challenge, "00000003") + assert r.response["status"] == 401 + fresh = dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + assert fresh.stale is True + + # the client restarts its count for the fresh nonce, which must not + # collide with the count already tracked for the expired one + assert self.send(env, fresh, "00000001").response["status"] == 200 + assert self.send(env, fresh, "00000002").response["status"] == 200 + + # and the expired nonce stays unusable + r = self.send(env, challenge, "00000004") + assert r.response["status"] == 401 + env.httpd_error_log.ignore_recent(lognos=["AH01776", NC_FAILED]) diff -Nru apache2-2.4.69/test/modules/aaa/test_008_onetime_nccheck.py apache2-2.4.71/test/modules/aaa/test_008_onetime_nccheck.py --- apache2-2.4.69/test/modules/aaa/test_008_onetime_nccheck.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/test_008_onetime_nccheck.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,194 @@ +"""One-time nonces (AuthDigestNonceLifetime 0), alone and with AuthDigestNcCheck. + +With a lifetime of 0 the server hands the client a nextnonce on every +successful response, and a nonce may be used once: it is accepted only if +it is newer than the last nonce that client used. The client counts from 1 +again for each new nonce, so with AuthDigestNcCheck also on, every request +legitimately carries nc=00000001. + +The security property here is the one from test_007_replay.py, applied to +the other piece of per-client state: + + A request which fails to authenticate MUST NOT invalidate the nonce + which the legitimate client is holding. + +It did, when the client's state was the last nonce *issued* to it: +note_digest_auth_failure() generates a fresh nonce and recorded it there, +and any request quoting the client's opaque can provoke a challenge. So an +eavesdropper who had captured one Authorization header could replay it at +will -- the replay itself was correctly rejected, but it moved the stored +nonce on, and the victim's next request was then refused. The opaque is in +the clear in every challenge and every request, and such a captured header +never goes stale for this purpose, since it works by failing. + +This needed no credentials and, despite where it was first noticed, no +AuthDigestNcCheck: the tests below run against both locations to pin that +the defect was in the one-time-nonce path, not in the combination. + +The state is now the last nonce the client actually *used*, which nothing +unauthenticated can move. +""" + +import pytest + +from . import digest_client as dc +from .env import AAATestEnv + +BOTH = ["onetime", "onetime-nccheck"] + +NC_FAILED = "AH01774" +NONCE_HASH_INVALID = "AH01776" +PASSWORD_MISMATCH = "AH01794" +CLIENT_UNKNOWN = "AH10618" + + +class TestOneTimeNonce: + + def url(self, env, location): + return env.mkurl("http", "aaa", f"/digest/{location}/secret.txt") + + def challenge(self, env, location): + r = env.curl_get(self.url(env, location)) + assert r.response["status"] == 401 + challenge = dc.DigestChallenge.parse( + r.response["header"]["www-authenticate"]) + assert challenge.opaque is not None, \ + "one-time nonces are tracked per client, so an opaque is required" + return challenge + + def header(self, location, challenge, nc="00000001", cnonce="onetime-cnonce", + response=None): + """A correct Authorization header, unless response= overrides the + digest -- an attacker can build that from an observed request + without knowing the password.""" + return dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri=f"/digest/{location}/secret.txt", nc=nc, + cnonce=cnonce, response=response) + + def send(self, env, location, auth): + return env.curl_get(self.url(env, location), + options=["-H", f"Authorization: {auth}"]) + + def follow_nextnonce(self, r, challenge): + """Advance the client to the nextnonce it was just handed.""" + ai = dc.parse_params(r.response["header"]["authentication-info"]) + assert "nextnonce" in ai + assert ai["nextnonce"] != challenge.nonce + challenge.nonce = ai["nextnonce"] + + def test_digest_080_nccheck_does_not_break_the_onetime_chain(self, env): + # Each nonce is new, so the client's count restarts at 1 every time + # and the nonce-count check must not object. (Before the nonce-count + # was tracked per-nonce this alternated 200, 401, 200, 401, ...) + challenge = self.challenge(env, "onetime-nccheck") + for _ in range(4): + r = self.send(env, "onetime-nccheck", self.header( + "onetime-nccheck", challenge, nc="00000001")) + assert r.response["status"] == 200 + self.follow_nextnonce(r, challenge) + + @pytest.mark.parametrize("location", BOTH) + def test_digest_081_onetime_nonce_rejects_immediate_replay(self, env, location): + challenge = self.challenge(env, location) + captured = self.header(location, challenge) + assert self.send(env, location, captured).response["status"] == 200 + + r = self.send(env, location, captured) + env.httpd_error_log.ignore_recent(lognos=[NC_FAILED]) + assert r.response["status"] == 401 + assert dc.DigestChallenge.parse( + r.response["header"]["www-authenticate"]).stale is True + + @pytest.mark.parametrize("location", BOTH) + def test_digest_082_onetime_nonce_rejects_replay_after_rotation(self, env, location): + # The captured header stays rejected once the client has moved on + # through the nextnonce chain. + challenge = self.challenge(env, location) + captured = self.header(location, challenge) + r = self.send(env, location, captured) + assert r.response["status"] == 200 + self.follow_nextnonce(r, challenge) + + r = self.send(env, location, self.header(location, challenge)) + assert r.response["status"] == 200 + self.follow_nextnonce(r, challenge) + + r = self.send(env, location, captured) + env.httpd_error_log.ignore_recent(lognos=[NC_FAILED]) + assert r.response["status"] == 401 + + @pytest.mark.parametrize("location", BOTH) + def test_digest_083_replay_does_not_invalidate_the_clients_nonce(self, env, location): + # The eavesdropper's version: no credentials, no forgery, just one + # captured Authorization header replayed after the client has moved + # on. Rejecting it is correct; denying the client's next request is + # not. + challenge = self.challenge(env, location) + captured = self.header(location, challenge) + r = self.send(env, location, captured) + assert r.response["status"] == 200 + self.follow_nextnonce(r, challenge) + + replay_status = self.send(env, location, captured).response["status"] + + r = self.send(env, location, self.header(location, challenge)) + env.httpd_error_log.ignore_recent(lognos=[NC_FAILED]) + assert replay_status == 401 + assert r.response["status"] == 200, \ + "the replay moved the client's one-time nonce on and locked it out" + + @pytest.mark.parametrize("location", BOTH) + def test_digest_084_bogus_request_does_not_invalidate_the_clients_nonce( + self, env, location): + # Same property with a forged digest rather than a captured one, so + # it holds however the attacker's request comes to fail. + challenge = self.challenge(env, location) + r = self.send(env, location, self.header(location, challenge)) + assert r.response["status"] == 200 + self.follow_nextnonce(r, challenge) + + bogus = self.header(location, challenge, cnonce="bogus", + response="0" * 32) + bogus_status = self.send(env, location, bogus).response["status"] + + r = self.send(env, location, self.header(location, challenge)) + env.httpd_error_log.ignore_recent( + lognos=[NC_FAILED, NONCE_HASH_INVALID, PASSWORD_MISMATCH]) + assert bogus_status == 401 + assert r.response["status"] == 200, \ + "the bogus request moved the client's one-time nonce on and locked it out" + + @pytest.mark.parametrize("location", BOTH) + def test_digest_085_replay_rejected_when_the_client_entry_is_gone(self, env, + location): + # The client table is small here -- conftest pins AuthDigestShmemSize + # to 1000 bytes, "~ 12 entries" -- so filling it makes gc() discard + # entries. The pressure has to come from clients which have + # authenticated: gc() discards the entries of clients which never + # did first, so bare requests no longer evict anybody who matters. + # + # A captured request must still not be replayable once the client's + # entry has gone. It used to be: check_nonce() skipped the one-time + # comparison entirely when the client was unknown, so the nonce was + # taken on trust and the replay served the protected resource. + challenge = self.challenge(env, location) + captured = self.header(location, challenge) + assert self.send(env, location, captured).response["status"] == 200 + assert self.send(env, location, captured).response["status"] == 401 + + # Note that the victim is left alone while the table fills: looking + # its entry up would move it to the front of its bucket, which is + # exactly what saves an entry from gc(). + for _ in range(30): + other = self.challenge(env, location) + assert self.send(env, location, + self.header(location, other)).response["status"] == 200 + + r = self.send(env, location, captured) + env.httpd_error_log.ignore_recent(lognos=[NC_FAILED, CLIENT_UNKNOWN]) + assert r.response["status"] == 401, \ + "captured request replayed once the client entry was evicted" + gone = dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + assert gone.opaque != challenge.opaque, \ + "the entry was not evicted, so the gc'd-client path is untested" diff -Nru apache2-2.4.69/test/modules/aaa/test_009_restart.py apache2-2.4.71/test/modules/aaa/test_009_restart.py --- apache2-2.4.69/test/modules/aaa/test_009_restart.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/test_009_restart.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,230 @@ +"""Per-client state across a server restart. + +The client table lives in a shared memory segment created by post_config, +and that segment does not survive a restart: the restart loop in +server/main.c clears pconf, which destroys the segment, and post_config +then builds a new one, empty. + +The nonce does survive, because the secret it is hashed with is kept in +retained data across restarts. So a client returning after a restart +presents a nonce which still verifies, naming per-client state which no +longer exists. + +The right answer for such a client is a stale=true challenge: its +credentials were never in doubt, only the server-side state backing its +nonce is gone, so it should retry silently against the fresh nonce rather +than being told its authentication failed. mod_auth_digest does exactly +that when it finds the client unknown (AH10618). + +It stopped doing it once the id had been handed to somebody else, which the +ids restarting from 1 made routine rather than exotic: the first clients +seen after a restart took exactly the ids the clients from before it were +still quoting. A returning client was then checked against a *different* +client's entry and reported as a nonce-count failure -- stale=false, which +a browser shows as a failed password. + +With one-time nonces it was a replay hole rather than a wrong answer to the +user. Single use is enforced by remembering the last nonce each client +used, and that memory dies with the segment while the nonce does not; the +one-time counter restarts at 0, so a nonce from before the restart outranks +anything the new holder of its id has reached, and was accepted. An +eavesdropper who had captured one used request only had to wait for a +restart. + +The ids are now seeded randomly for each segment, so a returning client's +opaque no longer names anybody, and it takes the unknown-client path above. +""" + +import sys + +import pytest + +from pyhttpd.backport import not_backported +from . import digest_client as dc +from .env import AAATestEnv + +NC_FAILED = "AH01774" +NONCE_INVALID = "AH01776" +CLIENT_UNKNOWN = "AH10618" +ONETIME_REUSED = "AH01779" + +_WIN32 = sys.platform == "win32" + +# on the tests that fail without it (the others pass there as well) +NOT_BACKPORTED = not_backported("r1937711", "random client ids after a restart") + + +class TestDigestRestart: + + def url(self, env, location, path="secret.txt"): + return env.mkurl("http", "aaa", f"/digest/{location}/{path}") + + def uri(self, location): + return f"/digest/{location}/secret.txt" + + def challenge(self, env, location): + r = env.curl_get(self.url(env, location)) + assert r.response["status"] == 401 + return dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + + def header(self, location, challenge, nc="00000001", cnonce="restart-cnonce"): + return dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri=self.uri(location), nc=nc, cnonce=cnonce) + + def send(self, env, location, auth): + return env.curl_get(self.url(env, location), + options=["-H", f"Authorization: {auth}"]) + + def reload(self, env): + assert env.apache_reload() == 0, "graceful restart failed" + + def challenge_of(self, r): + """The challenge carried by a 401 response.""" + return dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + + def follow_nextnonce(self, r, challenge): + ai = dc.parse_params(r.response["header"]["authentication-info"]) + assert "nextnonce" in ai + challenge.nonce = ai["nextnonce"] + + @pytest.mark.xfail(sys.platform == "win32", reason= + "mpm_winnt child is a separate process, " + "ap_retained_data does not survive restart") + def test_digest_090_returning_client_is_challenged_as_stale(self, env): + # A client which authenticated before a restart comes back afterwards + # with the nonce it was holding. The state naming its opaque is gone, + # so the request cannot be accepted -- but the client did nothing + # wrong, and must be told to retry rather than that it failed. + location = "nccheck" + self.reload(env) + challenge = self.challenge(env, location) + assert self.send(env, location, + self.header(location, challenge)).response["status"] == 200 + + self.reload(env) + + r = self.send(env, location, self.header(location, challenge, "00000002")) + env.httpd_error_log.ignore_recent(lognos=[NC_FAILED, CLIENT_UNKNOWN] + + ([NONCE_INVALID] if _WIN32 else [])) + assert r.response["status"] == 401 + fresh = self.challenge_of(r) + assert fresh.stale, \ + "a client returning after a restart was told its authentication " \ + "failed, rather than being asked to retry with a fresh nonce" + + # ...and the retry against that fresh challenge succeeds. + assert self.send(env, location, + self.header(location, fresh)).response["status"] == 200 + + @pytest.mark.xfail(sys.platform == "win32", reason= + "mpm_winnt child is a separate process, " + "ap_retained_data does not survive restart") + @NOT_BACKPORTED + def test_digest_091_returning_client_is_stale_even_if_its_id_was_reused(self, env): + # Same property, but now the id space has caught up: after the restart + # a new client is handed the id the returning client still quotes. + # That is not an unlikely coincidence -- the counter restarts from 1, + # so the first clients seen after a restart take exactly the ids the + # clients from before it are holding. + location = "nccheck" + self.reload(env) + challenge = self.challenge(env, location) + assert self.send(env, location, + self.header(location, challenge)).response["status"] == 200 + + self.reload(env) + + # A different client arrives first and is given the recycled id. + # Today that newcomer is handed the returning client's id, because + # the counter restarts at 1. The assertions below deliberately do not + # require it: a server which stops recycling ids satisfies this + # property by making the returning client simply unknown, which is + # the outcome under test either way. + newcomer = self.challenge(env, location) + assert self.send(env, location, + self.header(location, newcomer)).response["status"] == 200 + + r = self.send(env, location, self.header(location, challenge, "00000002")) + env.httpd_error_log.ignore_recent(lognos=[NC_FAILED, CLIENT_UNKNOWN] + + ([NONCE_INVALID] if _WIN32 else [])) + assert r.response["status"] == 401 + assert self.challenge_of(r).stale, \ + "a returning client was reported as a possible replay attack " \ + "because its id had been given to somebody else" + + @pytest.mark.xfail(sys.platform == "win32", reason= + "mpm_winnt child is a separate process, " + "ap_retained_data does not survive restart") + @NOT_BACKPORTED + def test_digest_092_onetime_nonce_from_before_a_restart_is_not_accepted(self, env): + # One-time nonces are ordered by a counter which restarts at 0 with + # the segment, while the nonce itself stays verifiable. A client + # holding an unused nonce from before the restart therefore presents + # a counter value the new server has not reached yet. + location = "onetime" + self.reload(env) + challenge = self.challenge(env, location) + for _ in range(5): + r = self.send(env, location, self.header(location, challenge)) + assert r.response["status"] == 200 + self.follow_nextnonce(r, challenge) + # challenge.nonce is now an unused nonce, well ahead of the counter + # that a restart will reset to 0. + + self.reload(env) + + newcomer = self.challenge(env, location) + r = self.send(env, location, self.header(location, newcomer)) + assert r.response["status"] == 200 + self.follow_nextnonce(r, newcomer) + + # The nonce from the previous server generation must not be usable. + stale_use = self.send(env, location, self.header(location, challenge)) + env.httpd_error_log.ignore_recent(lognos=[NC_FAILED, CLIENT_UNKNOWN, + ONETIME_REUSED] + + ([NONCE_INVALID] if _WIN32 else [])) + assert stale_use.response["status"] == 401, \ + "a one-time nonce issued before the restart was accepted after it" + + # ...and the client which legitimately owns that entry still works. + assert self.send(env, location, + self.header(location, newcomer)).response["status"] == 200, \ + "the stale nonce locked out the client holding that id" + + @pytest.mark.xfail(sys.platform == "win32", reason= + "mpm_winnt child is a separate process, " + "ap_retained_data does not survive restart") + @NOT_BACKPORTED + def test_digest_093_onetime_request_cannot_be_replayed_across_a_restart(self, env): + # The severity case. A one-time nonce is single-use because the server + # remembers the last nonce each client used -- and that memory does + # not survive a restart, while the nonce does. So an eavesdropper who + # captured one *successfully used* request needs only to wait for a + # restart: the counter it outranks has gone back to zero, and the id + # it names is handed straight back out. + location = "onetime" + self.reload(env) + challenge = self.challenge(env, location) + for _ in range(5): + r = self.send(env, location, self.header(location, challenge)) + assert r.response["status"] == 200 + captured = self.header(location, challenge) # a used request + self.follow_nextnonce(r, challenge) + + assert self.send(env, location, captured).response["status"] == 401, \ + "the captured request was not single-use before the restart" + + self.reload(env) + + # Somebody authenticates, so the recycled id names a live entry. + newcomer = self.challenge(env, location) + assert self.send(env, location, + self.header(location, newcomer)).response["status"] == 200 + + replay = self.send(env, location, captured) + env.httpd_error_log.ignore_recent(lognos=[NC_FAILED, CLIENT_UNKNOWN, + ONETIME_REUSED] + + ([NONCE_INVALID] if _WIN32 else [])) + assert replay.response["status"] == 401, \ + "a captured one-time request was replayed successfully after a restart" diff -Nru apache2-2.4.69/test/modules/aaa/test_010_eviction.py apache2-2.4.71/test/modules/aaa/test_010_eviction.py --- apache2-2.4.69/test/modules/aaa/test_010_eviction.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/test_010_eviction.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,134 @@ +"""Which client entries are discarded when the table is full. + +The client table is a fixed-size shared memory segment (AuthDigestShmemSize, +pinned small by conftest here), and gc() makes room by discarding one entry +from each bucket. An entry is allocated for any request which needs a +challenge, including one carrying no credentials at all, since the challenge +has to carry the identifier the client will be tracked by. An attacker can +therefore fill the table with bare requests as fast as it can send them. + +What that costs the clients already using the server depends on which entry +gc() picks. Discarding a client which has authenticated costs it a request: +its nonce-count and last-used nonce go with the entry, so its next request +is answered with a fresh challenge (stale=true) and has to be retried. +Discarding a client which has never authenticated costs nothing at all -- +the entry records nothing yet, and the client simply gets a new identifier +with its next challenge. + +So gc() prefers the entries which have never been used to authenticate, +which are exactly the ones a flood of unauthenticated requests creates. + +"Never used to authenticate" is last_nonce_time == 0, which is exact in both +of the configurations that track clients: an accepted request stores the +time its nonce was generated at, which is a wall-clock time for a nonce with +a lifetime and a counter which starts at 1 for a one-time nonce. Neither is +ever 0. The tests below run against both to pin that. +""" + +import sys + +import pytest + +from pyhttpd.backport import not_backported +from . import digest_client as dc +from .env import AAATestEnv + +NC_FAILED = "AH01774" +NONCE_INVALID = "AH01776" +CLIENT_UNKNOWN = "AH10618" +_WIN32 = sys.platform == "win32" + +# A location tracking clients for the nonce-count, and one tracking them for +# one-time nonces: the two put different kinds of value in last_nonce_time. +BOTH = ["nccheck", "onetime"] + +# on the tests that fail without it (the others pass there as well) +NOT_BACKPORTED = not_backported("r1937713", "evicting never-authenticated clients first") + + +class TestDigestEviction: + + def url(self, env, location): + return env.mkurl("http", "aaa", f"/digest/{location}/secret.txt") + + def uri(self, location): + return f"/digest/{location}/secret.txt" + + def challenge(self, env, location): + r = env.curl_get(self.url(env, location)) + assert r.response["status"] == 401 + return dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + + def header(self, location, challenge, nc="00000001", cnonce="eviction-cnonce"): + return dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri=self.uri(location), nc=nc, cnonce=cnonce) + + def send(self, env, location, auth): + return env.curl_get(self.url(env, location), + options=["-H", f"Authorization: {auth}"]) + + def advance(self, r, challenge): + """Prepare the client's next request, and return the nc to send with + it. A one-time nonce is replaced by the nextnonce just handed out, + and the count restarts at 1 for it; otherwise the nonce stays and the + count goes up.""" + ai = r.response["header"].get("authentication-info") + if ai: + params = dc.parse_params(ai) + if "nextnonce" in params: + challenge.nonce = params["nextnonce"] + return "00000001" + return "00000002" + + def flood(self, env, location, count): + """Bare requests, each of which allocates a client entry.""" + for _ in range(count): + env.curl_get(self.url(env, location)) + + @pytest.mark.xfail(sys.platform == "win32", reason= + "mpm_winnt child is a separate process, " + "ap_retained_data does not survive restart") + @pytest.mark.parametrize("location", BOTH) + @NOT_BACKPORTED + def test_digest_100_authenticated_client_survives_a_flood(self, env, location): + # The legitimate client authenticates, so its entry now records the + # nonce it used. + challenge = self.challenge(env, location) + r = self.send(env, location, self.header(location, challenge)) + assert r.response["status"] == 200 + nc = self.advance(r, challenge) + + # An attacker fills the table several times over with requests which + # carry no credentials at all. + self.flood(env, location, 60) + + # The victim carries on. Its entry must still be there: it is the + # only one in the table which is worth keeping. + r = self.send(env, location, self.header(location, challenge, nc)) + env.httpd_error_log.ignore_recent(lognos=[NC_FAILED, CLIENT_UNKNOWN] + + ([NONCE_INVALID] if _WIN32 else [])) + assert r.response["status"] == 200, \ + "a flood of unauthenticated requests evicted an authenticated client" + + @pytest.mark.parametrize("location", BOTH) + def test_digest_101_unused_entries_are_the_ones_discarded(self, env, location): + # The counterpart: the entries a flood creates are themselves the + # ones discarded, so a flood cannot fill the table permanently. The + # opaque handed out at the start of one no longer names an entry by + # the end, which the server reports by minting a new one rather than + # echoing it back -- and as a stale challenge, since nothing was + # wrong with the client's credentials. + first = self.challenge(env, location) + self.flood(env, location, 60) + + r = self.send(env, location, self.header(location, first)) + env.httpd_error_log.ignore_recent(lognos=[NC_FAILED, CLIENT_UNKNOWN] + + ([NONCE_INVALID] if _WIN32 else [])) + assert r.response["status"] == 401 + again = dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + assert again.opaque != first.opaque, \ + "the unused entry should have been discarded by the flood" + assert again.stale, \ + "a client whose unused entry was discarded should be re-challenged " \ + "as stale, not told its authentication failed" diff -Nru apache2-2.4.69/test/modules/aaa/test_011_authinfo_errors.py apache2-2.4.71/test/modules/aaa/test_011_authinfo_errors.py --- apache2-2.4.69/test/modules/aaa/test_011_authinfo_errors.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/test_011_authinfo_errors.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,93 @@ +"""Authentication-Info survives an error response. + +The header is added at fixups, so unless it goes into err_headers_out it +is dropped when the response turns into an error, costing a one-time-nonce +client the nextnonce and forcing a stale re-challenge. A 404 for a missing +file inside the protected area reaches the error path while fully +authenticated, so it exercises this without a handler like mod_dav. +""" + +from pyhttpd.backport import not_backported +from . import digest_client as dc +from .env import AAATestEnv + +# on the tests that fail without it (the others pass there as well) +NOT_BACKPORTED = not_backported("r1937888", "Authentication-Info on error responses") + + +class TestAuthInfoOnError: + + def url(self, env, location, path): + return env.mkurl("http", "aaa", f"/digest/{location}/{path}") + + def uri(self, location, path): + return f"/digest/{location}/{path}" + + def challenge(self, env, location, path="secret.txt"): + r = env.curl_get(self.url(env, location, path)) + assert r.response["status"] == 401 + return dc.DigestChallenge.parse(r.response["header"]["www-authenticate"]) + + def auth(self, env, location, path, challenge, nc="00000001"): + return dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri=self.uri(location, path), nc=nc, + cnonce="authinfo-cnonce") + + def get(self, env, location, path, challenge, nc="00000001"): + return env.curl_get(self.url(env, location, path), options=[ + "-H", f"Authorization: {self.auth(env, location, path, challenge, nc)}"]) + + @NOT_BACKPORTED + def test_digest_110_error_response_carries_authentication_info(self, env): + # A 404 for an authenticated request still confirms the response to + # the client: Authentication-Info with a valid rspauth. + location = "default" + challenge = self.challenge(env, location) + r = self.get(env, location, "no-such-file.txt", challenge) + assert r.response["status"] == 404 + assert "authentication-info" in r.response["header"], \ + "the error response dropped Authentication-Info" + ai = dc.parse_params(r.response["header"]["authentication-info"]) + expect = dc.rspauth_digest( + dc.ha1(AAATestEnv.DIGEST_USER, AAATestEnv.REALM, + AAATestEnv.DIGEST_PASSWORD), + challenge.nonce, "00000001", "authinfo-cnonce", "auth", + self.uri(location, "no-such-file.txt")) + assert ai.get("rspauth") == expect, "rspauth wrong on the error response" + + @NOT_BACKPORTED + def test_digest_111_onetime_client_continues_after_an_error(self, env): + # Under one-time nonces the error must still hand back a nextnonce, + # or the client's next request is stale-challenged. Prove the client + # can carry straight on to a real request with what the 404 gave it. + location = "onetime" + challenge = self.challenge(env, location) + r = self.get(env, location, "no-such-file.txt", challenge) + assert r.response["status"] == 404 + assert "authentication-info" in r.response["header"], \ + "the one-time-nonce error response dropped Authentication-Info" + ai = dc.parse_params(r.response["header"]["authentication-info"]) + assert "nextnonce" in ai, "no nextnonce to continue with after the error" + + challenge.nonce = ai["nextnonce"] + ok = self.get(env, location, "secret.txt", challenge) + assert ok.response["status"] == 200, \ + "the nextnonce from the error response was not usable" + + def test_digest_112_challenge_has_no_authentication_info(self, env): + # When the error is itself a 401, the challenge stands alone: the + # client is not handed a nextnonce it could not use beside a fresh + # nonce. A wrong password reaches note_digest_auth_failure. + location = "onetime" + challenge = self.challenge(env, location) + bad = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, "wrong-password", + method="GET", uri=self.uri(location, "secret.txt"), + cnonce="authinfo-cnonce") + r = env.curl_get(self.url(env, location, "secret.txt"), + options=["-H", f"Authorization: {bad}"]) + env.httpd_error_log.ignore_recent(lognos=["AH01794"]) # password mismatch + assert r.response["status"] == 401 + assert "authentication-info" not in r.response["header"], \ + "a 401 challenge carried an Authentication-Info header" diff -Nru apache2-2.4.69/test/modules/aaa/test_012_errordoc.py apache2-2.4.71/test/modules/aaa/test_012_errordoc.py --- apache2-2.4.69/test/modules/aaa/test_012_errordoc.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/test_012_errordoc.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,40 @@ +"""An ErrorDocument redirecting into a Digest-protected location. + +A request which fails header validation -- e.g. an HTTP/1.1 request with no +Host header -- is rejected in ap_check_request_header, before +ap_post_read_request runs. The Digest module sets up its per-request record +in a post_read_request hook, so that record was never created for such a +request. + +If ErrorDocument then redirects the failure into a Digest-protected +location, the auth hook runs for the redirect and walks back along ->prev +to the original request to find the shared record -- which is NULL here. +Dereferencing it crashes the child, so an unauthenticated client can take +down a worker with a single malformed request. +""" + +from pyhttpd.backport import skip_not_backported +from .env import AAATestEnv + +pytestmark = skip_not_backported("r1939032", "no Digest record for a request failing header validation") + + +class TestDigestErrorDocument: + + def test_digest_125_errordoc_into_digest_without_prr(self, env): + # Send an HTTP/1.1 request with the Host header removed; the server + # answers 400 before the Digest post_read_request hook runs, and the + # ErrorDocument points into a Digest-protected location. + url = env.mkurl("http", "aaa", "/anything") + r = env.curl_get(url, options=["--http1.1", "-H", "Host:"]) + + # The request must get an HTTP response, not a reset connection from + # a crashed child. + assert r.exit_code == 0, \ + f"no HTTP response (curl exit {r.exit_code}): the worker likely " \ + f"crashed handling the ErrorDocument" + assert r.response["status"] in (400, 401) + + # The server is still serving afterwards. + r = env.curl_get(env.mkurl("http", "aaa", "/digest/default/secret.txt")) + assert r.response["status"] == 401 diff -Nru apache2-2.4.69/test/modules/aaa/test_012_truncated_nonce.py apache2-2.4.71/test/modules/aaa/test_012_truncated_nonce.py --- apache2-2.4.69/test/modules/aaa/test_012_truncated_nonce.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/test_012_truncated_nonce.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,29 @@ +import re + +import pytest + +from . import digest_client as dc +from .env import AAATestEnv + +class TestDigestTruncatedNonce: + + def test_digest_012_01(self, env): + url = env.mkurl("http", "aaa", "/digest/default/secret.txt") + r1 = env.curl_get(url) + assert r1.response["status"] == 401 + challenge = dc.DigestChallenge.parse( + r1.response["header"]["www-authenticate"]) + # Truncate: take from the '=' onward, removing most of the base64 time prefix. + eq_pos = challenge.nonce.index("=") + truncated_nonce = challenge.nonce[eq_pos:] + + auth_header = dc.build_authorization( + AAATestEnv.DIGEST_USER, challenge, AAATestEnv.DIGEST_PASSWORD, + method="GET", uri="/digest/default/secret.txt", + nonce_val=truncated_nonce) + + start_pos = env.httpd_error_log.current_pos() + r2 = env.curl_get(url, options=['-H', f'Authorization: {auth_header}']) + assert r2.response["status"] == 401 + + env.httpd_error_log.ignore_recent(lognos=["AH01782"]) diff -Nru apache2-2.4.69/test/modules/aaa/test_013_proxy_domain.py apache2-2.4.71/test/modules/aaa/test_013_proxy_domain.py --- apache2-2.4.69/test/modules/aaa/test_013_proxy_domain.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/aaa/test_013_proxy_domain.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,28 @@ +"""AuthDigestDomain on a reverse-proxied location. + +Digest auth can run at a reverse proxy, for a location mapped by ProxyPass. +Such a request carries PROXYREQ_REVERSE, but its 401 is an ordinary +origin-style WWW-Authenticate (the client authenticates to this server, not +through it), so the configured AuthDigestDomain belongs in the challenge +just as for a non-proxied location. + +The suppression test treated any proxy request alike and dropped domain= +for the reverse-proxied case, so clients re-authenticated per URI prefix +and sent the Authorization header outside the intended protection space. +""" + +from pyhttpd.backport import not_backported +from . import digest_client as dc + +pytestmark = not_backported("r1939035", "domain= for reverse-proxied requests") + + +class TestDigestProxyDomain: + + def test_digest_130_domain_sent_for_reverse_proxy(self, env): + r = env.curl_get(env.mkurl("http", "aaa", "/pxdomain/secret.txt")) + assert r.response["status"] == 401 + challenge = dc.DigestChallenge.parse( + r.response["header"]["www-authenticate"]) + assert "/pxdomain/" in challenge.domain_list(), \ + "AuthDigestDomain was dropped from a reverse-proxied challenge" diff -Nru apache2-2.4.69/test/modules/arch/linux/README apache2-2.4.71/test/modules/arch/linux/README --- apache2-2.4.69/test/modules/arch/linux/README 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/arch/linux/README 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,109 @@ +mod_systemd tests +================= + +What is different about this module +----------------------------------- +mod_systemd has no directives. Everything it does is driven by the +environment httpd was started with, and almost everything it produces goes +to the service manager rather than to a client: + + - sd_notify(3) datagrams sent to $NOTIFY_SOCKET at four points: reading + the configuration (pre_config), configuration loaded (post_config), + the MPM starting (pre_mpm, the only notification carrying MAINPID), + and a status line refreshed by the monitor hook. + - listening sockets inherited from the service manager, found through + $LISTEN_FDS. mod_systemd registers the two optional functions + server/listen.c calls, so loading the module is what enables socket + activation and not loading it is what disables it. + - ap_extended_status forced on in pre_config, so that the monitor hook + has request counts to report. + - the watchdog keep-alive, WATCHDOG=1, sent while the service manager + asks for one. Which it does through the environment as well: + WatchdogSec= in the unit becomes $WATCHDOG_USEC, and $WATCHDOG_PID + names the process expected to report. + +None of that is observable over HTTP, so the tests observe it directly. + +How the tests run without systemd, and without privileges +--------------------------------------------------------- +There is no need for a service manager to exercise the protocol. Only +test_005, the last test of test_006, and test_007 involve systemd at all; +the rest run anywhere, and need nothing from the systemd package beyond the +libsystemd httpd itself is linked against. + + test_001_notify.py $NOTIFY_SOCKET is an ordinary AF_UNIX datagram + socket the test binds itself (env.NotifyListener). + sd_notify writes to whatever that variable names, so + the test reads httpd's notifications straight off the + socket. HttpdTestEnv.set_httpd_env() puts the + variable into the environment apachectl passes on. + + test_002_monitor.py The periodic status line. ap_run_monitor() is + called once every ten turns of the parent's ~1s + loop, so these tests wait up to 25 seconds. + + test_003_extended_status.py + The ExtendedStatus side effect, observed through + mod_status. + + test_004_socket_activation.py + The test opens the listening socket itself and + hands it to httpd as descriptor 3 with LISTEN_FDS + and LISTEN_PID set, which is the whole protocol. + systemd-socket-activate would do the same, but its + --now option is newer than the systemd on some + distributions, and doing it directly needs no + systemd tooling at all. + + test_006_watchdog.py The keep-alive. Mostly the same stand-in socket, + with $WATCHDOG_USEC set in the environment httpd is + started with; env.ForegroundServer runs httpd in the + foreground where $WATCHDOG_PID can name it, which + apachectl cannot do for a parent it has not forked + yet. The last test uses a real unit with + WatchdogSec=, where systemd's own + WatchdogTimestampMonotonic is the evidence that the + pings arrived. + + The keep-alive is sent from the monitor hook, so it + is only as frequent as that hook is: about every ten + seconds. A WatchdogSec shorter than twice that + cannot be met, and mod_systemd says so (AH10621) + rather than letting a healthy server be killed. + + test_005_service.py The real thing: a transient Type=notify unit run + with "systemd-run --user". This is what checks that + systemd holds the unit in "activating" until READY=1 + arrives, tracks the right MainPID, and shows the + reported STATUS= as the unit's status text. + Skipped when the user has no systemd manager. + + test_007_notify_reload.py + The reload protocol, as a transient + Type=notify-reload unit: that "systemctl reload" + waits for the new configuration to be in use, that a + configuration which does not parse fails the reload + without disturbing the running server, and that a + reload restarts it gracefully and only once. Also + skipped where systemd is older than 253, which is + where Type=notify-reload arrived. + +The whole package is skipped unless mod_systemd was built, which needs +configure --enable-systemd. A static module is enough for everything +except the test which has to leave mod_systemd out of the configuration; +that one needs --enable-systemd=shared. + +Running the systemd suites where there is no user session +--------------------------------------------------------- +"systemctl --user" needs a per-user manager, which a login session has but +a CI container does not; enabling lingering needs privileges. Where that +is a problem, run the tests inside a container with systemd as pid 1, +which rootless podman supports on a cgroup v2 host: + + podman run --rm -it --systemd=always \ + -v $PWD:/src:z -w /src registry.fedoraproject.org/fedora:latest \ + /usr/sbin/init + +then, in another terminal, "podman exec" into it and run pytest as a +non-root user with XDG_RUNTIME_DIR set. The other four suites need none of +this and run anywhere. diff -Nru apache2-2.4.69/test/modules/arch/linux/conftest.py apache2-2.4.71/test/modules/arch/linux/conftest.py --- apache2-2.4.69/test/modules/arch/linux/conftest.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/arch/linux/conftest.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,41 @@ +import logging +import os +import sys + +import pytest + +sys.path.append(os.path.join(os.path.dirname(__file__), '../../..')) + +from .env import SystemdTestEnv + + +def pytest_report_header(config, start_path): + env = SystemdTestEnv() + return f"mod_systemd [apache: {env.get_httpd_version()}, " \ + f"mpm: {env.mpm_module}, {env.prefix}]" + + +@pytest.fixture(scope="package") +def env(pytestconfig) -> SystemdTestEnv: + level = logging.INFO + console = logging.StreamHandler() + console.setLevel(level) + console.setFormatter(logging.Formatter('%(levelname)s: %(message)s')) + logging.getLogger('').addHandler(console) + logging.getLogger('').setLevel(level=level) + env = SystemdTestEnv(pytestconfig=pytestconfig) + env.require(env.has_systemd_module, "mod_systemd is not built, configure with --enable-systemd", + dep="mod_systemd", + detected=f"not found in {env.libexec_dir}") + env.setup_httpd() + env.apache_access_log_clear() + env.httpd_error_log.clear_log() + env.start_notify_listener() + yield env + env.stop_notify_listener() + + +@pytest.fixture(autouse=True, scope="package") +def _stop_package_scope(env): + yield + assert env.apache_stop() == 0 diff -Nru apache2-2.4.69/test/modules/arch/linux/env.py apache2-2.4.71/test/modules/arch/linux/env.py --- apache2-2.4.69/test/modules/arch/linux/env.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/arch/linux/env.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,638 @@ +import inspect +import logging +import os +import re +import shutil +import signal +import socket +import subprocess +import threading +import time +from typing import Callable, Dict, List, Optional + +from pyhttpd.env import HttpdTestEnv, HttpdTestSetup + +log = logging.getLogger(__name__) + + +class SystemdTestSetup(HttpdTestSetup): + + def __init__(self, env: 'HttpdTestEnv'): + super().__init__(env=env) + self.add_source_dir(os.path.dirname(inspect.getfile(SystemdTestSetup))) + # mod_systemd is only built with --enable-systemd, so it must not be + # a hard requirement; the tests skip when it is absent. + self.add_optional_modules(["systemd"]) + + +class SystemdTestEnv(HttpdTestEnv): + + def __init__(self, pytestconfig=None): + super().__init__(pytestconfig=pytestconfig) + self.add_httpd_log_modules(["core"]) + self._notify = None + + def setup_httpd(self, setup: HttpdTestSetup = None): + super().setup_httpd(setup=SystemdTestSetup(env=self)) + + @property + def systemd_is_dso(self) -> bool: + return os.path.isfile(os.path.join(self.libexec_dir, 'mod_systemd.so')) + + @property + def has_systemd_module(self) -> bool: + """Whether mod_systemd is available, however it was built: + --enable-systemd links it statically, --enable-systemd=shared + builds the DSO.""" + if self.systemd_is_dso: + return True + p = subprocess.run([self.httpd_bin, '-l'], capture_output=True, + text=True) + return re.search(r'^\s+mod_systemd\.c$', p.stdout, re.M) is not None + + @property + def notify(self) -> 'NotifyListener': + """The stand-in notification socket httpd reports to.""" + return self._notify + + def start_notify_listener(self) -> 'NotifyListener': + """Bind the notification socket and point httpd's $NOTIFY_SOCKET at it. + + This must happen before the server is first started, since libsystemd + reads $NOTIFY_SOCKET from the environment of the httpd process. + """ + assert self._notify is None + self._notify = NotifyListener( + os.path.join(self.server_dir, 'systemd-notify.sock')) + self.set_httpd_env('NOTIFY_SOCKET', self._notify.path) + return self._notify + + def stop_notify_listener(self): + if self._notify is not None: + self._notify.close() + self._notify = None + + def server_env(self) -> Dict[str, str]: + """The environment httpd is started with, as apachectl gets it.""" + return self._clean_path_env() + + @property + def httpd_bin(self) -> str: + return os.path.join(self.bin_dir, 'httpd') + + +class NotifyListener: + """A stand-in for the systemd notification socket. + + sd_notify(3) does nothing more than send a datagram to the AF_UNIX + socket named by $NOTIFY_SOCKET, so an unconnected datagram socket is + enough to observe everything mod_systemd reports, with no systemd + instance and no privileges involved. + + Datagrams are drained by a background thread so that the periodic + notifications from the monitor hook cannot fill the socket buffer + while a test is doing something else. + """ + + def __init__(self, path: str): + # sockaddr_un is limited to 108 bytes; well within reach for a + # source tree in a home directory, but check rather than fail + # obscurely inside bind(). + assert len(path) < 100, f"notification socket path too long: {path}" + if os.path.exists(path): + os.unlink(path) + self.path = path + self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) + self._sock.bind(path) + self._sock.settimeout(0.1) + self._lock = threading.Lock() + self._messages: List[Dict[str, str]] = [] + self._stop = threading.Event() + self._thread = threading.Thread(target=self._drain, daemon=True) + self._thread.start() + + def _drain(self): + while not self._stop.is_set(): + try: + data = self._sock.recv(8192) + except socket.timeout: + continue + except OSError: + break + try: + msg = self.parse(data) + except Exception as ex: + # Never let one unreadable datagram stop the listener: the + # tests would then see silence rather than a failure. + log.warning(f"undecodable notification {data!r}: {ex}") + continue + log.debug(f"notify: {msg}") + with self._lock: + self._messages.append(msg) + + def close(self): + self._stop.set() + self._thread.join(timeout=2) + self._sock.close() + if os.path.exists(self.path): + os.unlink(self.path) + + @staticmethod + def parse(data: bytes) -> Dict[str, str]: + """Split one notification datagram into its NAME=VALUE assignments. + + The last line carries no trailing newline in the MAINPID + notification, and a value may itself contain '='. + """ + msg = {} + for line in data.decode(errors='replace').split('\n'): + name, sep, value = line.partition('=') + if sep: + msg[name] = value + return msg + + @property + def messages(self) -> List[Dict[str, str]]: + with self._lock: + return list(self._messages) + + def clear(self): + with self._lock: + self._messages.clear() + + def wait_for(self, match: Callable[[Dict[str, str]], bool], + timeout: float = 5.0) -> Optional[Dict[str, str]]: + """Return the first message satisfying `match`, waiting for it to + arrive if it has not already. Returns None on timeout.""" + end = time.time() + timeout + seen = 0 + while True: + with self._lock: + pending = self._messages[seen:] + seen = len(self._messages) + for msg in pending: + if match(msg): + return msg + if time.time() >= end: + return None + time.sleep(0.05) + + def wait_for_key(self, key: str, timeout: float = 5.0) \ + -> Optional[Dict[str, str]]: + return self.wait_for(lambda m: key in m, timeout=timeout) + + def wait_for_status(self, pattern: str, timeout: float = 5.0) \ + -> Optional[Dict[str, str]]: + rx = re.compile(pattern) + return self.wait_for(lambda m: 'STATUS' in m and rx.search(m['STATUS']), + timeout=timeout) + + def statuses(self) -> List[str]: + return [m['STATUS'] for m in self.messages if 'STATUS' in m] + + +# The STATUS= line the monitor hook reports, from systemd_monitor() in +# modules/arch/unix/mod_systemd.c. Idle and busy are the percentages +# ap_get_sload() computes, and are -1 when there are no workers at all. +MONITOR_STATUS = re.compile( + r'^Total requests: (?P\d+);\s*' + r'Idle/Busy workers (?P-?\d+)%/(?P-?\d+)%;\s*' + r'Requests/sec: (?P\S+);\s*' + r'Bytes served/sec: (?P.*)B/sec$') + +# ap_run_monitor() is called every INTERVAL_OF_WRITABLE_PROBES (10) turns of +# the ~1s parent loop in ap_wait_or_timeout(), so a status update is up to +# roughly 10 seconds away. Waiting for one costs that; waiting to be sure +# none is coming costs the whole timeout, so keep it to a small multiple. +MONITOR_TIMEOUT = 25.0 + +# Showing that no report is coming costs the whole wait, so it only has to +# comfortably outlast one turn of that cycle. +NO_MONITOR_TIMEOUT = 15.0 + + +# The keep-alive notification is sent from the same monitor hook, so waiting +# for one costs the same as waiting for a status report. +WATCHDOG_TIMEOUT = MONITOR_TIMEOUT + +# The shortest WatchdogSec mod_systemd will accept without complaining, which +# is twice the monitor interval: the recommended keep-alive period is half the +# watchdog timeout, and half of anything shorter than this is out of reach of a +# hook which runs every ten seconds. Keep in step with mod_systemd.c. +SYSTEMD_MONITOR_INTERVAL = 10 +MIN_WATCHDOG_SEC = 2 * SYSTEMD_MONITOR_INTERVAL + + +def is_watchdog_ping(msg: Dict[str, str]) -> bool: + """Whether a notification carries the keep-alive ping. + + mod_systemd is free to send WATCHDOG=1 in a datagram of its own or + alongside whatever else it is reporting, so match on the assignment + rather than on the message being only that. + """ + return msg.get('WATCHDOG') == '1' + + +# A configuration for a server run directly rather than through apachectl, +# sharing the server root, module list and error log with the rest of the +# suite but with its own pid file and port. +STANDALONE_CONF = """ +ServerRoot "${server_dir}" +DefaultRuntimeDir logs +PidFile "${pidfile}" +Include "conf/${modules_conf}" +ServerName standalone.test +ErrorLog "logs/error_log" +LogLevel ${loglevel} +DocumentRoot "${server_dir}/htdocs" + + Require all granted + + + SSLSessionCache "shmcb:ssl_gcache_data(32000)" + +${extra} +Listen ${port} +""" + + +def write_server_conf(env: SystemdTestEnv, name: str, port: int, + modules_conf: str = 'modules.conf', + extra: str = '') -> str: + """Write a standalone configuration and return its path.""" + path = os.path.join(env.server_conf_dir, f'{name}.conf') + with open(path, 'w') as fd: + fd.write(STANDALONE_CONF + .replace('${server_dir}', env.server_dir) + .replace('${pidfile}', + os.path.join(env.server_logs_dir, f'{name}.pid')) + .replace('${loglevel}', 'debug' if env.verbosity else 'warn') + .replace('${modules_conf}', modules_conf) + .replace('${extra}', extra) + .replace('${port}', str(port))) + return path + + +def http_responds(port: int, timeout: float = 2.0) -> bool: + """One HTTP request, without curl, so that a listening socket which + nothing is serving cannot be mistaken for a running server: under socket + activation the listener exists before httpd does.""" + try: + with socket.create_connection(('127.0.0.1', port), 1.0) as c: + c.settimeout(timeout) + c.sendall(b'GET / HTTP/1.0\r\nHost: standalone.test\r\n\r\n') + return c.recv(64).startswith(b'HTTP/1.') + except OSError: + return False + + +def error_log_size(env: SystemdTestEnv) -> int: + """Where the shared error log ends now, so that a later read can take + only what one operation wrote.""" + try: + return os.path.getsize(env.httpd_error_log.path) + except OSError: + return 0 + + +def error_log_since(env: SystemdTestEnv, pos: int) -> str: + """The error log written since error_log_size() returned pos.""" + try: + with open(env.httpd_error_log.path, errors='replace') as fd: + fd.seek(pos) + return fd.read() + except OSError: + return '' + + +class ActivatedServer: + """An httpd handed a listening socket the way a service manager does. + + The protocol is only $LISTEN_FDS descriptors starting at 3, and + $LISTEN_PID naming the process they were meant for, so the test opens + the socket and speaks it directly. systemd-socket-activate would do + the same, but its --now option is too recent to rely on, and this + needs no systemd tooling at all. + + apachectl cannot pass descriptors, so httpd is run directly, in the + foreground: LISTEN_PID has to be the process which calls + sd_listen_fds(), and a daemonised parent would not be it. + """ + + def __init__(self, env: SystemdTestEnv, port: int, name: str = 'activate', + extra: str = '', listen_port: int = None, + modules_conf: str = 'modules.conf'): + self.env = env + self.port = port + # The port systemd-socket-activate binds, which is the same as the + # configured one unless a test wants them to disagree. + self.listen_port = port if listen_port is None else listen_port + self.name = name + self.pid_file = os.path.join(env.server_logs_dir, f'{name}.pid') + self.proc = None + self.stdout = None + self.stderr = None + self.conf_file = write_server_conf(env, name, port, + modules_conf=modules_conf, + extra=extra) + + @staticmethod + def modules_conf_without(env: SystemdTestEnv, module: str) -> str: + """Write a copy of the generated module list with one module left + out, to check what happens when it is not loaded.""" + name = f'modules-no-{module}.conf' + src = os.path.join(env.server_conf_dir, 'modules.conf') + rx = re.compile(rf'^\s*LoadModule\s+{module}_module\b') + with open(src) as fd: + lines = [l for l in fd if not rx.match(l)] + with open(os.path.join(env.server_conf_dir, name), 'w') as fd: + fd.writelines(lines) + return name + + def args(self, fd: int) -> List[str]: + # The shell moves the inherited socket to descriptor 3 and names + # itself in LISTEN_PID before exec'ing httpd in its place, which is + # the one thing this cannot do from the parent: the pid has to be + # the one which will call sd_listen_fds(). bash rather than sh + # because dash parses only one digit in a redirection, and the + # socket lands well above descriptor 9. + return [ + 'bash', '-c', + f'exec 3<&{fd}; export LISTEN_FDS=1 LISTEN_FDNAMES=activate ' + 'LISTEN_PID=$$; exec "$0" "$@"', + self.env.httpd_bin, '-DFOREGROUND', + '-d', self.env.server_dir, '-f', self.conf_file, + ] + + def start(self) -> 'ActivatedServer': + lsock = socket.create_server(('', self.listen_port), + family=socket.AF_INET6, + dualstack_ipv6=True, backlog=128) + try: + # A new session so that the whole group can be signalled on the + # way out. + self.proc = subprocess.Popen( + self.args(lsock.fileno()), env=self.env.server_env(), + start_new_session=True, pass_fds=(lsock.fileno(),), + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + finally: + # The child holds it now; keeping a copy here would leave the + # port bound after the server is gone. + lsock.close() + return self + + def _reap(self): + """Collect the output of a server which has exited.""" + if self.stderr is None and self.proc.poll() is not None: + self.stdout, self.stderr = self.proc.communicate() + + def wait_exit(self, timeout: float = 10.0) -> int: + """Wait for a server which is expected to fail to start.""" + self.stdout, self.stderr = self.proc.communicate(timeout=timeout) + return self.proc.returncode + + def is_live(self, timeout: float = 10.0) -> bool: + end = time.time() + timeout + while True: + if self.proc.poll() is not None: + # Collect its diagnostics, so that a test reporting the + # server did not come up can say why. + self._reap() + return False + if http_responds(self.port): + return True + if time.time() >= end: + return False + time.sleep(0.2) + + def is_running(self, settle: float = 2.0) -> bool: + """Whether the server is still up once it has had time to fail. + + A restart which cannot find its sockets takes a few milliseconds to + bring the parent down, and its old children go on serving after it, + so neither an immediate check nor a request proves anything. + """ + end = time.time() + settle + while time.time() < end: + if self.proc.poll() is not None: + return False + time.sleep(0.1) + return True + + def reload(self) -> int: + """Ask the running server to restart gracefully.""" + r = self.env.run([self.env.httpd_bin, '-d', self.env.server_dir, + '-f', self.conf_file, '-k', 'graceful'], + env=self.env.server_env()) + return r.exit_code + + def _signal_group(self, sig: int) -> bool: + """Signal every process still in the server's group, reporting + whether any remained. start_new_session() made the process we + launched the group leader, so its pid is the group id whether or + not it is still alive.""" + try: + os.killpg(self.proc.pid, sig) + return True + except OSError: + return False + + def stop(self): + if self.proc is None: + return + self._signal_group(signal.SIGTERM) + if self.proc.poll() is None: + try: + self.stdout, self.stderr = self.proc.communicate(timeout=10) + except subprocess.TimeoutExpired: + self._signal_group(signal.SIGKILL) + self.stdout, self.stderr = self.proc.communicate() + # A parent which died during a failed restart leaves its children + # behind, still holding the listening socket and still answering. + # They have to go too, or the next test finds the port taken. + end = time.time() + 5 + while self._signal_group(0): + if time.time() >= end: + self._signal_group(signal.SIGKILL) + break + time.sleep(0.1) + + def __enter__(self) -> 'ActivatedServer': + return self.start() + + def __exit__(self, *args): + self.stop() + + +# Type=notify-reload and ReloadSignal= both arrived in systemd 253. An +# unrecognised Type= does not degrade to anything, it stops the unit loading +# at all, so there is nothing to fall back to and the tests are skipped. +NOTIFY_RELOAD_VERSION = 253 + + +def systemd_version() -> int: + """The version of the systemd on this host, or 0 if it cannot be asked. + "systemctl --version" opens with "systemd 259 (259.8-1.fc44)".""" + try: + p = subprocess.run(['systemctl', '--version'], capture_output=True, + text=True, timeout=15) + except (OSError, subprocess.TimeoutExpired): + return 0 + m = re.match(r'systemd (\d+)', p.stdout) + return int(m.group(1)) if m else 0 + + +class TransientService: + """httpd run as a real transient systemd unit, with systemd-run. + + This is the only harness here which exercises the notification protocol + against systemd itself rather than a stand-in socket: systemd provides + NOTIFY_SOCKET, holds the service in "activating" until READY=1 arrives, + tracks MAINPID, and shows the reported STATUS= as the unit's status + text. It needs a per-user service manager, which a login session has + but a bare CI container does not. + + service_type selects what is exercised: "notify" for startup and + shutdown, "notify-reload" for the reload protocol on top of them. + """ + + def __init__(self, env: SystemdTestEnv, port: int, + name: str = None, extra: str = '', + service_type: str = 'notify', exec_reload: bool = True, + properties: List[str] = None): + self.env = env + self.port = port + self.unit = name or f'httpd-test-{os.getpid()}' + self.service_type = service_type + self.exec_reload = exec_reload + self.conf_file = write_server_conf(env, 'transient', port, extra=extra) + self.pid_file = os.path.join(env.server_logs_dir, 'transient.pid') + # Extra --property arguments for the unit, such as WatchdogSec= or + # ReloadSignal=. + self.properties = list(properties or []) + + def read_pid(self) -> Optional[int]: + try: + with open(self.pid_file) as fd: + return int(fd.read().strip()) + except (OSError, ValueError): + return None + + @staticmethod + def is_available() -> bool: + """Whether this user has a systemd manager to run services under.""" + if shutil.which('systemd-run') is None: + return False + if not os.environ.get('XDG_RUNTIME_DIR'): + return False + # Talking to the manager at all is the test: without a login + # session, or with lingering off, there is none to talk to. + try: + return subprocess.run(['systemctl', '--user', 'show', '-p', + 'Version'], capture_output=True, + timeout=15).returncode == 0 + except (OSError, subprocess.TimeoutExpired): + return False + + def systemctl(self, *args, + timeout: float = 60.0) -> subprocess.CompletedProcess: + return subprocess.run(['systemctl', '--user', *args], + capture_output=True, text=True, timeout=timeout) + + def show(self, prop: str) -> str: + r = self.systemctl('show', '-p', prop, '--value', f'{self.unit}.service') + return r.stdout.strip() + + def start(self, timeout: float = 20.0) -> subprocess.CompletedProcess: + httpd = self.env.httpd_bin + props = [ + f'--service-type={self.service_type}', + '--property=KillMode=mixed', + # A reload which is never reported finished holds the job open + # until this elapses, so keep it to the same bound as the start. + f'--property=TimeoutStartSec={int(timeout)}', + ] + if self.exec_reload: + props.append(f'--property=ExecReload={httpd} ' + f'-d {self.env.server_dir} ' + f'-f {self.conf_file} -k graceful') + props += [f'--property={p}' for p in self.properties] + r = subprocess.run([ + 'systemd-run', '--user', '--collect', '--quiet', + '--unit', self.unit, *props, + httpd, '-DFOREGROUND', + '-d', self.env.server_dir, '-f', self.conf_file, + ], capture_output=True, text=True, timeout=timeout) + return r + + def reload(self, timeout: float = 60.0) -> subprocess.CompletedProcess: + """Ask the manager to reload the unit. Under Type=notify-reload + this returns once the server has reported the reload finished; + under Type=notify, as soon as ExecReload= has exited.""" + return self.systemctl('reload', f'{self.unit}.service', + timeout=timeout) + + def rewrite_conf(self, extra: str = ''): + """Replace the configuration the unit reads on its next reload. + The path does not change, so ExecStart= and ExecReload= still name + it.""" + self.conf_file = write_server_conf(self.env, 'transient', self.port, + extra=extra) + + def wait_active(self, timeout: float = 20.0) -> bool: + end = time.time() + timeout + while time.time() < end: + if self.show('ActiveState') == 'active': + return True + time.sleep(0.2) + return False + + def stop(self): + self.systemctl('stop', f'{self.unit}.service') + self.systemctl('reset-failed', f'{self.unit}.service') + + def __enter__(self) -> 'TransientService': + return self + + def __exit__(self, *args): + self.stop() + + +class ForegroundServer(ActivatedServer): + """httpd run directly in the foreground, with extra environment. + + The watchdog protocol is keyed to a process id: sd_watchdog_enabled(3) + ignores $WATCHDOG_USEC unless $WATCHDOG_PID is unset or names the + process reading it. apachectl cannot be used to set that, since the + variable has to name the parent httpd and the pid is not known until it + exists, so the value is assigned in a shell which then exec's httpd in + its own place -- the same trick ActivatedServer uses for $LISTEN_PID. + + Assignments are shell words, so "$$" in a value is the pid httpd will + have. + """ + + def __init__(self, env: SystemdTestEnv, port: int, + name: str = 'foreground', extra: str = '', + setenv: Dict[str, str] = None, + modules_conf: str = 'modules.conf'): + super().__init__(env, port, name=name, extra=extra, + modules_conf=modules_conf) + self.setenv = dict(setenv or {}) + + def args(self, fd: int = None) -> List[str]: + assigns = ' '.join(f'{k}={v}' for k, v in self.setenv.items()) + return [ + 'bash', '-c', + f'export {assigns}; exec "$0" "$@"' if assigns else 'exec "$0" "$@"', + self.env.httpd_bin, '-DFOREGROUND', + '-d', self.env.server_dir, '-f', self.conf_file, + ] + + def start(self) -> 'ForegroundServer': + # A new session, so the whole group can be signalled on the way out + # even when a failed restart leaves children behind. + self.proc = subprocess.Popen( + self.args(), env=self.env.server_env(), start_new_session=True, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + return self diff -Nru apache2-2.4.69/test/modules/arch/linux/test_001_notify.py apache2-2.4.71/test/modules/arch/linux/test_001_notify.py --- apache2-2.4.69/test/modules/arch/linux/test_001_notify.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/arch/linux/test_001_notify.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,155 @@ +import time + +import pytest + +from pyhttpd.backport import not_backported + +from pyhttpd.conf import HttpdConf + + +@not_backported("r1937596-r1937647", "mod_systemd watchdog, notify and socket activation") +class TestSystemdNotify: + """The service notifications mod_systemd sends over $NOTIFY_SOCKET + across the server lifecycle.""" + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + conf = HttpdConf(env) + conf.add_vhost_test1() + conf.install() + # Each test drives startup itself, so leave the server down. + assert env.apache_stop() == 0 + + @staticmethod + def index_of(messages, match): + for i, msg in enumerate(messages): + if match(msg): + return i + return -1 + + def test_systemd_001_01_startup(self, env): + """Startup reports configuration reading, then readiness, then that + the MPM is serving.""" + env.notify.clear() + assert env.apache_restart() == 0 + assert env.notify.wait_for_status(r'^Reading configuration\.\.\.$'), \ + f"no reload notification, got {env.notify.statuses()}" + assert env.notify.wait_for_status(r'^Configuration loaded\.$'), \ + f"no ready notification, got {env.notify.statuses()}" + assert env.notify.wait_for_status(r'^Processing requests\.\.\.$'), \ + f"no pre_mpm notification, got {env.notify.statuses()}" + + def test_systemd_001_02_reloading_before_ready(self, env): + """RELOADING=1 is sent while the configuration is read, and READY=1 + only once it has been.""" + env.notify.clear() + assert env.apache_restart() == 0 + assert env.notify.wait_for_status(r'^Configuration loaded\.$') + msgs = env.notify.messages + reloading = self.index_of( + msgs, lambda m: m.get('RELOADING') == '1' + and m.get('STATUS') == 'Reading configuration...') + ready = self.index_of( + msgs, lambda m: m.get('READY') == '1' + and m.get('STATUS') == 'Configuration loaded.') + assert reloading >= 0 and ready >= 0 + assert reloading < ready, \ + "READY=1 was reported before the configuration was read" + + def test_systemd_001_03_mainpid(self, env): + """MAINPID is the pid of the parent process, the one httpd records + in its pid file.""" + env.notify.clear() + assert env.apache_restart() == 0 + msg = env.notify.wait_for_key('MAINPID') + assert msg, f"no MAINPID reported, got {env.notify.messages}" + assert msg.get('READY') == '1' + assert msg.get('STATUS') == 'Processing requests...' + assert int(msg['MAINPID']) == env.read_pid_file() + + def test_systemd_001_04_reload(self, env): + """A graceful restart reports reading the configuration and then + being ready again.""" + assert env.apache_restart() == 0 + pid = env.read_pid_file() + env.notify.clear() + assert env.apache_reload() == 0 + assert env.notify.wait_for_status(r'^Reading configuration\.\.\.$'), \ + f"no reload notification, got {env.notify.statuses()}" + assert env.notify.wait_for_status(r'^Configuration loaded\.$'), \ + f"no ready notification, got {env.notify.statuses()}" + # The parent survives a graceful restart, and the MPM is not started + # over, so the pid systemd tracks neither changes nor is re-reported. + assert env.read_pid_file() == pid + for msg in env.notify.messages: + assert 'MAINPID' not in msg or int(msg['MAINPID']) == pid + + def test_systemd_001_05_hard_restart(self, env): + """An ungraceful restart starts the MPM over, and re-reports the + main pid, which is still that of the surviving parent.""" + assert env.apache_restart() == 0 + pid = env.read_pid_file() + env.notify.clear() + assert env.apache_hard_restart() == 0 + assert env.notify.wait_for_status(r'^Configuration loaded\.$'), \ + f"no ready notification, got {env.notify.statuses()}" + msg = env.notify.wait_for_key('MAINPID') + assert msg, f"no MAINPID after restart, got {env.notify.messages}" + assert int(msg['MAINPID']) == pid + assert env.read_pid_file() == pid + + def test_systemd_001_06_notify_socket_kept(self, env): + """mod_systemd leaves NOTIFY_SOCKET in the environment, so a second + start after a stop is reported just like the first.""" + assert env.apache_restart() == 0 + assert env.apache_stop() == 0 + env.notify.clear() + assert env.apache_restart() == 0 + assert env.notify.wait_for_status(r'^Configuration loaded\.$') + + def test_systemd_001_07_stopping(self, env): + """Shutdown is announced before the process goes away.""" + assert env.apache_restart() == 0 + env.notify.clear() + assert env.apache_stop() == 0 + # The server is already gone, so anything it sent has arrived. + msg = env.notify.wait_for_key('STOPPING', timeout=1) + assert msg, f"no STOPPING=1 on shutdown, got {env.notify.messages}" + assert msg['STOPPING'] == '1' + assert msg.get('STATUS') == 'Shutting down.' + + def test_systemd_001_08_reloading_monotonic(self, env): + assert env.apache_restart() == 0 + env.notify.clear() + assert env.apache_reload() == 0 + msg = env.notify.wait_for(lambda m: m.get('RELOADING') == '1') + assert msg, "no RELOADING=1 on graceful restart" + assert 'MONOTONIC_USEC' in msg, \ + f"RELOADING=1 sent without MONOTONIC_USEC: {msg}" + # systemd compares this against its own reading of the same clock. + assert 0 < int(msg['MONOTONIC_USEC']) <= time.clock_gettime_ns( + time.CLOCK_MONOTONIC) // 1000 + + def test_systemd_001_09_watchdog(self, env): + """A server started with $WATCHDOG_USEC reports that it is alive. + + $WATCHDOG_PID is left unset, which sd_watchdog_enabled(3) takes to + mean any process may report: it has to be, since apachectl starts a + parent whose pid nothing knew in advance. The keep-alive arrives + with the startup notifications rather than only from the monitor + hook ten seconds later, which is what makes this cheap to check. + test_006_watchdog.py covers the rest of the protocol. + """ + assert env.apache_stop() == 0 + # Long enough that mod_systemd does not object to it (AH10622). + env.set_httpd_env('WATCHDOG_USEC', str(60 * 1000000)) + try: + env.notify.clear() + assert env.apache_restart() == 0 + msg = env.notify.wait_for_key('WATCHDOG', timeout=4) + assert msg, f"no watchdog keepalive was sent, " \ + f"got {env.notify.messages}" + assert msg['WATCHDOG'] == '1' + finally: + env.set_httpd_env('WATCHDOG_USEC', None) + assert env.apache_restart() == 0 diff -Nru apache2-2.4.69/test/modules/arch/linux/test_002_monitor.py apache2-2.4.71/test/modules/arch/linux/test_002_monitor.py --- apache2-2.4.69/test/modules/arch/linux/test_002_monitor.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/arch/linux/test_002_monitor.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,110 @@ +import re + +import pytest + +from pyhttpd.backport import not_backported + +from pyhttpd.conf import HttpdConf + +from .env import MONITOR_STATUS, MONITOR_TIMEOUT + + +@not_backported("r1937596-r1937647", "mod_systemd watchdog, notify and socket activation") +class TestSystemdMonitor: + """The periodic STATUS= line the monitor hook reports, which is what + "systemctl status httpd" shows below the unit description.""" + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + conf = HttpdConf(env, extras={ + 'base': """ + + SetHandler server-status + + """ + }) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + + @staticmethod + def wait_report(env, above=-1): + """Wait for a report from the monitor hook accounting for more than + `above` requests, and return its fields.""" + msg = env.notify.wait_for( + lambda m: 'STATUS' in m + and (g := MONITOR_STATUS.match(m['STATUS'])) is not None + and int(g.group('requests')) > above, + timeout=MONITOR_TIMEOUT) + assert msg, f"no monitor report within {MONITOR_TIMEOUT}s, " \ + f"server {'up' if env.is_live() else 'down'}, " \ + f"got {env.notify.messages}" + return MONITOR_STATUS.match(msg['STATUS']) + + @pytest.fixture(scope='class') + def reports(self, env, _class_scope): + """Two consecutive reports with requests served between them, and + the mod_status report as of the second. + + The hook runs once every ten turns of the parent's one second + loop, so waiting for a report costs up to ten seconds. The tests + below share one pair rather than each waiting for its own. + """ + env.notify.clear() + first = self.wait_report(env) + for _ in range(10): + r = env.curl_get(env.mkurl("http", "test1", "/")) + assert r.response['status'] == 200 + second = self.wait_report(env, above=int(first.group('requests'))) + r = env.curl_get(env.mkurl("http", "test1", "/server-status?auto")) + assert r.response['status'] == 200 + auto = {} + for line in r.response['body'].decode().splitlines(): + key, sep, value = line.partition(':') + if sep and key not in auto: + auto[key] = value.strip() + return {'first': first, 'second': second, 'auto': auto} + + def test_systemd_002_01_report_format(self, reports): + m = reports['first'] + assert int(m.group('requests')) >= 0 + assert m.group('bps') + + def test_systemd_002_02_requests_counted(self, reports): + """The request count reported to systemd tracks requests served.""" + before = int(reports['first'].group('requests')) + after = int(reports['second'].group('requests')) + assert after >= before + 10, \ + f"{after - before} requests reported, at least 10 were served" + + def test_systemd_002_03_rates_are_finite(self, reports): + """Neither rate is inf or nan. + + systemd_monitor() divides by an uptime in whole seconds, which is + zero for a report arriving in the first second after the scoreboard + records a restart. + """ + for which in ('first', 'second'): + m = reports[which] + rate = m.group('rate') + assert re.match(r'^-?\d', rate), f"{which} request rate is {rate!r}" + assert float(rate) >= 0 + assert 'inf' not in m.group('bps') and 'nan' not in m.group('bps'), \ + f"{which} byte rate is {m.group('bps')!r}" + + def test_systemd_002_04_report_repeats(self, env, reports): + """The status line keeps being refreshed while the server runs.""" + assert reports['first'].group(0) != reports['second'].group(0) + + def test_systemd_002_05_worker_percentages(self, reports): + """Idle and busy are percentages of the workers available, and are + reported as such.""" + m, auto = reports['second'], reports['auto'] + idle, busy = int(m.group('idle')), int(m.group('busy')) + assert 0 <= idle <= 100 and 0 <= busy <= 100 + # Integer division loses at most one point between the two. + assert 99 <= idle + busy <= 100 + # Busy workers are a minority of a mostly idle test server, which + # is what mod_status reports over the same scoreboard. + assert int(auto['IdleWorkers']) > int(auto['BusyWorkers']) + assert idle > busy diff -Nru apache2-2.4.69/test/modules/arch/linux/test_003_extended_status.py apache2-2.4.71/test/modules/arch/linux/test_003_extended_status.py --- apache2-2.4.69/test/modules/arch/linux/test_003_extended_status.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/arch/linux/test_003_extended_status.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,69 @@ +import pytest + +from pyhttpd.backport import not_backported + +from pyhttpd.conf import HttpdConf + +from .env import NO_MONITOR_TIMEOUT + + +@not_backported("r1937596-r1937647", "mod_systemd watchdog, notify and socket activation") +class TestSystemdExtendedStatus: + """mod_systemd turns ExtendedStatus on so that it has request counts to + report, which changes what the rest of the server records too.""" + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + yield + conf = HttpdConf(env) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + + def auto_report(self, env): + r = env.curl_get(env.mkurl("http", "test1", "/server-status?auto")) + assert r.response['status'] == 200 + return r.response['body'].decode() + + def install(self, env, extra=''): + conf = HttpdConf(env, extras={ + 'base': f""" + {extra} + + SetHandler server-status + + """ + }) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + + def test_systemd_003_01_enabled_by_default(self, env): + """Loading mod_systemd is enough to get extended status; no + ExtendedStatus directive is present in this configuration.""" + self.install(env) + body = self.auto_report(env) + assert 'Total Accesses:' in body, \ + "ExtendedStatus was not enabled by mod_systemd" + assert 'Total kBytes:' in body + + def test_systemd_003_02_directive_wins(self, env): + """An explicit ExtendedStatus off still takes effect: mod_systemd + sets the default in pre_config, before the configuration is read.""" + self.install(env, extra='ExtendedStatus off') + body = self.auto_report(env) + assert 'Total Accesses:' not in body, \ + "ExtendedStatus off was overridden by mod_systemd" + + def test_systemd_003_03_no_report_without_extended_status(self, env): + """With extended status off the monitor hook declines, so no status + line is reported to systemd.""" + env.notify.clear() + self.install(env, extra='ExtendedStatus off') + # Startup notifications are still sent... + assert env.notify.wait_for_status(r'^Processing requests\.\.\.$', + timeout=10) is not None + # ...but the periodic status line is not. + assert env.notify.wait_for_status(r'^Total requests: ', + timeout=NO_MONITOR_TIMEOUT) is None, \ + "a monitor report was sent with ExtendedStatus off" diff -Nru apache2-2.4.69/test/modules/arch/linux/test_004_socket_activation.py apache2-2.4.71/test/modules/arch/linux/test_004_socket_activation.py --- apache2-2.4.69/test/modules/arch/linux/test_004_socket_activation.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/arch/linux/test_004_socket_activation.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,92 @@ +import pytest + +from pyhttpd.backport import not_backported + +from .env import ActivatedServer + + +@not_backported("r1937596-r1937647", "mod_systemd watchdog, notify and socket activation") +class TestSystemdSocketActivation: + """Listening sockets passed in by the service manager. + + mod_systemd exports the two optional functions server/listen.c uses to + find them, so socket activation is enabled by loading the module and + disabled by not loading it, whatever the environment says. + + apachectl cannot pass file descriptors, so these tests open the + listening socket themselves and run httpd directly, in the foreground, + with the descriptor and the environment a service manager would give + it. No systemd process is involved. + """ + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + # The activated servers use the same server root; keep the one + # started by other tests out of the way. + assert env.apache_stop() == 0 + yield + assert env.apache_stop() == 0 + + def test_systemd_004_01_activated_listener(self, env): + """httpd serves on a socket it never opened itself.""" + env.notify.clear() + with ActivatedServer(env, port=env.http_port2) as server: + assert server.is_live(), \ + f"server did not come up: {server.stderr}" + r = env.curl_get(f"http://{env.http_addr}:{env.http_port2}/") + assert r.response['status'] == 200 + # The notification handshake works the same way as when httpd opens + # its own sockets. + assert env.notify.wait_for_status(r'^Configuration loaded\.$', timeout=0) + msg = env.notify.wait_for_key('MAINPID', timeout=0) + assert msg, f"httpd sent no MAINPID, got {env.notify.messages}" + assert msg['STATUS'] == 'Processing requests...' + + def test_systemd_004_02_no_socket_for_port(self, env): + """A Listen port the service manager did not pass is an error, not + a port httpd quietly opens for itself.""" + server = ActivatedServer(env, port=env.http_port2, + listen_port=env.proxy_port, + name='activate-wrongport') + with server: + assert server.wait_exit() != 0, "httpd started without a socket" + assert b'not configured in systemd' in server.stderr, \ + f"unexpected startup diagnostic: {server.stderr}" + + def test_systemd_004_03_disabled_without_module(self, env): + """Without mod_systemd the passed sockets are ignored and httpd + opens the configured port itself, the same arrangement that fails + in the test above.""" + if not env.systemd_is_dso: + pytest.skip("mod_systemd is linked statically and cannot be " + "left out of the configuration") + modules_conf = ActivatedServer.modules_conf_without(env, 'systemd') + server = ActivatedServer(env, port=env.http_port2, + listen_port=env.proxy_port, + name='activate-nomodule', + modules_conf=modules_conf) + with server: + assert server.is_live(), \ + f"server did not come up: {server.stderr}" + r = env.curl_get(f"http://{env.http_addr}:{env.http_port2}/") + assert r.response['status'] == 200 + + def test_systemd_004_04_graceful_restart(self, env): + """An activated server survives a graceful restart, which means + finding the passed sockets again after the environment naming them + has been cleared.""" + with ActivatedServer(env, port=env.http_port2, + name='activate-reload') as server: + assert server.is_live(), \ + f"server did not come up: {server.stderr}" + server.reload() + env.httpd_error_log.ignore_recent(lognos=['AH02487']) + # Check the parent first: when it dies here its children carry + # on holding the listening socket and answering, so a request + # succeeding proves nothing on its own. + assert server.is_running(), \ + "the parent exited on graceful restart" + assert server.is_live(timeout=5), \ + "the server did not survive a graceful restart" + r = env.curl_get(f"http://{env.http_addr}:{env.http_port2}/") + assert r.response['status'] == 200 diff -Nru apache2-2.4.69/test/modules/arch/linux/test_005_service.py apache2-2.4.71/test/modules/arch/linux/test_005_service.py --- apache2-2.4.69/test/modules/arch/linux/test_005_service.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/arch/linux/test_005_service.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,95 @@ +import time + +import pytest + +from pyhttpd.backport import not_backported + +from pyhttpd.depends import needs_dependency + +from .env import MONITOR_STATUS, MONITOR_TIMEOUT, TransientService, http_responds + +pytestmark = needs_dependency( + "systemd --user manager", TransientService.is_available(), + reason="no per-user systemd manager to run a transient service under", + detected="systemd-run --user is not usable here") + + +@not_backported("r1937596-r1937647", "mod_systemd watchdog, notify and socket activation") +class TestSystemdService: + """httpd as a real systemd service, end to end. + + Everything else here checks what mod_systemd sends. These check what + systemd does with it: hold the unit in "activating" until httpd is + ready, track the right process, and show the reported status text. + """ + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + assert env.apache_stop() == 0 + yield + assert env.apache_stop() == 0 + + @pytest.fixture + def service(self, env) -> TransientService: + svc = TransientService(env, port=env.http_port2) + yield svc + svc.stop() + + def test_systemd_005_01_type_notify(self, env, service): + """systemd-run returns once the unit is active, which for a + Type=notify service means READY=1 has been received, which + mod_systemd sends only after the configuration is loaded.""" + r = service.start() + assert r.returncode == 0, f"systemd-run failed: {r.stderr}" + assert service.show('ActiveState') == 'active' + assert http_responds(env.http_port2), \ + "the unit was active before the server would answer" + + def test_systemd_005_02_main_pid(self, env, service): + """The process systemd tracks is the httpd parent.""" + assert service.start().returncode == 0 + assert service.wait_active() + main_pid = int(service.show('MainPID')) + assert main_pid > 0 + assert main_pid == service.read_pid() + + def test_systemd_005_03_status_text(self, env, service): + """The status line "systemctl status" shows comes from the monitor + hook, and is refreshed while the server runs.""" + assert service.start().returncode == 0 + assert service.wait_active() + # First the post_config report, then the periodic one. + assert service.show('StatusText') in ('Configuration loaded.', + 'Processing requests...') + end = time.time() + MONITOR_TIMEOUT + text = None + while time.time() < end: + text = service.show('StatusText') + if MONITOR_STATUS.match(text): + break + time.sleep(0.5) + assert MONITOR_STATUS.match(text), \ + f"status text was never refreshed by the monitor hook: {text!r}" + + def test_systemd_005_04_reload(self, env, service): + """systemctl reload runs httpd -k graceful and the unit stays + active throughout.""" + assert service.start().returncode == 0 + assert service.wait_active() + pid = int(service.show('MainPID')) + r = service.systemctl('reload', f'{service.unit}.service') + assert r.returncode == 0, f"reload failed: {r.stderr}" + assert service.show('ActiveState') == 'active' + assert int(service.show('MainPID')) == pid + assert http_responds(env.http_port2) + + def test_systemd_005_05_stop(self, env, service): + """The unit stops cleanly, without systemd having to time out and + kill it.""" + assert service.start().returncode == 0 + assert service.wait_active() + r = service.systemctl('stop', f'{service.unit}.service') + assert r.returncode == 0, f"stop failed: {r.stderr}" + assert service.show('ActiveState') == 'inactive' + assert service.show('Result') == 'success' + assert not http_responds(env.http_port2) diff -Nru apache2-2.4.69/test/modules/arch/linux/test_006_watchdog.py apache2-2.4.71/test/modules/arch/linux/test_006_watchdog.py --- apache2-2.4.69/test/modules/arch/linux/test_006_watchdog.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/arch/linux/test_006_watchdog.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,217 @@ +import os +import re +import time + +import pytest + +from pyhttpd.backport import not_backported + +from pyhttpd.depends import needs_dependency + +from .env import (ForegroundServer, TransientService, MIN_WATCHDOG_SEC, + NO_MONITOR_TIMEOUT, WATCHDOG_TIMEOUT, is_watchdog_ping) + + +def watchdog_env(usec: int, pid: str = '$$') -> dict: + """The environment a service manager sets for a watched service. + + $WATCHDOG_PID names the process expected to report; "$$" is the shell + which exec's httpd, so it is the pid httpd will run as. + """ + env = {'WATCHDOG_USEC': str(usec)} + if pid is not None: + env['WATCHDOG_PID'] = pid + return env + + +@not_backported("r1937596-r1937647", "mod_systemd watchdog, notify and socket activation") +class TestSystemdWatchdog: + """The keep-alive ping of the systemd watchdog protocol. + + A service whose unit sets WatchdogSec= is started with $WATCHDOG_USEC + holding that timeout in microseconds, and $WATCHDOG_PID holding the pid + expected to report. The service must then send "WATCHDOG=1" to the + notification socket more often than the timeout, or systemd puts the + unit into a failed state with Result=watchdog. The timeout is armed + once start-up completes and, as test_006_06 relies on, stays armed + across a reload. + + Nothing here needs systemd: the ping goes to $NOTIFY_SOCKET like every + other notification, so the same stand-in socket observes it. Only the + last test involves a service manager. + """ + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + # These run their own servers on the second port; the shared one + # would only add its notifications to the same socket. + assert env.apache_stop() == 0 + yield + assert env.apache_stop() == 0 + + @pytest.fixture + def server_factory(self, env): + started = [] + + def make(usec=None, pid='$$', extra='', name='watchdog'): + setenv = watchdog_env(usec, pid) if usec is not None else {} + srv = ForegroundServer(env, port=env.http_port2, name=name, + extra=extra, setenv=setenv) + started.append(srv) + env.notify.clear() + srv.start() + assert srv.is_live(), \ + f"server did not come up: {srv.stderr!r}" + return srv + + yield make + for srv in started: + srv.stop() + + def test_systemd_006_01_no_ping_when_unwatched(self, env, server_factory): + """A server the service manager is not watching sends no keep-alive. + + The monitor hook still runs -- its status report is the evidence of + that -- so the absence of a ping is a decision and not silence. + """ + server_factory() + assert env.notify.wait_for_status(r'^Total requests: ', + timeout=WATCHDOG_TIMEOUT), \ + "the monitor hook never ran, so this proves nothing" + assert not [m for m in env.notify.messages if is_watchdog_ping(m)], \ + "a keep-alive was sent with $WATCHDOG_USEC unset" + + def test_systemd_006_02_ping_when_watched(self, env, server_factory): + """With the watchdog enabled the keep-alive is sent.""" + server_factory(usec=60 * 1000000) + assert env.notify.wait_for(is_watchdog_ping, + timeout=WATCHDOG_TIMEOUT), \ + f"no keep-alive within {WATCHDOG_TIMEOUT}s, " \ + f"got {env.notify.messages}" + + def test_systemd_006_03_ping_repeats(self, env, server_factory): + """The keep-alive is periodic, which is the whole point of it: one + ping would satisfy a test but not systemd.""" + server_factory(usec=60 * 1000000) + for n in range(2): + assert env.notify.wait_for(is_watchdog_ping, + timeout=WATCHDOG_TIMEOUT), \ + f"only {n} keep-alives arrived, got {env.notify.messages}" + env.notify.clear() + + def test_systemd_006_04_ping_without_extended_status(self, env, + server_factory): + """The keep-alive does not depend on ExtendedStatus. + + The monitor hook declines early when it has no request counts to + report (test_003_03), and a server whose status line is switched + off must still be reported as alive. + """ + server_factory(usec=60 * 1000000, extra='ExtendedStatus off') + assert env.notify.wait_for_status(r'^Total requests: ', + timeout=NO_MONITOR_TIMEOUT) is None, \ + "ExtendedStatus off did not stop the status report" + assert env.notify.wait_for(is_watchdog_ping, + timeout=WATCHDOG_TIMEOUT), \ + "no keep-alive with ExtendedStatus off" + + def test_systemd_006_05_no_ping_for_another_pid(self, env, server_factory): + """$WATCHDOG_PID naming a different process means the variables were + set for something further up the process tree, and must be ignored. + """ + server_factory(usec=60 * 1000000, pid='1') + assert env.notify.wait_for_status(r'^Total requests: ', + timeout=WATCHDOG_TIMEOUT), \ + "the monitor hook never ran, so this proves nothing" + assert not [m for m in env.notify.messages if is_watchdog_ping(m)], \ + "a keep-alive was sent although $WATCHDOG_PID was another process" + + def test_systemd_006_06_ping_across_reload(self, env, server_factory): + """The keep-alive survives a graceful restart. + + The parent re-reads its configuration in the same process, but + mod_systemd is unloaded and loaded again with it, so anything it + remembered about the watchdog is gone by the time the monitor hook + runs again. systemd keeps the timeout armed throughout. + """ + srv = server_factory(usec=60 * 1000000) + assert env.notify.wait_for(is_watchdog_ping, timeout=WATCHDOG_TIMEOUT) + env.notify.clear() + assert srv.reload() == 0 + assert srv.is_live() + assert env.notify.wait_for(is_watchdog_ping, + timeout=WATCHDOG_TIMEOUT), \ + f"no keep-alive after a reload, got {env.notify.messages}" + + def test_systemd_006_07_ping_covers_the_reload_window(self, env, + server_factory): + """A keep-alive is sent as the configuration is read, and again once + it is loaded. + + Reading the configuration happens outside the parent's monitor loop, + so nothing else reports during it. The watchdog stays armed while + the unit reloads, and a configuration which takes longer to parse + than the timeout would otherwise be killed halfway through. + """ + srv = server_factory(usec=60 * 1000000) + assert env.notify.wait_for(is_watchdog_ping, timeout=WATCHDOG_TIMEOUT) + env.notify.clear() + assert srv.reload() == 0 + assert env.notify.wait_for( + lambda m: 'RELOADING' in m and is_watchdog_ping(m), + timeout=WATCHDOG_TIMEOUT), \ + f"no keep-alive as the configuration was read, " \ + f"got {env.notify.messages}" + assert env.notify.wait_for( + lambda m: m.get('READY') == '1' and is_watchdog_ping(m), + timeout=WATCHDOG_TIMEOUT), \ + f"no keep-alive once the configuration was loaded, " \ + f"got {env.notify.messages}" + + def test_systemd_006_08_short_timeout_warned(self, env, server_factory): + """A WatchdogSec the parent cannot meet is reported. + + The keep-alive is sent from the monitor hook, which runs once every + ten turns of the parent's one second loop. A timeout of a few + seconds cannot be met however the module is written, and failing + silently would leave the server being killed and restarted with + nothing in the log to say why. + """ + server_factory(usec=2 * 1000000) + assert env.httpd_error_log.scan_recent( + re.compile(r'.*AH10621: .*[Ww]atchdog.*'), timeout=10), \ + "no warning about a watchdog timeout that cannot be met" + env.httpd_error_log.ignore_recent(lognos=['AH10621']) + + def test_systemd_006_09_workable_timeout_not_warned(self, env, + server_factory): + """A timeout the parent can meet is not complained about.""" + server_factory(usec=MIN_WATCHDOG_SEC * 1000000) + assert env.notify.wait_for(is_watchdog_ping, timeout=WATCHDOG_TIMEOUT) + with pytest.raises(TimeoutError): + env.httpd_error_log.scan_recent( + re.compile(r'.*AH10621: .*'), timeout=1) + + @needs_dependency("systemd --user manager", TransientService.is_available(), + reason="no per-user systemd manager", + detected="systemd-run --user is not usable here") + def test_systemd_006_10_service_keeps_watchdog_alive(self, env): + """The real thing: systemd records each keep-alive it receives, and + the unit stays active rather than failing with Result=watchdog.""" + with TransientService(env, port=env.http_port2, + properties=[f'WatchdogSec={MIN_WATCHDOG_SEC}s'], + name=f'httpd-wd-{os.getpid()}') as svc: + assert svc.start().returncode == 0 + assert svc.wait_active() + first = svc.show('WatchdogTimestampMonotonic') + assert first and int(first) > 0, \ + "systemd recorded no keep-alive at all" + end = time.time() + WATCHDOG_TIMEOUT + while time.time() < end: + if svc.show('WatchdogTimestampMonotonic') != first: + break + time.sleep(0.5) + assert svc.show('WatchdogTimestampMonotonic') != first, \ + "systemd received no further keep-alive" + assert svc.show('ActiveState') == 'active' + assert svc.show('Result') == 'success' diff -Nru apache2-2.4.69/test/modules/arch/linux/test_007_notify_reload.py apache2-2.4.71/test/modules/arch/linux/test_007_notify_reload.py --- apache2-2.4.69/test/modules/arch/linux/test_007_notify_reload.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/arch/linux/test_007_notify_reload.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,146 @@ +import os +import time + +import pytest + +from pyhttpd.backport import not_backported + +from pyhttpd.depends import needs_dependency + +from .env import (NOTIFY_RELOAD_VERSION, TransientService, error_log_since, + error_log_size, http_responds, systemd_version) + +pytestmark = [ + needs_dependency( + "systemd --user manager", TransientService.is_available(), + reason="no per-user systemd manager to run a transient service under", + detected="systemd-run --user is not usable here"), + # a version gate, not a missing dependency: keeps skipping when strict + pytest.mark.skipif( + systemd_version() < NOTIFY_RELOAD_VERSION, + reason=f"Type=notify-reload needs systemd {NOTIFY_RELOAD_VERSION}"), +] + + +@not_backported("r1937596-r1937647", "mod_systemd watchdog, notify and socket activation") +class TestNotifyReload: + """httpd as a Type=notify-reload unit. + + Under Type=notify a reload is only whatever ExecReload= does, and + systemctl returns as soon as that command exits. For "httpd -k + graceful" that is as soon as the signal has been sent, long before the + new configuration is in use, and it reports success however the restart + turns out. Type=notify-reload holds the reload job open until the + service sends RELOADING=1 and then READY=1, which mod_systemd sends + from pre_config and post_config, so the result is reported once it is + known. + + The unit here keeps ExecReload= and sets ReloadSignal=SIGCONT, which + httpd ignores. The manager runs ExecReload= first and sends + ReloadSignal= only once it has exited successfully, so: + + - "httpd -k graceful" stays the thing which restarts the server, and + because it parses the new configuration in its own process before + signalling, a configuration which does not parse fails the reload + without the running server being signalled at all. A bare + ReloadSignal= has the running parent read the new configuration and + exit if it does not parse. + + - the signal the manager then sends is redundant, so it is pointed at + one httpd does not act on. The default is SIGHUP, which httpd + takes as an *ungraceful* restart. + """ + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + assert env.apache_stop() == 0 + yield + assert env.apache_stop() == 0 + + @pytest.fixture + def service(self, env) -> TransientService: + svc = TransientService(env, port=env.http_port2, + name=f'httpd-reload-{os.getpid()}', + service_type='notify-reload', + properties=['ReloadSignal=SIGCONT']) + yield svc + svc.stop() + + def test_systemd_007_01_reload_waits_for_the_new_config(self, env, service): + """systemctl reload returns only once the reloaded server is + serving: the port added to the configuration answers without the + test waiting for it, because READY=1 is sent from post_config, by + which point the listeners are open.""" + assert service.start().returncode == 0 + assert service.wait_active() + assert http_responds(env.http_port2) + + service.rewrite_conf(extra=f'Listen {env.proxy_port}') + r = service.reload() + assert r.returncode == 0, f"reload failed: {r.stderr}" + assert http_responds(env.proxy_port, timeout=10.0), \ + "reload returned before the newly configured port was served" + assert http_responds(env.http_port2) + + def test_systemd_007_02_broken_config_fails_the_reload(self, env, service): + """A configuration which does not parse fails the reload and leaves + the running server alone. ExecReload= reads it in a process of its + own and exits without signalling, and the manager sends no reload + signal of its own once that has failed, so the parent never reads + it.""" + assert service.start().returncode == 0 + assert service.wait_active() + pid = int(service.show('MainPID')) + + service.rewrite_conf(extra='ThisDirectiveDoesNotExist on') + r = service.reload() + assert r.returncode != 0, \ + "reload of an unparseable configuration reported success" + assert service.show('ActiveState') == 'active', \ + "a failed reload took the unit out of active" + # The parent exits when a restart re-reads a configuration which + # does not parse, and its children go on serving for a while + # afterwards, so answering a request is not on its own proof that + # the server survived: check the process the manager tracks. + assert int(service.show('MainPID')) == pid, \ + "the parent was restarted despite the configuration not parsing" + assert http_responds(env.http_port2) + + def test_systemd_007_03_reload_restarts_gracefully_once(self, env, service): + """One reload is one graceful restart, and no ungraceful one. + Leaving ReloadSignal= at its SIGHUP default is what this catches: + httpd restarts ungracefully on SIGHUP, dropping the connections a + reload is supposed to keep.""" + if env.mpm_module not in ('mpm_event', 'mpm_worker'): + pytest.skip(f"{env.mpm_module} does not log the graceful restart") + assert service.start().returncode == 0 + assert service.wait_active() + + pos = error_log_size(env) + assert service.reload().returncode == 0 + # The restart is logged before the configuration is re-read, so it + # is written by the time READY=1 ends the reload; give the + # redundant signal which follows a moment to land as well. + time.sleep(2) + log = error_log_since(env, pos) + assert log.count("Attempting to restart") == 0, \ + f"the reload restarted the server ungracefully:\n{log}" + assert log.count("Doing graceful restart") == 1, \ + f"expected one graceful restart, log said:\n{log}" + + def test_systemd_007_04_reload_repeats(self, env, service): + """Reloading twice works. The manager ignores a RELOADING=1 which + is not stamped later than the reload it asked for, so a server + which got MONOTONIC_USEC wrong could report the first reload and + then leave the second to time out.""" + assert service.start().returncode == 0 + assert service.wait_active() + pid = int(service.show('MainPID')) + + for i in range(2): + r = service.reload() + assert r.returncode == 0, f"reload {i + 1} failed: {r.stderr}" + assert service.show('ActiveState') == 'active' + assert int(service.show('MainPID')) == pid, \ + "a graceful restart replaced the parent process" + assert http_responds(env.http_port2) diff -Nru apache2-2.4.69/test/modules/conftest.py apache2-2.4.71/test/modules/conftest.py --- apache2-2.4.69/test/modules/conftest.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/conftest.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,70 @@ +"""Fixtures shared by every test package below modules/. + +They act on the package-scoped ``env`` that each package's conftest defines, +so they live here rather than in the root conftest: tests outside modules/ +(pytest_suite) have no ``env``. +""" +import socket +import sys +import warnings + +import pytest + + +def _has_ipv6(): + try: + s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + s.bind(('::1', 0)) + s.close() + return True + except (socket.error, OSError): + return False + + +@pytest.fixture(autouse=True, scope="function") +def _function_scope(env, request): + env.set_current_test_name(request.node.name) + yield + env.check_error_log() + archive_dir = request.config.getoption("--archive") + if archive_dir: + fspath = str(request.fspath) + if 'modules/' in fspath: + package_name = fspath.split('modules/')[1].split('/')[0] + env.archive_test_conf(request.node.name, package_name, archive_dir) + env.set_current_test_name(None) + + +@pytest.fixture(autouse=True, scope="module") +def _module_scope(env): + yield + env.check_error_log() + + +@pytest.fixture(autouse=True, scope="package") +def _package_scope(env, request): + env.httpd_error_log.clear_ignored_matches() + env.httpd_error_log.clear_ignored_lognos() + if sys.platform == "win32": + env.httpd_error_log.add_ignored_lognos([ + "AH00098", + "AH10373", + "AH00336", + "AH00337", + "AH00338", + "AH00341", + "AH02322", + ]) + if sys.platform == "win32" and _has_ipv6(): + env.httpd_error_log.add_ignored_lognos(["AH00332"]) + yield + assert env.apache_stop() == 0 + env.check_error_log() + archive_dir = request.config.getoption("--archive") + if archive_dir == "": + warnings.warn("--archive option was empty, skipping archiving") + if archive_dir: + fspath = str(request.fspath) + if 'modules/' in fspath: + package_name = fspath.split('modules/')[1].split('/')[0] + env.archive_logs(package_name, archive_dir) diff -Nru apache2-2.4.69/test/modules/core/env.py apache2-2.4.71/test/modules/core/env.py --- apache2-2.4.69/test/modules/core/env.py 2024-07-11 08:18:12.000000000 +0000 +++ apache2-2.4.71/test/modules/core/env.py 2026-11-09 03:07:05.000000000 +0000 @@ -12,7 +12,7 @@ def __init__(self, env: 'HttpdTestEnv'): super().__init__(env=env) self.add_source_dir(os.path.dirname(inspect.getfile(CoreTestSetup))) - self.add_modules(["cgid"]) + self.add_modules(["cgid","include","userdir","suexec","headers"]) class CoreTestEnv(HttpdTestEnv): diff -Nru apache2-2.4.69/test/modules/core/htdocs/cgi/delay.py apache2-2.4.71/test/modules/core/htdocs/cgi/delay.py --- apache2-2.4.69/test/modules/core/htdocs/cgi/delay.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/core/htdocs/cgi/delay.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +import sys, time + +content="A delayed response.\n" + +print("Status: 200") +print(f"Request-Length: {len(content)}") +print("Content-Type: text/plain\n") +time.sleep(1) +sys.stdout.write(content) + diff -Nru apache2-2.4.69/test/modules/core/htdocs/cgi/env_parameters.py apache2-2.4.71/test/modules/core/htdocs/cgi/env_parameters.py --- apache2-2.4.69/test/modules/core/htdocs/cgi/env_parameters.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/core/htdocs/cgi/env_parameters.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +import os +import json + +print("Content-Type: application/json") +print() + +data = { + "REQUEST_METHOD": os.getenv("REQUEST_METHOD", ""), + "QUERY_STRING": os.getenv("QUERY_STRING", ""), +} + +print(json.dumps(data, indent=2)) diff -Nru apache2-2.4.69/test/modules/core/htdocs/cgiredir/redir.cgi apache2-2.4.71/test/modules/core/htdocs/cgiredir/redir.cgi --- apache2-2.4.69/test/modules/core/htdocs/cgiredir/redir.cgi 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/core/htdocs/cgiredir/redir.cgi 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 +# A CGI script which performs an RFC 3875 "local redirect" to the path +# given in the query string. + +import os +import sys + +query = os.environ.get("QUERY_STRING", "") +if query: + sys.stdout.write("Location: {0}\r\n\r\n".format(query)) +else: + sys.stdout.write("Content-Type: text/plain\r\n\r\n") + sys.stdout.write("usage: redir.cgi?/local/path\n") diff -Nru apache2-2.4.69/test/modules/core/htdocs/cgiredir/target apache2-2.4.71/test/modules/core/htdocs/cgiredir/target --- apache2-2.4.69/test/modules/core/htdocs/cgiredir/target 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/core/htdocs/cgiredir/target 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 +# A file with no extension recognized by mod_mime. It must never be +# run as a CGI script, only served as plain content. + +import sys + +mark = "EXECUTED" +sys.stdout.write("Content-Type: text/plain\r\n\r\n") +sys.stdout.write(mark + "-AS-CGI\n") diff -Nru apache2-2.4.69/test/modules/core/htdocs/ssi/exec.shtml apache2-2.4.71/test/modules/core/htdocs/ssi/exec.shtml --- apache2-2.4.69/test/modules/core/htdocs/ssi/exec.shtml 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/core/htdocs/ssi/exec.shtml 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ + \ No newline at end of file diff -Nru apache2-2.4.69/test/modules/core/htdocs/userdir/testuser/public_html/cgi-bin/.htaccess apache2-2.4.71/test/modules/core/htdocs/userdir/testuser/public_html/cgi-bin/.htaccess --- apache2-2.4.69/test/modules/core/htdocs/userdir/testuser/public_html/cgi-bin/.htaccess 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/core/htdocs/userdir/testuser/public_html/cgi-bin/.htaccess 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +RequestHeader note internal_note "empty_note" diff -Nru apache2-2.4.69/test/modules/core/htdocs/userdir/testuser/public_html/cgi-bin/test.cgi apache2-2.4.71/test/modules/core/htdocs/userdir/testuser/public_html/cgi-bin/test.cgi --- apache2-2.4.69/test/modules/core/htdocs/userdir/testuser/public_html/cgi-bin/test.cgi 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/core/htdocs/userdir/testuser/public_html/cgi-bin/test.cgi 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,4 @@ +#!/bin/sh +echo "Content-Type: text/plain" +echo +echo "OK" diff -Nru apache2-2.4.69/test/modules/core/test_001_encoding.py apache2-2.4.71/test/modules/core/test_001_encoding.py --- apache2-2.4.69/test/modules/core/test_001_encoding.py 2024-07-11 08:18:12.000000000 +0000 +++ apache2-2.4.71/test/modules/core/test_001_encoding.py 2026-11-09 03:07:05.000000000 +0000 @@ -66,10 +66,10 @@ ["/cgi-bin/%%32%65%%32%65/echo.py", 400, ["AH10244"]], ["/nothing/%%32%65%%32%65/%%32%65%%32%65/h2_env.py", 400, ["AH10244"]], ["/cgi-bin/%%32%65%%32%65/%%32%65%%32%65/h2_env.py", 400, ["AH10244"]], - ["/nothing/%25%32%65%25%32%65/echo.py", 404, ["AH01264"]], - ["/cgi-bin/%25%32%65%25%32%65/echo.py", 404, ["AH01264"]], - ["/nothing/%25%32%65%25%32%65/%25%32%65%25%32%65/h2_env.py", 404, ["AH01264"]], - ["/cgi-bin/%25%32%65%25%32%65/%25%32%65%25%32%65/h2_env.py", 404, ["AH01264"]], + ["/nothing/%25%32%65%25%32%65/echo.py", 404, ["AH01264", "AH02811"]], + ["/cgi-bin/%25%32%65%25%32%65/echo.py", 404, ["AH01264", "AH02811"]], + ["/nothing/%25%32%65%25%32%65/%25%32%65%25%32%65/h2_env.py", 404, ["AH01264", "AH02811"]], + ["/cgi-bin/%25%32%65%25%32%65/%25%32%65%25%32%65/h2_env.py", 404, ["AH01264", "AH02811"]], ]) def test_core_001_04(self, env, path, status, lognos: Optional[List[str]]): url = env.mkurl("https", "test1", path) diff -Nru apache2-2.4.69/test/modules/core/test_002_restarts.py apache2-2.4.71/test/modules/core/test_002_restarts.py --- apache2-2.4.69/test/modules/core/test_002_restarts.py 2024-07-11 08:18:12.000000000 +0000 +++ apache2-2.4.71/test/modules/core/test_002_restarts.py 2026-11-09 03:07:05.000000000 +0000 @@ -88,8 +88,8 @@ @pytest.mark.skipif(condition='STRESS_TEST' not in os.environ, reason="STRESS_TEST not set in env") -@pytest.mark.skipif(condition=not CoreTestEnv().h2load_is_at_least('1.41.0'), - reason="h2load unavailable or misses --connect-to option") +@CoreTestEnv.needs_h2load("1.41.0", + reason="h2load unavailable or misses --connect-to option") class TestRestarts: def test_core_002_01(self, env): diff -Nru apache2-2.4.69/test/modules/core/test_003_cgi_env_vars.py apache2-2.4.71/test/modules/core/test_003_cgi_env_vars.py --- apache2-2.4.69/test/modules/core/test_003_cgi_env_vars.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/core/test_003_cgi_env_vars.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,33 @@ +import pytest + +from pyhttpd.conf import HttpdConf + +class TestCGIEnvVars: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + conf = HttpdConf(env, extras={ + 'base': f""" + + AllowOverride None + Options +ExecCGI + + SetEnv REQUEST-METHOD OVERRIDDEN + SetEnv QUERY.STRING OVERRIDDEN + """, + }) + conf.add_vhost_cgi() + conf.install() + assert env.apache_restart() == 0 + + def test_cgi_003_01(self, env): + """ + CVE-2025-65082: + Configuration-defined env vars must not override + server-calculated CGI env vars. + """ + url = env.mkurl("http", "cgi", "/env_parameters.py?x=123") + r = env.curl_get(url) + assert r.response["status"] == 200 + assert r.response["json"]["REQUEST_METHOD"] == "GET" + assert r.response["json"]["QUERY_STRING"] == "x=123" diff -Nru apache2-2.4.69/test/modules/core/test_004_ssi.py apache2-2.4.71/test/modules/core/test_004_ssi.py --- apache2-2.4.69/test/modules/core/test_004_ssi.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/core/test_004_ssi.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,35 @@ +import sys +import pytest +import textwrap + +from pyhttpd.conf import HttpdConf + +@pytest.mark.skipif(sys.platform == "win32", + reason="exec cmd= needs /bin/sh; echo is a cmd.exe builtin, not an executable") +class TestSSIInjection: + + @pytest.fixture(autouse=True, scope="class") + def _class_scope(self, env): + conf = HttpdConf(env, extras={ + "base": textwrap.dedent(f""" + + Options +Includes + AddType text/html .shtml + AddOutputFilter INCLUDES .shtml + + """) + }) + conf.install() + assert env.apache_restart() == 0 + + def test_ssi_004_01(self, env): + """ + CVE-2025-58098: + Server Side Includes must not add query string to #exec cmd=... + """ + url = env.mkurl("http", "htdocs", "/ssi/exec.shtml?INJECTED") + r = env.curl_get(url) + + body = r.response["body"].decode("utf-8") + assert "SSI_OK" in body + assert "INJECTED" not in body \ No newline at end of file diff -Nru apache2-2.4.69/test/modules/core/test_005_userdir_headers.py apache2-2.4.71/test/modules/core/test_005_userdir_headers.py --- apache2-2.4.69/test/modules/core/test_005_userdir_headers.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/core/test_005_userdir_headers.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,43 @@ +import os +import re +import pytest + +from pyhttpd.conf import HttpdConf + +class TestUserdirHeaders: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + + userdir_base = os.path.join(env.server_dir, "htdocs", "userdir") + + conf = HttpdConf(env, extras={ + 'base': f""" + UserDir "{userdir_base}/*/public_html" + + + AllowOverride FileInfo + Options +ExecCGI + SetHandler cgi-script + + """ + }) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + + def test_core_005_01_requestheader_note_rejected(self, env): + url = env.mkurl("http", "test1", "/~testuser/cgi-bin/test.cgi") + r = env.curl_get(url) + + # Check error log for the rejection message + re_rejection = re.compile(r".*RequestHeader does not support the 'note' action.*") + + directive_rejected = False + try: + directive_rejected = env.httpd_error_log.scan_recent(re_rejection) + except TimeoutError: + pass + + assert directive_rejected, \ + "RequestHeader note directive was not rejected" diff -Nru apache2-2.4.69/test/modules/core/test_006_merge_lang.py apache2-2.4.71/test/modules/core/test_006_merge_lang.py --- apache2-2.4.69/test/modules/core/test_006_merge_lang.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/core/test_006_merge_lang.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,39 @@ +import os +import pytest + +from pyhttpd.conf import HttpdConf + + +class TestMergeLanguage: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + # Create a file with two language extensions so mod_mime + # populates r->content_languages with nelts == nalloc == 2. + doc_dir = os.path.join(env.server_dir, "htdocs", "test1") + with open(os.path.join(doc_dir, "doc.en.fr.html"), "w") as f: + f.write("Hello World\n") + + conf = HttpdConf(env, extras={ + f"test1.{env.http_tld}": """ + AddLanguage en .en + AddLanguage fr .fr + Header set Content-Language "de, es" + """, + }) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + + # Requesting a file with two language extensions while mod_headers + # adds two non-matching Content-Language tokens should not crash. + # The merge loop in merge_response_headers must refresh its pointer + # to r->content_languages->elts after apr_array_push reallocates. + def test_core_006_01(self, env): + url = env.mkurl("http", "test1", "/doc.en.fr.html") + r = env.curl_get(url) + assert r.response, "no response: server may have crashed" + assert r.response["status"] == 200 + cl = r.response["header"]["content-language"] + for lang in ["en", "fr", "de", "es"]: + assert lang in cl, f"expected '{lang}' in Content-Language: {cl}" diff -Nru apache2-2.4.69/test/modules/core/test_007_cgi_redirect.py apache2-2.4.71/test/modules/core/test_007_cgi_redirect.py --- apache2-2.4.69/test/modules/core/test_007_cgi_redirect.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/core/test_007_cgi_redirect.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,43 @@ +import pytest +import textwrap + +from pyhttpd.conf import HttpdConf + +class TestCGIRedirectHandler: + + @pytest.fixture(autouse=True, scope="class") + def _class_scope(self, env): + conf = HttpdConf(env, extras={ + "base": textwrap.dedent(f""" + + Options +ExecCGI + + ForceType application/x-httpd-cgi + + + """) + }) + conf.install() + assert env.apache_restart() == 0 + + def test_cgi_007_01(self, env): + """A file with no extension known to mod_mime is not a CGI script.""" + url = env.mkurl("http", "htdocs", "/cgiredir/target") + r = env.curl_get(url) + assert r.response["status"] == 200 + body = r.response["body"].decode("utf-8") + assert "#!/usr/bin/env python3" in body + assert "EXECUTED-AS-CGI" not in body + + def test_cgi_007_02(self, env): + """ + A local redirect from a CGI script must not make the redirect + target inherit the trusted content-type of the script, since + that would run the target as a CGI script too. + """ + url = env.mkurl("http", "htdocs", "/cgiredir/redir.cgi?/cgiredir/target") + r = env.curl_get(url) + assert r.response["status"] == 200 + body = r.response["body"].decode("utf-8") + assert "#!/usr/bin/env python3" in body + assert "EXECUTED-AS-CGI" not in body diff -Nru apache2-2.4.69/test/modules/core/test_008_restart.py apache2-2.4.71/test/modules/core/test_008_restart.py --- apache2-2.4.69/test/modules/core/test_008_restart.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/core/test_008_restart.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,59 @@ +import sys + +import pytest + +from pyhttpd.conf import HttpdConf + + +class TestRestart: + """The server goes on serving across a restart, graceful or not. + + Whatever the MPM does with its children, what has to hold afterwards + is that a request is answered. A restart which leaves the listening + socket open with nothing behind it is the failure this catches, and + it is not visible from the exit status of "httpd -k restart". + """ + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + conf = HttpdConf(env) + conf.add_vhost_test1() + conf.install() + # Restarting is rough on mod_cgid, which is loaded for this + # package: the daemon is signalled along with the rest of the + # process group (AH01239), and restarting again before it has + # unlinked its socket leaves the new one unable to bind + # (AH01243). Neither is what these tests are about. + env.httpd_error_log.add_ignored_lognos(['AH01239', 'AH01243']) + assert env.apache_restart() == 0 + + def get(self, env, when): + # --max-time, or a server which accepts the connection and then + # says nothing hangs here rather than failing. + r = env.curl_get(env.mkurl("http", "test1", "/"), + options=['--max-time', '10']) + assert r.exit_code == 0, f"no answer {when}: {r.stderr}" + assert r.response['status'] == 200, f"bad status {when}" + + def test_core_008_01_graceful(self, env): + self.get(env, "before the reload") + assert env.apache_reload() == 0 + self.get(env, "after a graceful reload") + + def test_core_008_02_ungraceful(self, env): + """"httpd -k restart" keeps the same parent and serves again.""" + self.get(env, "before the restart") + pid = env.read_pid_file() + assert pid, "no pid file" + assert env.apache_hard_restart() == 0 + assert env.read_pid_file() == pid, "the restart replaced the parent" + self.get(env, "after an ungraceful restart") + + @pytest.mark.xfail(sys.platform == "win32", reason= + "mpm_winnt restart does not wait for child exit, " + "rapid restarts cause AH02322 socket errors") + def test_core_008_03_repeated(self, env): + """Restarting twice in a row is no different from once.""" + for _ in range(2): + assert env.apache_hard_restart() == 0 + self.get(env, "after repeated restarts") diff -Nru apache2-2.4.69/test/modules/core/test_009_allow_methods.py apache2-2.4.71/test/modules/core/test_009_allow_methods.py --- apache2-2.4.69/test/modules/core/test_009_allow_methods.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/core/test_009_allow_methods.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,49 @@ +import pytest + +from pyhttpd.backport import not_backported +from pyhttpd.conf import HttpdConf + + +class TestAllowMethods: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + conf = HttpdConf(env) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + + # mod_aptest passes the methods named in AP-Test-Allow-Methods to + # ap_allow_methods() from a fixups hook; the default handler then + # merges its own and answers OPTIONS with the Allow header. + def options(self, env, methods=None): + options = ['-X', 'OPTIONS'] + if methods is not None: + options.extend(['-H', f'AP-Test-Allow-Methods: {methods}']) + r = env.curl_get(env.mkurl("http", "test1", "/index.html"), + options=options) + assert r.response, f"no response: {r.stderr}" + assert r.response["status"] == 200 + return [m.strip() for m in r.response["header"]["allow"].split(',')] + + def test_core_009_01(self, env): + allow = self.options(env) + for method in ["GET", "POST", "OPTIONS"]: + assert method in allow, f"expected {method} in Allow: {allow}" + + def test_core_009_02(self, env): + allow = self.options(env, "DELETE") + assert "DELETE" in allow, f"expected DELETE in Allow: {allow}" + + # An extension method reaches make_allow() only through method_list, + # which is emitted just when the M_INVALID bit is set in method_mask. + @not_backported("r1938291", "unregistered methods listed in Allow") + def test_core_009_03(self, env): + allow = self.options(env, "FROBNICATE") + assert "FROBNICATE" in allow, f"expected FROBNICATE in Allow: {allow}" + + @not_backported("r1938291", "unregistered methods listed in Allow") + def test_core_009_04(self, env): + allow = self.options(env, "DELETE, FROBNICATE") + for method in ["DELETE", "FROBNICATE"]: + assert method in allow, f"expected {method} in Allow: {allow}" diff -Nru apache2-2.4.69/test/modules/core/test_010_limit_xml_request_body.py apache2-2.4.71/test/modules/core/test_010_limit_xml_request_body.py --- apache2-2.4.69/test/modules/core/test_010_limit_xml_request_body.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/core/test_010_limit_xml_request_body.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,48 @@ +import os + +import pytest + +from pyhttpd.backport import not_backported +from pyhttpd.conf import HttpdConf + + +class TestLimitXMLRequestBody: + + # have httpd parse the configuration, like "httpd -t" does + def check_config(self, env, value): + conf = HttpdConf(env) + conf.add(f"LimitXMLRequestBody {value}") + conf.add_vhost_test1() + conf.install() + return env.run([os.path.join(env.bin_dir, 'httpd'), + "-t", "-d", env.server_dir, + "-f", os.path.join(env.server_dir, 'conf', 'httpd.conf')]) + + @pytest.mark.parametrize("value", ["0", "1", "123", "1000000"]) + def test_core_010_001(self, env, value): + r = self.check_config(env, value) + assert r.exit_code == 0, f"{r.stderr}" + + # only a whole non-negative decimal number is a valid limit + # (without the change, only "-1" was rejected) + @pytest.mark.parametrize("value", [ + pytest.param(v, marks=not_backported( + "r1939121", "rejecting invalid LimitXMLRequestBody values")) + for v in ["abc123", "123abc", "123foo456", "1.5", "0x10", "+", "-"] + ] + ["-1"]) + def test_core_010_002(self, env, value): + r = self.check_config(env, value) + assert r.exit_code != 0, f"{value} was accepted" + assert "LimitXMLRequestBody requires a non-negative integer" in r.stderr + + # more than can be stored or than the directive permits + @pytest.mark.parametrize("value", [ + "9223372036854775807", "9223372036854775808", "9" * 40, + ]) + @not_backported("r1939121", "rejecting LimitXMLRequestBody values beyond a " + "32-bit long, which atol() truncates on Windows", + platform="win32") + def test_core_010_003(self, env, value): + r = self.check_config(env, value) + assert r.exit_code != 0, f"{value} was accepted" + assert "LimitXMLRequestBody" in r.stderr diff -Nru apache2-2.4.69/test/modules/core/test_012_config_off_t.py apache2-2.4.71/test/modules/core/test_012_config_off_t.py --- apache2-2.4.69/test/modules/core/test_012_config_off_t.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/core/test_012_config_off_t.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,117 @@ +import os + +import pytest + +from pyhttpd.backport import not_backported +from pyhttpd.conf import HttpdConf + +# size and time directives parsed with ap_size_parameter_parse(), which can +# not be negative +DIRECTIVES = [ + "LimitRequestBody", "LimitXMLRequestBody", + "ScriptLogLength", "ScriptLogBuffer", + "CacheMinFileSize", "CacheMaxFileSize", "CacheReadSize", "CacheReadTime", + "CacheSocacheMaxTime", "CacheSocacheMinTime", "CacheSocacheReadSize", + "CacheSocacheReadTime", +] + +OFF_T = not_backported("r1939129, r1939163", "strict size parsing for off_t directives") +OFF_T_WIN = not_backported("r1939129, r1939163", "strict size parsing for off_t directives", + platform="win32") + +# Without the change (2.4.x), these directives take the number at the start of +# a value and ignore the rest, and these also take a value without one; +# LimitRequestBody was strict already, but has no units and other messages. +UNCHECKED_TAIL = [d for d in DIRECTIVES if d != "LimitRequestBody"] +NO_DIGITS_OK = ["LimitXMLRequestBody", "ScriptLogLength", "ScriptLogBuffer"] +# The cache handlers check apr_strtoff()'s status, but not whether it consumed +# digits, and glibc leaves errno unset when there are none; Windows CI accepted +# them too. A libc that rejects these already gives a non-strict XPASS, which +# is fine. +NO_DIGITS_OK += [d for d in DIRECTIVES if d.startswith("Cache")] +NO_RANGE_CHECK = ["ScriptLogLength", "ScriptLogBuffer"] + + +def cases(values, lacks, lacks_win=lambda d, v: False): + """Every (directive, value), marked where a server without the change + gets it wrong: lacks(directive, value), or only on Windows: lacks_win.""" + return [pytest.param(d, v, marks=OFF_T if lacks(d, v) + else OFF_T_WIN if lacks_win(d, v) else ()) + for v in values for d in DIRECTIVES] + + +class TestConfigOffT: + + # have httpd parse the configuration, like "httpd -t" does + def check_config(self, env, directive, value): + conf = HttpdConf(env) + conf.add(f"{directive} {value}") + conf.add_vhost_test1() + conf.install() + return env.run([os.path.join(env.bin_dir, 'httpd'), + "-t", "-d", env.server_dir, + "-f", os.path.join(env.server_dir, 'conf', 'httpd.conf')]) + + @pytest.mark.parametrize("directive, value", cases( + ["0", "1", "123", "1000000", "00123", + "512b", "512B", "1k", "1K", "2m", "2M", "1g", "1G"], + lambda d, v: d == "LimitRequestBody" and not v.isdigit())) + def test_core_012_001(self, env, directive, value): + r = self.check_config(env, directive, value) + assert r.exit_code == 0, f"{r.stderr}" + + # only a whole decimal number is valid + @pytest.mark.parametrize("directive, value", cases( + ["abc", "abc123", "123abc", "123foo456", "12.3", "0x10", "1kb", "k", + "+", "-"], + lambda d, v: (d in UNCHECKED_TAIL and v[0].isdigit()) or d in NO_DIGITS_OK)) + def test_core_012_002(self, env, directive, value): + r = self.check_config(env, directive, value) + assert r.exit_code != 0, f"{value} was accepted" + assert directive in r.stderr, f"{r.stderr}" + + # a number the directive does not accept, or too large to be stored + @pytest.mark.parametrize("directive, value", cases( + ["-1", "9223372036854775808", "9" * 40, "8589934592g"], + lambda d, v: (d in UNCHECKED_TAIL and v.endswith("g")) or d in NO_RANGE_CHECK, + # atol() into a long, which is 32 bits on Windows + lambda d, v: d == "LimitXMLRequestBody" and v in ("9223372036854775808", "9" * 40))) + def test_core_012_003(self, env, directive, value): + r = self.check_config(env, directive, value) + assert r.exit_code != 0, f"{value} was accepted" + assert directive in r.stderr, f"{r.stderr}" + + # this one has a lower limit as well + @pytest.mark.parametrize("value, valid", [ + ["1024", True], ["102400", True], ["1023", False], ["0", False], + pytest.param("1024abc", False, marks=OFF_T), ["abc", False], ["9" * 40, False], + ]) + def test_core_012_004(self, env, value, valid): + r = self.check_config(env, "CacheSocacheMaxSize", value) + if valid: + assert r.exit_code == 0, f"{r.stderr}" + else: + assert r.exit_code != 0, f"{value} was accepted" + assert "CacheSocacheMaxSize" in r.stderr, f"{r.stderr}" + + # LimitRequestBody reports invalid syntax (APR_EINVAL) and a number + # which does not fit (APR_ERANGE) differently. A value without digits + # is invalid syntax regardless of the status returned by the + # underlying strto*() implementation: FreeBSD and musl set EINVAL + # there, glibc does not. + # (a value without digits gets another message without the change) + @pytest.mark.parametrize("value, error", [ + pytest.param("abc", "requires a non-negative integer", marks=OFF_T), + pytest.param("+", "requires a non-negative integer", marks=OFF_T), + pytest.param("-", "requires a non-negative integer", marks=OFF_T), + pytest.param("abc123", "requires a non-negative integer", marks=OFF_T), + ["123abc", "requires a non-negative integer"], + ["12.3", "requires a non-negative integer"], + ["9223372036854775808", "argument is not parsable"], + ["-9223372036854775809", "argument is not parsable"], + ["9" * 40, "argument is not parsable"], + ]) + def test_core_012_005(self, env, value, error): + r = self.check_config(env, "LimitRequestBody", value) + assert r.exit_code != 0, f"{value} was accepted" + assert f"LimitRequestBody {error}" in r.stderr, f"{r.stderr}" diff -Nru apache2-2.4.69/test/modules/dav/test_002_options_trace.py apache2-2.4.71/test/modules/dav/test_002_options_trace.py --- apache2-2.4.69/test/modules/dav/test_002_options_trace.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/dav/test_002_options_trace.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,77 @@ +import os + +import pytest + +from pyhttpd.backport import not_backported +from pyhttpd.conf import HttpdConf + +DAV_METHODS = {"OPTIONS", "GET", "HEAD", "POST", "DELETE", "PROPFIND", + "PROPPATCH", "COPY", "MOVE", "LOCK", "UNLOCK"} + + +def _setup(env, trace): + dav_dir = os.path.join(env.gen_dir, 'dav') + os.makedirs(os.path.join(dav_dir, 'col'), exist_ok=True) + with open(os.path.join(dav_dir, 'file.txt'), 'w') as fd: + fd.write('hello\n') + conf = HttpdConf(env, extras={ + 'base': f""" + DavLockDB "{env.gen_dir}/davlock" + TraceEnable {trace} + """, + f"test1.{env.http_tld}": f""" + Alias /dav "{dav_dir}" + + Dav On + Require all granted + + """, + }) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + + +def _request(env, method, path): + r = env.curl_raw(env.mkurl("https", "test1", path), + options=['-X', method]) + assert r.response, f"no response: {r.stderr}" + allow = r.response["header"].get("allow", "") + return r.response["status"], {m.strip() for m in allow.split(',') if m.strip()} + + +class TestOptionsTraceDisabled: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + _setup(env, "off") + + # TRACE is not offered while the server refuses it + @pytest.mark.parametrize("path", ["/dav/file.txt", "/dav/col/"]) + @not_backported("r1939146", "DAV OPTIONS Allow honouring TraceEnable") + def test_dav_002_001(self, env, path): + status, allow = _request(env, "OPTIONS", path) + assert status == 200 + assert "TRACE" not in allow, f"Allow: {allow}" + assert DAV_METHODS <= allow, f"Allow: {allow}" + + def test_dav_002_002(self, env): + assert _request(env, "TRACE", "/dav/file.txt")[0] == 405 + + +class TestOptionsTraceEnabled: + + @pytest.fixture(autouse=True, scope='class', + params=["on", "extended"]) + def _class_scope(self, env, request): + _setup(env, request.param) + + # unchanged: TRACE is offered and works + @pytest.mark.parametrize("path", ["/dav/file.txt", "/dav/col/"]) + def test_dav_002_101(self, env, path): + status, allow = _request(env, "OPTIONS", path) + assert status == 200 + assert (DAV_METHODS | {"TRACE"}) <= allow, f"Allow: {allow}" + + def test_dav_002_102(self, env): + assert _request(env, "TRACE", "/dav/file.txt")[0] == 200 diff -Nru apache2-2.4.69/test/modules/dav/test_003_config_quota.py apache2-2.4.71/test/modules/dav/test_003_config_quota.py --- apache2-2.4.69/test/modules/dav/test_003_config_quota.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/dav/test_003_config_quota.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,38 @@ +import os + +import pytest + +from pyhttpd.backport import skip_not_backported +from pyhttpd.conf import HttpdConf + +pytestmark = skip_not_backported("r1939129", "DAVquota") + + +class TestConfigQuota: + + # have httpd parse the configuration, like "httpd -t" does + def check_config(self, env, value): + conf = HttpdConf(env) + conf.add(f"DavQuota {value}") + conf.add_vhost_test1() + conf.install() + return env.run([os.path.join(env.bin_dir, 'httpd'), + "-t", "-d", env.server_dir, + "-f", os.path.join(env.server_dir, 'conf', 'httpd.conf')]) + + @pytest.mark.parametrize("value", [ + "Off", "None", "0", "1", "123", "1000000", "00123", + ]) + def test_dav_003_01(self, env, value): + r = self.check_config(env, value) + assert r.exit_code == 0, f"{r.stderr}" + + # not Off, None or a size, in particular no negative number, which would + # be taken for one of the first two + @pytest.mark.parametrize("value", [ + "abc", "123abc", "12.3", "0x10", "-1", "-2", "9" * 40, + ]) + def test_dav_003_02(self, env, value): + r = self.check_config(env, value) + assert r.exit_code != 0, f"{value} was accepted" + assert "DAVquota must be Off, None, or a size in bytes" in r.stderr diff -Nru apache2-2.4.69/test/modules/dav/test_004_lock_timeout_overflow.py apache2-2.4.71/test/modules/dav/test_004_lock_timeout_overflow.py --- apache2-2.4.69/test/modules/dav/test_004_lock_timeout_overflow.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/dav/test_004_lock_timeout_overflow.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,86 @@ +import os +import re + +import pytest + +from pyhttpd.conf import HttpdConf + +LOCK_BODY = ( + '' + '' + '' + '' + '' +) + + +# BZ 65788: dav_get_timeout() in modules/dav/main/util.c parsed a +# "Timeout: Second-" header with atol(), which has no overflow +# detection. A large enough digit string overflows it, and the later +# "now + expires" addition overflows time_t too, so the lock ends up +# stored with a nonsensical (already-expired) timeout instead of the +# large-but-finite one the client asked for -- a LOCK request that looks +# successful (200, a valid Lock-Token) silently grants a lock that is +# already expired. +class TestLockTimeoutOverflow: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + dav_dir = os.path.join(env.gen_dir, 'dav-timeout-overflow') + os.makedirs(dav_dir, exist_ok=True) + for name in ('file1.txt', 'file2.txt', 'file3.txt'): + with open(os.path.join(dav_dir, name), 'w') as fd: + fd.write('hello\n') + + conf = HttpdConf(env, extras={ + 'base': f""" + DavLockDB "{env.gen_dir}/davlock-timeout-overflow" + """, + f"test1.{env.http_tld}": f""" + Alias /dav-timeout-overflow "{dav_dir}" + + Dav On + Require all granted + + """, + }) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + + def _lock(self, env, path, timeout_hdr): + url = env.mkurl("https", "test1", path) + return env.curl_raw(url, options=[ + '-X', 'LOCK', '-H', f'Timeout: {timeout_hdr}', + '-H', 'Content-Type: text/xml', '--data-binary', LOCK_BODY]) + + # an overflowing Second- value must not silently collapse to an + # already-expired (or otherwise nonsensical) timeout; it is clamped + # to the RFC 4918 TimeOut maximum of 2^32 - 1 instead + def test_dav_004_01(self, env): + r = self._lock(env, "/dav-timeout-overflow/file1.txt", + "Second-99999999999999999999999999999999") + assert r.response["status"] == 200, r.response + m = re.search(r'([^<]*)', r.stdout) + assert m, f"no in response: {r.stdout}" + assert m.group(1) == "Second-4294967295", \ + f"timeout: {m.group(1)}" + + # an ordinary, in-range timeout request is unaffected + def test_dav_004_02(self, env): + r = self._lock(env, "/dav-timeout-overflow/file2.txt", "Second-600") + assert r.response["status"] == 200, r.response + m = re.search(r'([^<]*)', r.stdout) + assert m, f"no in response: {r.stdout}" + assert m.group(1) == "Second-600", f"timeout: {m.group(1)}" + + # a Second- value with no digits at all parses the same way atol() + # already did (0, not an error), same as before this fix: an + # immediately-expired lock, not Infinite + def test_dav_004_03(self, env): + r = self._lock(env, "/dav-timeout-overflow/file3.txt", + "Second-notanumber") + assert r.response["status"] == 200, r.response + m = re.search(r'([^<]*)', r.stdout) + assert m, f"no in response: {r.stdout}" + assert m.group(1) == "Second-0", f"timeout: {m.group(1)}" diff -Nru apache2-2.4.69/test/modules/filters/conftest.py apache2-2.4.71/test/modules/filters/conftest.py --- apache2-2.4.69/test/modules/filters/conftest.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/filters/conftest.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,30 @@ +import logging +import os +import sys + +import pytest + +from .env import FiltersTestEnv + +sys.path.append(os.path.join(os.path.dirname(__file__), '../..')) + + +def pytest_report_header(config, start_path): + env = FiltersTestEnv() + return f"filters [apache: {env.get_httpd_version()}, mpm: {env.mpm_module}, {env.prefix}]" + + +@pytest.fixture(scope="package") +def env(pytestconfig) -> FiltersTestEnv: + logging.getLogger('').setLevel(level=logging.INFO) + env = FiltersTestEnv(pytestconfig=pytestconfig) + env.setup_httpd() + env.apache_access_log_clear() + env.httpd_error_log.clear_log() + return env + + +@pytest.fixture(autouse=True, scope="package") +def _stop_package_scope(env): + yield + assert env.apache_stop() == 0 diff -Nru apache2-2.4.69/test/modules/filters/env.py apache2-2.4.71/test/modules/filters/env.py --- apache2-2.4.69/test/modules/filters/env.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/filters/env.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,26 @@ +import inspect +import logging +import os + +from pyhttpd.env import HttpdTestEnv, HttpdTestSetup + +log = logging.getLogger(__name__) + + +class FiltersTestSetup(HttpdTestSetup): + + def __init__(self, env: 'HttpdTestEnv'): + super().__init__(env=env) + self.add_source_dir(os.path.dirname(inspect.getfile(FiltersTestSetup))) + self.add_modules(["substitute", "sed", "buffer"]) + self.add_cgi_module() + + +class FiltersTestEnv(HttpdTestEnv): + + def __init__(self, pytestconfig=None): + super().__init__(pytestconfig=pytestconfig) + self.add_httpd_log_modules(["substitute", "sed", "core"]) + + def setup_httpd(self, setup: HttpdTestSetup = None): + super().setup_httpd(setup=FiltersTestSetup(env=self)) diff -Nru apache2-2.4.69/test/modules/filters/htdocs/test1/cgi/echo.py apache2-2.4.71/test/modules/filters/htdocs/test1/cgi/echo.py --- apache2-2.4.69/test/modules/filters/htdocs/test1/cgi/echo.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/filters/htdocs/test1/cgi/echo.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +# Echo the request body back verbatim, so an InputSed test can see exactly +# what the input filter handed to the handler. Read to EOF rather than +# CONTENT_LENGTH bytes: mod_sed rewrites the body but not the header, so the +# two need not agree. +import sys + +body = sys.stdin.buffer.read() +print("Content-Type: text/plain") +print() +sys.stdout.flush() +sys.stdout.buffer.write(body) diff -Nru apache2-2.4.69/test/modules/filters/htdocs/test1/cgi/nobody.py apache2-2.4.71/test/modules/filters/htdocs/test1/cgi/nobody.py --- apache2-2.4.69/test/modules/filters/htdocs/test1/cgi/nobody.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/filters/htdocs/test1/cgi/nobody.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +# Headers and nothing else, so the output filters see a response whose body +# is a lone EOS. +print("Content-Type: text/html") +print() diff -Nru apache2-2.4.69/test/modules/filters/test_001_substitute_maxlen.py apache2-2.4.71/test/modules/filters/test_001_substitute_maxlen.py --- apache2-2.4.69/test/modules/filters/test_001_substitute_maxlen.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/filters/test_001_substitute_maxlen.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,83 @@ +import os + +import pytest + +from pyhttpd.backport import not_backported +from pyhttpd.conf import HttpdConf + +# SubstituteMaxLineLength caps the length of a line after substitution. Each +# flag combination reaches a different branch of do_pattmatch(): regex or +# literal ('n'), flattened into one bucket or left as separate ones ('q'). +MODES = { + 'regex_flatten': 'f', + 'regex_quick': 'q', + 'literal_flatten': 'nf', + 'literal_quick': 'nq', +} + +MAXLEN = 10 +# "x" expands to "yy", so each line grows by exactly one byte. +OVER = "x" + "a" * 9 # 10 bytes in, 11 out -- over the limit +OVER_MID = "aaaa" + "x" + "aaaaa" # same, with the match off the start +OVER_END = "a" * 9 + "x" # same again, but with no unmatched tail +EXACT = "x" + "a" * 8 # 9 bytes in, 10 out -- exactly at the limit + + +class TestSubstituteMaxLineLength: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + doc_dir = os.path.join(env.server_dir, "htdocs", "test1") + for name, text in [("over.html", OVER), ("over_mid.html", OVER_MID), + ("over_end.html", OVER_END), ("exact.html", EXACT)]: + with open(os.path.join(doc_dir, name), "w") as f: + f.write(text) + # APLOGNO(01328) "Line too long" is the expected rejection. + env.httpd_error_log.add_ignored_lognos(["AH01328"]) + + def configure(self, env, flags): + conf = HttpdConf(env, extras={ + f"test1.{env.http_tld}": f""" + SubstituteMaxLineLength {MAXLEN} + + AddOutputFilterByType SUBSTITUTE text/html + Substitute "s/x/yy/{flags}" + + """, + }) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + + def get(self, env, path): + return env.curl_get(env.mkurl("http", "test1", path)) + + # A line that would grow past the limit must be rejected, not truncated + # and not returned over-length. + # (without the change, a regex substitution with more of the line after + # the last match gets it wrong) + @pytest.mark.parametrize("mode, doc", [ + pytest.param(mode, doc, id=f"{mode}-{doc}", marks=not_backported( + "r1938306", "SubstituteMaxLineLength counting the unmatched tail") + if mode.startswith("regex") and doc != "over_end.html" else ()) + for mode in MODES for doc in ["over.html", "over_mid.html", "over_end.html"] + ]) + def test_filters_001_01(self, env, mode, doc): + self.configure(env, MODES[mode]) + r = self.get(env, f"/{doc}") + body = r.response["body"].decode() if r.response else None + assert not (r.response and r.response["status"] == 200 + and len(body) > MAXLEN), \ + f"{mode} {doc}: over-length line returned: " \ + f"{len(body) if body else None} bytes {body!r}" + + # A line that lands exactly on the limit is still served. + @pytest.mark.parametrize("mode", list(MODES), ids=list(MODES)) + def test_filters_001_02(self, env, mode): + self.configure(env, MODES[mode]) + r = self.get(env, "/exact.html") + assert r.response, f"{mode}: no response" + assert r.response["status"] == 200, f"{mode}: status {r.response['status']}" + body = r.response["body"].decode() + assert body == "yy" + "a" * 8, f"{mode}: body {body!r}" + assert len(body) == MAXLEN diff -Nru apache2-2.4.69/test/modules/filters/test_002_sed_ycomp.py apache2-2.4.71/test/modules/filters/test_002_sed_ycomp.py --- apache2-2.4.69/test/modules/filters/test_002_sed_ycomp.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/filters/test_002_sed_ycomp.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,62 @@ +import sys + +import pytest + +from pyhttpd.backport import not_backported +from pyhttpd.conf import HttpdConf + +# ycomp() reports "transform strings not the same size" when the two halves of +# a y/// command differ in length, but used to carry on afterwards: the loop +# kept advancing tsp, past the closing delimiter and past the NUL terminating +# the config line, reading whatever followed until it happened to stop. The +# error eventually reported was therefore the wrong one, "ending delimiter +# missing", raised once the walk fell off the end of the string. +TOO_SHORT = "transform strings not the same size" +FELL_OFF_THE_END = "ending delimiter missing" + + +# On Windows the "apachectl" pyhttpd drives is httpd.exe itself, where +# "-k start" means "start the installed service" and fails with AH00436 +# before the configuration is ever read, so there is no compile error to +# assert on. +@pytest.mark.skipif(sys.platform == "win32", + reason="httpd -k start is service control on Windows") +class TestSedYComp: + + def install(self, env, expr): + conf = HttpdConf(env, extras={ + f"test1.{env.http_tld}": f""" + + AddOutputFilterByType SED text/html + OutputSed "{expr}" + + """, + }) + conf.add_vhost_test1() + conf.install() + return env._run_apachectl("start") + + # Source longer than destination: rejected, naming the real problem. + @pytest.mark.parametrize("expr", ["y/abc/de/", "y/abcdef/de/", + "y/abcdefgh/x/"]) + @not_backported("r1938337", "mod_sed y command size mismatch error") + def test_filters_002_01(self, env, expr): + r = self.install(env, expr) + assert r.exit_code != 0, f"httpd started with bad expression {expr}" + assert TOO_SHORT in r.stderr, \ + f"{expr}: expected '{TOO_SHORT}', got: {r.stderr.strip()}" + assert FELL_OFF_THE_END not in r.stderr, \ + f"{expr}: parser walked off the end: {r.stderr.strip()}" + + # Destination longer than source is caught by the trailing check and has + # always reported the right error; keep it that way. + def test_filters_002_02(self, env): + r = self.install(env, "y/ab/abc/") + assert r.exit_code != 0 + assert TOO_SHORT in r.stderr, r.stderr + + # A well-formed y/// still compiles and the server starts. + def test_filters_002_03(self, env): + r = self.install(env, "y/abc/xyz/") + assert r.exit_code == 0, f"valid expression rejected: {r.stderr}" + assert env.is_live(), "httpd did not come up" diff -Nru apache2-2.4.69/test/modules/filters/test_003_sed.py apache2-2.4.71/test/modules/filters/test_003_sed.py --- apache2-2.4.69/test/modules/filters/test_003_sed.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/filters/test_003_sed.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,565 @@ +import os +import re + +import pytest + +from pyhttpd.backport import not_backported, skip_not_backported +from pyhttpd.conf import HttpdConf + +# for the cases a server without the change gets wrong +COMPILE_CHECKS = not_backported("r1938962", "mod_sed expression compile checks") + +# mod_sed implements the Solaris 10 sed language over the response (OutputSed) +# or the request body (InputSed). Almost none of it was covered before, so +# these tests walk the commands the module documents plus the paths mod_sed.c +# itself has: the 8000-byte output buffer, the transient-bucket flush, the +# empty-body short circuit and the error path. + +# The document most tests run against. Three lines, trailing newline. +DOC = "one monday two\nthree sunday four\nmonday monday monday\n" + +# huge.html: enough ordinary lines to push mod_sed past MAX_TRANSIENT_BUCKETS +# (50 buckets of MODSED_OUTBUF_SIZE, so 400000 bytes) and make it flush what it +# has to the client, then a single line past the 8 MB (MAX_BUF_SIZE) a line may +# grow to, which fails the evaluation. +HUGE_HEAD_LINES = 500 +HUGE_LINE_LEN = 900 +HUGE_HEAD_LEN = HUGE_HEAD_LINES * (HUGE_LINE_LEN + 1) +HUGE_TAIL_LEN = 9 * 1024 * 1024 + +# For a test whose failure mode is "never answers" rather than "answers +# wrongly". pyhttpd only gives curl --connect-timeout. +BOUNDED = ["--max-time", "30"] + + +class TestSed: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + self.docdir = os.path.join(env.server_dir, "htdocs", "test1") + self.write(env, "sed.html", DOC) + # Same text with no newline after the last line. + self.write(env, "nonl.html", DOC[:-1]) + self.write(env, "empty.html", "") + # One line per 4 KB, well past MODSED_OUTBUF_SIZE and past the 50 + # transient buckets mod_sed flushes at. + self.write(env, "big.html", + "".join(f"line {i:06d} monday {'x' * 4000}\n" + for i in range(500))) + # A byte from each interesting class for the l command: printable, + # tab, DEL and three high-bit bytes. + self.write_bytes(env, "bytes.html", b"a\tb\x7fc\xffd\x80e\xa9f\n") + self.write(env, "insert.txt", "INSERTED\n") + # First line has nothing for \(a*\) to capture, second has "aa". + self.write(env, "backref.html", "b\naab\n") + head = "".join( + f"line {i:06d} monday ".ljust(HUGE_LINE_LEN, "y") + "\n" + for i in range(HUGE_HEAD_LINES)) + assert len(head) == HUGE_HEAD_LEN + self.write(env, "huge.html", head + "z" * HUGE_TAIL_LEN + "\n") + + @staticmethod + def write(env, name, text): + TestSed.write_bytes(env, name, text.encode()) + + @staticmethod + def write_bytes(env, name, data): + path = os.path.join(env.server_dir, "htdocs", "test1", name) + with open(path, "wb") as f: + f.write(data) + + @staticmethod + def sed_path(env, name): + """The path of a document as an "r" command has to spell it: sed + reads a backslash in a filename as an escape and drops it, so a + Windows path only survives with forward slashes.""" + return os.path.join(env.server_dir, "htdocs", "test1", + name).replace(os.sep, "/") + + def configure(self, env, exprs, extra="", input_sed=False, fails=False): + if isinstance(exprs, str): + exprs = [exprs] + directive = "InputSed" if input_sed else "OutputSed" + lines = [f' {directive} "{e}"' + for e in exprs if isinstance(e, str)] + # A directive given as bytes may hold bytes which are not UTF-8, so + # it goes into a file of its own, written as-is. + raw = [e for e in exprs if isinstance(e, bytes)] + if raw: + path = os.path.join(env.server_dir, "conf", "sed-raw.conf") + with open(path, "wb") as f: + f.writelines(directive.encode() + b' "' + e + b'"\n' + for e in raw) + lines.append(f' Include "{path}"') + lines = "\n".join(lines) + conf = HttpdConf(env, extras={ + f"test1.{env.http_tld}": f""" + + AddOutputFilterByType SED text/html + AddInputFilter SED py + AddHandler cgi-script .py + Options +ExecCGI +{lines} + + {extra} + """, + }) + conf.add_vhost_test1() + conf.install() + if fails: + # Check the configuration with httpd -t, which reports the error + # on stderr on every platform; the server is not restarted. + r = env.run([os.path.join(env.bin_dir, "httpd"), + "-d", env.server_dir, + "-f", os.path.join(env.server_dir, "conf", + "httpd.conf"), "-t"]) + assert r.exit_code != 0, f"{exprs}: accepted" + return r.stderr + assert env.apache_restart() == 0 + + def get(self, env, path="/sed.html", options=None): + return env.curl_get(env.mkurl("http", "test1", path), options=options) + + def body(self, env, exprs, path="/sed.html", **kwargs): + self.configure(env, exprs, **kwargs) + r = self.get(env, path) + assert r.response, f"no response for {exprs}" + assert r.response["status"] == 200, \ + f"{exprs}: status {r.response['status']}" + return r.response["body"] + + # --- substitution ----------------------------------------------------- + + # s/// replaces the first match on each line and nothing else. + def test_filters_003_01(self, env): + assert self.body(env, "s/monday/MON/").decode() == \ + "one MON two\nthree sunday four\nMON monday monday\n" + + # s///g replaces every match on the line. + def test_filters_003_02(self, env): + assert self.body(env, "s/monday/MON/g").decode() == \ + "one MON two\nthree sunday four\nMON MON MON\n" + + # s///2 replaces only the second match. + def test_filters_003_03(self, env): + assert self.body(env, "s/monday/MON/2").decode() == \ + "one monday two\nthree sunday four\nmonday MON monday\n" + + # Several OutputSed directives accumulate into one script, applied in the + # order they appear. This is the configuration the manual shows. + def test_filters_003_04(self, env): + assert self.body(env, ["s/monday/MON/g", "s/sunday/SUN/g"]).decode() \ + == "one MON two\nthree SUN four\nMON MON MON\n" + + # A back reference in the replacement. + def test_filters_003_05(self, env): + assert self.body(env, r"s/\(mon\)day/[\1]/g").decode() == \ + "one [mon] two\nthree sunday four\n[mon] [mon] [mon]\n" + + # & in the replacement is the whole match. + def test_filters_003_06(self, env): + assert self.body(env, "s/monday/<&>/").decode() == \ + "one two\nthree sunday four\n monday monday\n" + + # --- addresses -------------------------------------------------------- + + # A line-number address. + def test_filters_003_07(self, env): + assert self.body(env, "2d").decode() == \ + "one monday two\nmonday monday monday\n" + + # $ is the last line. + def test_filters_003_08(self, env): + assert self.body(env, "$d").decode() == \ + "one monday two\nthree sunday four\n" + + # A regex range. + def test_filters_003_09(self, env): + assert self.body(env, "/sunday/,$d").decode() == "one monday two\n" + + # A negated address. + def test_filters_003_10(self, env): + assert self.body(env, "/sunday/!d").decode() == "three sunday four\n" + + # = writes the line number ahead of the line. + def test_filters_003_11(self, env): + assert self.body(env, "=").decode() == \ + "1\none monday two\n2\nthree sunday four\n3\nmonday monday monday\n" + + # --- hold space ------------------------------------------------------- + + # G appends the hold space, which starts empty: classic double spacing. + def test_filters_003_12(self, env): + assert self.body(env, "G").decode() == \ + "one monday two\n\nthree sunday four\n\nmonday monday monday\n\n" + + # h saves, then x swaps: every line is replaced by the one before it. + def test_filters_003_13(self, env): + assert self.body(env, "x").decode() == \ + "\none monday two\nthree sunday four\n" + + # H;$!d;x collects the whole document in the hold space and prints it once. + def test_filters_003_14(self, env): + assert self.body(env, ["H", "$!d", "x"]).decode() == "\n" + DOC + + # --- transliteration -------------------------------------------------- + + def test_filters_003_15(self, env): + assert self.body(env, "y/aeiou/AEIOU/").decode() == \ + "OnE mOndAy twO\nthrEE sUndAy fOUr\nmOndAy mOndAy mOndAy\n" + + # --- line handling ---------------------------------------------------- + + # A document whose last line has no newline gets one: documented Solaris + # sed behaviour, and the reason mod_sed drops Content-Length. + def test_filters_003_16(self, env): + assert self.body(env, "s/monday/MON/g", path="/nonl.html").decode() \ + == "one MON two\nthree sunday four\nMON MON MON\n" + + # An empty body stays empty; mod_sed must not invent a line. + def test_filters_003_17(self, env): + assert self.body(env, "s/monday/MON/", path="/empty.html") == b"" + + # mod_sed drops the handler's Content-Length because the body it produces + # is a different size. The length that reaches the client must be the one + # after filtering -- ap_content_length_filter recomputes it -- and never + # the file's own. + def test_filters_003_18(self, env): + self.configure(env, "s/monday/MONDAYMONDAY/g") + r = self.get(env) + assert r.response["status"] == 200 + grown = len(DOC) + 4 * len("MONDAY") + assert len(r.response["body"]) == grown + assert r.response["header"]["content-length"] == str(grown), \ + r.response["header"] + + # The handler still generates a body for HEAD -- the protocol filters + # discard it -- so the filter runs and HEAD must announce the same + # Content-Length the matching GET returns, not the file's own. + def test_filters_003_19(self, env): + self.configure(env, "s/monday/MONDAYMONDAY/g") + r = self.get(env, options=["-I"]) + assert r.response, "no response to HEAD" + assert r.response["status"] == 200 + grown = len(DOC) + 4 * len("MONDAY") + assert r.response["header"]["content-length"] == str(grown), \ + r.response["header"] + + # A handler which produces headers and no body reaches mod_sed as a lone + # EOS. There is nothing to evaluate, so the filter takes itself out of + # the chain without touching the response. + def test_filters_003_20(self, env): + self.configure(env, "s/monday/MON/") + r = self.get(env, "/cgi/nobody.py") + assert r.response, "no response" + assert r.response["status"] == 200 + assert r.response["body"] == b"" + + # A body far larger than the 8000-byte output buffer, and with more than + # the 50 transient buckets mod_sed flushes at, comes back intact. + def test_filters_003_21(self, env): + body = self.body(env, "s/monday/MON/", path="/big.html") + assert len(body) == 500 * (len("line 000000 MON ") + 4000 + 1) + lines = body.decode().splitlines() + assert len(lines) == 500 + assert lines[0] == "line 000000 MON " + "x" * 4000 + assert lines[499] == "line 000499 MON " + "x" * 4000 + + # --- the l command ---------------------------------------------------- + + # l writes the line in a printable form: control characters as the escapes + # in trans[], everything else non-printable as a three-digit octal escape. + # Bytes with the top bit set used to come out as nonsense ("\/77" for 0xff) + # because the shift was done on a signed char. + @not_backported("r1938487", "mod_sed l command sign extension") + def test_filters_003_22(self, env): + body = self.body(env, "l;d", path="/bytes.html") + assert body == b"a\\11b\\177c\\377d\\200e\\251f\n", body + + # --- error handling --------------------------------------------------- + + # A branch to a label that is never defined compiles -- the label could + # still arrive from a later OutputSed -- and fails at the first byte of + # every response instead. Nothing has been written at that point, so the + # failure can and must still become a 500. + def test_filters_003_23(self, env): + env.httpd_error_log.add_ignored_lognos(["AH02998", "AH10394"]) + self.configure(env, "b nolabel") + r = self.get(env) + assert r.response, "no response at all" + assert r.response["status"] == 500, r.response["status"] + assert env.httpd_error_log.scan_recent( + re.compile(r'.*undefined label: nolabel')) + + # The same failure once the response is already on its way. huge.html is + # 450 KB of ordinary lines -- enough for mod_sed to hit MAX_TRANSIENT_BUCKETS + # and flush the start of the response to the client -- followed by one line + # over the 8 MB a line may grow to, which fails the evaluation. + # + # The response cannot become a 500 any more, so it has to be terminated as + # what it is: a truncated 200. mod_sed used to skip both the final flush + # and the pass because of the error status, dropping the EOS with them, and + # the client was left waiting on a response that never ended. + @not_backported("r1938485", "mod_sed EOS on an evaluation error") + def test_filters_003_24(self, env): + env.httpd_error_log.add_ignored_lognos(["AH02998", "AH10394"]) + self.configure(env, "s/monday/MON/") + r = self.get(env, "/huge.html") + assert r.exit_code == 0, \ + f"curl failed ({r.exit_code}): the response was never terminated" + assert r.response["status"] == 200 + body = r.response["body"] + # Truncated: the lines evaluated before the failure, and no more. + assert 0 < len(body) < HUGE_HEAD_LEN + 1024, len(body) + assert body.startswith(b"line 000000 MON ") + assert env.httpd_error_log.scan_recent( + re.compile(r'.*error evaluating sed on output')) + + # SED_ABUFSIZE is the size of the append/read queue a line may build up. + # Filling it exactly used to write the terminating NULL one past the end + # of eval->abuf, over eval->aptr itself, and the next line then wrote + # through that NULL pointer. + @skip_not_backported("r1938486", "mod_sed r and w command buffer overflow (SIGSEGV)") + def test_filters_003_25(self, env): + env.httpd_error_log.add_ignored_lognos(["AH02998"]) + path = self.sed_path(env, "insert.txt") + self.configure(env, [f"r {path}"] * 20) + r = self.get(env) + assert r.response, "no response: the server died" + assert r.response["status"] == 200 + # 19 of the 20 fit; the last is refused, and says so. + assert r.response["body"].count(b"INSERTED") == 3 * 19 + assert env.httpd_error_log.scan_recent( + re.compile(r'.*too many reads after line')) + + # One below the limit has always worked and must keep working. + def test_filters_003_26(self, env): + path = self.sed_path(env, "insert.txt") + body = self.body(env, [f"r {path}"] * 19) + assert body.count(b"INSERTED") == 3 * 19 + + # --- backreferences --------------------------------------------------- + + # A starred backreference whose capture matched nothing repeats a + # zero-length match: the matcher steps the input pointer by the capture + # length, so it has to stop rather than repeat it, and the star can only + # ever match once. Both lines reduce to their first match plus the rest. + # + # --max-time because a regression here does not return an error, it stops + # answering: pyhttpd gives curl --connect-timeout but no overall limit, so + # without one of our own a failure hangs the whole run rather than failing + # it. Note the worker keeps spinning after curl gives up. + @skip_not_backported("r1938489", "mod_sed starred backreference loop (hangs a worker)") + def test_filters_003_27(self, env): + self.configure(env, r"s/\(a*\)\1*/X/") + r = self.get(env, "/backref.html", options=BOUNDED) + assert r.exit_code == 0, f"curl failed ({r.exit_code}): no response" + assert r.response["status"] == 200 + assert r.response["body"] == b"Xb\nXb\n" + + # --- input filter ----------------------------------------------------- + + # The same expression on a request body, which runs the same matcher. + @skip_not_backported("r1938489", "mod_sed starred backreference loop (hangs a worker)") + def test_filters_003_28(self, env): + self.configure(env, r"s/\(a*\)\1*/X/", input_sed=True) + r = env.curl_post_data(env.mkurl("http", "test1", "/cgi/echo.py"), + data="b\naab\n", options=list(BOUNDED)) + assert r.exit_code == 0, f"curl failed ({r.exit_code}): no response" + assert r.response["status"] == 200 + assert r.response["body"] == b"Xb\nXb\n" + + # InputSed rewrites the request body before the handler sees it. + def test_filters_003_29(self, env): + self.configure(env, "s/monday/MON/g", input_sed=True) + r = env.curl_post_data(env.mkurl("http", "test1", "/cgi/echo.py"), + data="one monday two\nmonday monday\n") + assert r.response, "no response" + assert r.response["status"] == 200 + assert r.response["body"] == b"one MON two\nMON MON\n" + + # A request body with no trailing newline gets one, as the manual warns. + def test_filters_003_30(self, env): + self.configure(env, "s/monday/MON/g", input_sed=True) + r = env.curl_post_data(env.mkurl("http", "test1", "/cgi/echo.py"), + data="one monday two") + assert r.response, "no response" + assert r.response["body"] == b"one MON two\n" + + # A handler which reads the request body with AP_MODE_GETLINE, not + # AP_MODE_READBYTES, still gets it filtered. + @not_backported("r1938963, r1938964", "mod_sed InputSed GETLINE and Content-Length handling") + def test_filters_003_33(self, env): + self.configure(env, "s/monday/MON/g", input_sed=True, extra=""" + + SetHandler aptest-getline-echo + SetInputFilter SED + + """) + r = env.curl_post_data(env.mkurl("http", "test1", "/getline"), + data="one monday two\nmonday monday\n") + assert r.response, "no response" + assert r.response["status"] == 200 + assert r.response["body"] == b"one MON two\nMON MON\n" + + # The request Content-Length is the length of the body before it was + # filtered, so once mod_sed has started reading the body it is removed: + # a handler must not find the original length. + @not_backported("r1938963, r1938964", "mod_sed InputSed GETLINE and Content-Length handling") + def test_filters_003_34(self, env): + self.configure(env, "s/monday/MONDAYMONDAY/g", input_sed=True, + extra=""" + + SetHandler aptest-getline-echo + SetInputFilter SED + + """) + data = "one monday two\nmonday monday\n" + r = env.curl_post_data(env.mkurl("http", "test1", "/getline"), + data=data) + assert r.response, "no response" + assert r.response["status"] == 200 + assert r.response["body"] == \ + data.replace("monday", "MONDAYMONDAY").encode() + assert "ap-test-content-length" not in r.response["header"], \ + r.response["header"] + + # --- interval expressions --------------------------------------------- + + # \{m,n\} repeats a character or a bracket expression. + @pytest.mark.parametrize("expr, out", [ + (r"s/e\{2\}/X/", + "one monday two\nthrX sunday four\nmonday monday monday\n"), + (r"s/[a-z]\{6\}/X/", + "one X two\nthree X four\nX monday monday\n"), + ]) + def test_filters_003_31(self, env, expr, out): + assert self.body(env, expr).decode() == out + + # The matcher only implements \{m,n\} for those, so applied to anything + # else -- a group, a backreference, the start of a group or a second + # interval -- the expression is rejected rather than compiled into + # something the matcher cannot run. + @pytest.mark.parametrize("expr", [ + r"s/\(a\)\{1,2\}/X/", + r"s/\(a\)\1\{1,2\}/X/", + r"s/\(\{1\}a\)/X/", + r"s/a\{1\}\{2,30\}/X/", + ]) + @not_backported("r1938962", "mod_sed expression compile checks") + def test_filters_003_32(self, env, expr): + err = self.configure(env, expr, fails=True) + assert "not allowed here" in err, err + + # --- backreferences to groups ----------------------------------------- + + # A backreference may only refer to a group which has been closed; one + # inside the group it refers to is rejected. + @pytest.mark.parametrize("expr", [ + pytest.param(r"s/\(\(a*\)\1\)/X/", marks=COMPILE_CHECKS), + pytest.param(r"s/\(\(\)\1*\)/X/", marks=COMPILE_CHECKS), + r"s/\(a\1\)/X/", + ]) + def test_filters_003_35(self, env, expr): + err = self.configure(env, expr, fails=True) + assert "out of range" in err, err + + # A backreference to a closed group, even with a later group still open. + @not_backported("r1938962", "mod_sed expression compile checks") + def test_filters_003_36(self, env): + assert self.body(env, r"s/\(\(mo\)n\2*\)day/[\2]/").decode() == \ + "one [mo] two\nthree sunday four\n[mo] monday monday\n" + + # --- blocks ----------------------------------------------------------- + + # Blocks nest up to 20 deep. Each } goes in its own directive: the + # rest of a line after } is not read. + def test_filters_003_37(self, env): + assert self.body(env, ["{" * 20 + "s/monday/MON/"] + ["}"] * 20) \ + .decode() == "one MON two\nthree sunday four\nMON monday monday\n" + + # Any deeper is rejected. + @not_backported("r1938965", "mod_sed depth and delimiter checks") + def test_filters_003_38(self, env): + err = self.configure(env, ["{" * 21 + "p"] + ["}"] * 21, fails=True) + assert "too many {'s" in err, err + + # --- bracket expressions ---------------------------------------------- + + # A bracket expression which ends after a range's "-" is rejected, on + # its own or following a longer directive. + @pytest.mark.parametrize("exprs", [ + ["s/[a-"], + pytest.param(["s/aaaa]*/b/", "s/[a-"], marks=COMPILE_CHECKS), + ]) + def test_filters_003_39(self, env, exprs): + err = self.configure(env, exprs, fails=True) + assert "imbalance" in err, err + + # --- unterminated expressions ----------------------------------------- + + # A regular expression without its closing delimiter is rejected, and + # is not completed from what an earlier, longer directive left behind. + @pytest.mark.parametrize("exprs", [ + ["s/a"], + ["s/aaaa/b/", "s/a"], + ]) + @not_backported("r1938962", "mod_sed expression compile checks") + def test_filters_003_40(self, env, exprs): + err = self.configure(env, exprs, fails=True) + assert "missing delimiter" in err, err + + # --- delimiters ------------------------------------------------------- + + # Any byte other than backslash and newline can be the delimiter, + # including one with the high bit set. + @not_backported("r1938962", "mod_sed expression compile checks") + def test_filters_003_41(self, env): + assert self.body(env, [b"s\xe9monday\xe9MON\xe9", + b"\\\xe9sunday\xe9s\xe9u\xe9U\xe9"]).decode() \ + == "one MON two\nthree sUnday four\nMON monday monday\n" + + # An s or y command which ends before its delimiter is rejected. + @pytest.mark.parametrize("expr", ["s", "/monday/s", "y", "/monday/y"]) + @not_backported("r1938965", "mod_sed depth and delimiter checks") + def test_filters_003_42(self, env, expr): + err = self.configure(env, expr, fails=True) + assert "command garbled" in err, err + + # y maps bytes with the high bit set like any other. + @not_backported("r1938965", "mod_sed depth and delimiter checks") + def test_filters_003_43(self, env): + self.write_bytes(env, "latin1.html", b"caf\xe9 na\xefve\n") + assert self.body(env, [b"y/\xe9\xef/ei/"], path="/latin1.html") == \ + b"cafe naive\n" + + # A regular expression which no longer fits once the compiled script + # has filled its buffer exactly, here with the names of r commands, is + # rejected without writing past the buffer. Only a memory checker sees + # the difference. + def test_filters_003_44(self, env): + err = self.configure(env, [f"r {'A' * 998}"] * 10 + + ["r 123456789", "/x/p"], fails=True) + assert "too much command text" in err, err + + # An interval count is rejected as too large however many digits it + # has, rather than wrapping around. + @pytest.mark.parametrize("expr", [ + r"s/o\{255\}/X/", + pytest.param(r"s/o\{4294967298\}/X/", marks=COMPILE_CHECKS), + pytest.param(r"s/o\{1,4294967298\}/X/", marks=COMPILE_CHECKS), + ]) + def test_filters_003_45(self, env, expr): + err = self.configure(env, expr, fails=True) + assert "Range endpoint too large" in err, err + + # A line number too large for a 64-bit count is rejected rather than + # wrapping around; one which fits, but which no document reaches, is + # accepted. + @not_backported("r1938965", "mod_sed depth and delimiter checks") + def test_filters_003_46(self, env): + err = self.configure(env, "99999999999999999999p", fails=True) + assert "command garbled" in err, err + + def test_filters_003_47(self, env): + assert self.body(env, "9223372036854775807d").decode() == DOC diff -Nru apache2-2.4.69/test/modules/filters/test_004_buffer_size.py apache2-2.4.71/test/modules/filters/test_004_buffer_size.py --- apache2-2.4.69/test/modules/filters/test_004_buffer_size.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/filters/test_004_buffer_size.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,38 @@ +import os + +import pytest + +from pyhttpd.backport import not_backported +from pyhttpd.conf import HttpdConf + +STRICT = not_backported("r1939129", "strict BufferSize parsing") + + +class TestBufferSize: + + # have httpd parse the configuration, like "httpd -t" does + def check_config(self, env, value): + conf = HttpdConf(env) + conf.add(["", f" BufferSize {value}", ""]) + conf.add_vhost_test1() + conf.install() + return env.run([os.path.join(env.bin_dir, 'httpd'), + "-t", "-d", env.server_dir, + "-f", os.path.join(env.server_dir, 'conf', 'httpd.conf')]) + + @pytest.mark.parametrize("value", ["1", "123", "131072", "00123"]) + def test_filters_004_01(self, env, value): + r = self.check_config(env, value) + assert r.exit_code == 0, f"{r.stderr}" + + # without the change, a number followed by anything else is taken + @pytest.mark.parametrize("value", [ + "abc", + pytest.param("123abc", marks=STRICT), + pytest.param("12.3", marks=STRICT), + "0x10", "0", "-1", "9" * 40, + ]) + def test_filters_004_02(self, env, value): + r = self.check_config(env, value) + assert r.exit_code != 0, f"{value} was accepted" + assert "BufferSize must be a size in bytes" in r.stderr diff -Nru apache2-2.4.69/test/modules/generators/conftest.py apache2-2.4.71/test/modules/generators/conftest.py --- apache2-2.4.69/test/modules/generators/conftest.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/conftest.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,35 @@ +import logging +import os + +import pytest +import sys + +from .env import GeneratorsTestEnv + +sys.path.append(os.path.join(os.path.dirname(__file__), '../..')) + + +def pytest_report_header(config, start_path): + env = GeneratorsTestEnv() + return f"generators [apache: {env.get_httpd_version()}, mpm: {env.mpm_module}, {env.prefix}]" + + +@pytest.fixture(scope="package") +def env(pytestconfig) -> GeneratorsTestEnv: + level = logging.INFO + console = logging.StreamHandler() + console.setLevel(level) + console.setFormatter(logging.Formatter('%(levelname)s: %(message)s')) + logging.getLogger('').addHandler(console) + logging.getLogger('').setLevel(level=level) + env = GeneratorsTestEnv(pytestconfig=pytestconfig) + env.setup_httpd() + env.apache_access_log_clear() + env.httpd_error_log.clear_log() + return env + + +@pytest.fixture(autouse=True, scope="package") +def _stop_package_scope(env): + yield + assert env.apache_stop() == 0 diff -Nru apache2-2.4.69/test/modules/generators/env.py apache2-2.4.71/test/modules/generators/env.py --- apache2-2.4.69/test/modules/generators/env.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/env.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,39 @@ +import inspect +import logging +import os +import subprocess + +from pyhttpd.env import HttpdTestEnv, HttpdTestSetup + +log = logging.getLogger(__name__) + + +class GeneratorsTestSetup(HttpdTestSetup): + + def __init__(self, env: 'HttpdTestEnv'): + super().__init__(env=env) + self.add_source_dir(os.path.dirname(inspect.getfile(GeneratorsTestSetup))) + self.add_modules(["cgid", "include", "headers", "autoindex"]) + + +class GeneratorsTestEnv(HttpdTestEnv): + + def __init__(self, pytestconfig=None): + super().__init__(pytestconfig=pytestconfig) + # A LogLevel for an unloaded module is a fatal config error. + log_modules = ["include", "core"] + if self.has_cgid_module: + log_modules.insert(0, "cgid") + self.add_httpd_log_modules(log_modules) + + def setup_httpd(self, setup: HttpdTestSetup = None): + super().setup_httpd(setup=GeneratorsTestSetup(env=self)) + + @property + def has_cgid_module(self) -> bool: + """Whether mod_cgid was built, shared or static.""" + if self.has_shared_module("cgid"): + return True + p = subprocess.run([os.path.join(self.bin_dir, "httpd"), "-l"], + capture_output=True, text=True) + return "mod_cgid.c" in p.stdout diff -Nru apache2-2.4.69/test/modules/generators/htdocs/autoindex/index.html apache2-2.4.71/test/modules/generators/htdocs/autoindex/index.html --- apache2-2.4.69/test/modules/generators/htdocs/autoindex/index.html 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/autoindex/index.html 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +autoindex testsautoindex tests diff -Nru apache2-2.4.69/test/modules/generators/htdocs/autoindex/listing/att.html apache2-2.4.71/test/modules/generators/htdocs/autoindex/listing/att.html --- apache2-2.4.69/test/modules/generators/htdocs/autoindex/listing/att.html 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/autoindex/listing/att.html 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +AT&Tpage diff -Nru apache2-2.4.69/test/modules/generators/htdocs/cgi/arglen.py apache2-2.4.71/test/modules/generators/htdocs/cgi/arglen.py --- apache2-2.4.69/test/modules/generators/htdocs/cgi/arglen.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/cgi/arglen.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +import os + +print("Content-Type: text/plain\n") +print(len(os.environ.get("QUERY_STRING", ""))) diff -Nru apache2-2.4.69/test/modules/generators/htdocs/cgi/argv.py apache2-2.4.71/test/modules/generators/htdocs/cgi/argv.py --- apache2-2.4.69/test/modules/generators/htdocs/cgi/argv.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/cgi/argv.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 +import sys + +print("Content-Type: text/plain\n") +for arg in sys.argv[1:]: + print(arg) diff -Nru apache2-2.4.69/test/modules/generators/htdocs/cgi/envcount.py apache2-2.4.71/test/modules/generators/htdocs/cgi/envcount.py --- apache2-2.4.69/test/modules/generators/htdocs/cgi/envcount.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/cgi/envcount.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +import os + +print("Content-Type: text/plain\n") +print(len(os.environ)) diff -Nru apache2-2.4.69/test/modules/generators/htdocs/cgi/noread.py apache2-2.4.71/test/modules/generators/htdocs/cgi/noread.py --- apache2-2.4.69/test/modules/generators/htdocs/cgi/noread.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/cgi/noread.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,3 @@ +#!/usr/bin/env python3 +print("Content-Type: text/plain\n") +print("not reading") diff -Nru apache2-2.4.69/test/modules/generators/htdocs/cgi/nph-hello.py apache2-2.4.71/test/modules/generators/htdocs/cgi/nph-hello.py --- apache2-2.4.69/test/modules/generators/htdocs/cgi/nph-hello.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/cgi/nph-hello.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,4 @@ +#!/usr/bin/env python3 +import sys + +sys.stdout.write("HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\nnph\n") diff -Nru apache2-2.4.69/test/modules/generators/htdocs/cgi/redirect.py apache2-2.4.71/test/modules/generators/htdocs/cgi/redirect.py --- apache2-2.4.69/test/modules/generators/htdocs/cgi/redirect.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/cgi/redirect.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 +print("Location: http://example.invalid/target\n") diff -Nru apache2-2.4.69/test/modules/generators/htdocs/cgi/sleep.py apache2-2.4.71/test/modules/generators/htdocs/cgi/sleep.py --- apache2-2.4.69/test/modules/generators/htdocs/cgi/sleep.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/cgi/sleep.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 +import time + +time.sleep(5) +print("Content-Type: text/plain\n") +print("too late") diff -Nru apache2-2.4.69/test/modules/generators/htdocs/cgi/sleep_trap.py apache2-2.4.71/test/modules/generators/htdocs/cgi/sleep_trap.py --- apache2-2.4.69/test/modules/generators/htdocs/cgi/sleep_trap.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/cgi/sleep_trap.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +import signal +import time + +signal.signal(signal.SIGTERM, signal.SIG_IGN) +time.sleep(10) +print("Content-Type: text/plain\n") +print("too late") diff -Nru apache2-2.4.69/test/modules/generators/htdocs/cgi/ssi/exec_bogus.shtml apache2-2.4.71/test/modules/generators/htdocs/cgi/ssi/exec_bogus.shtml --- apache2-2.4.69/test/modules/generators/htdocs/cgi/ssi/exec_bogus.shtml 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/cgi/ssi/exec_bogus.shtml 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru apache2-2.4.69/test/modules/generators/htdocs/cgi/ssi/exec_cgi.shtml apache2-2.4.71/test/modules/generators/htdocs/cgi/ssi/exec_cgi.shtml --- apache2-2.4.69/test/modules/generators/htdocs/cgi/ssi/exec_cgi.shtml 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/cgi/ssi/exec_cgi.shtml 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru apache2-2.4.69/test/modules/generators/htdocs/cgi/ssi/exec_cgi_missing.shtml apache2-2.4.71/test/modules/generators/htdocs/cgi/ssi/exec_cgi_missing.shtml --- apache2-2.4.69/test/modules/generators/htdocs/cgi/ssi/exec_cgi_missing.shtml 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/cgi/ssi/exec_cgi_missing.shtml 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru apache2-2.4.69/test/modules/generators/htdocs/cgi/ssi/exec_cgi_nph.shtml apache2-2.4.71/test/modules/generators/htdocs/cgi/ssi/exec_cgi_nph.shtml --- apache2-2.4.69/test/modules/generators/htdocs/cgi/ssi/exec_cgi_nph.shtml 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/cgi/ssi/exec_cgi_nph.shtml 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru apache2-2.4.69/test/modules/generators/htdocs/cgi/ssi/exec_cgi_query.shtml apache2-2.4.71/test/modules/generators/htdocs/cgi/ssi/exec_cgi_query.shtml --- apache2-2.4.69/test/modules/generators/htdocs/cgi/ssi/exec_cgi_query.shtml 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/cgi/ssi/exec_cgi_query.shtml 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru apache2-2.4.69/test/modules/generators/htdocs/cgi/ssi/exec_cgi_redirect.shtml apache2-2.4.71/test/modules/generators/htdocs/cgi/ssi/exec_cgi_redirect.shtml --- apache2-2.4.69/test/modules/generators/htdocs/cgi/ssi/exec_cgi_redirect.shtml 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/cgi/ssi/exec_cgi_redirect.shtml 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru apache2-2.4.69/test/modules/generators/htdocs/cgi/ssi/exec_noexec.shtml apache2-2.4.71/test/modules/generators/htdocs/cgi/ssi/exec_noexec.shtml --- apache2-2.4.69/test/modules/generators/htdocs/cgi/ssi/exec_noexec.shtml 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/cgi/ssi/exec_noexec.shtml 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru apache2-2.4.69/test/modules/generators/htdocs/cgi/ssi/exec_path_info.shtml apache2-2.4.71/test/modules/generators/htdocs/cgi/ssi/exec_path_info.shtml --- apache2-2.4.69/test/modules/generators/htdocs/cgi/ssi/exec_path_info.shtml 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/cgi/ssi/exec_path_info.shtml 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru apache2-2.4.69/test/modules/generators/htdocs/cgi/stderr_fail.py apache2-2.4.71/test/modules/generators/htdocs/cgi/stderr_fail.py --- apache2-2.4.69/test/modules/generators/htdocs/cgi/stderr_fail.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/htdocs/cgi/stderr_fail.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +import sys + +sys.stderr.write("cgid-stderr-marker\n") +sys.exit(1) diff -Nru apache2-2.4.69/test/modules/generators/test_001_cgid.py apache2-2.4.71/test/modules/generators/test_001_cgid.py --- apache2-2.4.69/test/modules/generators/test_001_cgid.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/test_001_cgid.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,239 @@ +import os +import re +import signal +import sys +import textwrap +import time + +import pytest + +from pyhttpd.backport import skip_not_backported +from pyhttpd.conf import HttpdConf + + +SSI_ERROR = "[an error occurred while processing this directive]" + + +@pytest.mark.skipif(sys.platform == "win32", reason="mod_cgid is Unix-only") +class TestCgid: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + env.require(env.has_cgid_module, "mod_cgid not built", + dep="mod_cgid", + detected=f"not found in {env.libexec_dir}") + conf = HttpdConf(env, extras={ + 'base': textwrap.dedent(f""" + LogLevel cgid:trace1 + LimitRequestLine 16384 + + Options +Includes + AcceptPathInfo On + AddType text/html .shtml + AddOutputFilter INCLUDES .shtml + + + Options -Includes +IncludesNOEXEC + + + SetHandler cgi-script + + """), + f"cgi.{env.http_tld}": textwrap.dedent(""" + ScriptLog logs/cgid_script.log + Header always set X-Cgid-Test on early + + CGIDScriptTimeout 1 + + """) + self.setenv_location("/envcount.py/small", 300) + + self.setenv_location("/envcount.py/large", 4200), + }) + conf.add_vhost_cgi() + conf.install() + assert env.apache_restart() == 0 + + @staticmethod + def setenv_location(path, count): + setenvs = "\n".join(f"SetEnv CGID_TEST_VAR_{i:04} v" + for i in range(count)) + return f'\n{setenvs}\n\n' + + def script_log(self, env): + return os.path.join(env.server_logs_dir, "cgid_script.log") + + def get(self, env, host, path, options=None): + r = env.curl_get(env.mkurl("http", host, path), + options=['--max-time', '15'] + (options or [])) + assert r.exit_code == 0, r.stderr + return r + + @pytest.mark.parametrize(["query", "argv"], [ + ["foo+bar%20baz", ["foo", "bar baz"]], + ["a%3Bb", ["a\\;b"]], + ["x=1", []], + ]) + def test_generators_001_01_isindex_args(self, env, query, argv): + """A query string without '=' is split on '+' into argv, + unescaped and then shell-escaped.""" + r = self.get(env, "cgi", f"/argv.py?{query}") + assert r.response["status"] == 200 + assert r.response["body"].decode().splitlines() == argv + + @skip_not_backported("cause not identified", "ScriptLog capture of a failing script's stderr") + def test_generators_001_02_script_log(self, env): + """ScriptLog set in a vhost records the script's stderr, then the + request headers, request body and err_headers_out.""" + log = self.script_log(env) + if os.path.exists(log): + os.unlink(log) + r = env.curl_post_data(env.mkurl("http", "cgi", "/stderr_fail.py"), + data="posted-body-marker") + assert r.response["status"] == 500 + with open(log) as fd: + content = fd.read() + assert "%error\ncgid-stderr-marker\n" in content + assert "%% 500 " in content + assert "%request\n" in content + assert "\nposted-body-marker\n" in content + assert "%response\nX-Cgid-Test: on\n" in content + env.httpd_error_log.ignore_recent( + lognos=["AH01215", "AH10599"], + matches=[r".*End of script output before headers: stderr_fail\.py"]) + + def test_generators_001_03_timeout(self, env): + """CGIDScriptTimeout gives 504 for a script which says nothing.""" + start = time.time() + r = self.get(env, "cgi", "/sleep.py") + assert r.response["status"] == 504 + assert time.time() - start < 4 + env.httpd_error_log.ignore_recent( + lognos=["AH01220"], + matches=[r".*Script timed out before returning headers: sleep\.py"]) + + def test_generators_001_04_timeout_sigkill(self, env): + """A timed-out script which ignores SIGTERM is sent SIGKILL.""" + r = self.get(env, "cgi", "/sleep_trap.py") + assert r.response["status"] == 504 + self.wait_for_log(env, "AH01259") + env.httpd_error_log.ignore_recent( + lognos=["AH01220", "AH01259"], + matches=[r".*Script timed out before returning headers: sleep_trap\.py"]) + + def test_generators_001_05_redirect(self, env): + """An absolute Location from a script with no Status is a 302.""" + r = self.get(env, "cgi", "/redirect.py") + assert r.response["status"] == 302 + assert r.response["header"]["location"] == "http://example.invalid/target" + + def test_generators_001_06_directory(self, env): + """A directory mapped to cgi-script is refused.""" + r = self.get(env, "cgi", "/ssi/") + assert r.response["status"] == 403 + env.httpd_error_log.ignore_recent(lognos=["AH01265"]) + + @pytest.mark.xfail(reason="the script's socket is reset when it exits " + "with unread input, discarding its output") + def test_generators_001_07_body_not_read(self, env): + """A script which exits without reading its request body + still has its response delivered.""" + r = env.curl_post_data(env.mkurl("http", "cgi", "/noread.py"), + data="unread") + env.httpd_error_log.ignore_recent(lognos=["AH02651", "AH10599"]) + assert r.exit_code == 0, r.stderr + assert r.response["status"] == 200 + assert r.response["body"].decode() == "not reading\n" + + @skip_not_backported("r1939165", "raised limit on CGI environment variables", backported_in="2.4.70") + def test_generators_001_08_env_count_large(self, env): + """More than 256 environment variables reach the script.""" + r = self.get(env, "cgi", "/envcount.py/small") + assert r.response["status"] == 200 + assert int(r.response["body"]) >= 300 + + def test_generators_001_09_env_count_overflow(self, env): + """A request environment larger than APACHE_ARG_MAX is refused.""" + r = self.get(env, "cgi", "/envcount.py/large") + assert r.response["status"] == 503 + # the daemon sees the aborted connection as a read error + env.httpd_error_log.ignore_recent(lognos=["AH10245", "AH01248"]) + + @skip_not_backported("r1939165", "raised limit on CGI request size", backported_in="2.4.70") + def test_generators_001_15_long_query(self, env): + """A 5000-character request URI, with a query string longer + than PATH_MAX, reaches the script.""" + path = "/arglen.py" + query = "x=" + "y" * (5000 - len(path) - len("?x=")) + r = self.get(env, "cgi", f"{path}?{query}") + assert r.response["status"] == 200 + assert int(r.response["body"]) == len(query) + + def test_generators_001_10_ssi_exec_cgi(self, env): + """#exec cgi= runs the script with the document's query string.""" + r = self.get(env, "cgi", "/ssi/exec_cgi.shtml?hello+world") + assert r.response["status"] == 200 + assert r.response["body"].decode().split() == ["hello", "world"] + + def test_generators_001_11_ssi_exec_cgi_redirect(self, env): + """#exec cgi= of a redirecting script inserts a link.""" + r = self.get(env, "cgi", "/ssi/exec_cgi_redirect.shtml") + assert r.response["status"] == 200 + assert r.response["body"].decode().strip() == \ + '' \ + 'http://example.invalid/target' + + @pytest.mark.parametrize(["path", "lognos"], [ + ["/ssi/exec_cgi_query.shtml", ["AH01230"]], + ["/ssi/exec_cgi_missing.shtml", ["AH01230"]], + ["/ssi/exec_bogus.shtml", ["AH01231"]], + ["/ssi/exec_noexec.shtml", ["AH01228"]], + ]) + def test_generators_001_12_ssi_exec_errors(self, env, path, lognos): + r = self.get(env, "cgi", path) + assert r.response["status"] == 200 + assert SSI_ERROR in r.response["body"].decode() + env.httpd_error_log.ignore_recent(lognos=lognos) + + def test_generators_001_14_ssi_exec_cgi_nph(self, env): + """#exec cgi= refuses an NPH script; the sub-request's failure + leaves the directive's output empty, not an error message.""" + r = self.get(env, "cgi", "/ssi/exec_cgi_nph.shtml") + assert r.response["status"] == 200 + assert r.response["body"].decode().strip() == "" + env.httpd_error_log.ignore_recent(lognos=["AH01263"]) + + def test_generators_001_13_ssi_exec_cmd_path_info(self, env): + """#exec cmd= sees the document's PATH_INFO.""" + r = self.get(env, "cgi", "/ssi/exec_path_info.shtml/extra/path") + assert r.response["status"] == 200 + assert r.response["body"].decode().strip() == "PI=/extra/path" + + @skip_not_backported("r1938602", "cgid daemon pid in the trace log", backported_in="2.4.70") + def test_generators_001_20_daemon_killed(self, env): + """CGI requests are served again once the cgid daemon which died + unexpectedly has been restarted.""" + pids = self.daemon_pids(env) + assert pids, "cgid daemon pid not logged" + os.kill(pids[-1], signal.SIGKILL) + end = time.time() + 10 + while len(self.daemon_pids(env)) == len(pids): + assert time.time() < end, "cgid daemon not restarted" + time.sleep(0.2) + r = self.get(env, "cgi", "/argv.py?alive") + assert r.response["status"] == 200 + assert r.response["body"].decode().split() == ["alive"] + env.httpd_error_log.ignore_recent(lognos=["AH01239"]) + + def daemon_pids(self, env): + """The pids of the cgid daemons started so far, oldest first.""" + with open(env.httpd_error_log.path) as fd: + return [int(pid) for pid in re.findall( + r"cgid daemon listening on \S+, pid (\d+)", fd.read())] + + def wait_for_log(self, env, logno, timeout=10): + end = time.time() + timeout + while time.time() < end: + with open(env.httpd_error_log.path) as fd: + if f" {logno}: " in fd.read(): + return + time.sleep(0.2) + assert False, f"{logno} not logged within {timeout}s" diff -Nru apache2-2.4.69/test/modules/generators/test_002_autoindex.py apache2-2.4.71/test/modules/generators/test_002_autoindex.py --- apache2-2.4.69/test/modules/generators/test_002_autoindex.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/generators/test_002_autoindex.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,34 @@ +import textwrap + +import pytest + +from pyhttpd.conf import HttpdConf + + +class TestAutoindex: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + domain = f"autoindex.{env.http_tld}" + conf = HttpdConf(env, extras={ + 'base': textwrap.dedent(f""" + LogLevel autoindex:trace1 + + Options +Indexes + IndexOptions FancyIndexing ScanHTMLTitles + + """), + }) + conf.add_vhost(domains=[domain], port=env.http_port, + doc_root="htdocs/autoindex") + conf.install() + assert env.apache_restart() == 0 + + def test_generators_002_01_scan_title_bare_amp(self, env): + """A ScanHTMLTitles description ending in a bare '&' (no ';'), as in + an "AT&T" , must be rendered without reading past the end of + the description. Before the fix this walked one byte past the + terminating NUL, which ASAN flags as a heap over-read.""" + r = env.curl_get(env.mkurl("http", "autoindex", "/listing/")) + assert r.response["status"] == 200 + assert "AT&T" in r.response["body"].decode() diff -Nru apache2-2.4.69/test/modules/http1/env.py apache2-2.4.71/test/modules/http1/env.py --- apache2-2.4.69/test/modules/http1/env.py 2024-07-11 08:18:12.000000000 +0000 +++ apache2-2.4.71/test/modules/http1/env.py 2026-11-09 03:07:05.000000000 +0000 @@ -5,6 +5,7 @@ from typing import Dict, Any from pyhttpd.certs import CertificateSpec +from pyhttpd.cmodules import ensure_built from pyhttpd.conf import HttpdConf from pyhttpd.env import HttpdTestEnv, HttpdTestSetup @@ -24,19 +25,22 @@ self._setup_data_1k_1m() def _add_h1test(self): - local_dir = os.path.dirname(inspect.getfile(H1TestSetup)) - p = subprocess.run([self.env.apxs, '-c', 'mod_h1test.c'], - capture_output=True, - cwd=os.path.join(local_dir, 'mod_h1test')) - rv = p.returncode - if rv != 0: - log.error(f"compiling md_h1test failed: {p.stderr}") - raise Exception(f"compiling md_h1test failed: {p.stderr}") + module_dir = self.env.test_modules_dir + if not self.env.isWindows: + local_dir = os.path.dirname(inspect.getfile(H1TestSetup)) + try: + ensure_built(os.path.join(local_dir, 'mod_h1test'), 'h1test', + self.env.apxs) + except RuntimeError as e: + log.error(f"compiling mod_h1test failed: {e}") + raise Exception(f"compiling mod_h1test failed: {e}") + + module_dir = f"{local_dir}/mod_h1test/.libs" modules_conf = os.path.join(self.env.server_dir, 'conf/modules.conf') with open(modules_conf, 'a') as fd: # load our test module which is not installed - fd.write(f"LoadModule h1test_module \"{local_dir}/mod_h1test/.libs/mod_h1test.so\"\n") + fd.write(f"LoadModule h1test_module \"{module_dir}/mod_h1test.so\"\n") def _setup_data_1k_1m(self): s90 = "01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678\n" diff -Nru apache2-2.4.69/test/modules/http1/htdocs/cgi/requestparser.py apache2-2.4.71/test/modules/http1/htdocs/cgi/requestparser.py --- apache2-2.4.69/test/modules/http1/htdocs/cgi/requestparser.py 2024-07-11 08:18:12.000000000 +0000 +++ apache2-2.4.71/test/modules/http1/htdocs/cgi/requestparser.py 2026-11-09 03:07:05.000000000 +0000 @@ -2,7 +2,10 @@ import os import sys from urllib import parse -import multipart # https://github.com/andrew-d/python-multipart (`apt install python3-multipart`) +try: # python-multipart >= 0.0.13 renamed the module, "multipart" is deprecated + import python_multipart as multipart +except ImportError: + import multipart # https://github.com/Kludex/python-multipart (`apt install python3-multipart`) import shutil diff -Nru apache2-2.4.69/test/modules/http1/test_004_post.py apache2-2.4.71/test/modules/http1/test_004_post.py --- apache2-2.4.69/test/modules/http1/test_004_post.py 2024-07-11 08:18:12.000000000 +0000 +++ apache2-2.4.71/test/modules/http1/test_004_post.py 2026-11-09 03:07:05.000000000 +0000 @@ -7,9 +7,12 @@ import pytest +from pyhttpd.env import HttpdTestEnv + from .env import H1Conf +@HttpdTestEnv.needs_multipart() class TestPost: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/http1/test_005_trailers.py apache2-2.4.71/test/modules/http1/test_005_trailers.py --- apache2-2.4.69/test/modules/http1/test_005_trailers.py 2024-07-11 08:18:12.000000000 +0000 +++ apache2-2.4.71/test/modules/http1/test_005_trailers.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,11 +1,13 @@ import os import pytest +from pyhttpd.env import HttpdTestEnv + from .env import H1Conf -# The trailer tests depend on "nghttp" as no other client seems to be able to send those -# rare things. +# These upload to the cgi handler, which parses the multipart body. +@HttpdTestEnv.needs_multipart() class TestTrailers: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/http2/Makefile.in apache2-2.4.71/test/modules/http2/Makefile.in --- apache2-2.4.69/test/modules/http2/Makefile.in 2021-08-20 16:07:44.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/Makefile.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ - -# no targets: we don't want to build anything by default. if you want the -# test programs, then "make test" -TARGETS = - -bin_PROGRAMS = - -PROGRAM_LDADD = $(EXTRA_LDFLAGS) $(PROGRAM_DEPENDENCIES) $(EXTRA_LIBS) -PROGRAM_DEPENDENCIES = \ - $(top_srcdir)/srclib/apr-util/libaprutil.la \ - $(top_srcdir)/srclib/apr/libapr.la - -include $(top_builddir)/build/rules.mk - -test: $(bin_PROGRAMS) - -# example for building a test proggie -# dbu_OBJECTS = dbu.lo -# dbu: $(dbu_OBJECTS) -# $(LINK) $(dbu_OBJECTS) $(PROGRAM_LDADD) diff -Nru apache2-2.4.69/test/modules/http2/conftest.py apache2-2.4.71/test/modules/http2/conftest.py --- apache2-2.4.69/test/modules/http2/conftest.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/conftest.py 2026-11-09 03:07:05.000000000 +0000 @@ -23,6 +23,16 @@ logging.getLogger('').addHandler(console) logging.getLogger('').setLevel(level=level) env = H2TestEnv(pytestconfig=pytestconfig) + # setup_httpd() raises if a module it LoadModules is missing, e.g. in a + # build configured with --disable-http2. Report that as the missing + # dependency instead. + env.require(env.has_shared_module("http2") and env.has_shared_module("proxy_http2"), + "mod_http2 and mod_proxy_http2 were not built", dep="mod_http2", + detected=f"no mod_http2.so and mod_proxy_http2.so in {env.libexec_dir}") + # the h2 vhosts are TLS, so setup_httpd() hard-requires mod_ssl and + # raises if it was not built. Report that as the missing dependency. + env.require(env.has_mod_ssl(), "no mod_ssl available", dep="mod_ssl", + detected=f"no mod_ssl.so in {env.libexec_dir}") env.setup_httpd() env.apache_access_log_clear() env.httpd_error_log.clear_log() diff -Nru apache2-2.4.69/test/modules/http2/env.py apache2-2.4.71/test/modules/http2/env.py --- apache2-2.4.69/test/modules/http2/env.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/env.py 2026-11-09 03:07:05.000000000 +0000 @@ -6,6 +6,7 @@ from typing import Dict, Any from pyhttpd.certs import CertificateSpec +from pyhttpd.cmodules import ensure_built from pyhttpd.conf import HttpdConf from pyhttpd.env import HttpdTestEnv, HttpdTestSetup @@ -25,32 +26,35 @@ self._setup_data_1k_1m() def _add_h2test(self): - local_dir = os.path.dirname(inspect.getfile(H2TestSetup)) - p = subprocess.run([self.env.apxs, '-c', 'mod_h2test.c'], - capture_output=True, - cwd=os.path.join(local_dir, 'mod_h2test')) - rv = p.returncode - if rv != 0: - log.error(f"compiling md_h2test failed: {p.stderr}") - raise Exception(f"compiling md_h2test failed: {p.stderr}") + module_dir = self.env.test_modules_dir + if not self.env.isWindows: + local_dir = os.path.dirname(inspect.getfile(H2TestSetup)) + try: + ensure_built(os.path.join(local_dir, 'mod_h2test'), 'h2test', + self.env.apxs) + except RuntimeError as e: + log.error(f"compiling mod_h2test failed: {e}") + raise Exception(f"compiling mod_h2test failed: {e}") + + module_dir = f"{local_dir}/mod_h2test/.libs" modules_conf = os.path.join(self.env.server_dir, 'conf/modules.conf') with open(modules_conf, 'a') as fd: # load our test module which is not installed - fd.write(f"LoadModule h2test_module \"{local_dir}/mod_h2test/.libs/mod_h2test.so\"\n") + fd.write(f"LoadModule h2test_module \"{module_dir}/mod_h2test.so\"\n") def _setup_data_1k_1m(self): s90 = "01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678\n" - with open(os.path.join(self.env.gen_dir, "data-1k"), 'w') as f: + with open(os.path.join(self.env.gen_dir, "data-1k"), 'w', newline='') as f: for i in range(10): f.write(f"{i:09d}-{s90}") - with open(os.path.join(self.env.gen_dir, "data-10k"), 'w') as f: + with open(os.path.join(self.env.gen_dir, "data-10k"), 'w', newline='') as f: for i in range(100): f.write(f"{i:09d}-{s90}") - with open(os.path.join(self.env.gen_dir, "data-100k"), 'w') as f: + with open(os.path.join(self.env.gen_dir, "data-100k"), 'w', newline='') as f: for i in range(1000): f.write(f"{i:09d}-{s90}") - with open(os.path.join(self.env.gen_dir, "data-1m"), 'w') as f: + with open(os.path.join(self.env.gen_dir, "data-1m"), 'w', newline='') as f: for i in range(10000): f.write(f"{i:09d}-{s90}") test1_docs = os.path.join(self.env.server_docs_dir, 'test1') diff -Nru apache2-2.4.69/test/modules/http2/htdocs/cgi/echo.py apache2-2.4.71/test/modules/http2/htdocs/cgi/echo.py --- apache2-2.4.69/test/modules/http2/htdocs/cgi/echo.py 2023-03-05 10:25:32.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/htdocs/cgi/echo.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,6 +1,5 @@ #!/usr/bin/env python3 import os, sys -import multipart status = '200 Ok' diff -Nru apache2-2.4.69/test/modules/http2/htdocs/cgi/requestparser.py apache2-2.4.71/test/modules/http2/htdocs/cgi/requestparser.py --- apache2-2.4.69/test/modules/http2/htdocs/cgi/requestparser.py 2023-05-02 10:05:52.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/htdocs/cgi/requestparser.py 2026-11-09 03:07:05.000000000 +0000 @@ -2,7 +2,10 @@ import os import sys from urllib import parse -import multipart # https://github.com/andrew-d/python-multipart (`apt install python3-multipart`) +try: # python-multipart >= 0.0.13 renamed the module, "multipart" is deprecated + import python_multipart as multipart +except ImportError: + import multipart # https://github.com/Kludex/python-multipart (`apt install python3-multipart`) import shutil diff -Nru apache2-2.4.69/test/modules/http2/test_003_get.py apache2-2.4.71/test/modules/http2/test_003_get.py --- apache2-2.4.69/test/modules/http2/test_003_get.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_003_get.py 2026-11-09 03:07:05.000000000 +0000 @@ -5,6 +5,7 @@ @pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") +@H2TestEnv.needs_nghttp() class TestGet: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/http2/test_004_post.py apache2-2.4.71/test/modules/http2/test_004_post.py --- apache2-2.4.69/test/modules/http2/test_004_post.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_004_post.py 2026-11-09 03:07:05.000000000 +0000 @@ -13,6 +13,8 @@ @pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") +@H2TestEnv.needs_nghttp() +@H2TestEnv.needs_multipart() class TestPost: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/http2/test_006_assets.py apache2-2.4.71/test/modules/http2/test_006_assets.py --- apache2-2.4.69/test/modules/http2/test_006_assets.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_006_assets.py 2026-11-09 03:07:05.000000000 +0000 @@ -4,6 +4,7 @@ @pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") +@H2TestEnv.needs_nghttp(assets=True) class TestAssets: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/http2/test_008_ranges.py apache2-2.4.71/test/modules/http2/test_008_ranges.py --- apache2-2.4.69/test/modules/http2/test_008_ranges.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_008_ranges.py 2026-11-09 03:07:05.000000000 +0000 @@ -5,6 +5,7 @@ import re import pytest +from pyhttpd.backport import not_backported from .env import H2Conf, H2TestEnv @@ -119,6 +120,8 @@ assert found, f'request not found in {self.LOGFILE}' # send a paced curl download that aborts in the middle of the transfer + @not_backported("cause not identified", "bytes_tx_O counted on an aborted " + "H2 transfer", platform="win32") def test_h2_008_03(self, env, repeat): path = '/data-100m' url = env.mkurl("https", "test1", f'{path}?03broken') diff -Nru apache2-2.4.69/test/modules/http2/test_009_timing.py apache2-2.4.71/test/modules/http2/test_009_timing.py --- apache2-2.4.69/test/modules/http2/test_009_timing.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_009_timing.py 2026-11-09 03:07:05.000000000 +0000 @@ -7,7 +7,7 @@ @pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") -@pytest.mark.skipif(not H2TestEnv().h2load_is_at_least('1.41.0'), reason="h2load misses --connect-to option") +@H2TestEnv.needs_h2load("1.41.0", reason="h2load misses --connect-to option") class TestTiming: LOGFILE = "" diff -Nru apache2-2.4.69/test/modules/http2/test_101_ssl_reneg.py apache2-2.4.71/test/modules/http2/test_101_ssl_reneg.py --- apache2-2.4.69/test/modules/http2/test_101_ssl_reneg.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_101_ssl_reneg.py 2026-11-09 03:07:05.000000000 +0000 @@ -65,8 +65,9 @@ # try to renegotiate the cipher, should fail with correct code def test_h2_101_02(self, env): - if not (env.curl_is_at_least('8.2.0') or env.curl_is_less_than('8.1.0')): - pytest.skip("need curl != 8.1.x version") + env.require(not env.curl_is_8_1_x(), "need curl != 8.1.x version", + dep="curl != 8.1.x", + detected=f"curl {env.curl_version_str()}") url = env.mkurl("https", "ssl", "/renegotiate/cipher/") r = env.curl_get(url, options=[ "-vvv", "--tlsv1.2", "--tls-max", "1.2", "--ciphers", "ECDHE-RSA-AES256-GCM-SHA384" @@ -88,8 +89,9 @@ # try to renegotiate a client certificate from Location # needs to fail with correct code def test_h2_101_03(self, env): - if not (env.curl_is_at_least('8.2.0') or env.curl_is_less_than('8.1.0')): - pytest.skip("need curl != 8.1.x version") + env.require(not env.curl_is_8_1_x(), "need curl != 8.1.x version", + dep="curl != 8.1.x", + detected=f"curl {env.curl_version_str()}") url = env.mkurl("https", "ssl", "/renegotiate/verify/") r = env.curl_get(url, options=["-vvv", "--tlsv1.2", "--tls-max", "1.2"]) assert 0 != r.exit_code @@ -109,8 +111,9 @@ # try to renegotiate a client certificate from Directory # needs to fail with correct code def test_h2_101_04(self, env): - if not (env.curl_is_at_least('8.2.0') or env.curl_is_less_than('8.1.0')): - pytest.skip("need curl != 8.1.x version") + env.require(not env.curl_is_8_1_x(), "need curl != 8.1.x version", + dep="curl != 8.1.x", + detected=f"curl {env.curl_version_str()}") url = env.mkurl("https", "ssl", "/ssl-client-verify/index.html") r = env.curl_get(url, options=["-vvv", "--tlsv1.2", "--tls-max", "1.2"]) assert 0 != r.exit_code, f"{r}" @@ -130,6 +133,8 @@ # make 10 requests on the same connection, none should produce a status code # reported by erki@example.ee def test_h2_101_05(self, env): + env.require(env.has_h2load(), "h2load not available", dep="h2load", + detected=f"tried to run {env.h2load}") r = env.run([env.h2load, "-n", "10", "-c", "1", "-m", "1", "-vvvv", f"{env.https_base_url}/ssl-client-verify/index.html"]) assert 0 == r.exit_code @@ -163,8 +168,9 @@ # Check that status works with ErrorDoc, see pull #174, fixes #172 def test_h2_101_11(self, env): - if not (env.curl_is_at_least('8.2.0') or env.curl_is_less_than('8.1.0')): - pytest.skip("need curl != 8.1.x version") + env.require(not env.curl_is_8_1_x(), "need curl != 8.1.x version", + dep="curl != 8.1.x", + detected=f"curl {env.curl_version_str()}") url = env.mkurl("https", "ssl", "/renegotiate/err-doc-cipher") r = env.curl_get(url, options=[ "-vvv", "--tlsv1.2", "--tls-max", "1.2", "--ciphers", "ECDHE-RSA-AES256-GCM-SHA384" diff -Nru apache2-2.4.69/test/modules/http2/test_103_upgrade.py apache2-2.4.71/test/modules/http2/test_103_upgrade.py --- apache2-2.4.69/test/modules/http2/test_103_upgrade.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_103_upgrade.py 2026-11-09 03:07:05.000000000 +0000 @@ -4,6 +4,7 @@ @pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") +@H2TestEnv.needs_nghttp() class TestUpgrade: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/http2/test_104_padding.py apache2-2.4.71/test/modules/http2/test_104_padding.py --- apache2-2.4.69/test/modules/http2/test_104_padding.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_104_padding.py 2026-11-09 03:07:05.000000000 +0000 @@ -9,6 +9,7 @@ @pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") +@H2TestEnv.needs_nghttp() class TestPadding: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/http2/test_105_timeout.py apache2-2.4.71/test/modules/http2/test_105_timeout.py --- apache2-2.4.69/test/modules/http2/test_105_timeout.py 2024-07-11 08:18:12.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_105_timeout.py 2026-11-09 03:07:05.000000000 +0000 @@ -142,8 +142,10 @@ def test_h2_105_12(self, env): # long connection timeout, short stream timeout # sending a slow POST - if not env.curl_is_at_least('8.0.0'): - pytest.skip(f'need at least curl v8.0.0 for this') + env.require(env.curl_is_at_least('8.0.0'), + 'need at least curl v8.0.0 for this', + dep='curl >= 8.0.0', + detected=f"curl {env.curl_version_str()}") if not env.httpd_is_at_least("2.5.0"): pytest.skip(f'need at least httpd 2.5.0 for this') conf = H2Conf(env) @@ -159,7 +161,7 @@ time.sleep(2) try: piper.send("0123456789\n") - except BrokenPipeError: + except (BrokenPipeError, OSError): break piper.close() assert piper.response, f'{piper}' diff -Nru apache2-2.4.69/test/modules/http2/test_106_shutdown.py apache2-2.4.71/test/modules/http2/test_106_shutdown.py --- apache2-2.4.69/test/modules/http2/test_106_shutdown.py 2026-09-01 10:21:58.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_106_shutdown.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,6 +1,7 @@ # # mod-h2 test suite # check HTTP/2 timeout behaviour +import sys # import time from threading import Thread @@ -21,6 +22,8 @@ conf.install() assert env.apache_restart() == 0 + @pytest.mark.skipif(sys.platform == "win32", + reason="mpm_winnt restarts child process on reload, killing in-flight h2 streams") def test_h2_106_01(self, env): url = env.mkurl("https", "cgi", "/necho.py") lines = 100000 @@ -87,6 +90,9 @@ # env.httpd_error_log.ignore_recent( lognos = [ - "AH03490" # scoreboard is full, not at MaxRequestWorkers + "AH03490", # scoreboard is full, not at MaxRequestWorkers + # MaxRequestWorkers lowered to fit the ServerLimit, as logged + # by prefork, worker and event + "AH00181", "AH00319", "AH00516", ] ) \ No newline at end of file diff -Nru apache2-2.4.69/test/modules/http2/test_107_frame_lengths.py apache2-2.4.71/test/modules/http2/test_107_frame_lengths.py --- apache2-2.4.69/test/modules/http2/test_107_frame_lengths.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_107_frame_lengths.py 2026-11-09 03:07:05.000000000 +0000 @@ -16,6 +16,7 @@ @pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") +@H2TestEnv.needs_nghttp() class TestFrameLengths: URI_PATHS = [] diff -Nru apache2-2.4.69/test/modules/http2/test_108_goaway.py apache2-2.4.71/test/modules/http2/test_108_goaway.py --- apache2-2.4.69/test/modules/http2/test_108_goaway.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_108_goaway.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,105 @@ +# +# mod-h2 test suite +# check the error code of GOAWAY frames sent by the server +# +import os +import socket +import ssl +import struct +import sys + +import pytest + +from pyhttpd.backport import not_backported +from .env import H2Conf, H2TestEnv + + +@pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") +class TestGoaway: + + PREFACE = b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n' + FRAME_SETTINGS = 0x04 + FRAME_GOAWAY = 0x07 + # error codes of RFC 9113, section 7 + ERR_NO_ERROR = 0x00 + ERR_INADEQUATE_SECURITY = 0x0c + ERR_HTTP_1_1_REQUIRED = 0x0d + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + conf = H2Conf(env, extras={ + 'base': [ + "AcceptFilter http none", + "Timeout 1", + "KeepAliveTimeout 1", + "SSLCipherSuite HIGH:!aNULL", + ] + }) + conf.add_vhost_cgi() + conf.install() + assert env.apache_restart() == 0 + + @staticmethod + def _frame(ftype, payload=b''): + # length, type, flags and the stream id 0 + return len(payload).to_bytes(3, 'big') + bytes([ftype, 0]) + bytes(4) + payload + + @staticmethod + def _recv_exact(sock, n): + data = b'' + while len(data) < n: + chunk = sock.recv(n - len(data)) + if not chunk: + break + data += chunk + return data + + def _goaway_error(self, env, ctx): + """Open a connection, send the client preface and return the error + code of the first GOAWAY frame the server sends, None if there is none.""" + with socket.create_connection(('localhost', int(env.https_port)), timeout=10) as raw: + with ctx.wrap_socket(raw, server_hostname='localhost') as sock: + assert sock.selected_alpn_protocol() == 'h2' + sock.sendall(self.PREFACE + self._frame(self.FRAME_SETTINGS)) + while True: + head = self._recv_exact(sock, 9) + if len(head) < 9: + return None + length = int.from_bytes(head[0:3], 'big') + payload = self._recv_exact(sock, length) + if head[3] == self.FRAME_GOAWAY: + return struct.unpack('>II', payload[:8])[1] + + @staticmethod + def _tls_context(ciphers=None, tls12_only=False): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + ctx.set_alpn_protocols(['h2']) + if tls12_only: + ctx.maximum_version = ssl.TLSVersion.TLSv1_2 + if ciphers: + ctx.set_ciphers(ciphers) + return ctx + + # An idle connection that runs into the server timeout is closed + # gracefully. The GOAWAY must carry NO_ERROR and not the APR status + # of the timeout. (Without the change only the worker MPM and mpm_winnt + # send that.) + @(not_backported("r1939156", "HTTP/2 error codes in GOAWAY") + if os.environ.get("MPM") == "worker" or sys.platform == "win32" + else pytest.mark.usefixtures()) + def test_h2_108_01_idle_timeout(self, env): + error = self._goaway_error(env, self._tls_context()) + assert error == self.ERR_NO_ERROR, f"GOAWAY error code {error}" + + # A TLS connection that is not acceptable for HTTP/2 is refused with + # INADEQUATE_SECURITY (RFC 9113, section 9.2.1). + @not_backported("r1939156", "HTTP/2 error codes in GOAWAY") + def test_h2_108_02_inadequate_security(self, env): + try: + ctx = self._tls_context(ciphers='AES128-SHA256', tls12_only=True) + error = self._goaway_error(env, ctx) + except ssl.SSLError as ex: + pytest.skip(f"cipher not available: {ex}") + assert error == self.ERR_INADEQUATE_SECURITY, f"GOAWAY error code {error}" diff -Nru apache2-2.4.69/test/modules/http2/test_111_header_limit_log.py apache2-2.4.71/test/modules/http2/test_111_header_limit_log.py --- apache2-2.4.69/test/modules/http2/test_111_header_limit_log.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_111_header_limit_log.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,53 @@ +import re + +import pytest + +from pyhttpd.backport import not_backported +from .env import H2Conf, H2TestEnv + +# h2_stream_add_header() logs a limit violation once per stream, not once per +# offending header. The guard used to be h2_stream_is_ready(), but +# set_error_response() only sets rtmp->http_status these days and never makes +# the stream "ready" while headers are still being read, so every oversized +# header logged another line. +# AH10181 is logged from the "too many headers" branch, whose only guard was +# h2_stream_is_ready(). Each header past LimitRequestFields hits it again. +LOGNO = "AH10181" +LIMIT = 5 +NHEADERS = 25 + + +@pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") +class TestHeaderLimitLog: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + conf = H2Conf(env, extras={ + f"test1.{env.http_tld}": [ + f"LimitRequestFields {LIMIT}", + "LogLevel http2:info", + ], + }) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + + @not_backported("r1938328", "header limit message logged once per stream", + backported_in="2.4.70") + def test_h2_111_01(self, env): + env.httpd_error_log.clear_log() + # Many more headers than LimitRequestFields allows. + options = ["--http2"] + for i in range(NHEADERS): + options.extend(["-H", f"X-Extra-{i}: v"]) + r = env.curl_get(env.mkurl("https", "test1", "/index.html"), 5, + options=options) + assert r.exit_code == 0, f"curl failed: {r.stderr}" + assert r.response["status"] == 431, \ + f"expected 431, got {r.response['status']}" + + with open(env.httpd_error_log.path) as fd: + hits = [ln for ln in fd if LOGNO in ln] + assert len(hits) == 1, \ + f"{LOGNO} logged {len(hits)} times for {NHEADERS} headers over " \ + f"LimitRequestFields {LIMIT}, expected once:\n" + "".join(hits) diff -Nru apache2-2.4.69/test/modules/http2/test_200_header_invalid.py apache2-2.4.71/test/modules/http2/test_200_header_invalid.py --- apache2-2.4.69/test/modules/http2/test_200_header_invalid.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_200_header_invalid.py 2026-11-09 03:07:05.000000000 +0000 @@ -5,6 +5,7 @@ @pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") +@H2TestEnv.needs_nghttp() class TestInvalidHeaders: @pytest.fixture(autouse=True, scope='class') @@ -168,11 +169,13 @@ conf.install() assert env.apache_restart() == 0 url = env.mkurl("https", "cgi", "/") - opt = [] - for i in range(21): - opt += ["-H", "x{0}: 1".format(i)] - r = env.curl_get(url, options=opt) - assert 431 == r.response["status"] + + for desc, opts in [ + ("regular", [v for i in range(21) for v in ("-H", f"x{i}: 1")]), + ("cookie", [v for i in range(21) for v in ("-H", f"cookie: c{i}=v{i}")]), + ]: + r = env.curl_get(url, options=opts) + assert r.response["status"] == 431, f"{desc} header failed" conf = H2Conf(env) conf.add(""" LimitRequestFields 0 @@ -213,8 +216,12 @@ # invalid chars in method def test_h2_200_16(self, env): - if not env.h2load_is_at_least('1.45.0'): - pytest.skip(f'nhttp2 version too old') + # this drives nghttp, not h2load: gate on the version of the client + # actually used, so an absent h2load does not skip a runnable test + env.require(env.nghttp_is_at_least('1.45.0'), + 'nghttp2 version too old', + dep='nghttp >= 1.45.0', + detected=f"nghttp {env.nghttp_version_str()}") conf = H2Conf(env) conf.add_vhost_cgi() conf.install() diff -Nru apache2-2.4.69/test/modules/http2/test_202_trailer.py apache2-2.4.71/test/modules/http2/test_202_trailer.py --- apache2-2.4.69/test/modules/http2/test_202_trailer.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_202_trailer.py 2026-11-09 03:07:05.000000000 +0000 @@ -14,6 +14,7 @@ # The trailer tests depend on "nghttp" as no other client seems to be able to send those # rare things. @pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") +@H2TestEnv.needs_nghttp() class TestTrailers: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/http2/test_203_rfc9113.py apache2-2.4.71/test/modules/http2/test_203_rfc9113.py --- apache2-2.4.69/test/modules/http2/test_203_rfc9113.py 2026-07-06 11:52:40.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_203_rfc9113.py 2026-11-09 03:07:05.000000000 +0000 @@ -71,3 +71,28 @@ if lognos is not None: env.httpd_error_log.ignore_recent(lognos = lognos) + # RFC 9113, ch. 8.3.1: an OPTIONS request in asterisk form carries ':path: *' + def test_h2_203_03_options_asterisk(self, env): + url = env.mkurl("https", "test1", "/") + r = env.curl_get(url, options=['--http2', '-X', 'OPTIONS', + '--request-target', '*']) + assert r.exit_code == 0, f'curl output: {r.stderr}' + assert r.response["protocol"] == "HTTP/2", f'curl output: {r.stdout}' + assert r.response["status"] == 200, f'curl output: {r.stdout}' + + # the asterisk form is only valid for OPTIONS, other ':path' values + # not starting with '/' remain malformed. nghttp2 may already reset + # the stream (curl fails), otherwise we answer with a 400. + @pytest.mark.parametrize(["method", "target"], [ + ['GET', '*'], + ['POST', '*'], + ['OPTIONS', '**'], + ['OPTIONS', 'index.html'], + ]) + def test_h2_203_04_invalid_path(self, env, method, target): + url = env.mkurl("https", "test1", "/") + r = env.curl_get(url, options=['--http2', '-X', method, + '--request-target', target]) + assert r.exit_code != 0 or r.response["status"] == 400, \ + f'curl exit: {r.exit_code}, output: {r.stdout}' + diff -Nru apache2-2.4.69/test/modules/http2/test_400_push.py apache2-2.4.71/test/modules/http2/test_400_push.py --- apache2-2.4.69/test/modules/http2/test_400_push.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_400_push.py 2026-11-09 03:07:05.000000000 +0000 @@ -6,6 +6,7 @@ # The push tests depend on "nghttp" @pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") +@H2TestEnv.needs_nghttp() class TestPush: @pytest.fixture(autouse=True, scope='class') @@ -198,3 +199,20 @@ assert r.response["status"] == 200 promises = r.results["streams"][r.response["id"]]["promises"] assert 1 == len(promises) + + +@pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") +class TestPushNoDiary: + + # push diary disabled, with debug logging + def test_h2_400_60(self, env): + conf = H2Conf(env) + conf.add(""" + H2PushDiarySize 0 + LogLevel http2:debug + """) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + r = env.curl_get(env.mkurl("https", "test1", "/index.html")) + assert r.response["status"] == 200 diff -Nru apache2-2.4.69/test/modules/http2/test_401_early_hints.py apache2-2.4.71/test/modules/http2/test_401_early_hints.py --- apache2-2.4.69/test/modules/http2/test_401_early_hints.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_401_early_hints.py 2026-11-09 03:07:05.000000000 +0000 @@ -5,6 +5,7 @@ # The push tests depend on "nghttp" @pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") +@H2TestEnv.needs_nghttp() class TestEarlyHints: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/http2/test_500_proxy.py apache2-2.4.71/test/modules/http2/test_500_proxy.py --- apache2-2.4.69/test/modules/http2/test_500_proxy.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_500_proxy.py 2026-11-09 03:07:05.000000000 +0000 @@ -7,6 +7,7 @@ @pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") +@H2TestEnv.needs_nghttp() class TestProxy: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/http2/test_503_proxy_fwd.py apache2-2.4.71/test/modules/http2/test_503_proxy_fwd.py --- apache2-2.4.69/test/modules/http2/test_503_proxy_fwd.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_503_proxy_fwd.py 2026-11-09 03:07:05.000000000 +0000 @@ -46,8 +46,10 @@ assert r.json['port'] == f'{env.http_port}' def test_h2_503_02_fwd_proxy_h2_off(self, env): - if not env.curl_is_at_least('8.1.0'): - pytest.skip(f'need at least curl v8.1.0 for this') + env.require(env.curl_is_at_least('8.1.0'), + 'need at least curl v8.1.0 for this', + dep='curl >= 8.1.0', + detected=f"curl {env.curl_version_str()}") url = f'http://localhost:{env.http_port}/hello.py' proxy_host = f'test1.{env.http_tld}' options = [ @@ -62,8 +64,10 @@ # test the HTTP/2 setup working def test_h2_503_03_proxy_fwd_h2_on(self, env): - if not env.curl_is_at_least('8.1.0'): - pytest.skip(f'need at least curl v8.1.0 for this') + env.require(env.curl_is_at_least('8.1.0'), + 'need at least curl v8.1.0 for this', + dep='curl >= 8.1.0', + detected=f"curl {env.curl_version_str()}") self.config_fwd_proxy(env, h2_enabled=True) url = f'http://localhost:{env.http_port}/hello.py' proxy_host = f'test1.{env.http_tld}' diff -Nru apache2-2.4.69/test/modules/http2/test_600_h2proxy.py apache2-2.4.71/test/modules/http2/test_600_h2proxy.py --- apache2-2.4.69/test/modules/http2/test_600_h2proxy.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_600_h2proxy.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,4 +1,6 @@ import pytest +import re +import socket from .env import H2Conf, H2TestEnv @@ -120,7 +122,7 @@ 'UseCanonicalPhysicalPort On' ]) conf.start_vhost(domains=[f'cgi.{env.http_tld}'], - port=5004, doc_root="htdocs/cgi") + port=env.http_port2, doc_root="htdocs/cgi") conf.add("AddHandler cgi-script .py") conf.end_vhost() conf.install() @@ -198,3 +200,27 @@ # stream (exit_code != 0) or give a 503 response. if r.exit_code == 0: assert r.response['status'] in [502, 503] + + # test missing host header + def test_h2_600_33(self, env): + + conf = H2Conf(env, extras={ + 'base': [ + 'ProxyPreserveHost on', + f'ProxyPass /test/ h2c://127.0.0.1:{env.http_port}/', + ], + }) + conf.install() + assert env.apache_restart() == 0 + + with socket.create_connection( + ('localhost', int(env.http_port))) as sock: + sock.sendall("GET /test/\r\n\r\n".encode()) + warn_message = re.compile( + r'.*HTTP/0\.9 request \(with no Host header\)' + r' and preserve host set, forcing hostname.*' + ) + assert env.httpd_error_log.scan_recent(warn_message, timeout=5) + env.httpd_error_log.ignore_recent(matches=[ + r'.*HTTP/0\.9 request \(with no Host header\).*', ], + lognos=["AH10511"]) diff -Nru apache2-2.4.69/test/modules/http2/test_601_h2proxy_twisted.py apache2-2.4.71/test/modules/http2/test_601_h2proxy_twisted.py --- apache2-2.4.69/test/modules/http2/test_601_h2proxy_twisted.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_601_h2proxy_twisted.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,8 +1,10 @@ import json import logging import os +import sys import pytest +from pyhttpd.backport import not_backported from .env import H2Conf, H2TestEnv @@ -18,7 +20,9 @@ assert env.apache_restart() == 0 @pytest.mark.parametrize("name", [ - "data-1k", "data-10k", "data-100k", "data-1m", + "data-1k", "data-10k", "data-100k", + pytest.param("data-1m", marks=not_backported( + "cause not identified", "H2 proxy upload of 1MB", platform="win32")), ]) def test_h2_601_01_echo_uploads(self, env, name): fpath = os.path.join(env.gen_dir, name) @@ -72,8 +76,12 @@ def test_h2_601_05_echo_fail_many(self, env): if not env.httpd_is_at_least('2.4.58'): pytest.skip(f'needs httpd 2.4.58') - if not env.curl_is_at_least('8.0.0'): - pytest.skip(f'need at least curl v8.0.0 for this') + if sys.platform == "win32": + pytest.skip("command line too long for Windows (200 parallel curl requests)") + env.require(env.curl_is_at_least('8.0.0'), + 'need at least curl v8.0.0 for this', + dep='curl >= 8.0.0', + detected=f"curl {env.curl_version_str()}") count = 200 fpath = os.path.join(env.gen_dir, "data-100k") args = [env.curl, '--parallel', '--parallel-max', '20'] diff -Nru apache2-2.4.69/test/modules/http2/test_700_load_get.py apache2-2.4.71/test/modules/http2/test_700_load_get.py --- apache2-2.4.69/test/modules/http2/test_700_load_get.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_700_load_get.py 2026-11-09 03:07:05.000000000 +0000 @@ -4,8 +4,7 @@ @pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") -@pytest.mark.skipif(not H2TestEnv().h2load_is_at_least('1.41.0'), - reason="h2load misses --connect-to option") +@H2TestEnv.needs_h2load("1.41.0", reason="h2load misses --connect-to option") class TestLoadGet: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/http2/test_710_load_post_static.py apache2-2.4.71/test/modules/http2/test_710_load_post_static.py --- apache2-2.4.69/test/modules/http2/test_710_load_post_static.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_710_load_post_static.py 2026-11-09 03:07:05.000000000 +0000 @@ -5,6 +5,7 @@ @pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") +@H2TestEnv.needs_h2load() class TestLoadPostStatic: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/http2/test_711_load_post_cgi.py apache2-2.4.71/test/modules/http2/test_711_load_post_cgi.py --- apache2-2.4.69/test/modules/http2/test_711_load_post_cgi.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_711_load_post_cgi.py 2026-11-09 03:07:05.000000000 +0000 @@ -5,6 +5,7 @@ @pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") +@H2TestEnv.needs_h2load() class TestLoadCgi: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/http2/test_712_buffering.py apache2-2.4.71/test/modules/http2/test_712_buffering.py --- apache2-2.4.69/test/modules/http2/test_712_buffering.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_712_buffering.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,4 +1,5 @@ from datetime import timedelta +import sys import pytest @@ -38,6 +39,8 @@ piper.stutter_check(chunks, stutter) def test_h2_712_02(self, env): + if sys.platform == "win32": + pytest.skip("h2proxy echo buffering unreliable on mpm_winnt") # same as 712_01 but via mod_proxy_http2 # url = env.mkurl("https", "cgi", "/h2proxy/h2test/echo") @@ -48,6 +51,8 @@ piper.stutter_check(chunks, stutter) def test_h2_712_03(self, env): + if sys.platform == "win32": + pytest.skip("h2proxy echo buffering unreliable on mpm_winnt") # same as 712_02 but with smaller chunks # url = env.mkurl("https", "cgi", "/h2proxy/h2test/echo") diff -Nru apache2-2.4.69/test/modules/http2/test_800_websockets.py apache2-2.4.71/test/modules/http2/test_800_websockets.py --- apache2-2.4.69/test/modules/http2/test_800_websockets.py 2026-05-07 09:55:24.000000000 +0000 +++ apache2-2.4.71/test/modules/http2/test_800_websockets.py 2026-11-09 03:07:05.000000000 +0000 @@ -10,7 +10,7 @@ import packaging.version import pytest -import websockets +from pyhttpd.depends import needs_dependency from pyhttpd.result import ExecResult from pyhttpd.ws_util import WsFrameReader, WsFrame @@ -19,9 +19,19 @@ log = logging.getLogger(__name__) -ws_version = packaging.version.parse(websockets.version.version) ws_version_min = packaging.version.Version('10.4') +# ws_server.py, which these tests run as a subprocess, needs this too. The +# import is guarded so that an absent package is reported as the missing +# optional dependency it is, rather than failing collection of the whole +# module - which took the rest of the http2 suite's reporting with it. +try: + import websockets + ws_version = packaging.version.parse(websockets.version.version) +except ImportError: + websockets = None + ws_version = None + def ws_run(env: H2TestEnv, path, authority=None, do_input=None, inbytes=None, send_close=True, timeout=5, scenario='ws-stdin', @@ -87,8 +97,13 @@ @pytest.mark.skipif(condition=H2TestEnv.is_unsupported(), reason="mod_http2 not supported here") @pytest.mark.skipif(condition=not H2TestEnv().httpd_is_at_least("2.4.60"), reason=f'need at least httpd 2.4.60 for this') -@pytest.mark.skipif(condition=ws_version < ws_version_min, - reason=f'websockets is {ws_version}, need at least {ws_version_min}') +@needs_dependency(f"websockets >= {ws_version_min}", + ws_version is not None and ws_version >= ws_version_min, + reason=(f'websockets is {ws_version}, need at least {ws_version_min}' + if ws_version is not None + else 'no websockets python package available'), + detected=(f"websockets {ws_version}" if ws_version is not None + else "python package 'websockets' not installed")) class TestWebSockets: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/md/conftest.py apache2-2.4.71/test/modules/md/conftest.py --- apache2-2.4.69/test/modules/md/conftest.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/conftest.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,10 +1,13 @@ import logging import os +import shutil import sys import pytest sys.path.append(os.path.join(os.path.dirname(__file__), '../..')) +from pyhttpd.depends import apply_gate, strict_optional_enabled + from .md_env import MDTestEnv from .md_acme import MDPebbleRunner, MDBoulderRunner @@ -19,6 +22,29 @@ ) +def pytest_collection_modifyitems(config, items): + """Gate the tests that need the pebble ACME server on it being installed. + + The acme fixture starts 'pebble' and 'pebble-challtestsrv'. Without them + every such test used to error in setup; it is a missing dependency, so + skip (or fail under --strict-optional) before an httpd is started. + """ + if MDTestEnv.get_acme_server() != 'pebble': + return + tools = ('pebble', 'pebble-challtestsrv') + found = {tool: shutil.which(tool) for tool in tools} + if all(found.values()): + return + missing = [(f"{', '.join(t for t in tools if not found[t])} (ACME test server)", + "pebble ACME test server not installed", + ", ".join(f"{t}: {found[t] or 'not on PATH'}" for t in tools))] + strict = strict_optional_enabled(config) + md_dir = os.path.dirname(os.path.abspath(__file__)) + for item in items: + if str(item.path).startswith(md_dir) and 'acme' in item.fixturenames: + apply_gate(item, missing, strict) + + @pytest.fixture(scope="package") def env(pytestconfig) -> MDTestEnv: level = logging.INFO @@ -28,6 +54,10 @@ logging.getLogger('').addHandler(console) logging.getLogger('').setLevel(level=level) env = MDTestEnv(pytestconfig=pytestconfig) + # mod_md needs jansson and libcurl to be built; setup_httpd() raises if + # the module it LoadModules is missing. Report the missing dependency. + env.require(env.has_shared_module("md"), "mod_md was not built", dep="mod_md", + detected=f"no mod_md.so in {env.libexec_dir}") env.setup_httpd() env.apache_access_log_clear() env.httpd_error_log.clear_log() diff -Nru apache2-2.4.69/test/modules/md/dns01.py apache2-2.4.71/test/modules/md/dns01.py --- apache2-2.4.69/test/modules/md/dns01.py 2021-12-14 11:33:27.000000000 +0000 +++ apache2-2.4.71/test/modules/md/dns01.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,12 +1,28 @@ #!/usr/bin/env python3 +import os import subprocess import sys -curl = "curl" +from configparser import ConfigParser, ExtendedInterpolation + challtestsrv = "localhost:8055" +def curl_bin(): + """The curl binary the test framework is configured to use.""" + if 'CURL' in os.environ: + return os.environ['CURL'] + pyhttpd_dir = os.path.join(os.path.dirname(__file__), '..', '..', 'pyhttpd') + config_ini = os.getenv("PYHTTPD_CONFIG", os.path.join(pyhttpd_dir, 'config.ini')) + config = ConfigParser(interpolation=ExtendedInterpolation()) + config.read(config_ini) + return config.get('global', 'curl_bin', fallback='curl') + + +curl = curl_bin() + + def run(args): sys.stderr.write(f"run: {' '.join(args)}\n") p = subprocess.Popen(args, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE) diff -Nru apache2-2.4.69/test/modules/md/dns01_record.py apache2-2.4.71/test/modules/md/dns01_record.py --- apache2-2.4.69/test/modules/md/dns01_record.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/md/dns01_record.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# A MDChallengeDns01 command which records the arguments it was invoked with, +# so that a test can check how mod_md passes them on, and then hands the work +# to dns01.py so that the challenge still succeeds. + +import os +import sys + +sys.path.append(os.path.dirname(__file__)) + +import dns01 + + +def main(argv): + if len(argv) < 4: + sys.stderr.write(f"{argv[0]} without arguments\n") + sys.exit(2) + with open(argv[1], 'a+') as fd: + fd.write(f'{argv}\n') + if argv[2] == 'setup': + if len(argv) != 5: + sys.stderr.write("wrong number of arguments: " + "dns01_record.py <log> setup <domain> <challenge>\n") + sys.exit(2) + rv = dns01.setup(argv[3], argv[4]) + elif argv[2] == 'teardown': + rv = dns01.teardown(argv[3]) + else: + sys.stderr.write(f"unknown option {argv[2]}\n") + rv = 2 + sys.exit(rv) + + +if __name__ == "__main__": + main(sys.argv) diff -Nru apache2-2.4.69/test/modules/md/dns01_v2.py apache2-2.4.71/test/modules/md/dns01_v2.py --- apache2-2.4.69/test/modules/md/dns01_v2.py 2024-06-06 14:29:10.000000000 +0000 +++ apache2-2.4.71/test/modules/md/dns01_v2.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,12 +1,28 @@ #!/usr/bin/env python3 +import os import subprocess import sys -curl = "curl" +from configparser import ConfigParser, ExtendedInterpolation + challtestsrv = "localhost:8055" +def curl_bin(): + """The curl binary the test framework is configured to use.""" + if 'CURL' in os.environ: + return os.environ['CURL'] + pyhttpd_dir = os.path.join(os.path.dirname(__file__), '..', '..', 'pyhttpd') + config_ini = os.getenv("PYHTTPD_CONFIG", os.path.join(pyhttpd_dir, 'config.ini')) + config = ConfigParser(interpolation=ExtendedInterpolation()) + config.read(config_ini) + return config.get('global', 'curl_bin', fallback='curl') + + +curl = curl_bin() + + def run(args): sys.stderr.write(f"run: {' '.join(args)}\n") p = subprocess.Popen(args, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE) diff -Nru apache2-2.4.69/test/modules/md/md_cert_util.py apache2-2.4.71/test/modules/md/md_cert_util.py --- apache2-2.4.69/test/modules/md/md_cert_util.py 2024-09-17 12:10:23.000000000 +0000 +++ apache2-2.4.71/test/modules/md/md_cert_util.py 2026-11-09 03:07:05.000000000 +0000 @@ -11,7 +11,12 @@ from http.client import HTTPConnection from urllib.parse import urlparse +from cryptography.hazmat._oid import ExtensionOID +from cryptography.hazmat.bindings._rust import ObjectIdentifier +from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption, load_pem_private_key + from cryptography import x509 +from cryptography.x509 import DNSName, ExtensionNotFound SEC_PER_DAY = 24 * 60 * 60 @@ -21,7 +26,6 @@ class MDCertUtil(object): # Utility class for inspecting certificates in test cases - # Uses PyOpenSSL: https://pyopenssl.org/en/stable/index.html @classmethod def load_server_cert(cls, host_ip, host_port, host_name, tls=None, ciphers=None): @@ -42,12 +46,12 @@ connection.setblocking(1) connection.set_tlsext_host_name(host_name.encode('utf-8')) connection.do_handshake() - peer_cert = connection.get_peer_certificate() - return MDCertUtil(None, cert=peer_cert) + ossl_cert = connection.get_peer_certificate() + return MDCertUtil(None, cert=ossl_cert.to_cryptography()) @classmethod def parse_pem_cert(cls, text): - cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, text.encode('utf-8')) + cert = x509.load_pem_x509_certificate(text.encode('utf-8')) return MDCertUtil(None, cert=cert) @classmethod @@ -72,24 +76,26 @@ return None def __init__(self, cert_path, cert=None): + self.cert = cert + self.privkey = None if cert_path is not None: self.cert_path = cert_path # load certificate and private key if cert_path.startswith("http"): - cert_data = self.get_plain(cert_path, 1) - else: - cert_data = MDCertUtil._load_binary_file(cert_path) - - for file_type in (OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_ASN1): - try: - self.cert = OpenSSL.crypto.load_certificate(file_type, cert_data) - except Exception as error: - self.error = error - if cert is not None: - self.cert = cert - - if self.cert is None: - raise self.error + assert False + try: + with open(cert_path) as fd: + cert = x509.load_pem_x509_certificate("".join(fd.readlines()).encode()) + except Exception as error: + self.error = error + if cert is not None: + self.cert = cert + if self.cert is None: + raise self.error + + def add_privkey(self, path, password=None): + with open(path) as fd: + self.privkey = load_pem_private_key("".join(fd.readlines()).encode(), password=password) def get_issuer(self): return self.cert.get_issuer() @@ -97,21 +103,20 @@ def get_serial(self): # the string representation of a serial number is not unique. Some # add leading 0s to align with word boundaries. - return ("%lx" % (self.cert.get_serial_number())).upper() + return ("%lx" % (self.cert.serial_number)).upper() @staticmethod def _get_serial(cert) -> int: if isinstance(cert, x509.Certificate): return cert.serial_number if isinstance(cert, MDCertUtil): - return cert.get_serial_number() - elif isinstance(cert, OpenSSL.crypto.X509): - return cert.get_serial_number() + return cert.cert.serial_number elif isinstance(cert, str): # assume a hex number return int(cert, 16) elif isinstance(cert, int): return cert + assert False, f'{cert}' return 0 def get_serial_number(self): @@ -121,89 +126,33 @@ return self._get_serial(self.cert) == self._get_serial(other) def get_not_before(self): - tsp = self.cert.get_notBefore() - return self._parse_tsp(tsp) + try: + return self.cert.not_valid_before_utc + except AttributeError: + return self.cert.not_valid_before def get_not_after(self): - tsp = self.cert.get_notAfter() - return self._parse_tsp(tsp) - - def get_cn(self): - return self.cert.get_subject().CN + try: + return self.cert.not_valid_after_utc + except AttributeError: + return self.cert.not_valid_after def get_key_length(self): - return self.cert.get_pubkey().bits() + return self.cert.public_key().key_size def get_san_list(self): - text = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_TEXT, self.cert).decode("utf-8") - m = re.search(r"X509v3 Subject Alternative Name:(\s+critical)?\s*(.*)", text) - sans_list = [] - if m: - sans_list = m.group(2).split(",") - - def _strip_prefix(s): - return s.split(":")[1] if s.strip().startswith("DNS:") else s.strip() - return list(map(_strip_prefix, sans_list)) + sans = self.cert.extensions.get_extension_for_class(x509.SubjectAlternativeName) + return sans.value.get_values_for_type(DNSName) def get_must_staple(self): - text = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_TEXT, self.cert).decode("utf-8") - m = re.search(r"1.3.6.1.5.5.7.1.24:\s*\n\s*0....", text) - if not m: - # Newer openssl versions print this differently - m = re.search(r"TLS Feature:\s*\n\s*status_request\s*\n", text) - return m is not None + try: + self.cert.extensions.get_extension_for_oid(ExtensionOID.TLS_FEATURE) + return True + except ExtensionNotFound: + return False @classmethod def validate_privkey(cls, privkey_path, passphrase=None): - privkey_data = cls._load_binary_file(privkey_path) - if passphrase: - privkey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, privkey_data, passphrase) - else: - privkey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, privkey_data) - return privkey.check() - - def validate_cert_matches_priv_key(self, privkey_path): - # Verifies that the private key and cert match. - privkey_data = MDCertUtil._load_binary_file(privkey_path) - privkey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, privkey_data) - context = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD) - context.use_privatekey(privkey) - context.use_certificate(self.cert) - context.check_privatekey() - - # --------- _utils_ --------- - - def astr(self, s): - return s.decode('utf-8') - - def _parse_tsp(self, tsp): - # timestampss returned by PyOpenSSL are bytes - # parse date and time part - s = ("%s-%s-%s %s:%s:%s" % (self.astr(tsp[0:4]), self.astr(tsp[4:6]), self.astr(tsp[6:8]), - self.astr(tsp[8:10]), self.astr(tsp[10:12]), self.astr(tsp[12:14]))) - timestamp = datetime.strptime(s, '%Y-%m-%d %H:%M:%S') - # adjust timezone - tz_h, tz_m = 0, 0 - m = re.match(r"([+\-]\d{2})(\d{2})", self.astr(tsp[14:])) - if m: - tz_h, tz_m = int(m.group(1)), int(m.group(2)) if tz_h > 0 else -1 * int(m.group(2)) - return timestamp.replace(tzinfo=self.FixedOffset(60 * tz_h + tz_m)) - - @classmethod - def _load_binary_file(cls, path): - with open(path, mode="rb") as file: - return file.read() - - class FixedOffset(tzinfo): - - def __init__(self, offset): - self.__offset = timedelta(minutes=offset) - - def utcoffset(self, dt): - return self.__offset - - def tzname(self, dt): - return None - - def dst(self, dt): - return timedelta(0) + with open(privkey_path) as fd: + privkey = load_pem_private_key("".join(fd.readlines()).encode(), password=passphrase) + return privkey is not None diff -Nru apache2-2.4.69/test/modules/md/md_conf.py apache2-2.4.71/test/modules/md/md_conf.py --- apache2-2.4.69/test/modules/md/md_conf.py 2025-11-04 14:51:48.000000000 +0000 +++ apache2-2.4.71/test/modules/md/md_conf.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,3 +1,5 @@ +import os + from .md_env import MDTestEnv from pyhttpd.conf import HttpdConf @@ -6,7 +8,7 @@ def __init__(self, env: MDTestEnv, text=None, std_ports=True, local_ca=True, std_vhosts=True, proxy=False, - admin=None): + admin=None, no_renewal=False): super().__init__(env=env) if admin is None: @@ -25,6 +27,11 @@ f"MDCACertificateFile {env.server_dir}/acme-ca.pem", "", ]) + if no_renewal: + # A renewal still running when the server stops delays the stop, + # which httpd logs as a warning. A failing handler vetoes it. + cmd = os.path.join(env.test_dir, "../modules/md/msg_fail_on.py") + self.add(f"MDMessageCmd {cmd} {env.gen_dir}/message.log renewing") if std_ports: self.add(f"MDPortMap 80:{env.http_port} 443:{env.https_port}") if env.ssl_module == "mod_tls": @@ -46,7 +53,7 @@ " ProxyRequests On", " ProxyVia On", " # be totally open", - " AllowCONNECT 0-56535", + " AllowCONNECT 0-65535", " <Proxy *>", " # No require or other restrictions, this is just a test server", " </Proxy>", diff -Nru apache2-2.4.69/test/modules/md/md_env.py apache2-2.4.71/test/modules/md/md_env.py --- apache2-2.4.69/test/modules/md/md_env.py 2024-09-17 12:10:23.000000000 +0000 +++ apache2-2.4.71/test/modules/md/md_env.py 2026-11-09 03:07:05.000000000 +0000 @@ -90,8 +90,9 @@ @classmethod def has_a2md(cls): d = os.path.dirname(inspect.getfile(HttpdTestEnv)) + config_ini = os.getenv("PYHTTPD_CONFIG", os.path.join(d, 'config.ini')) config = ConfigParser(interpolation=ExtendedInterpolation()) - config.read(os.path.join(d, 'config.ini')) + config.read(config_ini) bin_dir = config.get('global', 'bindir') a2md_bin = os.path.join(bin_dir, 'a2md') return os.path.isfile(a2md_bin) @@ -212,16 +213,21 @@ def check_acme(self): if self._acme_server_ok: return True - if self._acme_server_down: - pytest.skip(msg="ACME server not running") - return False - if self.is_live(self.acme_url, timeout=timedelta(seconds=0.5)): - self._acme_server_ok = True - return True - else: + if not self._acme_server_down: + if self.is_live(self.acme_url, timeout=timedelta(seconds=0.5)): + self._acme_server_ok = True + return True self._acme_server_down = True - pytest.fail(msg="ACME server not running", pytrace=False) - return False + # Both the first test to notice and every one after it report the + # same way: a configured-but-unreachable ACME server is a missing + # dependency, so skip, or fail under --strict-optional. Previously + # the first failed and the rest skipped, and both calls passed the + # 'msg' keyword that pytest removed in 8.0, raising TypeError. + self.require(False, "ACME server not running", + dep="ACME test server", + detected=f"ACME={self.get_acme_server()}, " + f"no response from {self.acme_url}") + return False def get_ca_pem_file(self, hostname: str) -> Optional[str]: pem_file = super().get_ca_pem_file(hostname) @@ -340,7 +346,7 @@ md = self.get_md_status(domain) assert md assert 'state' in md, "md is unexpected: {0}".format(md) - assert md['state'] is MDTestEnv.MD_S_COMPLETE, f"unexpected state: {md['state']}" + assert md['state'] is MDTestEnv.MD_S_COMPLETE, f"unexpected state: {md}" pkey_file = self.store_domain_file(domain, self.pkey_fname(pkey)) cert_file = self.store_domain_file(domain, self.cert_fname(pkey)) r = self.run(['ls', os.path.dirname(pkey_file)]) @@ -359,7 +365,7 @@ # check private key, validate certificate, etc MDCertUtil.validate_privkey(self.store_domain_file(domain, 'privkey.pem')) cert = MDCertUtil(self.store_domain_file(domain, 'pubcert.pem')) - cert.validate_cert_matches_priv_key(self.store_domain_file(domain, 'privkey.pem')) + cert.add_privkey(self.store_domain_file(domain, 'privkey.pem')) # No longer check CN, it may not be set or is not trusted anyway # assert cert.get_cn() == domain, f'CN: expected "{domain}", got {cert.get_cn()}' # check SANs @@ -474,14 +480,22 @@ via_domain=None, use_https=True): try_until = time.time() + timeout renewals = {} + statuses = {} names = names.copy() while len(names) > 0: if time.time() >= try_until: + self.save_renewal_failure(names, statuses) return False for name in names: - mds = self.get_md_status(name, via_domain=via_domain, use_https=use_https) + try: + mds = self.get_md_status(name, via_domain=via_domain, use_https=use_https) + except AssertionError: + self.save_renewal_failure(names, statuses) + raise + statuses[name] = mds if mds is None: log.debug("not managed by md: %s" % name) + self.save_renewal_failure(names, statuses) return False if 'renewal' in mds: @@ -496,9 +510,41 @@ time.sleep(0.1) if restart: time.sleep(0.1) - return self.apache_restart() == 0 + if self.apache_restart() != 0: + self.save_renewal_failure(names, statuses) + return False return True + def save_renewal_failure(self, names, statuses): + """Keep diagnostics before the next test clears the log and MD store. + + Only logs, configuration and public job/status JSON are copied, not + private keys or ACME account credentials. + """ + test_name = self.current_test_name or "renewal" + safe_name = re.sub(r'[^A-Za-z0-9_.-]', '_', test_name) + dest = os.path.join(self.gen_dir, "failed-tests", safe_name) + try: + os.makedirs(dest, exist_ok=True) + with open(os.path.join(dest, "md-status.json"), "w") as fd: + json.dump(statuses, fd, indent=2) + for src, name in [ + (self.httpd_error_log.path, "error_log"), + (os.path.join(self.server_dir, "conf", "httpd.conf"), "httpd.conf"), + (os.path.join(self.gen_dir, "pebble.log"), "pebble.log"), + ]: + if os.path.isfile(src): + shutil.copyfile(src, os.path.join(dest, name)) + for name in statuses: + src = self.path_job(name) + if os.path.isfile(src): + safe_domain = re.sub(r'[^A-Za-z0-9_.-]', '_', name) + shutil.copyfile(src, os.path.join(dest, f"{safe_domain}-job.json")) + log.error("renewal did not complete for %s; diagnostics in %s; status: %s", + names, dest, statuses) + except OSError: + log.exception("unable to save renewal diagnostics in %s", dest) + def is_renewing(self, name): stat = self.get_certificate_status(name) return 'renewal' in stat diff -Nru apache2-2.4.69/test/modules/md/test_001_store.py apache2-2.4.71/test/modules/md/test_001_store.py --- apache2-2.4.69/test/modules/md/test_001_store.py 2022-05-16 11:36:20.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_001_store.py 2026-11-09 03:07:05.000000000 +0000 @@ -4,6 +4,8 @@ import pytest +from pyhttpd.depends import needs_dependency + from .md_env import MDTestEnv @@ -11,7 +13,7 @@ return md['name'] -@pytest.mark.skipif(condition=not MDTestEnv.has_a2md(), reason="no a2md available") +@needs_dependency("a2md", MDTestEnv.has_a2md(), reason="no a2md available") class TestStore: @pytest.fixture(autouse=True, scope='function') @@ -21,7 +23,7 @@ # verify expected binary version def test_md_001_001(self, env: MDTestEnv): r = env.run([env.a2md_bin, "-V"]) - m = re.match(r'version: (\d+\.\d+\.\d+)(-git)?$', r.stdout) + m = re.match(r'version: (\d+\.\d+\.\d+)(-(git|dev))?$', r.stdout) assert m, f"expected version info in '{r.stdout}'" # verify that store is clean diff -Nru apache2-2.4.69/test/modules/md/test_010_store_migrate.py apache2-2.4.71/test/modules/md/test_010_store_migrate.py --- apache2-2.4.69/test/modules/md/test_010_store_migrate.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_010_store_migrate.py 2026-11-09 03:07:05.000000000 +0000 @@ -3,11 +3,13 @@ import os import pytest +from pyhttpd.depends import needs_dependency + from .md_conf import MDConf from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_a2md(), reason="no a2md available") +@needs_dependency("a2md", MDTestEnv.has_a2md(), reason="no a2md available") class TestStoreMigrate: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/md/test_100_reg_add.py apache2-2.4.71/test/modules/md/test_100_reg_add.py --- apache2-2.4.69/test/modules/md/test_100_reg_add.py 2022-05-16 11:36:20.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_100_reg_add.py 2026-11-09 03:07:05.000000000 +0000 @@ -2,12 +2,15 @@ import pytest +from pyhttpd.depends import needs_dependency + from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_a2md(), reason="no a2md available") -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("a2md", MDTestEnv.has_a2md(), reason="no a2md available") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestRegAdd: @pytest.fixture(autouse=True, scope='function') diff -Nru apache2-2.4.69/test/modules/md/test_110_reg_update.py apache2-2.4.71/test/modules/md/test_110_reg_update.py --- apache2-2.4.69/test/modules/md/test_110_reg_update.py 2022-05-16 11:36:20.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_110_reg_update.py 2026-11-09 03:07:05.000000000 +0000 @@ -2,12 +2,15 @@ import pytest +from pyhttpd.depends import needs_dependency + from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_a2md(), reason="no a2md available") -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("a2md", MDTestEnv.has_a2md(), reason="no a2md available") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestRegUpdate: NAME1 = "greenbytes2.de" diff -Nru apache2-2.4.69/test/modules/md/test_120_reg_list.py apache2-2.4.71/test/modules/md/test_120_reg_list.py --- apache2-2.4.69/test/modules/md/test_120_reg_list.py 2022-05-16 11:36:20.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_120_reg_list.py 2026-11-09 03:07:05.000000000 +0000 @@ -4,12 +4,15 @@ import pytest +from pyhttpd.depends import needs_dependency + from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_a2md(), reason="no a2md available") -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("a2md", MDTestEnv.has_a2md(), reason="no a2md available") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestRegAdd: @pytest.fixture(autouse=True, scope='function') diff -Nru apache2-2.4.69/test/modules/md/test_202_acmev2_regs.py apache2-2.4.71/test/modules/md/test_202_acmev2_regs.py --- apache2-2.4.69/test/modules/md/test_202_acmev2_regs.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_202_acmev2_regs.py 2026-11-09 03:07:05.000000000 +0000 @@ -4,13 +4,16 @@ import json import pytest +from pyhttpd.depends import needs_dependency + from .md_conf import MDConf from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_a2md(), reason="no a2md available") -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("a2md", MDTestEnv.has_a2md(), reason="no a2md available") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestAcmeAcc: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/md/test_300_conf_validate.py apache2-2.4.71/test/modules/md/test_300_conf_validate.py --- apache2-2.4.69/test/modules/md/test_300_conf_validate.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_300_conf_validate.py 2026-11-09 03:07:05.000000000 +0000 @@ -7,12 +7,15 @@ import pytest +from pyhttpd.depends import needs_dependency + from .md_conf import MDConf from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestConf: @pytest.fixture(autouse=True, scope='class') @@ -101,6 +104,7 @@ # test case: two MDomains, one correct vhost name def test_md_300_006(self, env): MDConf(env, text=""" + MDRenewMode manual MDomain not-forbidden.org www.not-forbidden.org mail.not-forbidden.org test3.not-forbidden.org <VirtualHost *:12346> ServerName example2.org @@ -118,6 +122,7 @@ # test case: two MDomains, two correct vhost names def test_md_300_007(self, env): MDConf(env, text=""" + MDRenewMode manual MDomain not-forbidden.org www.not-forbidden.org mail.not-forbidden.org test3.not-forbidden.org <VirtualHost *:12346> ServerName example2.org @@ -138,6 +143,7 @@ # test case: two MDomains, overlapping vhosts def test_md_300_008(self, env): MDConf(env, text=""" + MDRenewMode manual MDomain not-forbidden.org www.not-forbidden.org mail.not-forbidden.org test3.not-forbidden.org <VirtualHost *:12346> ServerName example2.org @@ -184,6 +190,7 @@ def test_md_300_010(self, env): conf = MDConf(env) conf.add(""" + MDRenewMode manual MDomain not-forbidden.org www.not-forbidden.org mail.not-forbidden.org test3.not-forbidden.org <VirtualHost *:12346> @@ -204,7 +211,11 @@ "not-forbidden.org", "test3.not-forbidden.org", "test4.not-forbidden.org" ]) conf.install() - assert env.apache_fail() == 0 + # not an error: mod_md warns and the server starts (check_coverage()) + assert env.apache_restart() == 0, f'{env.apachectl_stderr}' + assert env.httpd_error_log.scan_recent(re.compile( + r'.* AH10040: .* the name/alias test4\.not-forbidden\.org itself is not managed'), + timeout=1) env.apache_stop() env.httpd_error_log.ignore_recent([ "AH10040" # A requested MD certificate will not match ServerName @@ -214,6 +225,7 @@ def test_md_300_011b(self, env): env.apache_stop() MDConf(env, text=""" + MDRenewMode manual MDomain not-forbidden.org auto mail.not-forbidden.org <VirtualHost *:%s> @@ -244,6 +256,7 @@ # test case: one md covers two vhosts def test_md_300_013(self, env): MDConf(env, text=""" + MDRenewMode manual MDomain example2.org test-a.example2.org test-b.example2.org <VirtualHost *:12346> ServerName test-a.example2.org @@ -344,7 +357,11 @@ "not.secret.com", "secret.com" ]) conf.install() - assert env.apache_fail() == 0 + # accepted: a name of the vhost that the MD does not cover is only + # warned about, see test_md_300_011a + assert env.apache_restart() == 0, f'{env.apachectl_stderr}' + env.apache_stop() + env.httpd_error_log.ignore_recent(["AH10040"]) # this is unreliable on debian #assert env.httpd_error_log.scan_recent( # re.compile(r'.*Virtual Host not.secret.com:0 matches Managed Domain \'secret.com\', ' @@ -355,6 +372,7 @@ # test case: use MDRequireHttps in an <if> construct, but not in <Directory def test_md_300_022(self, env): MDConf(env, text=""" + MDRenewMode manual MDomain secret.com <If "1 == 1"> MDRequireHttps temporary @@ -424,6 +442,7 @@ conf = MDConf(env) domain = f"t300-026.{env.http_tld}" conf.add(f""" + MDRenewMode manual MDomain {domain} """) conf.add_vhost(port=env.http_port, domains=[domain], with_ssl=False) @@ -474,6 +493,7 @@ dalias = f"t300-028alias.{env.http_tld}" conf.add_vhost(port=env.http_port, domains=[domaina, domainb, dalias], with_ssl=False) conf.add(f""" + MDRenewMode manual MDMembers manual MDomain {domaina} MDomain {domainb} {dalias} diff -Nru apache2-2.4.69/test/modules/md/test_310_conf_store.py apache2-2.4.71/test/modules/md/test_310_conf_store.py --- apache2-2.4.69/test/modules/md/test_310_conf_store.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_310_conf_store.py 2026-11-09 03:07:05.000000000 +0000 @@ -2,6 +2,8 @@ import time import pytest + +from pyhttpd.depends import needs_dependency import os from .md_conf import MDConf @@ -12,15 +14,22 @@ NS_PER_DAY = MS_PER_DAY * 1000 -@pytest.mark.skipif(condition=not MDTestEnv.has_a2md(), reason="no a2md available") -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("a2md", MDTestEnv.has_a2md(), reason="no a2md available") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestConf: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, acme): acme.start(config='default') env.check_acme() + env.httpd_error_log.add_ignored_lognos(["AH10045", "AH10105"]) + # the failing message command of MDConf(no_renewal=True) + env.httpd_error_log.add_ignored_matches([r'.*urn:org:apache:httpd:log:AH10109:.*']) + yield + env.httpd_error_log.remove_ignored_lognos(["AH10045", "AH10105"]) + env.httpd_error_log.clear_ignored_matches() @pytest.fixture(autouse=True, scope='function') def _method_scope(self, env, request): @@ -133,6 +142,7 @@ # test case: assign separate contact info based on VirtualHost def test_md_310_107(self, env): MDConf(env, admin="", text=""" + MDRenewMode manual MDomain testdomain.org www.testdomain.org mail.testdomain.org MDomain testdomain2.org www.testdomain2.org mail.testdomain2.org @@ -190,7 +200,7 @@ # test case: drive mode always def test_md_310_112(self, env): - MDConf(env, text=""" + MDConf(env, no_renewal=True, text=""" MDRenewMode always MDomain testdomain.org www.testdomain.org mail.testdomain.org """).install() @@ -245,6 +255,7 @@ # test case: automatically collect md names from vhost config def test_md_310_117(self, env): conf = MDConf(env, text=""" + MDRenewMode manual MDMember auto MDomain testdomain.org """) @@ -421,7 +432,7 @@ ("always", 2) ]) def test_md_310_207(self, env, renew_mode, exp_code): - MDConf(env, text=""" + MDConf(env, no_renewal=True, text=""" MDRenewMode %s MDomain testdomain.org www.testdomain.org mail.testdomain.org """ % renew_mode).install() @@ -589,7 +600,7 @@ assert env.apache_restart() == 0, f'{env.apachectl_stderr}' assert env.a2md(["list"]).json['output'][0]['renew-mode'] == 1 # test case: drive mode always - MDConf(env, text=""" + MDConf(env, no_renewal=True, text=""" MDRenewMode always MDomain testdomain.org www.testdomain.org mail.testdomain.org """).install() @@ -771,6 +782,7 @@ assert env.a2md(["add", name]).exit_code == 0 assert env.a2md(["update", name, "contacts", "admin@" + name]).exit_code == 0 assert env.a2md(["update", name, "agreement", env.acme_tos]).exit_code == 0 + MDConf(env).install() assert env.apache_restart() == 0, f'{env.apachectl_stderr}' # setup: drive it assert env.a2md(["drive", name]).exit_code == 0 diff -Nru apache2-2.4.69/test/modules/md/test_502_acmev2_drive.py apache2-2.4.71/test/modules/md/test_502_acmev2_drive.py --- apache2-2.4.69/test/modules/md/test_502_acmev2_drive.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_502_acmev2_drive.py 2026-11-09 03:07:05.000000000 +0000 @@ -7,6 +7,8 @@ from datetime import timedelta import pytest + +from pyhttpd.depends import needs_dependency from pyhttpd.certs import CertificateSpec from .md_conf import MDConf @@ -14,9 +16,10 @@ from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_a2md(), reason="no a2md available") -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("a2md", MDTestEnv.has_a2md(), reason="no a2md available") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestDrivev2: @pytest.fixture(autouse=True, scope='class') @@ -395,7 +398,7 @@ # check new cert env.check_md_credentials([name, "test." + domain]) new_cert = MDCertUtil(env.store_domain_file(name, 'pubcert.pem')) - assert not old_cert.same_serial_as(new_cert.get_serial) + assert not old_cert.same_serial_as(new_cert.get_serial()) @pytest.mark.parametrize("renew_window,test_data_list", [ ("14d", [ @@ -440,6 +443,11 @@ md = env.a2md(["list", name]).json['output'][0] assert md["renew"] == tc["renew"], \ "Expected renew == {} indicator in {}, test case {}".format(tc["renew"], md, tc) + env.httpd_error_log.ignore_recent( + lognos=[ + "AH10045" + ] + ) @pytest.mark.parametrize("key_type,key_params,exp_key_length", [ ("RSA", [2048], 2048), @@ -466,6 +474,11 @@ # check cert key length cert = MDCertUtil(env.store_domain_file(name, 'pubcert.pem')) assert cert.get_key_length() == exp_key_length + env.httpd_error_log.ignore_recent( + lognos=[ + "AH10045" + ] + ) # test_502_203 removed, as ToS agreement is not really checked in ACMEv2 @@ -550,4 +563,4 @@ # check: key file is encrypted PEM md = env.a2md(["list", name]).json['output'][0] acc = md['ca']['account'] - MDCertUtil.validate_privkey(env.path_account_key(acc), lambda *args: encrypt_key) + MDCertUtil.validate_privkey(env.path_account_key(acc), encrypt_key) diff -Nru apache2-2.4.69/test/modules/md/test_602_roundtrip.py apache2-2.4.71/test/modules/md/test_602_roundtrip.py --- apache2-2.4.69/test/modules/md/test_602_roundtrip.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_602_roundtrip.py 2026-11-09 03:07:05.000000000 +0000 @@ -4,13 +4,16 @@ import pytest +from pyhttpd.depends import needs_dependency + from .md_conf import MDConf from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_a2md(), reason="no a2md available") -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("a2md", MDTestEnv.has_a2md(), reason="no a2md available") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestRoundtripv2: @pytest.fixture(autouse=True, scope='class') @@ -19,6 +22,9 @@ env.APACHE_CONF_SRC = "data/test_roundtrip" env.clear_store() MDConf(env).install() + env.httpd_error_log.add_ignored_lognos(["AH10045"]) + yield + env.httpd_error_log.remove_ignored_lognos(["AH10045"]) @pytest.fixture(autouse=True, scope='function') def _method_scope(self, env, request): diff -Nru apache2-2.4.69/test/modules/md/test_702_auto.py apache2-2.4.71/test/modules/md/test_702_auto.py --- apache2-2.4.69/test/modules/md/test_702_auto.py 2025-11-04 14:51:48.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_702_auto.py 2026-11-09 03:07:05.000000000 +0000 @@ -3,6 +3,8 @@ from datetime import timedelta import pytest + +from pyhttpd.depends import needs_dependency from pyhttpd.certs import CertificateSpec from pyhttpd.env import HttpdTestEnv @@ -11,8 +13,9 @@ from .md_conf import MDConf -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestAutov2: @pytest.fixture(autouse=True, scope='class') @@ -26,6 +29,10 @@ @pytest.fixture(autouse=True, scope='function') def _method_scope(self, env, request): + # A renewal can still be running after the previous test activated + # its certificate. Do not remove its store while the watchdog writes + # to it, or carry that renewal into the next test's restart. + assert env.apache_stop() == 0 env.clear_store() self.test_domain = env.get_request_domain(request) @@ -208,8 +215,8 @@ # check temporary cert from server cert2 = MDCertUtil(env.path_fallback_cert(domain)) assert cert1.same_serial_as(cert2), \ - "Unexpected temporary certificate on vhost %s. Expected cn: %s , "\ - "but found cn: %s" % (name_a, cert2.get_cn(), cert1.get_cn()) + f"Unexpected temporary certificate on vhost {name_a}." \ + f" Expected cn: {cert2}, but found cn: {cert1}" # test case: drive MD with only invalid challenges, domains should stay 503'd def test_md_702_006(self, env): @@ -297,6 +304,59 @@ assert env.apache_restart() == 0, f'{env.apachectl_stderr}' env.check_md_complete(domain) + # Specify a valid http proxy for a single MDomain + def test_md_702_008b(self, env): + if not env.httpd_is_at_least("2.5.0"): + pytest.xfail("MDHttpProxy inside MDomainSet not backported") + domain = self.test_domain + domains = [domain] + # + conf = MDConf(env, admin=f"admin@{domain}", proxy=True) + conf.add_drive_mode("always") + conf.start_md(domains) + conf.add(f" MDHttpProxy http://localhost:{env.proxy_port}") + conf.end_md() + conf.install() + # + # - restart (-> drive), check that md is in store + assert env.apache_restart() == 0, f'{env.apachectl_stderr}' + assert env.await_completion([domain]) + assert env.apache_restart() == 0, f'{env.apachectl_stderr}' + env.check_md_complete(domain) + + # Specify a non-working http proxy for MDomain A and a valid http proxy for MDomain B + def test_md_702_008c(self, env): + if not env.httpd_is_at_least("2.5.0"): + pytest.xfail("MDHttpProxy inside MDomainSet not backported") + domain_a = f"a{self.test_domain}" + domain_b = f"b{self.test_domain}" + conf = MDConf(env, admin=f"admin@{domain_a}", proxy=True) + conf.start_md([domain_a]) + conf.add(f" MDHttpProxy http://localhost:1") + conf.end_md() + conf.add_vhost(domains=[domain_a]) + conf.start_md([domain_b]) + conf.add(f" MDHttpProxy http://localhost:{env.proxy_port}") + conf.end_md() + conf.add_vhost(domains=[domain_b]) + conf.install() + assert env.apache_restart() == 0, f'{env.apachectl_stderr}' + assert env.await_completion([domain_b], restart=False) + md = env.await_error(domain_a) + assert md + assert md['renewal']['errors'] > 0 + assert md['renewal']['last']['status-description'] == 'Connection refused' + assert 'account' not in md['ca'] + # + env.httpd_error_log.ignore_recent( + lognos = [ + "AH10056" # Unsuccessful in contacting ACME server + ], + matches = [ + r'.*Unsuccessful in contacting ACME server at .*' + ] + ) + # Force cert renewal due to critical remaining valid duration # Assert that new cert activation is delayed def test_md_702_009(self, env, acme): @@ -343,9 +403,15 @@ conf.add_md(domains) conf.add_vhost(domains) conf.install() - assert env.apache_restart() == 0, f'{env.apachectl_stderr}' - md = env.await_error(domain) - assert md["renewal"]["errors"] > 0 + try: + assert env.apache_restart() == 0, f'{env.apachectl_stderr}' + md = env.await_error(domain) + assert md and md["renewal"]["errors"] > 0 + finally: + env.httpd_error_log.ignore_recent( + lognos=["AH10173", "AH10056"], + matches=[r'.*None of the ACME challenge methods configured for this domain are suitable.*'] + ) # # now the same with a 80 mapped to a supported port conf = MDConf(env, admin="admin@" + domain) @@ -358,16 +424,6 @@ assert env.apache_restart() == 0, f'{env.apachectl_stderr}' env.check_md(domains) assert env.await_completion([domain]) - # - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10173", # None of the ACME challenge methods configured for this domain are suitable - "AH10056" # None of the ACME challenge methods configured for this domain are suitable - ], - matches = [ - r'.*None of the ACME challenge methods configured for this domain are suitable.*' - ] - ) def test_md_702_011(self, env): domain = self.test_domain @@ -381,9 +437,15 @@ conf.add_md(domains) conf.add_vhost(domains) conf.install() - assert env.apache_restart() == 0, f'{env.apachectl_stderr}' - md = env.await_error(domain) - assert md["renewal"]["errors"] > 0 + try: + assert env.apache_restart() == 0, f'{env.apachectl_stderr}' + md = env.await_error(domain) + assert md and md["renewal"]["errors"] > 0 + finally: + env.httpd_error_log.ignore_recent( + lognos=["AH10173", "AH10056"], + matches=[r'.*None of the ACME challenge methods configured for this domain are suitable.*'] + ) # # now the same with a 443 mapped to a supported port conf = MDConf(env, admin="admin@" + domain) @@ -397,16 +459,6 @@ assert env.apache_restart() == 0, f'{env.apachectl_stderr}' env.check_md(domains) assert env.await_completion([domain]) - # - env.httpd_error_log.ignore_recent( - lognos = [ - "AH10173", # None of the ACME challenge methods configured for this domain are suitable - "AH10056" # None of the ACME challenge methods configured for this domain are suitable - ], - matches = [ - r'.*None of the ACME challenge methods configured for this domain are suitable.*' - ] - ) # test case: one MD with several dns names. sign up. remove the *first* name # in the MD. restart. should find and keep the existing MD. @@ -626,6 +678,16 @@ stat = env.get_md_status(domain) assert stat["proto"]["acme-tls/1"] == [] MDConf(env).install() + if not env.httpd_is_at_least("2.5.0"): + # On 2.4.x, mod_watchdog does not stop its threads before a + # child exits, so the renewal can still be signing when + # OpenSSL's atexit handler has run, failing with APR_ENOTIMPL. + # Logged by the exiting child after the restart, so possibly + # after this test: ignore it from here on. + env.httpd_error_log.add_ignored_matches([ + r'.*\(70023\)[^:]*: (signing|jwk signed message)$', + r'.*\(70023\)[^:]*: md\[test-md-702-041-[^]]*\]$', + ]) assert env.apache_restart() == 0, f'{env.apachectl_stderr}' env.httpd_error_log.ignore_recent(matches=[ r'.*None of offered challenge types for domain.*', @@ -847,4 +909,3 @@ assert env.apache_restart() == 0, f'{env.apachectl_stderr}' assert env.await_completion(domains) env.check_md_complete(domains[0]) - diff -Nru apache2-2.4.69/test/modules/md/test_710_profiles.py apache2-2.4.71/test/modules/md/test_710_profiles.py --- apache2-2.4.69/test/modules/md/test_710_profiles.py 2025-11-04 14:51:48.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_710_profiles.py 2026-11-09 03:07:05.000000000 +0000 @@ -4,6 +4,8 @@ from datetime import timedelta import pytest + +from pyhttpd.depends import needs_dependency from pyhttpd.certs import CertificateSpec from pyhttpd.env import HttpdTestEnv @@ -12,8 +14,9 @@ from .md_conf import MDConf -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestProfiles: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/md/test_720_wildcard.py apache2-2.4.71/test/modules/md/test_720_wildcard.py --- apache2-2.4.69/test/modules/md/test_720_wildcard.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_720_wildcard.py 2026-11-09 03:07:05.000000000 +0000 @@ -3,12 +3,15 @@ import pytest +from pyhttpd.depends import needs_dependency + from .md_conf import MDConf, MDConf from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestWildcard: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/md/test_721_dns01.py apache2-2.4.71/test/modules/md/test_721_dns01.py --- apache2-2.4.69/test/modules/md/test_721_dns01.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_721_dns01.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,99 @@ +# test the arguments mod_md invokes MDChallengeDns01 with +import os + +import pytest + +from pyhttpd.backport import not_backported +from pyhttpd.depends import needs_dependency + +from .md_conf import MDConf +from .md_env import MDTestEnv + + +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") +class TestDns01: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env, acme): + env.APACHE_CONF_SRC = "data/test_auto" + acme.start(config='default') + env.check_acme() + env.clear_store() + MDConf(env).install() + assert env.apache_restart() == 0, f'{env.apachectl_stderr}' + + @pytest.fixture(autouse=True, scope='function') + def _method_scope(self, env, request): + env.clear_store() + self.test_domain = env.get_request_domain(request) + self.dnscmd = os.path.join(env.test_dir, "../modules/md/dns01_record.py") + self.dnslog = os.path.join(env.gen_dir, "dns01.log") + if os.path.isfile(self.dnslog): + os.remove(self.dnslog) + + def configure_httpd(self, env, domain, version=None): + conf = MDConf(env) + conf.add("MDCAChallenges dns-01") + conf.add(f"MDChallengeDns01 {self.dnscmd} {self.dnslog}") + if version is not None: + conf.add(f"MDChallengeDns01Version {version}") + conf.add_md([domain]) + conf.add_vhost([domain]) + conf.install() + assert env.apache_restart() == 0, f'{env.apachectl_stderr}' + + def calls(self, action): + """The recorded argv of each invocation of the challenge command.""" + if not os.path.isfile(self.dnslog): + return [] + with open(self.dnslog) as fd: + argvs = [eval(line.strip()) for line in fd if line.startswith('[')] + return [argv for argv in argvs if len(argv) > 2 and argv[2] == action] + + # the domain and the challenge token are separate arguments, whatever they + # contain, and version 1 tears down with the domain alone + def test_md_721_001(self, env): + domain = self.test_domain + self.configure_httpd(env, domain) + assert env.await_completion([domain]) + setups = self.calls('setup') + assert len(setups) == 1, f'{setups}' + assert setups[0][0:4] == [self.dnscmd, self.dnslog, 'setup', domain] + assert len(setups[0]) == 5, f'no separate challenge argument: {setups[0]}' + teardowns = self.calls('teardown') + assert len(teardowns) == 1, f'{teardowns}' + assert teardowns[0] == [self.dnscmd, self.dnslog, 'teardown', domain] + + # version 2 tears down with the challenge token as a further argument + def test_md_721_002(self, env): + domain = self.test_domain + self.configure_httpd(env, domain, version=2) + assert env.await_completion([domain]) + setups = self.calls('setup') + assert len(setups) == 1, f'{setups}' + teardowns = self.calls('teardown') + assert len(teardowns) == 1, f'{teardowns}' + assert teardowns[0] == [self.dnscmd, self.dnslog, 'teardown', domain, + setups[0][4]] + + # an argument configured with quotes stays one argument + @not_backported("r1938494", "MDChallengeDns01 arguments passed verbatim") + def test_md_721_003(self, env): + domain = self.test_domain + dnslog = os.path.join(env.gen_dir, "dns01 with spaces.log") + if os.path.isfile(dnslog): + os.remove(dnslog) + self.dnslog = dnslog + conf = MDConf(env) + conf.add("MDCAChallenges dns-01") + conf.add(f'MDChallengeDns01 "{self.dnscmd}" "{dnslog}"') + conf.add_md([domain]) + conf.add_vhost([domain]) + conf.install() + assert env.apache_restart() == 0, f'{env.apachectl_stderr}' + assert env.await_completion([domain]) + setups = self.calls('setup') + assert len(setups) == 1, f'{setups}' + assert setups[0][1] == dnslog diff -Nru apache2-2.4.69/test/modules/md/test_730_static.py apache2-2.4.71/test/modules/md/test_730_static.py --- apache2-2.4.69/test/modules/md/test_730_static.py 2025-11-04 14:51:48.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_730_static.py 2026-11-09 03:07:05.000000000 +0000 @@ -2,14 +2,17 @@ from datetime import timedelta import pytest + +from pyhttpd.depends import needs_dependency from pyhttpd.certs import CertificateSpec from .md_conf import MDConf from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestStatic: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/md/test_740_acme_errors.py apache2-2.4.71/test/modules/md/test_740_acme_errors.py --- apache2-2.4.69/test/modules/md/test_740_acme_errors.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_740_acme_errors.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,12 +1,15 @@ # test ACME error responses and their processing import pytest +from pyhttpd.depends import needs_dependency + from .md_conf import MDConf from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestAcmeErrors: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/md/test_741_setup_errors.py apache2-2.4.71/test/modules/md/test_741_setup_errors.py --- apache2-2.4.69/test/modules/md/test_741_setup_errors.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_741_setup_errors.py 2026-11-09 03:07:05.000000000 +0000 @@ -3,12 +3,15 @@ import pytest +from pyhttpd.depends import needs_dependency + from .md_conf import MDConf from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestSetupErrors: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/md/test_790_failover.py apache2-2.4.71/test/modules/md/test_790_failover.py --- apache2-2.4.69/test/modules/md/test_790_failover.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_790_failover.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,11 +1,14 @@ import pytest +from pyhttpd.depends import needs_dependency + from .md_env import MDTestEnv from .md_conf import MDConf -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestFailover: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/md/test_800_must_staple.py apache2-2.4.71/test/modules/md/test_800_must_staple.py --- apache2-2.4.69/test/modules/md/test_800_must_staple.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_800_must_staple.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,13 +1,17 @@ # test mod_md must-staple support +import time import pytest +from pyhttpd.depends import needs_dependency + from .md_conf import MDConf from .md_cert_util import MDCertUtil from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestMustStaple: domain = None @@ -21,7 +25,8 @@ @pytest.fixture(autouse=True, scope='function') def _method_scope(self, env, request): - self.domain = env.get_class_domain(self.__class__) + env.clear_store() + self.domain = env.get_request_domain(request) def configure_httpd(self, env, domain, add_lines=""): conf = MDConf(env, admin="admin@" + domain) @@ -43,6 +48,7 @@ def test_md_800_002(self, env): self.configure_httpd(env, self.domain, "MDMustStaple off") assert env.apache_restart() == 0, f'{env.apachectl_stderr}' + assert env.await_completion([self.domain]) env.check_md_complete(self.domain) cert1 = MDCertUtil(env.store_domain_file(self.domain, 'pubcert.pem')) assert not cert1.get_must_staple() @@ -50,7 +56,9 @@ assert 'ocsp' not in stat or stat['ocsp'] == "no response sent" # MD that must staple and toggle off again - @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder") + @needs_dependency("OCSP responder", not MDTestEnv.lacks_ocsp(), + reason="no OCSP responder", + detected=f"ACME={MDTestEnv.get_acme_server()} serves no OCSP") def test_md_800_003(self, env): self.configure_httpd(env, self.domain, "MDMustStaple on") assert env.apache_restart() == 0, f'{env.apachectl_stderr}' @@ -66,7 +74,9 @@ assert not cert1.get_must_staple() # MD that must staple - @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder") + @needs_dependency("OCSP responder", not MDTestEnv.lacks_ocsp(), + reason="no OCSP responder", + detected=f"ACME={MDTestEnv.get_acme_server()} serves no OCSP") @pytest.mark.skipif(MDTestEnv.get_ssl_module() != "mod_ssl", reason="only for mod_ssl") def test_md_800_004(self, env): # mod_ssl stapling is off, expect no stapling diff -Nru apache2-2.4.69/test/modules/md/test_801_stapling.py apache2-2.4.71/test/modules/md/test_801_stapling.py --- apache2-2.4.69/test/modules/md/test_801_stapling.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_801_stapling.py 2026-11-09 03:07:05.000000000 +0000 @@ -4,14 +4,17 @@ import time from datetime import timedelta import pytest + +from pyhttpd.depends import needs_dependency from pyhttpd.certs import CertificateSpec from .md_conf import MDConf from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestStapling: @pytest.fixture(autouse=True, scope='class') @@ -113,7 +116,9 @@ # MD with stapling on/off and mod_ssl stapling on # expect to see stapling response in all cases - @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder") + @needs_dependency("OCSP responder", not MDTestEnv.lacks_ocsp(), + reason="no OCSP responder", + detected=f"ACME={MDTestEnv.get_acme_server()} serves no OCSP") def test_md_801_002(self, env): md = self.mdA self.configure_httpd(env, md, ssl_stapling=True).install() @@ -183,7 +188,9 @@ assert not stat["stapling"] # 2 MDs, md stapling on+off, ssl stapling on - @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder") + @needs_dependency("OCSP responder", not MDTestEnv.lacks_ocsp(), + reason="no OCSP responder", + detected=f"ACME={MDTestEnv.get_acme_server()} serves no OCSP") def test_md_801_004(self, env): md_a = self.mdA md_b = self.mdB @@ -217,7 +224,9 @@ # MD, check that restart leaves response unchanged, reconfigure keep interval, # should remove the file on restart and get a new one - @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder") + @needs_dependency("OCSP responder", not MDTestEnv.lacks_ocsp(), + reason="no OCSP responder", + detected=f"ACME={MDTestEnv.get_acme_server()} serves no OCSP") def test_md_801_005(self, env): # TODO: mod_watchdog seems to have problems sometimes with fast restarts # turn stapling on, wait for it to appear in connections @@ -263,7 +272,9 @@ # MD, check that stapling renew window works. Set a large window # that causes response to be retrieved all the time. - @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder") + @needs_dependency("OCSP responder", not MDTestEnv.lacks_ocsp(), + reason="no OCSP responder", + detected=f"ACME={MDTestEnv.get_acme_server()} serves no OCSP") def test_md_801_006(self, env): # turn stapling on, wait for it to appear in connections md = self.mdA @@ -302,7 +313,9 @@ assert mtime1 != mtime3 # MD, make a MDomain with static files, check that stapling works - @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder") + @needs_dependency("OCSP responder", not MDTestEnv.lacks_ocsp(), + reason="no OCSP responder", + detected=f"ACME={MDTestEnv.get_acme_server()} serves no OCSP") def test_md_801_007(self, env): # turn stapling on, wait for it to appear in connections md = self.mdA @@ -331,7 +344,9 @@ assert ocsp_file # Use certificate files in direct config, check that stapling works - @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder") + @needs_dependency("OCSP responder", not MDTestEnv.lacks_ocsp(), + reason="no OCSP responder", + detected=f"ACME={MDTestEnv.get_acme_server()} serves no OCSP") def test_md_801_008(self, env): # turn stapling on, wait for it to appear in connections md = self.mdA @@ -357,7 +372,9 @@ # Turn on stapling for a certificate without OCSP responder and issuer # (certificates without issuer prevent mod_ssl asking around for stapling) - @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder") + @needs_dependency("OCSP responder", not MDTestEnv.lacks_ocsp(), + reason="no OCSP responder", + detected=f"ACME={MDTestEnv.get_acme_server()} serves no OCSP") def test_md_801_009(self, env): md = self.mdA domains = [md] @@ -387,7 +404,9 @@ # Turn on stapling for an MDomain not used in any virtualhost # There was a crash in server-status in this case - @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder") + @needs_dependency("OCSP responder", not MDTestEnv.lacks_ocsp(), + reason="no OCSP responder", + detected=f"ACME={MDTestEnv.get_acme_server()} serves no OCSP") def test_md_801_010(self, env): env.clear_ocsp_store() md = self.mdA @@ -407,7 +426,9 @@ # scheduling. # This checks the mistaken assert() reported in # <https://bz.apache.org/bugzilla/show_bug.cgi?id=65567> - @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder") + @needs_dependency("OCSP responder", not MDTestEnv.lacks_ocsp(), + reason="no OCSP responder", + detected=f"ACME={MDTestEnv.get_acme_server()} serves no OCSP") def test_md_801_011(self, env): domains = [ f'test-801-011-{i}-{env.DOMAIN_SUFFIX}' for i in range(7)] self.configure_httpd(env, domains, """ diff -Nru apache2-2.4.69/test/modules/md/test_810_ec.py apache2-2.4.71/test/modules/md/test_810_ec.py --- apache2-2.4.69/test/modules/md/test_810_ec.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_810_ec.py 2026-11-09 03:07:05.000000000 +0000 @@ -3,12 +3,15 @@ import pytest +from pyhttpd.depends import needs_dependency + from .md_conf import MDConf from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestAutov2: @pytest.fixture(autouse=True, scope='class') @@ -54,6 +57,9 @@ {'spec': "secp256r1", 'ciphers': "ECDSA", 'keylen': 256}, {'spec': "", 'ciphers': "RSA", 'keylen': 0}, ]) + env.httpd_error_log.ignore_recent( + lognos=["AH10373"] + ) # set EC key type override on MD and get certificate def test_md_810_002(self, env): @@ -70,6 +76,9 @@ {'spec': "secp384r1", 'ciphers': "ECDSA", 'keylen': 384}, {'spec': "", 'ciphers': "RSA", 'keylen': 0}, ]) + env.httpd_error_log.ignore_recent( + lognos=["AH10373"] + ) # set two key spec, ec before rsa def test_md_810_003a(self, env): diff -Nru apache2-2.4.69/test/modules/md/test_820_locks.py apache2-2.4.71/test/modules/md/test_820_locks.py --- apache2-2.4.69/test/modules/md/test_820_locks.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_820_locks.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,6 +1,8 @@ import os import pytest + +from pyhttpd.depends import needs_dependency from filelock import Timeout, FileLock from .md_cert_util import MDCertUtil @@ -8,8 +10,9 @@ from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestLocks: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/md/test_900_notify.py apache2-2.4.71/test/modules/md/test_900_notify.py --- apache2-2.4.69/test/modules/md/test_900_notify.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_900_notify.py 2026-11-09 03:07:05.000000000 +0000 @@ -5,12 +5,15 @@ import pytest +from pyhttpd.depends import needs_dependency + from .md_conf import MDConf, MDConf from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestNotify: notify_cmd = None notify_log = None diff -Nru apache2-2.4.69/test/modules/md/test_901_message.py apache2-2.4.71/test/modules/md/test_901_message.py --- apache2-2.4.69/test/modules/md/test_901_message.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_901_message.py 2026-11-09 03:07:05.000000000 +0000 @@ -5,14 +5,17 @@ import time from datetime import timedelta import pytest + +from pyhttpd.depends import needs_dependency from pyhttpd.certs import CertificateSpec from .md_conf import MDConf from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestMessage: @pytest.fixture(autouse=True, scope='class') @@ -214,7 +217,9 @@ assert nlines[0].strip() == f"['{self.mcmd}', '{self.mlog}', 'expiring', '{domain}']" # MD, check messages from stapling - @pytest.mark.skipif(MDTestEnv.lacks_ocsp(), reason="no OCSP responder") + @needs_dependency("OCSP responder", not MDTestEnv.lacks_ocsp(), + reason="no OCSP responder", + detected=f"ACME={MDTestEnv.get_acme_server()} serves no OCSP") def test_md_901_020(self, env): domain = self.test_domain domains = [domain] diff -Nru apache2-2.4.69/test/modules/md/test_910_cleanups.py apache2-2.4.71/test/modules/md/test_910_cleanups.py --- apache2-2.4.69/test/modules/md/test_910_cleanups.py 2025-02-05 12:41:51.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_910_cleanups.py 2026-11-09 03:07:05.000000000 +0000 @@ -4,12 +4,15 @@ import pytest +from pyhttpd.depends import needs_dependency + from .md_conf import MDConf from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestCleanups: @pytest.fixture(autouse=True, scope='class') diff -Nru apache2-2.4.69/test/modules/md/test_920_status.py apache2-2.4.71/test/modules/md/test_920_status.py --- apache2-2.4.69/test/modules/md/test_920_status.py 2025-11-04 14:51:48.000000000 +0000 +++ apache2-2.4.71/test/modules/md/test_920_status.py 2026-11-09 03:07:05.000000000 +0000 @@ -5,6 +5,8 @@ from datetime import timedelta import pytest + +from pyhttpd.depends import needs_dependency from pyhttpd.certs import CertificateSpec from .md_conf import MDConf @@ -13,8 +15,9 @@ from .md_env import MDTestEnv -@pytest.mark.skipif(condition=not MDTestEnv.has_acme_server(), - reason="no ACME test server configured") +@needs_dependency("ACME test server", MDTestEnv.has_acme_server(), + reason="no ACME test server configured", + detected=f"ACME={MDTestEnv.get_acme_server()}") class TestStatus: @pytest.fixture(autouse=True, scope='class') @@ -115,6 +118,7 @@ MDBaseServer on MDPortMap http:- https:{env.https_port} MDStapling on +MDServerStatus on ServerName {domain} <IfModule ssl_module> diff -Nru apache2-2.4.69/test/modules/metadata/conftest.py apache2-2.4.71/test/modules/metadata/conftest.py --- apache2-2.4.69/test/modules/metadata/conftest.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/metadata/conftest.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,35 @@ +import logging +import os + +import pytest +import sys + +from .env import MetadataTestEnv + +sys.path.append(os.path.join(os.path.dirname(__file__), '../..')) + + +def pytest_report_header(config, start_path): + env = MetadataTestEnv() + return f"metadata [apache: {env.get_httpd_version()}, mpm: {env.mpm_module}, {env.prefix}]" + + +@pytest.fixture(scope="package") +def env(pytestconfig) -> MetadataTestEnv: + level = logging.INFO + console = logging.StreamHandler() + console.setLevel(level) + console.setFormatter(logging.Formatter('%(levelname)s: %(message)s')) + logging.getLogger('').addHandler(console) + logging.getLogger('').setLevel(level=level) + env = MetadataTestEnv(pytestconfig=pytestconfig) + env.setup_httpd() + env.apache_access_log_clear() + env.httpd_error_log.clear_log() + return env + + +@pytest.fixture(autouse=True, scope="package") +def _stop_package_scope(env): + yield + assert env.apache_stop() == 0 diff -Nru apache2-2.4.69/test/modules/metadata/env.py apache2-2.4.71/test/modules/metadata/env.py --- apache2-2.4.69/test/modules/metadata/env.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/metadata/env.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,40 @@ +import inspect +import logging +import os + +from pyhttpd.env import HttpdTestEnv, HttpdTestSetup + +log = logging.getLogger(__name__) + + +class MetadataTestSetup(HttpdTestSetup): + + def __init__(self, env: 'HttpdTestEnv'): + super().__init__(env=env) + self.add_source_dir(os.path.dirname(inspect.getfile(MetadataTestSetup))) + self.add_modules(["mime", "mime_magic", "env", "include"]) + # mod_mime_libmagic needs libmagic at build time, so it must not + # be a hard requirement; its tests skip when it is absent. + self.add_optional_modules(["mime_libmagic", "remoteip"]) + + +class MetadataTestEnv(HttpdTestEnv): + + def __init__(self, pytestconfig=None): + super().__init__(pytestconfig=pytestconfig) + # Only raise the log level for mod_mime_libmagic when it is built: + # a LogLevel for an unloaded module is a fatal config error, which + # would break the whole suite (including the mod_mime_magic tests) + # on platforms without libmagic, such as Windows. + log_modules = ["mime_magic", "env", "include", "core"] + if self.has_libmagic_module: + log_modules.insert(1, "mime_libmagic") + self.add_httpd_log_modules(log_modules) + + def setup_httpd(self, setup: HttpdTestSetup = None): + super().setup_httpd(setup=MetadataTestSetup(env=self)) + + @property + def has_libmagic_module(self) -> bool: + """Whether mod_mime_libmagic was built.""" + return self.has_shared_module("mime_libmagic") diff -Nru apache2-2.4.69/test/modules/metadata/samples.py apache2-2.4.71/test/modules/metadata/samples.py --- apache2-2.4.69/test/modules/metadata/samples.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/metadata/samples.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,49 @@ +# Sample file contents for content type detection tests. Each is +# served without an extension, so mod_mime sets no type and the magic +# modules have to derive one from the content. mod_mime_magic only +# applies its magic rules to files of at least 64 bytes, so samples +# meant to match a rule are padded past that. +import gzip +import os + +SAMPLES = { + "html": b"<html>\n<body>hello</body>\n</html>\n", + "html-doctype": b"<!DOCTYPE html>\n<html><head><title>t" + b"hi\n", + # (no commas: libmagic 5.45 calls lines with a consistent number + # of commas text/csv) + "text": b"hello world this is plain ascii text\n" + b"with a second line of a different length\n" * 2, + "text-utf8": "café naïve résumé\n".encode() * 4, + "text-latin1": b"caf\xe9 na\xefve\n" * 4, + "csrc": b"#include \n\nint main(int argc, char **argv)\n{\n" + b" printf(\"hi\\n\");\n return 0;\n}\n", + "json": b'{"a": 1, "b": [1, 2, 3], "c": {"d": "e", "f": "some more text"}}\n', + "xml": b"\n1234\n", + "rfc822": b"From: a@example.com\nTo: b@example.com\nSubject: hi there\n\n" + b"the body of the message\n", + "shell": b"#!/bin/sh\necho hi\necho there\necho this is a shell script\n" + b"exit 0\n", + "png": b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR" + b"\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa" + + b"\x00" * 64, + "gif": b"GIF89a\x10\x00\x10\x00\x80\x00\x00" + b"\x00" * 64, + "pdf": b"%PDF-1.4\n%\xe2\xe3\xcf\xd3\n1 0 obj\n<< /Type /Catalog >>\nendobj\n", + "gzip": gzip.compress(bytes(range(32, 127)) * 4, mtime=0), + "elf": b"\x7fELF\x02\x01\x01\x00" + b"\x00" * 8 + + b"\x02\x00\x3e\x00\x01\x00\x00\x00" + b"\x00" * 100, + # deterministic bytes which libmagic reports as application/octet-stream + "binary": bytes((i * 7919) % 256 for i in range(4096)), + "empty": b"", +} + + +def write_samples(doc_dir): + """Write every sample into doc_dir, plus png.txt (PNG content + with a .txt extension).""" + os.makedirs(doc_dir, exist_ok=True) + for name, content in SAMPLES.items(): + with open(os.path.join(doc_dir, name), "wb") as f: + f.write(content) + with open(os.path.join(doc_dir, "png.txt"), "wb") as f: + f.write(SAMPLES["png"]) diff -Nru apache2-2.4.69/test/modules/metadata/test_001_mime_magic.py apache2-2.4.71/test/modules/metadata/test_001_mime_magic.py --- apache2-2.4.69/test/modules/metadata/test_001_mime_magic.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/metadata/test_001_mime_magic.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,136 @@ +import os +import re +import sys + +import pytest + +from pyhttpd.backport import not_backported +from pyhttpd.conf import HttpdConf + +# on the tests that fail without it (the others pass there as well) +NOT_BACKPORTED = not_backported("r1938070, r1938258-r1938260", "mod_mime_magic fixes") + + +class TestMimeMagic: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + # A small magic file: one entry giving both a type and an + # encoding, and one with a file(1)-style descriptive note in + # place of the encoding. The token-based text detection is left + # to handle everything else. + magic_file = os.path.join(env.gen_dir, "magic") + with open(magic_file, "w") as f: + f.write("0\tstring\tTESTMAGIC\tapplication/x-test-magic\tx-test-encoding\n") + f.write("0\tstring\tTESTNOTE\tapplication/x-test-note (some note)\n") + # 32-bit values with the high bit set, signed and unsigned + f.write("0\tbelong\t0xcafebabe\tapplication/x-test-belong\n") + f.write("0\tulelong\t0xcafebabe\tapplication/x-test-ulelong\n") + # an indirect rule whose computed offset is (long at 0) - 1; + # for a file beginning with a zero long that is -1, which a + # bounds check testing only the upper bound would let through + f.write("0\tlelong\t0\tx\n") + f.write(">(0.l-1)\tbyte\tx\tapplication/x-test-indir\n") + + # Files without an extension, so mod_mime sets no type and + # mod_mime_magic has to derive one from the content. + doc_dir = os.path.join(env.server_dir, "htdocs", "test1", "magic") + os.makedirs(doc_dir, exist_ok=True) + # (a rule is only tested on files of at least 64 bytes) + with open(os.path.join(doc_dir, "softmagic"), "wb") as f: + f.write(b"TESTMAGIC" + b" and some more content" * 4 + b"\n") + with open(os.path.join(doc_dir, "softnote"), "wb") as f: + f.write(b"TESTNOTE" + b" and some more content" * 4 + b"\n") + with open(os.path.join(doc_dir, "html-plain"), "wb") as f: + f.write(b"\nhello\n") + with open(os.path.join(doc_dir, "belong"), "wb") as f: + f.write(b"\xca\xfe\xba\xbe" + b"\0" * 64) + with open(os.path.join(doc_dir, "ulelong"), "wb") as f: + f.write(b"\xbe\xba\xfe\xca" + b"\0" * 64) + # zero long at offset 0, then padding past the 64-byte minimum + with open(os.path.join(doc_dir, "indir"), "wb") as f: + f.write(b"\0" * 100) + # HTML token followed by an ESC byte. + with open(os.path.join(doc_dir, "html-escape"), "wb") as f: + f.write(b"\n\x1b[1mhello\x1b[0m\n") + + conf = HttpdConf(env, extras={ + 'base': f""" + MimeMagicFile "{magic_file}" + """, + }) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + + # type and encoding from a magic file entry + @NOT_BACKPORTED + def test_metadata_001_01_softmagic(self, env): + url = env.mkurl("http", "test1", "/magic/softmagic") + r = env.curl_get(url) + assert r.response, "no response: server may have crashed" + assert r.response["status"] == 200 + assert r.response["header"]["content-type"] == "application/x-test-magic" + assert r.response["header"]["content-encoding"] == "x-test-encoding" + + # type from the HTML token, no encoding + def test_metadata_001_02_html(self, env): + url = env.mkurl("http", "test1", "/magic/html-plain") + r = env.curl_get(url) + assert r.response, "no response: server may have crashed" + assert r.response["status"] == 200 + assert r.response["header"]["content-type"] == "text/html" + assert "content-encoding" not in r.response["header"] + + # type from the HTML token, escape sequences in the content + @NOT_BACKPORTED + def test_metadata_001_03_html_escapes(self, env): + url = env.mkurl("http", "test1", "/magic/html-escape") + r = env.curl_get(url) + assert r.response, "no response: server may have crashed" + assert r.response["status"] == 200 + assert r.response["header"]["content-type"] == "text/html" + assert "content-encoding" not in r.response["header"] + + # type from a magic file entry followed by a descriptive note: the + # type must end at the whitespace, and the note is not an encoding + @NOT_BACKPORTED + def test_metadata_001_04_softmagic_note(self, env): + url = env.mkurl("http", "test1", "/magic/softnote") + r = env.curl_get(url) + assert r.response, "no response: server may have crashed" + assert r.response["status"] == 200 + assert r.response["header"]["content-type"] == "application/x-test-note" + assert "content-encoding" not in r.response["header"] + assert env.httpd_error_log.scan_recent( + re.compile(r'.*AH10623: .*ignoring invalid content encoding.*')) + env.httpd_error_log.ignore_recent(lognos=["AH10623"]) + + # a long value with the high bit set matches the rule's value, + # whether compared signed or unsigned + # The 0xcafebabe rule value only fits a 64-bit C long: on an LLP64 + # platform (Windows) long is 32 bits, where the parser's strtol() + # cannot represent it and the sign-extension corner this guards + # against does not arise. + @pytest.mark.skipif(sys.platform == "win32", + reason="magic value needs a 64-bit long") + @pytest.mark.parametrize("name", ["belong", "ulelong"]) + @NOT_BACKPORTED + def test_metadata_001_05_long_high_bit(self, env, name): + url = env.mkurl("http", "test1", f"/magic/{name}") + r = env.curl_get(url) + assert r.response, "no response: server may have crashed" + assert r.response["status"] == 200 + assert r.response["header"]["content-type"] == f"application/x-test-{name}" + + # an indirect rule computing a negative offset must not read outside + # the sniff buffer. The file begins with a zero long, so ">(0.l-1)" + # resolves to offset -1; without the fix that slips a bounds check + # testing only the upper bound and reads before the buffer, which a + # sanitizer build (the ASan and UBSan CI jobs) aborts on, leaving the + # request with no response. + def test_metadata_001_06_indirect_negative_offset(self, env): + url = env.mkurl("http", "test1", "/magic/indir") + r = env.curl_get(url) + assert r.response, "no response: server may have crashed" + assert r.response["status"] == 200 diff -Nru apache2-2.4.69/test/modules/metadata/test_002_mime_libmagic.py apache2-2.4.71/test/modules/metadata/test_002_mime_libmagic.py --- apache2-2.4.69/test/modules/metadata/test_002_mime_libmagic.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/metadata/test_002_mime_libmagic.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,91 @@ +import os +import pytest + +from pyhttpd.conf import HttpdConf +from .samples import write_samples + + +def get_type(env, path): + """The Content-Type header of GET path on test1, or None.""" + r = env.curl_get(env.mkurl("http", "test1", path)) + assert r.response, "no response: server may have crashed" + assert r.response["status"] == 200 + assert "content-encoding" not in r.response["header"] + return r.response["header"].get("content-type") + + +class TestMimeLibmagic: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + env.require(env.has_libmagic_module, "mod_mime_libmagic is not built", + dep="mod_libmagic", + detected=f"not found in {env.libexec_dir}") + write_samples(os.path.join(env.server_dir, "htdocs", "test1", "libmagic")) + conf = HttpdConf(env, extras={ + 'base': "MimeLibmagic On", + }) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + + @pytest.mark.parametrize(["name", "ctype"], [ + ("html", "text/html"), + ("html-doctype", "text/html"), + ("text", "text/plain"), + ("text-utf8", "text/plain"), + ("text-latin1", "text/plain"), + ("csrc", "text/x-c"), + ("json", "application/json"), + ("xml", "text/xml"), + ("rfc822", "message/rfc822"), + ("shell", "text/x-shellscript"), + ("png", "image/png"), + ("gif", "image/gif"), + ("pdf", "application/pdf"), + # no decompression support, the compressed file itself is typed + ("gzip", "application/gzip"), + # libmagic appends ", no program header" to the type + ("elf", "application/x-executable"), + # application/octet-stream from libmagic leaves the type unset + ("binary", None), + ("empty", "text/plain"), + ]) + def test_metadata_002_01_types(self, env, name, ctype): + assert get_type(env, f"/libmagic/{name}") == ctype + + # mod_mime's extension mapping wins over the content + def test_metadata_002_02_extension(self, env): + assert get_type(env, "/libmagic/png.txt") == "text/plain" + + +class TestMimeLibmagicCharset: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + env.require(env.has_libmagic_module, "mod_mime_libmagic is not built", + dep="mod_libmagic", + detected=f"not found in {env.libexec_dir}") + write_samples(os.path.join(env.server_dir, "htdocs", "test1", "libmagic")) + conf = HttpdConf(env, extras={ + 'base': """ + MimeLibmagic On + MimeLibmagicCharset On + """, + }) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + + @pytest.mark.parametrize(["name", "ctype"], [ + ("html", "text/html; charset=us-ascii"), + ("text", "text/plain; charset=us-ascii"), + ("text-utf8", "text/plain; charset=utf-8"), + ("text-latin1", "text/plain; charset=iso-8859-1"), + # only text types get a charset + ("json", "application/json"), + ("png", "image/png"), + ("binary", None), + ]) + def test_metadata_002_10_charset(self, env, name, ctype): + assert get_type(env, f"/libmagic/{name}") == ctype diff -Nru apache2-2.4.69/test/modules/metadata/test_002_setenvfromfile.py apache2-2.4.71/test/modules/metadata/test_002_setenvfromfile.py --- apache2-2.4.69/test/modules/metadata/test_002_setenvfromfile.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/metadata/test_002_setenvfromfile.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,206 @@ +import os +import re +import pytest + +from pyhttpd.backport import skip_not_backported +from pyhttpd.conf import HttpdConf + +pytestmark = skip_not_backported("r1938506", "SetEnvFromFile") + + +def _write_ssi_echo(path, names): + """An SSI page echoing each variable as NAME=[value]. An unset + variable echoes the default "(none)", so a set-but-empty variable + ("[]") is distinguishable from one that never made it into the table. + """ + with open(path, "w") as f: + for name in names: + f.write(f'{name}=[]\n') + + +class TestSetEnvFromFile: + """Parsing behaviour of a well-formed file.""" + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + # A plain name=value pair, an explicitly empty value, a line with + # no '=' (also empty), a backslash line continuation, and a line + # padded with surrounding whitespace that must be stripped. + env_file = os.path.join(env.gen_dir, "setenv.env") + with open(env_file, "w") as f: + f.write("# metadata SetEnvFromFile happy-path fixture\n") + f.write("\n") + f.write("ENV_SIMPLE=simple value\n") + f.write("ENV_EMPTY=\n") + f.write("ENV_NOEQ\n") + f.write("ENV_CONT=first \\\n") + f.write("second\n") + f.write(" ENV_WS=trimmed value \n") + + doc_dir = os.path.join(env.server_dir, "htdocs", "test1") + os.makedirs(doc_dir, exist_ok=True) + _write_ssi_echo(os.path.join(doc_dir, "fromfile.shtml"), + ["ENV_SIMPLE", "ENV_EMPTY", "ENV_NOEQ", "ENV_CONT", + "ENV_WS", "ENV_UNDEFINED"]) + + conf = HttpdConf(env, extras={ + 'base': f""" + SetEnvFromFile "{env_file}" + + Options +Includes + AddType text/html .shtml + AddOutputFilter INCLUDES .shtml + + """, + }) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + + def test_metadata_002_01_parsing(self, env): + url = env.mkurl("http", "test1", "/fromfile.shtml") + r = env.curl_get(url) + assert r.response, "no response: server may have crashed" + assert r.response["status"] == 200 + body = r.response["body"].decode("utf-8") + # a plain name=value pair + assert "ENV_SIMPLE=[simple value]" in body + # an explicitly empty value is set, not absent + assert "ENV_EMPTY=[]" in body + # a line with no '=' yields an empty value + assert "ENV_NOEQ=[]" in body + # a backslash continues the value on the next line (no space added + # by the join; the space here is the one before the backslash) + assert "ENV_CONT=[first second]" in body + # leading/trailing whitespace on the line is stripped, from both + # the name (else the echo would be "(none)") and the value (else a + # trailing space would remain) + assert "ENV_WS=[trimmed value]" in body + # a variable never named in the file is left unset + assert "ENV_UNDEFINED=[(none)]" in body + + +class TestSetEnvFromFileMalformed: + """A line with no variable name is skipped with a warning, and later + lines are still parsed.""" + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + env_file = os.path.join(env.gen_dir, "setenv-malformed.env") + with open(env_file, "w") as f: + f.write("# a line beginning with '=' has an empty name\n") + f.write("=orphan value\n") + f.write("ENV_OK=present\n") + + doc_dir = os.path.join(env.server_dir, "htdocs", "test1") + os.makedirs(doc_dir, exist_ok=True) + _write_ssi_echo(os.path.join(doc_dir, "malformed.shtml"), ["ENV_OK"]) + + conf = HttpdConf(env, extras={ + 'base': f""" + SetEnvFromFile "{env_file}" + + Options +Includes + AddType text/html .shtml + AddOutputFilter INCLUDES .shtml + + """, + }) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + + def test_metadata_002_02_malformed_line(self, env): + # The malformed line is skipped with an AH10632 warning. Where that + # warning surfaces is MPM-dependent: when the config is parsed before + # the error log is open (e.g. the event MPM on Unix) it goes to + # apachectl's stderr; with mpm_winnt the config is parsed again by the + # child with the error log already open, so it lands in the error log + # instead (and possibly more than once). apachectl_stderr is also + # None on the winnt startup path, so guard it. Accept the warning in + # either place. + stderr = env.apachectl_stderr or "" + warn_re = re.compile( + r'.*AH10632: SetEnvFromFile: Skipping malformed line.*') + found = "AH10632" in stderr + if not found: + try: + found = env.httpd_error_log.scan_recent(warn_re) + except TimeoutError: + found = False + assert found, "AH10632 warning not found in stderr or error log" + # The warning is expected here; keep it from failing the teardown log + # check (it may appear in the error log several times under winnt). + env.httpd_error_log.ignore_recent(lognos=["AH10632"]) + # ... and the well-formed line after it is still applied + url = env.mkurl("http", "test1", "/malformed.shtml") + r = env.curl_get(url) + assert r.response, "no response: server may have crashed" + assert r.response["status"] == 200 + assert "ENV_OK=[present]" in r.response["body"].decode("utf-8") + + +class TestSetEnvFromFileHtaccess: + """SetEnvFromFile is not permitted in .htaccess (RSRC_CONF|ACCESS_CONF), + even where AllowOverride FileInfo would allow SetEnv itself.""" + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + ht_dir = os.path.join(env.server_dir, "htdocs", "test1", "htaccess") + os.makedirs(ht_dir, exist_ok=True) + with open(os.path.join(ht_dir, "index.html"), "w") as f: + f.write("hello\n") + # SetEnv (FileInfo) would be accepted here; SetEnvFromFile must not. + with open(os.path.join(ht_dir, ".htaccess"), "w") as f: + f.write('SetEnvFromFile "conf/whatever.env"\n') + + conf = HttpdConf(env, extras={ + 'base': f""" + + AllowOverride FileInfo + + """, + }) + conf.add_vhost_test1() + conf.install() + # the server starts fine; .htaccess is parsed per request + assert env.apache_restart() == 0 + + def test_metadata_002_03_htaccess_rejected(self, env): + url = env.mkurl("http", "test1", "/htaccess/index.html") + r = env.curl_get(url) + assert r.response, "no response: server may have crashed" + # the illegal directive makes .htaccess processing fail -> 500 + assert r.response["status"] == 500 + # logged (at alert level, so check_error_log does not flag it, but + # guard anyway) with the config parser's context rejection + assert env.httpd_error_log.scan_recent( + re.compile(r'.*SetEnvFromFile not allowed here.*')) + env.httpd_error_log.ignore_recent(matches=[r'.*SetEnvFromFile not allowed here.*']) + + +class TestSetEnvFromFileMissing: + """Pointing at a file that cannot be opened is a fatal config error.""" + + def test_metadata_002_04_missing_file(self, env): + missing = os.path.join(env.gen_dir, "does-not-exist.env") + conf = HttpdConf(env, extras={ + 'base': f'SetEnvFromFile "{missing}"', + }) + conf.add_vhost_test1() + conf.install() + # httpd must refuse to start ... + assert env.apache_fail() == 0 + # ... reporting why. The message is emitted at config-parse time; + # apachectl_stderr only captures it on the non-winnt start path (it is + # None under mpm_winnt), so only assert on it where it was captured. + if env.apachectl_stderr: + assert "Could not open file" in env.apachectl_stderr + + # restore a working, running server so the log check and package + # teardown are clean + env.httpd_error_log.clear_log() + good = HttpdConf(env) + good.add_vhost_test1() + good.install() + assert env.apache_restart() == 0 diff -Nru apache2-2.4.69/test/modules/metadata/test_003_compare.py apache2-2.4.71/test/modules/metadata/test_003_compare.py --- apache2-2.4.69/test/modules/metadata/test_003_compare.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/metadata/test_003_compare.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,86 @@ +import logging +import os +import pytest + +from pyhttpd.conf import HttpdConf +from .samples import SAMPLES, write_samples + +log = logging.getLogger(__name__) + +# (sample, mod_mime_magic type, mod_mime_magic encoding, mod_mime_libmagic type) +# mod_mime_magic uses the stock conf/magic and its token-based text +# detection, which knows nothing of most of these, so many are untyped. +EXPECTED = [ + ("html", "text/html", None, "text/html"), + ("html-doctype", None, None, "text/html"), + ("text", None, None, "text/plain"), + ("text-utf8", None, None, "text/plain"), + ("text-latin1", None, None, "text/plain"), + ("csrc", "text/plain", None, "text/x-c"), + ("json", None, None, "application/json"), + ("xml", "text/xml", None, "text/xml"), + ("rfc822", "message/rfc822", "7bit", "message/rfc822"), + ("shell", None, None, "text/x-shellscript"), + ("png", "image/png", None, "image/png"), + ("gif", "image/gif", None, "image/gif"), + ("pdf", None, None, "application/pdf"), + ("gzip", "application/octet-stream", "x-gzip", "application/gzip"), + ("elf", None, None, "application/x-executable"), + ("binary", None, None, None), + ("empty", "text/plain", None, "text/plain"), + ("png.txt", "text/plain", None, "text/plain"), +] + + +def get_headers(env, host, path): + r = env.curl_get(env.mkurl("http", host, path)) + assert r.response, "no response: server may have crashed" + assert r.response["status"] == 200, r.response + h = r.response["header"] + return h.get("content-type"), h.get("content-encoding") + + +class TestCompare: + """The same files served by mod_mime_magic (vhost test1) and by + mod_mime_libmagic (vhost test2).""" + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + env.require(env.has_libmagic_module, "mod_mime_libmagic is not built", + dep="mod_libmagic", + detected=f"not found in {env.libexec_dir}") + for vhost in ["test1", "test2"]: + write_samples(os.path.join(env.server_dir, "htdocs", vhost, "cmp")) + # No MimeMagicFile in the main server, so test2 inherits no + # magic rules and only mod_mime_libmagic runs there. + conf = HttpdConf(env, extras={ + f"test1.{env.http_tld}": f'MimeMagicFile "{env.prefix}/conf/magic"', + f"test2.{env.http_tld}": "MimeLibmagic On", + }) + conf.add_vhost_test1() + conf.add_vhost_test2() + conf.install() + assert env.apache_restart() == 0 + + @pytest.mark.parametrize(["name", "mm_type", "mm_enc", "lm_type"], EXPECTED) + def test_metadata_003_01_mime_magic(self, env, name, mm_type, mm_enc, lm_type): + assert get_headers(env, "test1", f"/cmp/{name}") == (mm_type, mm_enc) + + @pytest.mark.parametrize(["name", "mm_type", "mm_enc", "lm_type"], EXPECTED) + def test_metadata_003_02_libmagic(self, env, name, mm_type, mm_enc, lm_type): + assert get_headers(env, "test2", f"/cmp/{name}") == (lm_type, None) + + # Log a side-by-side table of what the two modules actually report. + def test_metadata_003_03_report(self, env): + rows = [("sample", "mod_mime_magic", "mod_mime_libmagic")] + for name in list(SAMPLES) + ["png.txt"]: + mm = get_headers(env, "test1", f"/cmp/{name}") + lm = get_headers(env, "test2", f"/cmp/{name}") + fmt = lambda t: f"{t[0]}" + (f" ({t[1]})" if t[1] else "") + rows.append((name, fmt(mm), fmt(lm))) + widths = [max(len(r[i]) for r in rows) for i in range(3)] + table = "\n".join(" ".join(c.ljust(widths[i]) for i, c in enumerate(r)) + for r in rows) + log.info("content types reported:\n%s", table) + with open(os.path.join(env.gen_dir, "mime-compare.txt"), "w") as f: + f.write(table + "\n") diff -Nru apache2-2.4.69/test/modules/metadata/test_004_remoteip.py apache2-2.4.71/test/modules/metadata/test_004_remoteip.py --- apache2-2.4.69/test/modules/metadata/test_004_remoteip.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/metadata/test_004_remoteip.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,334 @@ +import os +import re +import socket +import time + +import pytest + +from pyhttpd.backport import not_backported, skip_not_backported +from pyhttpd.depends import needs_dependency + +from pyhttpd.conf import HttpdConf +from .env import MetadataTestEnv + +# on the cases that fail without it (the others pass there as well) +PER_VHOST = not_backported("r1938908", "mod_remoteip per-vhost lists", + backported_in="2.4.71") + + +@needs_dependency("mod_remoteip", MetadataTestEnv.has_shared_module("remoteip"), + reason="mod_remoteip not available") +class TestRemoteIp: + LOG_FILE = "test_remoteip.log" + PEER = "127.0.0.1" + CLIENT = "203.0.113.7" + CDN = "198.51.100.9" + + # The test requests come from the internal proxy PEER and carry + # "X-Forwarded-For: CLIENT, CDN". The proxies are given directly or in + # a list file, in the server config or in the vhost. Each case expects + # the client and the (external) proxies seen by mod_remoteip. + @pytest.mark.parametrize(["scope", "directives", "listed", "proxies"], [ + ["vhost", [f"RemoteIPInternalProxy {PEER}", f"RemoteIPTrustedProxy {CDN}"], + [], CDN], + pytest.param("vhost", [f"RemoteIPInternalProxy {PEER}", + "RemoteIPTrustedProxyList {list}"], + [CDN], CDN, marks=PER_VHOST), + pytest.param("vhost", ["RemoteIPTrustedProxyList {list}", + f"RemoteIPInternalProxy {PEER}"], + [CDN], CDN, marks=PER_VHOST), + pytest.param("vhost", ["RemoteIPTrustedProxy 10.142.1.99", + "RemoteIPInternalProxyList {list}"], + [PEER, CDN], "-", marks=PER_VHOST), + ["server", [f"RemoteIPInternalProxy {PEER}", "RemoteIPTrustedProxyList {list}"], + [CDN], CDN], + ]) + def test_metadata_004_01(self, env, scope, directives, listed, proxies): + list_file = os.path.join(env.gen_dir, "remoteip_proxies.lst") + with open(list_file, "w") as f: + f.write("".join(f"{ip}\n" for ip in listed)) + lines = [d.format(list=list_file) for d in directives] + conf = HttpdConf(env, extras={ + "base": [ + "RemoteIPHeader X-Forwarded-For", + f'CustomLog logs/{self.LOG_FILE} "%a %{{remoteip-proxy-ip-list}}n"', + ] + (lines if scope == "server" else []) + }) + conf.start_vhost(domains=[f"test1.{env.http_tld}"], doc_root="htdocs/test1") + if scope == "vhost": + conf.add(lines) + conf.end_vhost() + conf.install() + assert env.apache_restart() == 0 + + log_path = os.path.join(env.server_logs_dir, self.LOG_FILE) + open(log_path, 'w').close() + r = env.curl_get(env.mkurl("https", "test1", "/"), options=[ + '-H', f"X-Forwarded-For: {self.CLIENT}, {self.CDN}"]) + assert r.response["status"] == 200 + with open(log_path) as f: + lines = [l.strip() for l in f if l.strip() + and not l.startswith(f"{self.PEER} -")] + assert len(lines) == 1, f"expected 1 test line, got {lines}" + assert lines[0] == f"{self.CLIENT} {proxies}" + + WARNING = r".*both the main server and the virtual host" + + # The proxy entries of the main server and of a vhost are merged, the + # ones of the vhost taking precedence where subnets overlap, and a + # warning tells when both have some. + @pytest.mark.parametrize(["main", "vhost", "proxies", "warning"], [ + # only one of the scopes + [[f"RemoteIPInternalProxy {PEER}", f"RemoteIPTrustedProxy {CDN}"], + [], CDN, False], + [[], [f"RemoteIPInternalProxy {PEER}", f"RemoteIPTrustedProxy {CDN}"], + CDN, False], + # (both scopes: merged, and warned about, only with the change) + # RemoteIPInternalProxy in the main server, a list in the vhost + pytest.param([f"RemoteIPInternalProxy {PEER}"], ["RemoteIPInternalProxyList {cdn}"], + "-", True, marks=PER_VHOST), + pytest.param([f"RemoteIPInternalProxy {PEER}"], ["RemoteIPTrustedProxyList {cdn}"], + CDN, True, marks=PER_VHOST), + # a list in the main server, a direct entry in the vhost + pytest.param(["RemoteIPInternalProxyList {peer}"], [f"RemoteIPTrustedProxy {CDN}"], + CDN, True, marks=PER_VHOST), + # direct entries in both + pytest.param([f"RemoteIPInternalProxy {PEER}"], [f"RemoteIPTrustedProxy {CDN}"], + CDN, True, marks=PER_VHOST), + # the CDN is trusted by the main server and internal for the vhost + pytest.param([f"RemoteIPInternalProxy {PEER}", f"RemoteIPTrustedProxy {CDN}"], + [f"RemoteIPInternalProxy {CDN}"], "-", True, marks=PER_VHOST), + pytest.param([f"RemoteIPInternalProxy {PEER}", f"RemoteIPTrustedProxy {CDN}"], + ["RemoteIPInternalProxyList {cdn}"], "-", True, marks=PER_VHOST), + ]) + def test_metadata_004_02(self, env, main, vhost, proxies, warning): + files = {"cdn": self.CDN, "peer": self.PEER} + for name, ip in files.items(): + with open(os.path.join(env.gen_dir, f"remoteip_{name}.lst"), "w") as f: + f.write(f"{ip}\n") + paths = {n: os.path.join(env.gen_dir, f"remoteip_{n}.lst") for n in files} + conf = HttpdConf(env, extras={ + "base": [ + "RemoteIPHeader X-Forwarded-For", + f'CustomLog logs/{self.LOG_FILE} "%a %{{remoteip-proxy-ip-list}}n"', + ] + [d.format(**paths) for d in main] + }) + conf.start_vhost(domains=[f"test1.{env.http_tld}"], doc_root="htdocs/test1") + conf.add([d.format(**paths) for d in vhost]) + conf.end_vhost() + conf.install() + pos = env.httpd_error_log.current_pos() + assert env.apache_restart() == 0 + warned = env.httpd_error_log.wait_for(re.compile(self.WARNING), pos, timeout=1) + assert warned == warning + if warned: + env.httpd_error_log.ignore_recent(matches=[self.WARNING]) + + log_path = os.path.join(env.server_logs_dir, self.LOG_FILE) + open(log_path, 'w').close() + r = env.curl_get(env.mkurl("https", "test1", "/"), options=[ + '-H', f"X-Forwarded-For: {self.CLIENT}, {self.CDN}"]) + assert r.response["status"] == 200 + with open(log_path) as f: + lines = [l.strip() for l in f if l.strip() + and not l.startswith(f"{self.PEER} -")] + assert len(lines) == 1, f"expected 1 test line, got {lines}" + assert lines[0] == f"{self.CLIENT} {proxies}" + + # -- PROXY protocol -------------------------------------------------- + + # A v2 header: the signature, then version/command, family/transport, + # the length of what follows, and the addresses. The commands are LOCAL + # (0) and PROXY (1); a health checker such as an AWS NLB sends LOCAL, + # for which the receiver must keep the connection's own address and + # ignore any address information present (bug 63893). + PP2_SIG = b"\x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a" + PP2_LOCAL = 0x20 + PP2_PROXY = 0x21 + PP2_UNSPEC = 0x00 + PP2_TCP4 = 0x11 + # CLIENT:4321 -> CDN:80, as a PROXY protocol TCPv4 address block + PP2_ADDRS = (socket.inet_aton(CLIENT) + socket.inet_aton(CDN) + + (4321).to_bytes(2, "big") + (80).to_bytes(2, "big")) + + @classmethod + def pp2_header(cls, cmd, fam=0x00, addrs=b""): + return cls.PP2_SIG + bytes([cmd, fam]) + len(addrs).to_bytes(2, "big") + addrs + + def pp_install(self, env): + """PROXY protocol on its own port, so the harness's own requests + on the usual ports are unaffected; log the client address seen.""" + conf = HttpdConf(env, extras={ + "base": [ + f"Listen {env.http_port2}", + ] + }) + conf.start_vhost(domains=[f"test1.{env.http_tld}"], port=env.http_port2, + doc_root="htdocs/test1") + conf.add(["RemoteIPProxyProtocol On", + f'CustomLog logs/{self.LOG_FILE} "%a"']) + conf.end_vhost() + conf.install() + assert env.apache_restart() == 0 + + def pp_request(self, env, header, request=True): + """Send a PROXY protocol header, then a request unless told not to; + return (status, client address logged) - None for each if nothing + came back.""" + log_path = os.path.join(env.server_logs_dir, self.LOG_FILE) + open(log_path, 'w').close() + sock = socket.create_connection(("127.0.0.1", env.http_port2), timeout=5) + sock.sendall(header) + if request: + sock.sendall(f"GET / HTTP/1.1\r\nHost: test1.{env.http_tld}\r\n" + f"Connection: close\r\n\r\n".encode()) + data = b"" + try: + while True: + chunk = sock.recv(65536) + if not chunk: + break + data += chunk + except (socket.timeout, ConnectionError): + pass + sock.close() + status = int(data.split(b" ", 2)[1]) if data.startswith(b"HTTP/") else None + logged = None + for _ in range(20): + with open(log_path) as f: + logged = f.read().strip() or None + if logged or status is None: + break + time.sleep(0.1) + return status, logged + + # Control: a PROXY command is honoured, the client is the one it names. + def test_metadata_004_03(self, env): + self.pp_install(env) + hdr = self.pp2_header(self.PP2_PROXY, self.PP2_TCP4, self.PP2_ADDRS) + assert self.pp_request(env, hdr) == (200, self.CLIENT) + + # A LOCAL command (ver_cmd 0x20) is valid: the request is served as + # coming from the connection's own address, and nothing is logged as + # an error. Bug 63893. + @skip_not_backported("r1938927", "PROXY protocol EOF is not an error", + backported_in="2.4.71") + def test_metadata_004_04(self, env): + self.pp_install(env) + hdr = self.pp2_header(self.PP2_LOCAL, self.PP2_UNSPEC) + assert self.pp_request(env, hdr) == (200, self.PEER) + + # LOCAL with an address block present: it must be ignored. + @skip_not_backported("r1938927", "PROXY protocol EOF is not an error", + backported_in="2.4.71") + def test_metadata_004_05(self, env): + self.pp_install(env) + hdr = self.pp2_header(self.PP2_LOCAL, self.PP2_TCP4, self.PP2_ADDRS) + assert self.pp_request(env, hdr) == (200, self.PEER) + + # A health check which connects, sends an incomplete header and goes + # away is not an error worth logging (bug 63893 comment 12). + @skip_not_backported("r1938927", "PROXY protocol EOF is not an error", + backported_in="2.4.71") + def test_metadata_004_06(self, env): + self.pp_install(env) + pos = env.httpd_error_log.current_pos() + assert self.pp_request(env, self.PP2_SIG, request=False) == (None, None) + logged = env.httpd_error_log.wait_for(re.compile(r".*AH10184"), pos, + timeout=1) + assert not logged, "peer going away mid-header was logged as an error" + + # -- REMOTE_PORT ----------------------------------------------------- + + # The port the request came from must be the one mod_remoteip found, + # the same one %{remote}p logs and mod_rewrite's %{REMOTE_PORT} gives, + # wherever a configuration asks for it: the REMOTE_PORT variable of + # ap_expr (here via mod_headers) and the CGI/SSI REMOTE_PORT variable + # (here via mod_include). Bug 70295. + REMOTE_PORT_FIX = not_backported("gh-896", "REMOTE_PORT from the useragent address") + + def remote_port_install(self, env): + """A PROXY protocol vhost on http_port2 and an X-Forwarded-For one + on http_port, both exposing REMOTE_PORT three ways: the response + header 'X-Remote-Port' (ap_expr), the body of remote_port.shtml + (CGI variables via mod_include) and the access log (%{remote}p).""" + docs = os.path.join(env.server_docs_dir, "test1") + os.makedirs(docs, exist_ok=True) + with open(os.path.join(docs, "remote_port.shtml"), "w") as f: + f.write('\n') + exposed = [ + 'Header set X-Remote-Port "expr=%{REMOTE_PORT}"', + "Options +Includes", + "AddType text/html .shtml", + "AddOutputFilter INCLUDES .shtml", + f'CustomLog logs/{self.LOG_FILE} "%a %{{remote}}p"', + ] + conf = HttpdConf(env, extras={ + "base": [ + f"Listen {env.http_port2}", + ] + }) + conf.start_vhost(domains=[f"test1.{env.http_tld}"], port=env.http_port2, + doc_root="htdocs/test1") + conf.add(["RemoteIPProxyProtocol On"] + exposed) + conf.end_vhost() + conf.start_vhost(domains=[f"test1.{env.http_tld}"], port=env.http_port, + doc_root="htdocs/test1") + conf.add(["RemoteIPHeader X-Forwarded-For", + f"RemoteIPInternalProxy {self.PEER}"] + exposed) + conf.end_vhost() + conf.install() + assert env.apache_restart() == 0 + + def remote_port_logged(self, env): + log_path = os.path.join(env.server_logs_dir, self.LOG_FILE) + for _ in range(20): + with open(log_path) as f: + lines = [l.strip() for l in f if l.strip()] + if lines: + break + time.sleep(0.1) + assert len(lines) == 1, f"expected 1 log line, got {lines}" + return lines[0] + + def remote_port_clear_log(self, env): + open(os.path.join(env.server_logs_dir, self.LOG_FILE), 'w').close() + + # PROXY protocol: the client's port is known (4321 in PP2_ADDRS) and + # every place must report it, not the proxy's TCP source port. + @REMOTE_PORT_FIX + def test_metadata_004_07(self, env): + self.remote_port_install(env) + self.remote_port_clear_log(env) + hdr = self.pp2_header(self.PP2_PROXY, self.PP2_TCP4, self.PP2_ADDRS) + sock = socket.create_connection(("127.0.0.1", env.http_port2), timeout=5) + sock.sendall(hdr + f"GET /remote_port.shtml HTTP/1.0\r\n" + f"Host: test1.{env.http_tld}\r\n\r\n".encode()) + data = b"" + while True: + chunk = sock.recv(65536) + if not chunk: + break + data += chunk + sock.close() + head, _, body = data.partition(b"\r\n\r\n") + lines = head.decode().split("\r\n") + assert lines[0].split(" ")[1] == "200", lines[0] + headers = {k.strip().lower(): v.strip() for k, v in + (l.split(":", 1) for l in lines[1:] if ":" in l)} + assert self.remote_port_logged(env) == f"{self.CLIENT} 4321" + assert headers.get("x-remote-port") == "4321" + assert body.decode().strip() == "4321" + + # RemoteIPHeader: the client's port is not known, mod_remoteip reports + # 0 for it rather than the proxy's, and so must every place. + @REMOTE_PORT_FIX + def test_metadata_004_08(self, env): + self.remote_port_install(env) + self.remote_port_clear_log(env) + r = env.curl_get(env.mkurl("http", "test1", "/remote_port.shtml"), options=[ + '-H', f"X-Forwarded-For: {self.CLIENT}"]) + assert r.response["status"] == 200 + assert self.remote_port_logged(env) == f"{self.CLIENT} 0" + assert r.response["header"]["x-remote-port"] == "0" + assert r.response["body"].decode().strip() == "0" diff -Nru apache2-2.4.69/test/modules/proxy/env.py apache2-2.4.71/test/modules/proxy/env.py --- apache2-2.4.69/test/modules/proxy/env.py 2026-09-28 17:44:38.000000000 +0000 +++ apache2-2.4.71/test/modules/proxy/env.py 2026-11-09 03:07:05.000000000 +0000 @@ -2,6 +2,7 @@ import logging import os import socket +import sys from threading import Thread from pyhttpd.certs import CertificateSpec @@ -20,6 +21,7 @@ self._host = host self._port = port self._done = False + self._request = None def start(self): def process(): @@ -44,14 +46,28 @@ Content-Length: 5 Hello""".encode() - + def _process(self): + _read_buffer = 8192 if sys.platform == "win32" else 4096 while not self._done: try: c, client_address = self._socket.accept() try: - data = c.recv(4096) + data = c.recv(_read_buffer) + # capture request to backend + self._request = data c.sendall(self._make_response(data)) + # Half-close for an orderly FIN instead of letting the + # OS tear the socket down under us. An abrupt close() + # right after sendall() can, depending on the platform's + # TCP stack and buffering, surface to the peer as a + # reset rather than a clean end of the response, which + # httpd's proxy and cache code treat as an aborted + # connection and a reason to discard the cache entry. + try: + c.shutdown(socket.SHUT_WR) + except OSError: + pass finally: c.close() except socket.timeout: @@ -66,7 +82,8 @@ super().__init__(env=env) self.add_source_dir(os.path.dirname(inspect.getfile(ProxyTestSetup))) self.add_modules(["proxy", "proxy_http", "proxy_ajp", "proxy_balancer", - "lbmethod_byrequests", "remoteip"]) + "proxy_connect", "proxy_uwsgi", "lbmethod_byrequests", + "remoteip"]) class ProxyTestEnv(HttpdTestEnv): diff -Nru apache2-2.4.69/test/modules/proxy/test_01_http.py apache2-2.4.71/test/modules/proxy/test_01_http.py --- apache2-2.4.69/test/modules/proxy/test_01_http.py 2023-05-02 10:05:52.000000000 +0000 +++ apache2-2.4.71/test/modules/proxy/test_01_http.py 2026-11-09 03:07:05.000000000 +0000 @@ -59,8 +59,10 @@ # check that we see the document we expect there (host matching worked) # we need to explicitly provide a Host: header since mod_proxy cannot # resolve the name via DNS. - if not env.curl_is_at_least('8.0.0'): - pytest.skip(f'need at least curl v8.0.0 for this') + env.require(env.curl_is_at_least('8.0.0'), + 'need at least curl v8.0.0 for this', + dep='curl >= 8.0.0', + detected=f"curl {env.curl_version_str()}") domain = f"{via}.{env.http_tld}" r = env.curl_get(f"http://127.0.0.1:{env.http_port}/alive.json", 5, options=[ '-H', f"Host: {domain}", diff -Nru apache2-2.4.69/test/modules/proxy/test_02_unix.py apache2-2.4.71/test/modules/proxy/test_02_unix.py --- apache2-2.4.69/test/modules/proxy/test_02_unix.py 2024-07-11 08:18:12.000000000 +0000 +++ apache2-2.4.71/test/modules/proxy/test_02_unix.py 2026-11-09 03:07:05.000000000 +0000 @@ -110,8 +110,10 @@ # check that we see the document we expect there (host matching worked) # we need to explicitly provide a Host: header since mod_proxy cannot # resolve the name via DNS. - if not env.curl_is_at_least('8.0.0'): - pytest.skip(f'need at least curl v8.0.0 for this') + env.require(env.curl_is_at_least('8.0.0'), + 'need at least curl v8.0.0 for this', + dep='curl >= 8.0.0', + detected=f"curl {env.curl_version_str()}") domain = f"{via}.{env.http_tld}" r = env.curl_get(f"http://127.0.0.1:{env.http_port}/alive.json", 5, options=[ '-H', f"Host: {domain}", diff -Nru apache2-2.4.69/test/modules/proxy/test_03_response.py apache2-2.4.71/test/modules/proxy/test_03_response.py --- apache2-2.4.69/test/modules/proxy/test_03_response.py 2026-09-28 17:44:38.000000000 +0000 +++ apache2-2.4.71/test/modules/proxy/test_03_response.py 2026-11-09 03:07:05.000000000 +0000 @@ -16,6 +16,16 @@ return """HTTP/1.1 200 OK\r\nContent-Type: text/html\x00extra\r\n\r\n""".encode() + if "/empty-header" in path: + body = b"Hello" + return ( + b"HTTP/1.1 200 OK\r\n" + b"X-Empty:\r\n" + b"Content-Length: 5\r\n" + b"\r\n" + + body + ) + if "/forwarded" in path: headers = data.split(b"\r\n\r\n")[0].decode("latin-1") forwarded = [] @@ -70,6 +80,7 @@ conf.add([ "RemoteIPHeader X-Forwarded-For", "RemoteIPTrustedProxy 0.0.0.0", + "RemoteIPTrustedProxy 127.0.0.1", ]) conf.end_vhost() conf.install() @@ -122,3 +133,22 @@ env.httpd_error_log.ignore_recent( lognos=["AH00957", "AH00959", "AH01114"] ) + + + # empty backend response header values are valid + def test_proxy_03_004(self, env): + r = env.curl_get(env.mkurl("http", "test1", "/empty-header")) + assert r.response["status"] == 200 + assert "x-empty" in r.response["header"] + assert r.response["body"] == b"Hello" + + # a trailing empty RemoteIPHeader token must not underflow the trim pointer + def test_proxy_03_005(self, env): + env.require(env.has_shared_module("remoteip"), "need mod_remoteip for this", + dep="mod_remoteip", + detected=f"no mod_remoteip.so in {env.libexec_dir}") + + r = env.curl_get(env.mkurl("http", "test1", "/forwarded"), options=[ + '-H', 'X-Forwarded-For: 192.0.2.1,', + ]) + assert r.response["status"] == 200 diff -Nru apache2-2.4.69/test/modules/proxy/test_03_uwsgi.py apache2-2.4.71/test/modules/proxy/test_03_uwsgi.py --- apache2-2.4.69/test/modules/proxy/test_03_uwsgi.py 2026-09-29 11:28:59.000000000 +0000 +++ apache2-2.4.71/test/modules/proxy/test_03_uwsgi.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,91 +1,3 @@ - -""" -Tests for mod_proxy_uwsgi fix. - -Per PEP 3333, WSGI applications must not generate hop-by-hop -headers. Remove any such headers received from the backend. -""" - -import re - -import pytest - -from pyhttpd.conf import HttpdConf -from .uwsgi_faker import UwsgiFaker - - -UWSGI_PORT = 5200 - - -@pytest.fixture(autouse=True, scope="class") -def _proxy_uwsgi_setup(env): - faker = UwsgiFaker(port=UWSGI_PORT) - faker.start() - - conf = HttpdConf(env) - conf.start_vhost( - domains=[env.d_reverse], - port=env.http_port, - with_ssl=False, - ) - conf.add([ - "LogLevel proxy_uwsgi:debug", - f"ProxyPass / uwsgi://127.0.0.1:{UWSGI_PORT}/", - ]) - conf.end_vhost() - conf.install() - - assert env.apache_restart() == 0 - - yield - - faker.stop() - - -class TestProxyUwsgi: - - def test_proxy_03_001(self, env): - """uWSGI backend response is forwarded correctly.""" - r = env.curl_get( - f"http://{env.d_reverse}:{env.http_port}/", - 5, - ) - assert r.response["status"] == 200 - assert r.json["host"] == "uwsgi-faker" - - def test_proxy_03_002(self, env): - """No Transfer-Encoding header reaches the client.""" - r = env.curl_get( - f"http://{env.d_reverse}:{env.http_port}/", - 5, - ) - assert r.response["status"] == 200 - assert "transfer-encoding" not in r.response["header"] - - def test_proxy_03_003(self, env): - """Transfer-Encoding is stripped by mod_proxy_uwsgi.""" - r = env.curl_get( - f"http://{env.d_reverse}:{env.http_port}/?te=1", - 5, - ) - - print("response =", r.response) - print("stderr =", getattr(r, "stderr", None)) - print("stdout =", getattr(r, "stdout", None)) - print("exitcode =", getattr(r, "exit_code", None)) - print("json =", getattr(r, "json", None)) - - assert r.response is not None - assert r.response["status"] == 200 - assert "transfer-encoding" not in r.response["header"] - - assert r.json["host"] == "uwsgi-faker" - - assert env.httpd_error_log.scan_recent( - re.compile( - r".*uwsgi: removing hop-by-hop header 'Transfer-Encoding'" - ) - ) """ Tests for mod_proxy_uwsgi fix. @@ -118,6 +30,7 @@ conf.add([ "LogLevel proxy_uwsgi:debug", f"ProxyPass / uwsgi://127.0.0.1:{UWSGI_PORT}/", + "LogLevel trace4", ]) conf.end_vhost() conf.install() @@ -153,7 +66,7 @@ """Transfer-Encoding is stripped by mod_proxy_uwsgi.""" r = env.curl_get( f"http://{env.d_reverse}:{env.http_port}/?te=1", - 5, + 5, ) print("response =", r.response) diff -Nru apache2-2.4.69/test/modules/proxy/test_04_ajp.py apache2-2.4.71/test/modules/proxy/test_04_ajp.py --- apache2-2.4.69/test/modules/proxy/test_04_ajp.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/proxy/test_04_ajp.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,127 @@ +import pytest +import re + +from pyhttpd.conf import HttpdConf +from .env import TCPFaker + +magic_numbers = b"\x41\x42" +send_headers_type = b"\x04" +send_chunk_type = b"\x03" +status_code_200 = b"\x00\xc8" +status_msg_ok = b"OK" +ajp_max_size = 8192 + + +def ajp_packet_from_tomcat(body): + return magic_numbers + len(body).to_bytes(2, "big") + body + + +class _AJPFaker(TCPFaker): + + @staticmethod + def max_size_message(data): + body = send_headers_type + b"\x00" * (ajp_max_size - 1) + return ajp_packet_from_tomcat(body) + + @staticmethod + def num_headers_len(data): + # message_type(send_headers) + status_code_200 + len(inner_body) + # + "OK" + body = (send_headers_type + + status_code_200 + + len(status_msg_ok).to_bytes(2, "big") + + status_msg_ok + + b"\x00") + return ajp_packet_from_tomcat(body) + + @staticmethod + def missing_null_terminator(data): + # message_type(send_headers) + status_code_200 + len(inner_body) + # + "OK" without null terminator + body = (send_headers_type + + status_code_200 + + len(status_msg_ok).to_bytes(2, "big") + + status_msg_ok) + return ajp_packet_from_tomcat(body) + + @staticmethod + def tiny_body_chunk(data): + # message_type(send_body_chunk) + chunk_size(0xffff),3 bytes total + chunk_body = (send_chunk_type + b"\xff\xff") + return ajp_packet_from_tomcat(chunk_body) + + +class TestProxyAjp: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + env.require(env.has_shared_module("proxy_ajp"), "mod_proxy_ajp not available", + dep="mod_proxy_ajp", + detected=f"no mod_proxy_ajp.so in {env.libexec_dir}") + faker = _AJPFaker("127.0.0.1", env.http_port2) + faker.start() + conf = HttpdConf(env) + conf.start_vhost(domains=[f"test1.{env.http_tld}"], port=env.http_port) + conf.add([ + f"ProxyPass / ajp://127.0.0.1:{env.http_port2}/", + ]) + conf.end_vhost() + conf.install() + assert env.apache_restart() == 0 + yield faker + faker.stop() + + def test_proxy_004_01(self, env, _class_scope): + _class_scope._make_response = _AJPFaker.max_size_message + r = env.curl_get(env.mkurl("http", "test1", "/")) + assert r.response["status"] == 503 + try: + found = env.httpd_error_log.scan_recent( + re.compile(r'.*AH01081:.*'), timeout=5) + except TimeoutError: + found = False + + assert found + env.httpd_error_log.ignore_recent( + lognos=["AH01081", "AH01080", "AH01031", "AH00878", "AH00992"]) + + def test_proxy_004_02(self, env, _class_scope): + _class_scope._make_response = _AJPFaker.tiny_body_chunk + env.curl_get(env.mkurl("http", "test1", "/")) + try: + found = env.httpd_error_log.scan_recent( + re.compile(r'.*ajp_parse_data: Message too small.*'), + timeout=5) + except TimeoutError: + found = False + + assert found + env.httpd_error_log.ignore_recent( + lognos=["AH00893"], + matches=[r'.*ajp_parse_data: Message too small.*']) + + def test_proxy_004_03(self, env, _class_scope): + _class_scope._make_response = _AJPFaker.num_headers_len + r = env.curl_get(env.mkurl("http", "test1", "/")) + assert r.exit_code == 0 + + _class_scope._make_response = _AJPFaker.missing_null_terminator + r = env.curl_get(env.mkurl("http", "test1", "/")) + assert r.exit_code == 0 + + try: + found = env.httpd_error_log.scan_recent( + re.compile(r'.*AH03229: ajp_msg_get_uint16.*'), timeout=5) + except TimeoutError: + found = False + assert found + + try: + found = env.httpd_error_log.scan_recent( + re.compile(r'.*AH03229: ajp_msg_get_string.*'), timeout=5) + except TimeoutError: + found = False + assert found + + env.httpd_error_log.ignore_recent( + lognos=["AH03229", "AH10405", "AH00985", "AH00893"]) diff -Nru apache2-2.4.69/test/modules/proxy/test_05_uwsgi.py apache2-2.4.71/test/modules/proxy/test_05_uwsgi.py --- apache2-2.4.69/test/modules/proxy/test_05_uwsgi.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/proxy/test_05_uwsgi.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,73 @@ +import pytest + +from pyhttpd.conf import HttpdConf +from .env import TCPFaker + + +class _UWSGIFaker(TCPFaker): + + @staticmethod + def hello(data): + body = b"Hello" + return ( + b"HTTP/1.1 200 OK\r\n" + b"Content-Type: text/plain\r\n" + b"Content-Length: 5\r\n" + b"\r\n" + + body + ) + + @staticmethod + def empty_header(data): + body = b"Hello" + return ( + b"HTTP/1.1 200 OK\r\n" + b"X-Empty:\r\n" + b"Content-Length: 5\r\n" + b"\r\n" + + body + ) + + +class TestProxyUwsgi: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + env.require(env.has_shared_module("proxy_uwsgi"), "mod_proxy_uwsgi not available", + dep="mod_proxy_uwsgi", + detected=f"no mod_proxy_uwsgi.so in {env.libexec_dir}") + faker = _UWSGIFaker("127.0.0.1", env.http_port2) + faker.start() + conf = HttpdConf(env) + conf.start_vhost(domains=[f"test1.{env.http_tld}"], port=env.http_port) + conf.add([ + f"ProxyPass / uwsgi://127.0.0.1:{env.http_port2}/", + ]) + conf.end_vhost() + conf.install() + assert env.apache_restart() == 0 + yield faker + faker.stop() + + # verify uwsgi request header + def test_proxy_005_01(self, env, _class_scope): + _class_scope._make_response = _UWSGIFaker.hello + r = env.curl_get(env.mkurl("http", "test1", "/")) + assert r.response["status"] == 200 + assert r.response["body"] == b"Hello" + + data = _class_scope._request + + assert data[0] == 0x00 # standard WSGI request + datasize = data[1] + (data[2] * 256) # read from 16bit little-endian + assert data[3] == 0x00 # standard WSGI request + assert len(data) == 4 + datasize + + # empty backend response header values are valid + def test_proxy_005_02(self, env, _class_scope): + _class_scope._make_response = _UWSGIFaker.empty_header + r = env.curl_get(env.mkurl("http", "test1", "/")) + assert r.response["status"] == 200 + assert "x-empty" in r.response["header"] + assert r.response["body"] == b"Hello" + diff -Nru apache2-2.4.69/test/modules/proxy/test_07_cache_control.py apache2-2.4.71/test/modules/proxy/test_07_cache_control.py --- apache2-2.4.69/test/modules/proxy/test_07_cache_control.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/proxy/test_07_cache_control.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,173 @@ +import os +import time +import uuid + +import pytest + +from pyhttpd.conf import HttpdConf +from .env import TCPFaker + + +# Backend answering // with the Cache-Control header lines of +# CASES[index], counting the requests it gets for every path. +class _CacheFaker(TCPFaker): + + def __init__(self, host, port, cases): + super().__init__(host, port) + self.cases = cases + self.hits = {} + + def _make_response(self, data): + head = data.split(b"\r\n\r\n")[0].decode("latin-1").split("\r\n") + path = head[0].split(" ")[1] + self.hits[path] = self.hits.get(path, 0) + 1 + cookie = "" + for line in head[1:]: + if line.lower().startswith("cookie:"): + cookie = line.split(":", 1)[1].strip() + cc, extra = self.cases[int(path.rsplit("/", 1)[1])] + body = f"hit={self.hits[path]} cookie={cookie}\n" + lines = ["HTTP/1.1 200 OK", + "Last-Modified: Thu, 01 Jan 2026 00:00:00 GMT", + 'ETag: "t"', + "Content-Type: text/plain", + f"Content-Length: {len(body)}", + "Connection: close"] + lines += [f"Cache-Control: {v}" for v in cc] + lines += extra + return ("\r\n".join(lines) + "\r\n\r\n" + body).encode() + + +# Cache-Control field lines and further response headers of the backend +NO_STORE = [ + [["no-store"], []], + [["private"], []], + [["private, no-store"], []], + [["private,no-store,max-age=0"], []], + [["private, no-store, max-age=0"], []], + [["private , no-store , max-age=0"], []], + [["max-age=60, private"], []], + [["max-age=60, no-store"], []], + [["no-store, max-age=60"], []], + [["no-store, foo=bar"], []], + [["foo=bar, no-store"], []], + [["foo=\"a, b\", no-store"], []], + [["private", "no-store", "max-age=0"], []], +] +STORED = [ + [["public, max-age=60"], []], + [["max-age=60"], []], + [["max-age=60, must-revalidate"], []], + [["must-revalidate, max-age=60"], []], + [["public, s-maxage=60"], []], + [["max-age=60, stale-while-revalidate=30"], []], + [["max-age=60, stale-if-error=30"], []], + [["foo=\"a, b\", max-age=60"], []], +] +QUALIFIED = [ + [["max-age=60, private=\"X-Secret\""], []], + [["max-age=60, private=\"X-Secret, Set-Cookie\""], []], + [["max-age=60, no-cache=\"X-Secret,Set-Cookie\""], []], +] +USER = [ + [["private, no-store, max-age=0"], []], +] +CASES = NO_STORE + STORED + QUALIFIED + USER +for _c in QUALIFIED: + _c[1] += ["Set-Cookie: a=b", "X-Secret: s", "X-Public: p"] + + +class TestCacheControl: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + faker = _CacheFaker("127.0.0.1", env.http_port2, CASES) + faker.start() + cache_dir = os.path.join(env.gen_dir, "cache-control") + os.makedirs(cache_dir, exist_ok=True) + + conf = HttpdConf(env) + conf.start_vhost(domains=[f"test1.{env.http_tld}"], port=env.http_port, + doc_root="htdocs", with_ssl=False) + conf.add([ + f'CacheRoot "{cache_dir}"', + 'CacheEnable disk "/cc/"', + 'CacheHeader on', + 'CacheStorePrivate Off', + 'CacheStoreNoStore Off', + f"ProxyPass /cc/ http://127.0.0.1:{env.http_port2}/", + ]) + conf.end_vhost() + conf.install() + assert env.apache_restart() == 0 + TestCacheControl.faker = faker + TestCacheControl.run = uuid.uuid4().hex[:12] + yield + faker.stop() + + def get(self, env, index, cookie=None): + url = env.mkurl("http", "test1", f"/cc/{self.run}/{index}") + options = ["-H", f"Cookie: {cookie}"] if cookie else [] + r = env.curl_get(url, options=options) + assert r.response["status"] == 200, f"{r}" + return r.response + + def backend_hits(self, index): + return self.faker.hits.get(f"/{self.run}/{index}", 0) + + # a response that must not be stored is never served from the cache + @pytest.mark.parametrize("index", range(len(NO_STORE))) + def test_proxy_07_001(self, env, index): + cc = ", ".join(CASES[index][0]) + r1 = self.get(env, index) + r2 = self.get(env, index) + assert self.backend_hits(index) == 2, f"Cache-Control: {cc} was cached" + assert not r1["header"].get("x-cache", "").startswith("HIT") + assert not r2["header"].get("x-cache", "").startswith("HIT") + assert r2["body"] == b"hit=2 cookie=\n" + + # a response that may be stored is served from the cache + @pytest.mark.parametrize("index", range(len(NO_STORE), + len(NO_STORE) + len(STORED))) + def test_proxy_07_002(self, env, index): + cc = ", ".join(CASES[index][0]) + r1 = self.get(env, index) + # The cache entry for r1 is committed by the CACHE_SAVE filter on + # the EOS-only pass that follows the body, which can still be + # running server side for an instant after the body itself has + # already reached this process. Give it room to land before relying + # on it, instead of risking a spurious backend hit on r2. + time.sleep(0.1) + r2 = self.get(env, index) + assert self.backend_hits(index) == 1, f"Cache-Control: {cc} not cached" + assert r2["header"]["x-cache"].startswith("HIT") + assert r2["body"] == r1["body"] + + # fields listed in private="..." and no-cache="..." are not stored, + # whether one or several are listed, the rest of the response is + @pytest.mark.parametrize("index", range(len(NO_STORE) + len(STORED), + len(NO_STORE) + len(STORED) + len(QUALIFIED))) + def test_proxy_07_003(self, env, index): + cc = ", ".join(CASES[index][0]) + r1 = self.get(env, index) + assert r1["header"]["x-secret"] == "s" + assert r1["header"]["set-cookie"] == "a=b" + # See test_proxy_07_002: give the CACHE_SAVE filter's commit time + # to land before asserting on it. + time.sleep(0.1) + r2 = self.get(env, index) + assert self.backend_hits(index) == 1, f"Cache-Control: {cc} not cached" + assert r2["header"]["x-cache"].startswith("HIT") + assert r2["header"]["x-public"] == "p" + assert "x-secret" not in r2["header"], f"X-Secret stored: {cc}" + if "Set-Cookie" in cc: + assert "set-cookie" not in r2["header"], f"Set-Cookie stored: {cc}" + + # a user specific response is not given to another request + def test_proxy_07_004(self, env): + index = len(CASES) - 1 + r1 = self.get(env, index, cookie="session=first") + assert r1["body"] == b"hit=1 cookie=session=first\n" + r2 = self.get(env, index) + assert b"session=first" not in r2["body"] + assert self.backend_hits(index) == 2 diff -Nru apache2-2.4.69/test/modules/proxy/test_08_cache_vary_star.py apache2-2.4.71/test/modules/proxy/test_08_cache_vary_star.py --- apache2-2.4.69/test/modules/proxy/test_08_cache_vary_star.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/proxy/test_08_cache_vary_star.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,133 @@ +import os +import time +import uuid + +import pytest + +from pyhttpd.conf import HttpdConf +from .env import TCPFaker + + +# Backend answering // with a body that depends on the request's +# Cookie, and a Cache-Control that makes the response otherwise cacheable. +class _IdentityFaker(TCPFaker): + + def __init__(self, host, port): + super().__init__(host, port) + self.hits = {} + + def _make_response(self, data): + head = data.split(b"\r\n\r\n")[0].decode("latin-1").split("\r\n") + path = head[0].split(" ")[1] + self.hits[path] = self.hits.get(path, 0) + 1 + cookie = "anonymous" + for line in head[1:]: + if line.lower().startswith("cookie:"): + cookie = line.split(":", 1)[1].strip() + body = f"body-for-{cookie}\n" + lines = ["HTTP/1.1 200 OK", + "Last-Modified: Thu, 01 Jan 2026 00:00:00 GMT", + 'ETag: "t"', + "Cache-Control: public, max-age=60", + "Content-Type: text/plain", + f"Content-Length: {len(body)}", + "Connection: close"] + return ("\r\n".join(lines) + "\r\n\r\n" + body).encode() + + +class TestCacheVaryStar: + """BZ 70289: mod_cache's cacheability check for "Vary: *" only looked + at r->headers_out, so a Vary: * set via "Header always" (which writes + to r->err_headers_out) was missed by the check, while storage itself + does merge err_headers_out in. This let a response meant for one + client be stored and served back to a different client.""" + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + faker = _IdentityFaker("127.0.0.1", env.http_port2) + faker.start() + cache_dir = os.path.join(env.gen_dir, "cache-vary-star") + os.makedirs(cache_dir, exist_ok=True) + + conf = HttpdConf(env) + conf.start_vhost(domains=[f"test1.{env.http_tld}"], port=env.http_port, + doc_root="htdocs", with_ssl=False) + conf.add([ + f'CacheRoot "{cache_dir}"', + 'CacheEnable disk "/cv/"', + 'CacheEnable disk "/cv2/"', + 'CacheEnable disk "/cv3/"', + 'CacheHeader on', + '', + ' Header always set Vary "*"', + '', + '', + ' Header set Vary "*"', + '', + f"ProxyPass /cv/ http://127.0.0.1:{env.http_port2}/", + f"ProxyPass /cv2/ http://127.0.0.1:{env.http_port2}/", + f"ProxyPass /cv3/ http://127.0.0.1:{env.http_port2}/", + ]) + conf.end_vhost() + conf.install() + assert env.apache_restart() == 0 + TestCacheVaryStar.faker = faker + TestCacheVaryStar.run = uuid.uuid4().hex[:12] + yield + faker.stop() + + def get(self, env, path_prefix="/cv/", index=0, cookie=None): + url = env.mkurl("http", "test1", f"{path_prefix}{self.run}/{index}") + options = ["-H", f"Cookie: {cookie}"] if cookie else [] + r = env.curl_get(url, options=options) + assert r.response["status"] == 200, f"{r}" + return r.response + + def backend_hits(self, index=0): + return self.faker.hits.get(f"/{self.run}/{index}", 0) + + # a victim's response must never be handed to another client under a + # Vary: * header that was set via "Header always" + def test_proxy_08_001(self, env): + r1 = self.get(env, cookie="session=victim") + assert r1["body"] == b"body-for-session=victim\n" + # give the CACHE_SAVE filter's commit a moment to land, the same + # way test_07_cache_control.py does for its own positive cases; + # here it only matters because we are proving a negative. + time.sleep(0.1) + r2 = self.get(env) + assert not r2["header"].get("x-cache", "").startswith("HIT"), \ + "Vary: * response (via Header always) was served from cache" + assert r2["body"] == b"body-for-anonymous\n", \ + "victim's response body leaked to an anonymous request" + assert self.backend_hits() == 2, "Vary: * response was stored" + + # positive control: the same disk cache setup, minus "Header always + # set Vary *", does cache and serve a repeat request from disk. This + # rules out test_proxy_08_001 passing merely because disk storage is + # broken in this setup (e.g. a missing CacheRoot directory) rather + # than because mod_cache correctly rejected the Vary: * response. + def test_proxy_08_002(self, env): + r1 = self.get(env, path_prefix="/cv2/", index=1) + assert r1["body"] == b"body-for-anonymous\n" + time.sleep(0.1) + r2 = self.get(env, path_prefix="/cv2/", index=1) + assert r2["header"]["x-cache"].startswith("HIT"), \ + "response without Vary: * was not served from the disk cache" + assert r2["body"] == r1["body"] + assert self.backend_hits(1) == 1, \ + "response without Vary: * was not cached" + + # a victim's response must never be handed to another client under a + # Vary: * header set via plain "Header set" (which writes to + # r->headers_out directly, the case the pre-fix code already checked) + def test_proxy_08_003(self, env): + r1 = self.get(env, path_prefix="/cv3/", index=2, cookie="session=victim") + assert r1["body"] == b"body-for-session=victim\n" + time.sleep(0.1) + r2 = self.get(env, path_prefix="/cv3/", index=2) + assert not r2["header"].get("x-cache", "").startswith("HIT"), \ + "Vary: * response (via headers_out) was served from cache" + assert r2["body"] == b"body-for-anonymous\n", \ + "victim's response body leaked to an anonymous request" + assert self.backend_hits(2) == 2, "Vary: * response was stored" diff -Nru apache2-2.4.69/test/modules/proxy/uwsgi_faker.py apache2-2.4.71/test/modules/proxy/uwsgi_faker.py --- apache2-2.4.69/test/modules/proxy/uwsgi_faker.py 2026-09-29 11:28:59.000000000 +0000 +++ apache2-2.4.71/test/modules/proxy/uwsgi_faker.py 2026-11-09 03:07:05.000000000 +0000 @@ -144,149 +144,3 @@ break t = threading.Thread(target=_handle, args=(conn,), daemon=True) t.start() -""" -Minimal fake uWSGI backend for testing mod_proxy_uwsgi. - -Listens on a TCP port and speaks the uWSGI wire protocol. mod_proxy_uwsgi -sends a binary uwsgi packet (4-byte header + CGI key/value pairs) followed by -the optional request body, and expects a plain HTTP/1.x response back. - -The QUERY_STRING CGI variable controls what the response looks like: - - QUERY_STRING="" - normal 200 response, no Transfer-Encoding - QUERY_STRING="te=1" - 200 response with Transfer-Encoding: chunked injected - (simulates a buggy Python app; mod_proxy_uwsgi must - strip it before forwarding to the client) -""" -import socket -import struct -import threading - - -BODY = b'{"host": "uwsgi-faker"}' - - -def _parse_uwsgi_vars(data): - """Parse the CGI key/value payload from a uwsgi packet. - - Packet layout (from the uWSGI protocol spec): - [modifier1: u8][payload_len: u16le][modifier2: u8][payload: bytes] - Payload is a sequence of: - [keylen: u16le][key: bytes][vallen: u16le][val: bytes] - - Returns a dict of the decoded variables, or {} on parse error. - """ - if len(data) < 4: - return {} - payload_len = struct.unpack_from(" end: - break - klen = struct.unpack_from(" end: - break - key = data[pos:pos + klen].decode("latin-1") - pos += klen - if pos + 2 > end: - break - vlen = struct.unpack_from(" end: - break - val = data[pos:pos + vlen].decode("latin-1") - pos += vlen - vars_[key] = val - return vars_ - - -def _recv_uwsgi_request(conn): - """Read a complete uwsgi request packet from the connection. - - Returns the raw bytes of the complete packet (header + payload), or b"" - on connection close. - """ - # Read the 4-byte uwsgi header - header = b"" - while len(header) < 4: - chunk = conn.recv(4 - len(header)) - if not chunk: - return b"" - header += chunk - - payload_len = struct.unpack_from(" SSLTestEnv: + level = logging.INFO + console = logging.StreamHandler() + console.setLevel(level) + console.setFormatter(logging.Formatter('%(levelname)s: %(message)s')) + logging.getLogger('').addHandler(console) + logging.getLogger('').setLevel(level=level) + env = SSLTestEnv(pytestconfig=pytestconfig) + # setup_httpd() raises outright when a required module is not built, + # which is neither a skip nor a useful failure. mod_ssl being absent is + # a missing dependency, so report it as one before we get there. + env.require(env.has_mod_ssl(), "no mod_ssl available", dep="mod_ssl", + detected=f"no mod_ssl.so in {env.libexec_dir}") + env.setup_httpd() + env.apache_access_log_clear() + env.httpd_error_log.clear_log() + return env + + +@pytest.fixture(autouse=True, scope="package") +def require_openssl(env): + env.require(env.has_tool("openssl"), "openssl not installed", + dep="openssl", detected="no openssl on $PATH") + + +@pytest.fixture(autouse=True, scope="package") +def _stop_package_scope(env): + yield + assert env.apache_stop() == 0 diff -Nru apache2-2.4.69/test/modules/ssl/env.py apache2-2.4.71/test/modules/ssl/env.py --- apache2-2.4.69/test/modules/ssl/env.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/ssl/env.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,26 @@ +import inspect +import logging +import os + +from pyhttpd.env import HttpdTestEnv, HttpdTestSetup + +log = logging.getLogger(__name__) + + +class SSLTestSetup(HttpdTestSetup): + + def __init__(self, env: 'HttpdTestEnv'): + super().__init__(env=env) + self.add_source_dir(os.path.dirname(inspect.getfile(SSLTestSetup))) + self.add_modules(["ssl"]) + self.add_optional_modules(["socache_redis"]) + + +class SSLTestEnv(HttpdTestEnv): + + def __init__(self, pytestconfig=None): + super().__init__(pytestconfig=pytestconfig) + self.add_httpd_log_modules(["http", "ssl", "core"]) + + def setup_httpd(self, setup: HttpdTestSetup = None): + super().setup_httpd(setup=SSLTestSetup(env=self)) \ No newline at end of file diff -Nru apache2-2.4.69/test/modules/ssl/test_001_sni.py apache2-2.4.71/test/modules/ssl/test_001_sni.py --- apache2-2.4.69/test/modules/ssl/test_001_sni.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/ssl/test_001_sni.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,38 @@ +import os +import pytest + +from pyhttpd.conf import HttpdConf + + +class TestSNI: + LOG_FILE = "test_sni.log" + + @pytest.fixture(autouse=True, scope="class") + def _class_scope(self, env): + conf = HttpdConf(env, extras={ + "base": + f'CustomLog logs/{self.LOG_FILE} "%{{SSL_TLS_SNI}}x"' + }) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + + # check sni logging characters + def test_ssl_001_01(self, env): + log_path = os.path.join(env.server_logs_dir, self.LOG_FILE) + + open(log_path, 'w').close() + sni = "httpd\x01\n2024\".org" + + r = env.run(args=[ + 'openssl', 's_client', + '-connect', f"localhost:{env.https_port}", + '-servername', sni + ], intext="GET / HTTP/1.1\n\n") + assert r.exit_code == 0 + + with open(log_path, 'rb') as f: + log_content = f.read() + + assert sni.encode() not in log_content, \ + f"found unescaped characters in {self.LOG_FILE}.log" diff -Nru apache2-2.4.69/test/modules/ssl/test_003_verify_client_perdir.py apache2-2.4.71/test/modules/ssl/test_003_verify_client_perdir.py --- apache2-2.4.69/test/modules/ssl/test_003_verify_client_perdir.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/ssl/test_003_verify_client_perdir.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,144 @@ +import os +import re + +import pytest + +from pyhttpd.backport import not_backported +from pyhttpd.certs import CertificateSpec +from pyhttpd.conf import HttpdConf + +# SSLVerifyClient in a asks for the certificate once the handshake +# has finished, and the two protocol families do that quite differently: +# ssl_hook_Access_classic() renegotiates for TLSv1.2 and below, while +# ssl_hook_Access_modern() uses TLSv1.3 Post-Handshake Authentication +# (RFC 8446), logging AH10129 as it starts and AH10158 when it cannot proceed. +# +# What the enclosing virtual host asks for matters as much as the protocol: +# with "SSLVerifyClient optional" the certificate is requested during the +# initial handshake, which changes what is left for the per-directory setting +# to do. +PHA_STARTED = "AH10129" +VHOSTS = {"unset": "", "optional": "SSLVerifyClient optional"} + + +class TestVerifyClientPerDir: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + doc_dir = os.path.join(env.server_dir, "htdocs", "test1", "secure") + os.makedirs(doc_dir, exist_ok=True) + with open(os.path.join(doc_dir, "index.html"), "w") as f: + f.write("secret\n") + env.httpd_error_log.add_ignored_lognos( + ["AH02261", "AH02262", "AH02263", "AH10158", "AH10373", + "AH02040"]) + env.httpd_error_log.add_ignored_matches([ + r'.*SSL Library Error.*', + r'.*certificate verify failed.*', + ]) + + def install(self, env, proto, vhost_verify, vhost_depth=5, loc_depth=None, + loc_verify="SSLVerifyClient require"): + loc_lines = loc_verify + if loc_depth is not None: + loc_lines += f"\n SSLVerifyDepth {loc_depth}" + conf = HttpdConf(env, extras={ + f"test1.{env.http_tld}": f""" + LogLevel ssl:debug + SSLProtocol -all +{proto} + SSLCACertificateFile "{env.ca.cert_file}" + SSLVerifyDepth {vhost_depth} + {vhost_verify} + + {loc_lines} + + """, + }) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + + def get(self, env, proto, version, vhost_verify="", + path="/secure/index.html", with_cert=True, + vhost_depth=5, loc_depth=None, + loc_verify="SSLVerifyClient require"): + self.install(env, proto, vhost_verify, vhost_depth, loc_depth, + loc_verify) + env.httpd_error_log.clear_log() + # Pin the version at both ends: "--tlsvX" alone is a floor, not a pin. + # Force HTTP/1.1 because per-directory verification is refused outright + # on an HTTP/2 secondary connection, which would look like a protocol + # failure while proving nothing about the TLS version. + options = ["-v", "--http1.1", f"--tlsv{version}", "--tls-max", version] + if with_cert: + creds = env.ca.issue_cert( + CertificateSpec(name="perdir-client", client=True)) + options.extend(["--cert", creds.cert_file, + "--key", creds.pkey_file]) + r = env.curl_get(env.mkurl("https", "test1", path), options=options) + if r.exit_code == 0: + got = re.search(r'SSL connection using (TLSv[0-9.]+)', r.stderr or "") + assert got and got.group(1) == proto, \ + f"negotiated {got and got.group(1)}, wanted {proto}" + return r + + def logged(self, env, logno): + with open(env.httpd_error_log.path) as fd: + return any(logno in line for line in fd) + + # TLSv1.2 reaches the per-directory setting by renegotiating, whatever the + # virtual host asked for. + @pytest.mark.parametrize("vhost", list(VHOSTS), ids=list(VHOSTS)) + def test_ssl_003_01(self, env, vhost): + r = self.get(env, "TLSv1.2", "1.2", VHOSTS[vhost]) + assert r.exit_code == 0, f"handshake failed: {r.stderr}" + assert r.response["status"] == 200 + + # On TLSv1.3 it uses Post-Handshake Authentication, and a client which + # offers the extension is served just the same. + def test_ssl_003_02(self, env): + r = self.get(env, "TLSv1.3", "1.3", VHOSTS["unset"]) + assert r.exit_code == 0, f"handshake failed: {r.stderr}" + assert r.response["status"] == 200 + assert self.logged(env, PHA_STARTED), \ + f"{PHA_STARTED} absent: the certificate was not requested " \ + "post-handshake, so this is not exercising PHA" + + # Without a certificate the location is refused, so the cases above are + # really being verified rather than served unconditionally. + @pytest.mark.parametrize("vhost", list(VHOSTS), ids=list(VHOSTS)) + def test_ssl_003_03(self, env, vhost): + r = self.get(env, "TLSv1.3", "1.3", VHOSTS[vhost], with_cert=False) + assert not (r.exit_code == 0 and r.response + and r.response["status"] == 200), \ + "resource served without a client certificate" + + # The rest of the virtual host is unaffected. + def test_ssl_003_04(self, env): + r = self.get(env, "TLSv1.3", "1.3", VHOSTS["unset"], path="/index.html") + assert r.exit_code == 0, f"handshake failed: {r.stderr}" + assert r.response["status"] == 200 + + # A virtual host asking for an optional certificate has already collected + # one during the handshake, so tightening to "require" for a location has + # nothing left to ask for. This used to attempt Post-Handshake + # Authentication anyway, which OpenSSL refuses once a certificate was + # requested in the handshake - SSL_R_INVALID_CONFIG, reported as AH10158 + # and turned into 403, even for a client which offered the + # post_handshake_auth extension and presented a valid certificate. + @not_backported("r1938504", "no SSL_VERIFY_CLIENT_ONCE with post-handshake auth") + def test_ssl_003_05(self, env): + r = self.get(env, "TLSv1.3", "1.3", VHOSTS["optional"]) + assert r.exit_code == 0, f"handshake failed: {r.stderr}" + assert r.response["status"] == 200 + + # SSLVerifyDepth 0 accepts only a self-signed certificate, so the CA + # issued one used here is refused - which establishes that the depth is + # applied at all, before asking where it is applied from. + @pytest.mark.parametrize("proto,version", [("TLSv1.2", "1.2"), + ("TLSv1.3", "1.3")]) + def test_ssl_003_06(self, env, proto, version): + r = self.get(env, proto, version, VHOSTS["unset"], vhost_depth=0) + assert not (r.exit_code == 0 and r.response + and r.response["status"] == 200), \ + "certificate accepted at SSLVerifyDepth 0" diff -Nru apache2-2.4.69/test/modules/ssl/test_004_socache_redis.py apache2-2.4.71/test/modules/ssl/test_004_socache_redis.py --- apache2-2.4.69/test/modules/ssl/test_004_socache_redis.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/ssl/test_004_socache_redis.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,91 @@ +import socket +import time +from threading import Thread + +import pytest + +from pyhttpd.backport import not_backported +from pyhttpd.depends import needs_dependency + +from pyhttpd.conf import HttpdConf +from .env import SSLTestEnv + + +class SilentRedis: + # accepts connections and never answers, so a client waits for its + # read timeout + + def __init__(self): + self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._socket.bind(('127.0.0.1', 0)) + self._socket.listen(5) + self._socket.settimeout(0.2) + self._done = False + self._conns = [] + self._thread = Thread(target=self._run, daemon=True) + + @property + def port(self): + return self._socket.getsockname()[1] + + def _run(self): + while not self._done: + try: + self._conns.append(self._socket.accept()[0]) + except socket.timeout: + pass + + def start(self): + self._thread.start() + + def stop(self): + self._done = True + self._thread.join(timeout=5) + for c in self._conns: + c.close() + self._socket.close() + + +@needs_dependency("mod_socache_redis", SSLTestEnv.has_shared_module("socache_redis"), + reason="mod_socache_redis not available") +class TestSocacheRedis: + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + # storing the session of a TLSv1.2 handshake waits for the redis reply + env.httpd_error_log.add_ignored_lognos(["AH03478"]) + yield + env.httpd_error_log.remove_ignored_lognos(["AH03478"]) + + # RedisTimeout is passed to the redis client in whole seconds, a + # positive value is rounded up, 0 does not wait at all + @pytest.mark.parametrize(["timeout", "seconds"], [ + ["0", 0], + ] + [pytest.param(timeout, seconds, marks=not_backported( + "r1938833", "RedisTimeout unit conversion")) + for timeout, seconds in [["1ms", 1], ["1s", 1], ["1500ms", 2]]]) + def test_ssl_004_01(self, env, timeout, seconds): + redis = SilentRedis() + redis.start() + try: + conf = HttpdConf(env, extras={ + "base": [ + f"SSLSessionCache redis:127.0.0.1:{redis.port}", + f"RedisTimeout {timeout}", + "SSLProtocol TLSv1.2", + ] + }) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0 + url = env.mkurl("https", "test1", "/") + start = time.monotonic() + r = env.curl_get(url, options=["--tlsv1.2", "--tls-max", "1.2", + "--max-time", "10"]) + elapsed = time.monotonic() - start + finally: + redis.stop() + assert r.exit_code == 0, f"{r.stdout}{r.stderr}" + assert r.response["status"] == 200 + assert elapsed >= seconds - 0.2, f"waited only {elapsed:.1f}s" + assert elapsed < seconds + 1.5, f"waited {elapsed:.1f}s" diff -Nru apache2-2.4.69/test/modules/ssl/test_005_vhost_compat.py apache2-2.4.71/test/modules/ssl/test_005_vhost_compat.py --- apache2-2.4.69/test/modules/ssl/test_005_vhost_compat.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/ssl/test_005_vhost_compat.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,309 @@ +import os +import re + +import pytest + +from pyhttpd.certs import CertificateSpec +from pyhttpd.conf import HttpdConf + + +class TestVhostCompatSecure: + """Under the default 'secure' SSLVHostSNIPolicy, requests routed to a + virtual host with different SSL parameters (certificate or auth + settings) than the handshake host are rejected with 421.""" + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env): + alt_domain = f"test2.{env.http_tld}" + alt_creds = env.ca.issue_cert( + CertificateSpec(name="ssl005-alt", domains=[alt_domain], + key_type='rsa2048')) + + for name in ("test1", "test2"): + doc = os.path.join(env.server_dir, "htdocs", name) + os.makedirs(doc, exist_ok=True) + with open(os.path.join(doc, "index.html"), "w") as f: + f.write(f"vhost {name}\n") + + conf = HttpdConf(env, extras={ + f"test1.{env.http_tld}": "LogLevel ssl:debug", + alt_domain: f""" + LogLevel ssl:debug + SSLCertificateFile "{alt_creds.cert_file}" + SSLCertificateKeyFile "{alt_creds.pkey_file}" + """, + }) + conf.add_vhost_test1() + conf.start_vhost(domains=[alt_domain], + port=env.https_port, + doc_root="htdocs/test2", + with_certificates=False) + conf.end_vhost() + conf.install() + assert env.apache_restart() == 0 + + env.httpd_error_log.add_ignored_lognos(["AH02032"]) + + # SNI selects a vhost and Host: names a different one with a + # different certificate. Must return 421. + def test_ssl_005_01(self, env): + url = env.mkurl("https", "test1", "/index.html") + alt_host = f"test2.{env.http_tld}" + r = env.curl_get(url, options=[ + "--http1.1", "-H", f"Host: {alt_host}", + ]) + assert r.exit_code == 0, f"curl failed: {r.stderr}" + assert r.response["status"] == 421 + + # Same vhost for SNI and Host: — must succeed. + def test_ssl_005_02(self, env): + url = env.mkurl("https", "test1", "/index.html") + r = env.curl_get(url, options=["--http1.1"]) + assert r.exit_code == 0, f"curl failed: {r.stderr}" + assert r.response["status"] == 200 + + # No SNI at all, with Host: selecting a vhost whose SSL parameters + # differ from the default (handshake) host. Must return 421. + def test_ssl_005_03(self, env): + alt_host = f"test2.{env.http_tld}" + r = env.run([ + 'openssl', 's_client', + '-connect', f'127.0.0.1:{env.https_port}', + '-CAfile', env.ca.cert_file, + '-noservername', + '-tls1_3', + '-ign_eof', + ], intext=( + f"GET /index.html HTTP/1.1\r\n" + f"Host: {alt_host}\r\n" + f"Connection: close\r\n" + f"\r\n" + )) + assert r.exit_code == 0, f"openssl s_client failed: {r.stderr}" + assert re.search(r'HTTP/1\.[01] 421', r.stdout), \ + f"expected 421 in response, got: {r.stdout[:500]}" + + # No SNI, but Host: matches the default (handshake) vhost — must + # succeed because the SSL parameters are the same. + def test_ssl_005_04(self, env): + default_host = f"test1.{env.http_tld}" + r = env.run([ + 'openssl', 's_client', + '-connect', f'127.0.0.1:{env.https_port}', + '-CAfile', env.ca.cert_file, + '-noservername', + '-tls1_3', + '-ign_eof', + ], intext=( + f"GET /index.html HTTP/1.1\r\n" + f"Host: {default_host}\r\n" + f"Connection: close\r\n" + f"\r\n" + )) + assert r.exit_code == 0, f"openssl s_client failed: {r.stderr}" + assert re.search(r'HTTP/1\.[01] 200', r.stdout), \ + f"expected 200 in response, got: {r.stdout[:500]}" + + # TLS 1.3 session resumption: establish a session with vhost A, then + # resume it without SNI while Host: names vhost B (different SSL + # config). The resumed connection must also be rejected with 421. + def test_ssl_005_05(self, env): + default_host = f"test1.{env.http_tld}" + alt_host = f"test2.{env.http_tld}" + sess_dir = os.path.join(env.gen_dir, "ssl005") + os.makedirs(sess_dir, exist_ok=True) + sess_file = os.path.join(sess_dir, "session.pem") + + r = env.run([ + 'openssl', 's_client', + '-connect', f'127.0.0.1:{env.https_port}', + '-CAfile', env.ca.cert_file, + '-servername', default_host, + '-tls1_3', + '-sess_out', sess_file, + '-ign_eof', + ], intext=( + f"GET /index.html HTTP/1.1\r\n" + f"Host: {default_host}\r\n" + f"Connection: close\r\n" + f"\r\n" + )) + assert r.exit_code == 0, f"session setup failed: {r.stderr}" + assert os.path.isfile(sess_file), "no session ticket saved" + + r = env.run([ + 'openssl', 's_client', + '-connect', f'127.0.0.1:{env.https_port}', + '-CAfile', env.ca.cert_file, + '-noservername', + '-tls1_3', + '-sess_in', sess_file, + '-ign_eof', + ], intext=( + f"GET /index.html HTTP/1.1\r\n" + f"Host: {alt_host}\r\n" + f"Connection: close\r\n" + f"\r\n" + )) + assert r.exit_code == 0, f"resumed connection failed: {r.stderr}" + assert re.search(r'HTTP/1\.[01] 421', r.stdout), \ + f"expected 421 on resumed connection, got: {r.stdout[:500]}" + + +class TestVhostCompatAuthonly: + """Under 'authonly' SSLVHostSNIPolicy, vhosts with different server + certificates but identical authentication settings are compatible. + This is the regression fix for PR 69743: the 'secure' default was + too strict for deployments that intentionally use different certs on + the same IP:port.""" + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env, request): + has_policy = env.has_directive("SSLVHostSNIPolicy") + if not has_policy: + request.applymarker(pytest.mark.xfail( + reason="SSLVHostSNIPolicy not available", strict=False)) + alt_domain = f"test2.{env.http_tld}" + alt_creds = env.ca.issue_cert( + CertificateSpec(name="ssl005-authonly", domains=[alt_domain], + key_type='rsa2048')) + + for name in ("test1", "test2"): + doc = os.path.join(env.server_dir, "htdocs", name) + os.makedirs(doc, exist_ok=True) + with open(os.path.join(doc, "index.html"), "w") as f: + f.write(f"vhost {name}\n") + + extras = { + f"test1.{env.http_tld}": "LogLevel ssl:debug", + alt_domain: f""" + LogLevel ssl:debug + SSLCertificateFile "{alt_creds.cert_file}" + SSLCertificateKeyFile "{alt_creds.pkey_file}" + """, + } + if has_policy: + extras['base'] = 'SSLVHostSNIPolicy authonly' + conf = HttpdConf(env, extras=extras) + conf.add_vhost_test1() + conf.start_vhost(domains=[alt_domain], + port=env.https_port, + doc_root="htdocs/test2", + with_certificates=False) + conf.end_vhost() + conf.install() + assert env.apache_restart() == 0 + + # Under 'authonly', a Host: mismatch to a vhost with a different + # certificate but same auth settings is allowed. This is the + # regression that PR 69743 fixed. + def test_ssl_005_10(self, env): + url = env.mkurl("https", "test1", "/index.html") + alt_host = f"test2.{env.http_tld}" + r = env.curl_get(url, options=[ + "--http1.1", "-H", f"Host: {alt_host}", + ]) + assert r.exit_code == 0, f"curl failed: {r.stderr}" + assert r.response["status"] == 200 + + # Same check without SNI — should also be allowed under 'authonly' + # when only the certificate differs. + def test_ssl_005_11(self, env): + alt_host = f"test2.{env.http_tld}" + r = env.run([ + 'openssl', 's_client', + '-connect', f'127.0.0.1:{env.https_port}', + '-CAfile', env.ca.cert_file, + '-noservername', + '-tls1_3', + '-ign_eof', + ], intext=( + f"GET /index.html HTTP/1.1\r\n" + f"Host: {alt_host}\r\n" + f"Connection: close\r\n" + f"\r\n" + )) + assert r.exit_code == 0, f"openssl s_client failed: {r.stderr}" + assert re.search(r'HTTP/1\.[01] 200', r.stdout), \ + f"expected 200 in response, got: {r.stdout[:500]}" + + +class TestVhostCompatAuthonlyAuth: + """Under 'authonly' SSLVHostSNIPolicy, vhosts with different client + authentication settings (different SSLCACertificateFile) must still + be blocked — this is the core CVE-2025-23048 scenario.""" + + @pytest.fixture(autouse=True, scope='class') + def _class_scope(self, env, request): + has_policy = env.has_directive("SSLVHostSNIPolicy") + if not has_policy: + request.applymarker(pytest.mark.xfail( + reason="SSLVHostSNIPolicy not available", strict=False)) + alt_domain = f"test2.{env.http_tld}" + + sub_ca = env.ca.issue_cert( + CertificateSpec(name="ssl005-subca")) + sub_ca_file = os.path.join(env.server_dir, "ssl005-subca.pem") + sub_ca.save_cert_pem(sub_ca_file) + + for name in ("test1", "test2"): + doc = os.path.join(env.server_dir, "htdocs", name) + os.makedirs(doc, exist_ok=True) + with open(os.path.join(doc, "index.html"), "w") as f: + f.write(f"vhost {name}\n") + + extras = { + f"test1.{env.http_tld}": f""" + LogLevel ssl:debug + SSLVerifyClient optional + SSLCACertificateFile "{env.ca.cert_file}" + """, + alt_domain: f""" + LogLevel ssl:debug + SSLVerifyClient optional + SSLCACertificateFile "{sub_ca_file}" + """, + } + if has_policy: + extras['base'] = 'SSLVHostSNIPolicy authonly' + conf = HttpdConf(env, extras=extras) + conf.add_vhost_test1() + conf.start_vhost(domains=[alt_domain], + port=env.https_port, + doc_root="htdocs/test2") + conf.end_vhost() + conf.install() + assert env.apache_restart() == 0 + + env.httpd_error_log.add_ignored_lognos(["AH02032"]) + + # Even under 'authonly', different SSLCACertificateFile between + # vhosts must return 421. + def test_ssl_005_20(self, env): + url = env.mkurl("https", "test1", "/index.html") + alt_host = f"test2.{env.http_tld}" + r = env.curl_get(url, options=[ + "--http1.1", "-H", f"Host: {alt_host}", + ]) + assert r.exit_code == 0, f"curl failed: {r.stderr}" + assert r.response["status"] == 421 + + # Without SNI, same scenario — must also be 421. + def test_ssl_005_21(self, env): + alt_host = f"test2.{env.http_tld}" + r = env.run([ + 'openssl', 's_client', + '-connect', f'127.0.0.1:{env.https_port}', + '-CAfile', env.ca.cert_file, + '-noservername', + '-tls1_3', + '-ign_eof', + ], intext=( + f"GET /index.html HTTP/1.1\r\n" + f"Host: {alt_host}\r\n" + f"Connection: close\r\n" + f"\r\n" + )) + assert r.exit_code == 0, f"openssl s_client failed: {r.stderr}" + assert re.search(r'HTTP/1\.[01] 421', r.stdout), \ + f"expected 421 in response, got: {r.stdout[:500]}" diff -Nru apache2-2.4.69/test/modules/ssl/test_006_ct_shutdown.py apache2-2.4.71/test/modules/ssl/test_006_ct_shutdown.py --- apache2-2.4.69/test/modules/ssl/test_006_ct_shutdown.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/modules/ssl/test_006_ct_shutdown.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,30 @@ +"""SSL-CT must join its service thread before APR destroys the child pool.""" +import os +import time + +from pyhttpd.conf import HttpdConf + + +def test_ssl_ct_clean_shutdown(env): + env.require(env.has_shared_module("ssl_ct"), "mod_ssl_ct was not built", + dep="mod_ssl_ct", detected=f"no mod_ssl_ct.so in {env.libexec_dir}") + assert env.apache_stop() == 0 + scts = os.path.join(env.server_dir, "scts") + os.makedirs(scts, exist_ok=True) + conf = HttpdConf(env) + if "ssl_ct" not in env.static_modules(): + conf.add(f'LoadModule ssl_ct_module "{env.libexec_dir}/mod_ssl_ct.so"') + conf.add([ + f'CTSCTStorage "{scts}"', + "LogLevel ssl_ct:debug", + ]) + conf.add_vhost_test1() + conf.install() + assert env.apache_restart() == 0, env.apachectl_stderr + # The service thread checks MPM state once per second. Stop while it is + # asleep: in an APR pool-debug build, the join from pchild's pre-cleanup + # deadlocked with apr_thread_exit() while the thread's pool shared + # pchild's mutex. Keep shutdown warnings visible to the fixtures. + time.sleep(0.1) + assert env.apache_stop() == 0 + env.check_error_log() diff -Nru apache2-2.4.69/test/pyhttpd/backport.py apache2-2.4.71/test/pyhttpd/backport.py --- apache2-2.4.69/test/pyhttpd/backport.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/backport.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,90 @@ +"""Marks for tests of changes the httpd under test does not have yet. + +Trunk (2.5) gets a fix and its test first; the test is then carried over to +2.4.x before, or without, the fix. On 2.4.x such a test is an expected +failure, or has to be skipped outright when failing means hanging, crashing +the server or leaving errors in the error log. On trunk the marks do nothing. + +When the change is backported, add backported_in, the 2.4.x version it first +appears in, on trunk and copy the file over; older 2.4.x releases keep the +expectation. + + pytestmark = not_backported("r1939033", "nonce-count validation") + + @skip_not_backported("r1938487", "recursion limit in mod_sed", + backported_in="2.4.71") + def test_... + +platform limits the expectation to one sys.platform, for a failure on 2.4.x +which trunk does not have there. + +The marks only record their arguments; pyhttpd.gating applies them at +collection, against the httpd that pyhttpd.build found. Both suites use these. +""" + +import re +import sys + +import pytest + + +def _version_tuple(version) -> tuple: + if isinstance(version, tuple): + return version + (0,) * (3 - len(version)) + m = re.match(r"(\d+)\.(\d+)(?:\.(\d+))?", str(version)) + if not m: + raise ValueError(f"cannot parse httpd version {version!r}") + return int(m.group(1)), int(m.group(2)), int(m.group(3) or 0) + + +def has_change(server_version, backported_in: str = None) -> bool: + """Whether a server of this version has a trunk change: trunk always + does, 2.4.x only from the version it was backported in. A -dev suffix + counts as the release, a backport lands while the tree is -dev. + + *server_version* is a string ("2.4.70-dev") or a (major, minor, patch) + tuple.""" + version = _version_tuple(server_version) + if version >= (2, 5, 0): + return True + return backported_in is not None and version >= _version_tuple(backported_in) + + +def lacks_change(server_version, backported_in: str = None, + platform: str = None) -> bool: + """Whether the expectation of a not_backported mark holds here: the + server lacks the change, and *platform* (if given) is this one.""" + return (not has_change(server_version, backported_in) + and (platform is None or sys.platform == platform)) + + +def reason(rev: str, what: str, platform: str = None) -> str: + return f"{what} ({rev}) not backported" + (f" [{platform}]" if platform else "") + + +def _mark(name: str, rev: str, what: str, backported_in, platform): + kwargs = {k: v for k, v in + (("backported_in", backported_in), ("platform", platform)) + if v is not None} + return getattr(pytest.mark, name)(rev, what, **kwargs) + + +def not_backported(rev: str, what: str, backported_in: str = None, + platform: str = None): + """Expected to fail until *what* (trunk change *rev*) is backported. + + Only a failure of the test function counts; an error in the setup or + teardown of its fixtures is reported as an error. Mark only the tests + (or parameters, via pytest.param(..., marks=...)) that fail without the + change: on the others the mark would hide a regression. + + Not strict: a test that happens to pass is reported as XPASS.""" + return _mark("not_backported", rev, what, backported_in, platform) + + +def skip_not_backported(rev: str, what: str, backported_in: str = None, + platform: str = None): + """Not run until *what* (trunk change *rev*) is backported. For tests + which would hang, crash the server, or fail the error log check in their + teardown, none of which an xfail contains.""" + return _mark("skip_not_backported", rev, what, backported_in, platform) diff -Nru apache2-2.4.69/test/pyhttpd/build-win.bat apache2-2.4.71/test/pyhttpd/build-win.bat --- apache2-2.4.69/test/pyhttpd/build-win.bat 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/build-win.bat 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,293 @@ +@ECHO off +@REM +@REM Build script for Apache httpd on Windows using CMake + vcpkg. +@REM +@REM This script bootstraps a complete build environment from scratch: +@REM - Installs PortableGit if git is not available +@REM - Installs Visual Studio 2022 Build Tools (cl, cmake, nmake) if not found +@REM - Installs dependencies (apr, openssl, curl, etc.) via vcpkg +@REM - Clones the httpd source if not already in the source tree +@REM - Builds and installs httpd +@REM +@REM Usage: build-win.bat [repo_url] [branch] +@REM +@REM repo_url Git repository URL (default: https://github.com/apache/httpd.git) +@REM branch Git branch to clone (default: trunk) +@REM +@REM If run from within the httpd source tree, uses that source directly. +@REM Otherwise clones the repo into %USERPROFILE%\httpd-trunk. +@REM +@REM Examples: +@REM build-win.bat +@REM build-win.bat https://github.com/myfork/httpd.git +@REM build-win.bat https://github.com/myfork/httpd.git my-branch +@REM build-win.bat https://github.com/apache/httpd.git 2.4.x +@REM + +@REM ------------------------------------- +@REM Make sure the PATH is limited, but only +@REM if build tools are not already available +@REM ------------------------------------- +where cl.exe >nul 2>&1 && where cmake.exe >nul 2>&1 && where nmake.exe >nul 2>&1 +if %ERRORLEVEL% EQU 0 goto :path_ok + @REM Find powershell.exe while PATH still has it, before we reset + SET "POWERSHELL_DIR=" + for /f "delims=" %%i in ('where powershell.exe 2^>nul') do ( + if not defined POWERSHELL_DIR set "POWERSHELL_DIR=%%~dpi" + ) + set "PATH=C:\Windows\system32;C:\Windows" + if defined POWERSHELL_DIR set "PATH=%PATH%;%POWERSHELL_DIR%" +:path_ok + +@REM ------------------------------------- +@REM check for git otherwise install it +@REM ------------------------------------- +where git.exe >nul 2>&1 +if %ERRORLEVEL% EQU 0 goto :git_ok + +@REM git not in PATH, check if PortableGit exists but PATH is missing it +if EXIST "%USERPROFILE%\PortableGit\bin\git.exe" ( + echo "PortableGit found, adding to PATH..." + SET "PATH=%USERPROFILE%\PortableGit\bin;%PATH%" + goto :git_ok +) + +@REM PortableGit not installed, download and install it +echo "git.exe not found, installing PortableGit..." +curl -L -o git-portable.exe "https://github.com/git-for-windows/git/releases/download/v2.48.1.windows.1/PortableGit-2.48.1-64-bit.7z.exe" +git-portable.exe -y -o"%USERPROFILE%\PortableGit" +if NOT EXIST "%USERPROFILE%\PortableGit\bin\git.exe" ( + echo "ERROR: PortableGit installation failed." + exit /b 1 +) +SET "PATH=%USERPROFILE%\PortableGit\bin;%PATH%" +echo "PortableGit installed successfully." + +:git_ok + +@REM ------------------------------------- +@REM Variables used in this script +@REM ------------------------------------- +@REM httpd git repo URL and branch: use arguments or defaults +SET "HTTPD_REPO=%~1" +if "%HTTPD_REPO%"=="" SET "HTTPD_REPO=https://github.com/apache/httpd.git" +SET "HTTPD_BRANCH=%~2" +if "%HTTPD_BRANCH%"=="" SET "HTTPD_BRANCH=trunk" + +@REM Find httpd source: try script location first, then fall back to clone +SET "SCRIPT_DIR=%~dp0" +for %%i in ("%SCRIPT_DIR%..\..") do SET "CANDIDATE=%%~fi" +if EXIST "%CANDIDATE%\include\httpd.h" ( + SET "HTTPD_SRC=%CANDIDATE%" + echo "Using existing httpd source in %HTTPD_SRC%" + goto :src_ok +) +SET "HTTPD_SRC=%USERPROFILE%\httpd-trunk" +if EXIST "%HTTPD_SRC%\include\httpd.h" ( + echo "Using existing httpd source in %HTTPD_SRC%" + if EXIST "%HTTPD_SRC%\.git" ( + echo "Updating %HTTPD_SRC%" + git -C "%HTTPD_SRC%" pull + ) + goto :src_ok +) +if EXIST "%HTTPD_SRC%\.git" ( + echo "Updating incomplete httpd clone in %HTTPD_SRC%..." + git -C "%HTTPD_SRC%" pull +) else ( + echo "Cloning httpd source in %HTTPD_SRC%..." + git clone -b "%HTTPD_BRANCH%" "%HTTPD_REPO%" "%HTTPD_SRC%" +) +if NOT EXIST "%HTTPD_SRC%\include\httpd.h" ( + echo "ERROR: httpd source not found after clone." + exit /b 1 +) +cd /d "%HTTPD_SRC%\test\pyhttpd" +:src_ok + +SET CWD=%CD% +SET "CWD=%CWD:\=/%" +@REM ------------------------------------- +SET BUILD_TYPE="Release" +SET VCPKG_ROOT=%CWD%\vcpkg +SET VCPKG_TRIPLET=x64-windows-release +SET VCPKG_DIRECTORY="vcpkg\installed\%VCPKG_TRIPLET%" +SET VCPKG_DIRECTORY_LIB=%VCPKG_DIRECTORY% +SET GENERATOR="NMake Makefiles" +@REM SET GENERATOR="Ninja" +SET CMAKE_VERSION="4.0" +SET HTTPD_INSTALL_DIRECTORY="C:/Users/%USERNAME%/Projects/http-debug/apache" + +@REM ------------------------------------- +@REM Check for MS build tools cl.exe cmake etc. +@REM ------------------------------------- +where cl.exe >nul 2>&1 && where cmake.exe >nul 2>&1 && where nmake.exe >nul 2>&1 +if %ERRORLEVEL% EQU 0 ( + echo "cl.exe, cmake.exe and nmake.exe already in PATH, skipping vcvars64.bat" + SET "VCVARS_DONE=1" + goto :tools_ok +) + +SET "VCVARS_DONE=0" +if EXIST "C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvars64.bat" ( + echo "found1: Professional edition" + SET "VCVARS_BAT=C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvars64.bat" + goto :tools_ok +) +if EXIST "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" ( + echo "found2: Community edition" + SET "VCVARS_BAT=C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" + goto :tools_ok +) +if EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat" ( + echo "found3: Build Tools edition" + SET "VCVARS_BAT=C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat" + goto :tools_ok +) + +@REM None found - tell the user to install Visual Studio Build Tools +echo "ERROR: No Visual Studio 2022 build tools found." +echo "Please install one of:" +echo " - Visual Studio 2022 Professional" +echo " - Visual Studio 2022 Community" +echo " - Visual Studio 2022 Build Tools" +echo "from https://visualstudio.microsoft.com/downloads/" + +@REM ------------------------------------- +@REM try to downlaod and install it. +@REM ------------------------------------- + +curl -L -o vs_buildtools.exe "https://aka.ms/vs/17/release/vs_buildtools.exe" +vs_buildtools.exe --quiet --wait --norestart --nocache ^ + --add Microsoft.VisualStudio.Workload.VCTools ^ + --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 ^ + --add Microsoft.VisualStudio.Component.Windows11SDK.22621 ^ + --includeRecommended + +@REM After install, check again for BuildTools vcvars64.bat +if NOT EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat" ( + echo "ERROR: Build Tools installation failed or vcvars64.bat not found." + exit /b 1 +) +echo "found3: Build Tools edition (just installed)" +call "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat" + +@REM Verify cl.exe, cmake.exe and nmake.exe are now available +where cl.exe >nul 2>&1 +if %ERRORLEVEL% NEQ 0 ( + echo "ERROR: cl.exe still not found after installing Build Tools." + exit /b 1 +) +where cmake.exe >nul 2>&1 +if %ERRORLEVEL% NEQ 0 ( + echo "ERROR: cmake.exe not found. Please install CMake." + exit /b 1 +) +where nmake.exe >nul 2>&1 +if %ERRORLEVEL% NEQ 0 ( + echo "ERROR: nmake.exe not found after installing Build Tools." + exit /b 1 +) +SET "VCVARS_DONE=1" + +:tools_ok + +@REM ------------------------------------- +@REM Setup build env using VS build 2022 +@REM ------------------------------------- +if "%VCVARS_DONE%" == "1" ( + echo "Build environment already configured, skipping vcvars64.bat" +) else ( + call "%VCVARS_BAT%" +) + +where cl.exe >nul 2>&1 +if %ERRORLEVEL% NEQ 0 ( + echo "ERROR: cl.exe still not found after installing Build Tools." + exit /b 1 +) +where cmake.exe >nul 2>&1 +if %ERRORLEVEL% NEQ 0 ( + echo "ERROR: cmake.exe not found in PATH. Please install CMake." + exit /b 1 +) +where nmake.exe >nul 2>&1 +if %ERRORLEVEL% NEQ 0 ( + echo "ERROR: nmake.exe not found in PATH." + exit /b 1 +) + +@REM ------------------------------------- +@REM Installs via MS vcpkg manager all the +@REM packages needed to build httpd +@REM ------------------------------------- +if NOT EXIST vcpkg\ ( + echo "Adding...." + git submodule add --force https://github.com/microsoft/vcpkg.git vcpkg + PUSHD vcpkg\ + CALL bootstrap-vcpkg.bat -disableMetrics +) else ( + echo "Updating...." + git submodule update --remote --merge vcpkg\ + PUSHD vcpkg\ +) + +@REM For some reason using the manifest doesn't install the default-features +vcpkg.exe install --triplet %VCPKG_TRIPLET% apr[private-headers] apr-util pcre2 openssl nghttp2 curl libxml2 jansson + +POPD + +@REM ------------------------------------- +@REM Create a temp dir for cmake files +@REM ------------------------------------- +if not EXIST build-asf-win\ ( + mkdir build-asf-win\ +) else ( + rmdir /s /q build-asf-win\ + mkdir build-asf-win\ +) + +PUSHD build-asf-win\ + +@REM ------------------------------------- +@REM Run cmake so that it configures the +@REM and creates the needed makefiles +@REM ------------------------------------- +cmake "%HTTPD_SRC%" -B . ^ + -G %GENERATOR% ^ + -DCMAKE_BUILD_TYPE=%BUILD_TYPE% ^ + -DCMAKE_TOOLCHAIN_FILE=%CWD%/vcpkg/scripts/buildsystems/vcpkg.cmake ^ + -DVCPKG_TARGET_TRIPLET=%VCPKG_TRIPLET% ^ + -DNGHTTP2_INCLUDE_DIR=%CWD%/vcpkg/installed/%VCPKG_TRIPLET%/include ^ + -DAPR_INCLUDE_DIR=%CWD%/vcpkg/installed/%VCPKG_TRIPLET%/include ^ + -DJANSSON_INCLUDE_DIR=%CWD%/vcpkg/installed/%VCPKG_TRIPLET%/include ^ + "-DAPR_LIBRARIES=%CWD%/vcpkg/installed/%VCPKG_TRIPLET%/lib/libapr-1.lib;%CWD%/vcpkg/installed/%VCPKG_TRIPLET%/lib/libaprutil-1.lib" ^ + -DNGHTTP2_LIBRARIES=%CWD%/vcpkg/installed/%VCPKG_TRIPLET%/lib/nghttp2.lib ^ + -DJANSSON_LIBRARIES=%CWD%/vcpkg/installed/%VCPKG_TRIPLET%/lib/jansson.lib ^ + -DENABLE_MIME_MAGIC=A ^ + -DBUILD_TEST_MODULES=true ^ + -DCMAKE_POLICY_VERSION_MINIMUM=%CMAKE_VERSION% ^ + --install-prefix %HTTPD_INSTALL_DIRECTORY% + +@REM ------------------------------------- +@REM Build httpd +@REM ------------------------------------- +cmake --build . + +@REM ------------------------------------- +@REM Install httpd only +@REM ------------------------------------- +cmake --install . --config %BUILD_TYPE% +POPD + +@REM ------------------------------------- +@REM Copy the dependencies. +@REM ------------------------------------- +if %BUILD_TYPE% == "Debug" ( + SET VCPKG_DIRECTORY_LIB=%VCPKG_DIRECTORY_LIB%\debug +) + +@REM ------------------------------------- +@REM Copy the libraries needed to run httpd +@REM ------------------------------------- +xcopy %VCPKG_DIRECTORY_LIB%\bin\* %HTTPD_INSTALL_DIRECTORY%\bin /y /f /i diff -Nru apache2-2.4.69/test/pyhttpd/build.py apache2-2.4.71/test/pyhttpd/build.py --- apache2-2.4.69/test/pyhttpd/build.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/build.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,514 @@ +"""Build discovery: which httpd is under test, and what it can do. + +One place answers that for both suites. ``resolve_build(config)`` finds the +httpd from, in order of precedence, ``--apxs``, ``--httpd`` (+ ``--conf``), or +the ``config.ini`` that the autoconf build generates for the modules/ suite, +probes it once per session, and returns an :class:`HttpdInfo`. The collection +time gating in :mod:`pyhttpd.gating` and the report header read it; the +classic framework builds its configuration on the same object. + +The probing mirrors the discovery logic in Apache::TestConfigParse (perl-apache-test): +run ``httpd -v`` / ``-V`` / ``-l`` and parse the inherited install ``httpd.conf`` +to learn the server version, MPM, compile-time ``-D`` defines, and the full set +of available modules (static + dynamically loaded). + +The merged module set drives ```` blocks, the module-name token +resolution in :mod:`apache_pytest.config`, and (eventually) the ``need_module`` +test marks. +""" + +from __future__ import annotations + +import configparser +import os +import re +import subprocess +import sys +from dataclasses import dataclass, field +from pathlib import Path + +# httpd -V emits lines like: -D APACHE_MPM_DIR="server/mpm/prefork" +_DEFINE_RE = re.compile(r"^\s*-D\s+([A-Z0-9_]+)(?:=(.*))?\s*$") +# httpd -l / module conf lines end in ".c"; -l indents them two spaces. +_STATIC_MOD_RE = re.compile(r"^\s*(\w+\.c)\s*$") +# LoadModule foo_module modules/mod_foo.so (optionally commented with leading #, +# the file optionally quoted) +_LOADMODULE_RE = re.compile(r'^\s*(#?)\s*LoadModule\s+(\S+)\s+(?:"([^"]+)"|(\S+))', + re.IGNORECASE) +# Include conf/extra/*.conf, IncludeOptional "conf.d/*.conf" +_INCLUDE_RE = re.compile(r'^\s*Include(?:Optional)?\s+(?:"([^"]+)"|(\S+))', + re.IGNORECASE) +_VERSION_RE = re.compile(r"Apache/(\d+)\.(\d+)\.(\d+)") + + +@dataclass +class LoadDirective: + """One ``LoadModule symbol file`` mapping from an install conf.""" + + symbol: str # e.g. "session_module" + so: str # path as written, e.g. "modules/mod_session.so" + active: bool # False if the line was commented out + + @property + def cname(self) -> str: + """Module source name, e.g. "mod_session.c" (used as the modules-set key).""" + stem = Path(self.so).name + for suffix in (".so", ".dll"): + if stem.endswith(suffix): + stem = stem[: -len(suffix)] + break + if stem.startswith("lib"): # libphp.so -> mod_php.c + stem = "mod_" + stem[3:] + return f"{stem}.c" + + @property + def is_mpm(self) -> bool: + return self.symbol.startswith("mpm_") or "mpm_" in Path(self.so).name + + +@dataclass +class HttpdInfo: + """Everything probed from an httpd binary + its inherited config.""" + + httpd: Path + version: tuple[int, int, int] + mpm: str + defines: dict[str, str | bool] = field(default_factory=dict) + # Set of module source-file names, e.g. {"core.c", "mod_proxy.c", ...}. + # This is the union of statically-compiled modules and the modules the + # inherited httpd.conf loads (active LoadModule lines) — matching + # Apache::Test's $self->{modules} hash keyed on ".c". Membership here + # means "the classic suite's server has this module", which is what drives + # the blocks and the need_module marks. (The modules/ suite + # writes its own LoadModule lines and asks HttpdTestEnv.has_shared_module.) + modules: set[str] = field(default_factory=set) + # Every LoadModule directive discovered in the inherited conf, with absolute + # .so paths. Re-emitted into the generated httpd.conf so the test server + # actually loads them (the generated conf does not Include the install conf). + load_directives: list[LoadDirective] = field(default_factory=list) + # TAKE1 directives inherited from the install conf (wanted_config in + # TestConfigParse.pm:34): ServerAdmin, TypesConfig, DocumentRoot, ServerRoot. + # Keyed by directive name (as written), value is the single argument. + inherited: dict[str, str] = field(default_factory=dict) + # Modules compiled into the binary (``httpd -l``), e.g. {"mod_so.c"}. + static_modules: set[str] = field(default_factory=set) + # Where the build was found: "apxs", "httpd" or "config.ini". + source: str = "" + # Install prefix, if known. + prefix: Path | None = None + # apxs of the build, if known: builds the test modules. + apxs: Path | None = None + # The install httpd.conf the LoadModule lines were inherited from. + inherited_conf: Path | None = None + # Where the build's shared modules are (apxs LIBEXECDIR). + libexecdir: Path | None = None + # Extra -D defines given with --defines. + extra_defines: list[str] = field(default_factory=list) + + @property + def version_str(self) -> str: + return ".".join(str(p) for p in self.version) + + def has_module(self, name: str) -> bool: + """True if module ``name`` is available. + + Accepts every form the Perl tests use: a bare name (``status``), a + ``mod_``-prefixed name (``mod_status``), or any of those with a ``.c``/ + ``.so`` suffix. Modules are stored as source names (``mod_status.c``), + so a bare name is also tried with the ``mod_`` prefix -- matching + Apache::Test's need_module/have_module, which accept ``need_module 'status'``. + """ + stem = name + for suffix in (".c", ".so"): + if stem.endswith(suffix): + stem = stem[: -len(suffix)] + break + candidates = {f"{stem}.c"} + if not stem.startswith("mod_"): + candidates.add(f"mod_{stem}.c") + return bool(candidates & self.modules) + + def version_at_least(self, major: int, minor: int, patch: int = 0) -> bool: + return self.version >= (major, minor, patch) + + +def _run(cmd: list[str]) -> str: + """Run a probe command, returning combined stdout (httpd prints to stdout).""" + proc = subprocess.run( # noqa: S603 - trusted, caller-provided httpd path + cmd, + capture_output=True, + text=True, + check=False, + ) + # httpd -V can exit non-zero if it wants a config; we only need the banner. + return proc.stdout + proc.stderr + + +def probe_version(httpd: Path) -> tuple[int, int, int]: + out = _run([str(httpd), "-v"]) + m = _VERSION_RE.search(out) + if not m: + raise RuntimeError(f"could not parse version from `{httpd} -v`:\n{out}") + return (int(m.group(1)), int(m.group(2)), int(m.group(3))) + + +def probe_defines_and_mpm(httpd: Path) -> tuple[dict[str, str | bool], str]: + """Parse ``httpd -V`` for ``-D`` defines, server MPM, and module magic info.""" + out = _run([str(httpd), "-V"]) + defines: dict[str, str | bool] = {} + mpm = "" + for line in out.splitlines(): + m = _DEFINE_RE.match(line) + if m: + key, val = m.group(1), m.group(2) + defines[key] = val.strip('"') if val else True + continue + # "Server MPM: event" (2.4+/2.5) + sm = re.match(r"\s*Server MPM:\s*(.+)", line, re.IGNORECASE) + if sm: + mpm = sm.group(1).strip().lower() + if not mpm: + # 2.0-era fallback: derive from APACHE_MPM_DIR=server/mpm/ + mpm_dir = defines.get("APACHE_MPM_DIR") + if isinstance(mpm_dir, str): + mpm = Path(mpm_dir).name.lower() + return defines, mpm + + +def probe_static_modules(httpd: Path) -> set[str]: + """Modules compiled statically into the binary (``httpd -l``).""" + out = _run([str(httpd), "-l"]) + mods: set[str] = set() + for line in out.splitlines(): + m = _STATIC_MOD_RE.match(line) + if m: + mods.add(m.group(1)) + return mods + + +def parse_loadmodules(conf: Path, server_root: Path, + _seen: set | None = None) -> list[LoadDirective]: + """Parse ``LoadModule`` lines (active and commented) from an install httpd.conf. + + The suite is built with ``--enable-load-all-modules`` upstream; locally the + stock install conf lists most modules commented out. We capture both so the + generated config can re-emit them and so module-presence checks succeed. + Relative ``.so`` paths are resolved against ``server_root`` (the install + prefix, ``apxs -q PREFIX``), and so are the files of active ``Include`` / + ``IncludeOptional`` lines, which are followed (distributions load their + modules from conf.d/ or mods-enabled/). Sections are not evaluated: a + LoadModule in an ```` counts as active. + """ + seen = set() if _seen is None else _seen + directives: list[LoadDirective] = [] + if not conf.is_file() or conf.resolve() in seen: + return directives + seen.add(conf.resolve()) + for raw in conf.read_text(errors="replace").splitlines(): + inc = _INCLUDE_RE.match(raw) + if inc: + pattern = Path(inc.group(1) or inc.group(2)) + if not pattern.is_absolute(): + pattern = server_root / pattern + if pattern.is_dir(): + pattern = pattern / "*" + for path in sorted(pattern.parent.glob(pattern.name)): + directives.extend(parse_loadmodules(path, server_root, seen)) + continue + m = _LOADMODULE_RE.match(raw) + if not m: + continue + commented, symbol, so = m.group(1), m.group(2), m.group(3) or m.group(4) + so_path = Path(so) + if not so_path.is_absolute(): + so_path = server_root / so_path + directives.append( + LoadDirective(symbol=symbol, so=str(so_path), active=(commented == "")) + ) + return directives + + +# TAKE1 directives inherited from the install conf (wanted_config TAKE1). +# ServerRoot is intentionally NOT applied (spec_apply maps it to a no-op so the +# test serverroot wins); we still record it for completeness but config.py +# ignores it. +_INHERIT_TAKE1 = ("ServerAdmin", "TypesConfig", "DocumentRoot", "ServerRoot") +_TAKE1_RE = re.compile( + r"^\s*(" + "|".join(_INHERIT_TAKE1) + r")\s+(.+?)\s*$" +) + + +def parse_inherited_take1(conf: Path) -> dict[str, str]: + """Parse last-wins TAKE1 directives from an install conf (wanted_config).""" + found: dict[str, str] = {} + if not conf.is_file(): + return found + for raw in conf.read_text(errors="replace").splitlines(): + if raw.lstrip().startswith("#"): + continue + m = _TAKE1_RE.match(raw) + if m: + found[m.group(1)] = m.group(2).strip().strip('"') + return found + + +def probe( + httpd: Path, + inherited_conf: Path | None = None, + server_root: Path | None = None, +) -> HttpdInfo: + """Full probe of an httpd binary and (optionally) its install config. + + :param httpd: path to the httpd executable. + :param inherited_conf: install ``httpd.conf`` to scan for LoadModule lines. + Typically ``/conf/httpd.conf`` derived from ``apxs -q SYSCONFDIR``. + :param server_root: install prefix used to resolve relative ``.so`` paths + (``apxs -q PREFIX``). Defaults to the parent of ``inherited_conf``'s dir. + """ + version = probe_version(httpd) + defines, mpm = probe_defines_and_mpm(httpd) + static_modules = probe_static_modules(httpd) + modules = set(static_modules) + load_directives: list[LoadDirective] = [] + inherited: dict[str, str] = {} + if inherited_conf is not None: + root = server_root or inherited_conf.parent.parent + inherited = parse_inherited_take1(inherited_conf) + all_directives = parse_loadmodules(inherited_conf, root) + # Inherit only ACTIVE LoadModule lines, matching Apache::Test's + # inherit_load_module (which parses the live config, not commented + # lines). A build intended for the suite activates all modules + # (--enable-load-all-modules); locally the install conf is activated to + # match. Commented lines are ignored entirely. + # (once each: httpd itself warns about loading a module twice) + symbols: set[str] = set() + load_directives = [d for d in all_directives if d.active + and not (d.symbol in symbols or symbols.add(d.symbol))] + for d in load_directives: + if Path(d.so).exists(): + modules.add(d.cname) + return HttpdInfo( + httpd=httpd, + version=version, + mpm=mpm, + defines=defines, + modules=modules, + load_directives=load_directives, + inherited=inherited, + static_modules=static_modules, + inherited_conf=inherited_conf, + ) + + +# --------------------------------------------------------------------------- # +# Finding the build under test +# --------------------------------------------------------------------------- # + + +class NoServerError(Exception): + """Raised when no httpd could be located from the options or config.ini. + + A plain exception, not pytest.fail, so the collection time callers can + catch it without turning into an INTERNALERROR. + """ + + +class TargetError(NoServerError): + """An httpd was named with --apxs or --httpd, and it is not usable. + + Never answered with another build: testing something other than what + was asked for is worse than not testing. + """ + + +def apxs_query(apxs: Path, var: str) -> str: + proc = subprocess.run( # noqa: S603 - trusted path + ["perl", str(apxs), "-q", var], capture_output=True, text=True, check=True + ) + return proc.stdout.strip() + + +_apxs_cache: dict[str, dict[str, str]] = {} + + +def _apxs_vars(apxs: Path) -> dict[str, str]: + """The apxs variables that locate a build; TargetError if apxs is unusable.""" + if str(apxs) in _apxs_cache: + return _apxs_cache[str(apxs)] + if not apxs.is_file(): + raise TargetError(f"apxs {apxs}: no such file") + found = {} + for var in ("SBINDIR", "SYSCONFDIR", "PREFIX", "LIBEXECDIR"): + try: + found[var] = apxs_query(apxs, var) + except (OSError, subprocess.CalledProcessError) as exc: + detail = getattr(exc, "stderr", None) or exc + raise TargetError(f"apxs {apxs}: 'apxs -q {var}' failed: {detail}") from exc + _apxs_cache[str(apxs)] = found + return found + + +def resolve_paths( + config, +) -> tuple[Path, Path | None, Path | None, Path | None, list[str]]: + """Resolve (httpd, apxs, inherited_conf, install_prefix, defines) from options. + + --httpd, --conf and --prefix win over what --apxs reports. Raises + :class:`NoServerError` if neither --httpd nor --apxs is given, and + :class:`TargetError` if what was given is not usable. + """ + apxs_opt = config.getoption("--apxs") + httpd_opt = config.getoption("--httpd") + defines = [d for d in config.getoption("--defines").split() if d] + + apxs = Path(apxs_opt) if apxs_opt else None + inherited_conf: Path | None = None + install_prefix: Path | None = None + if apxs is not None: + found = _apxs_vars(apxs) + install_prefix = Path(found["PREFIX"]) + inherited_conf = Path(found["SYSCONFDIR"]) / "httpd.conf" + if httpd_opt is None: + httpd_opt = str(Path(found["SBINDIR"]) / "httpd") + + conf_opt = config.getoption("--conf") + prefix_opt = config.getoption("--prefix") + if conf_opt is not None: + inherited_conf = Path(conf_opt) + if prefix_opt is not None: + install_prefix = Path(prefix_opt) + elif inherited_conf is not None and install_prefix is None: + install_prefix = inherited_conf.parent.parent + + if httpd_opt is None: + raise NoServerError("must pass --httpd or --apxs") + if not Path(httpd_opt).is_file(): + given = "--httpd" if config.getoption("--httpd") else f"the httpd of apxs {apxs}" + raise TargetError(f"{given} {httpd_opt}: no such file") + if inherited_conf is not None and not inherited_conf.is_file(): + given = "--conf" if conf_opt is not None else f"the httpd.conf of apxs {apxs}" + raise TargetError(f"{given} {inherited_conf}: no such file") + return Path(httpd_opt), apxs, inherited_conf, install_prefix, defines + + +def config_ini_path() -> Path: + """The autoconf generated config.ini of the modules/ suite.""" + return Path(os.getenv("PYHTTPD_CONFIG", Path(__file__).resolve().parent / "config.ini")) + + +def _probe_target(httpd: Path, apxs: Path | None, inherited_conf: Path | None, + prefix: Path | None, libexecdir: Path | None, + defines: list[str], source: str) -> HttpdInfo: + try: + info = probe(httpd, inherited_conf, prefix) + except (OSError, RuntimeError) as exc: + raise TargetError(f"{httpd}: {exc}") from exc + info.source = source + info.prefix = prefix + info.apxs = apxs + info.libexecdir = libexecdir + info.extra_defines = defines + return info + + +def _probe_options(config) -> HttpdInfo: + """The build named by --apxs / --httpd (+ --conf, --prefix).""" + httpd, apxs, inherited_conf, prefix, defines = resolve_paths(config) + if apxs is not None: + libexecdir = Path(_apxs_vars(apxs)["LIBEXECDIR"]) + else: + # /bin/httpd and /modules, the layout of Windows too + libexecdir = (prefix if prefix is not None + else httpd.resolve().parent.parent) / "modules" + return _probe_target(httpd, apxs, inherited_conf, prefix, libexecdir, defines, + "apxs" if apxs is not None else "httpd") + + +def _probe_config_ini(path: Path, defines: list[str]) -> HttpdInfo: + """Probe the build described by a generated config.ini. + + Through its apxs when it has one, exactly as if that had been passed with + --apxs, so both suites see the build the way they do in a + ``make check-all-pytest`` run. + """ + ini = configparser.ConfigParser(interpolation=configparser.ExtendedInterpolation()) + ini.read(path) + apxs: Path | None = Path(ini.get("global", "apxs", fallback="") or "") + if apxs is not None and apxs.name and apxs.is_file(): + found = _apxs_vars(apxs) + httpd = Path(found["SBINDIR"]) / "httpd" + conf = Path(found["SYSCONFDIR"]) / "httpd.conf" + prefix = Path(found["PREFIX"]) + libexecdir = Path(found["LIBEXECDIR"]) + else: + apxs = None + name = ini.get("httpd", "name", fallback="httpd") + if sys.platform == "win32": + httpd = Path(ini.get("global", "bindir")) / f"{name}.exe" + else: + httpd = Path(ini.get("global", "sbindir", + fallback=ini.get("global", "bindir"))) / name + prefix = Path(ini.get("global", "prefix")) + conf = prefix / "conf" / "httpd.conf" + libexecdir = Path(ini.get("global", "libexecdir", fallback=str(prefix / "modules"))) + if not httpd.is_file(): + raise NoServerError(f"{path}: no httpd binary at {httpd}") + return _probe_target(httpd, apxs, conf if conf.is_file() else None, prefix, + libexecdir, defines, "config.ini") + + +_BUILD_KEY = None +# The build of the session, for code without a pytest config to ask: an +# HttpdTestEnv() created at import time to evaluate a skipif condition. +_current: HttpdInfo | None = None +_why_none = "" + + +def current() -> HttpdInfo | None: + """The build resolve_build() found for this session; None before, or without.""" + return _current + + +def why_no_build() -> str: + """Why resolve_build() found no httpd, for the failure of a fixture needing one.""" + return _why_none or "no httpd: pass --apxs or --httpd, or configure the tree" + + +def resolve_build(config) -> HttpdInfo | None: + """Probe the httpd under test, once per session; None if there is none. + + Precedence: ``--apxs``, then ``--httpd`` (+ ``--conf``), then the + config.ini generated by the build. An httpd named on the command line + that is not usable raises :class:`TargetError`; it is never replaced by + the build of config.ini. None (nothing named, no usable config.ini) is + not an error here: callers that need a server fail with why_no_build(). + + pyhttpd.plugin calls this in pytest_configure, so that everything after + it - the HttpdTestEnv of modules/, collection time gating, the classic + suite's fixture, the report header - tests and gates the same build. + """ + global _BUILD_KEY, _current, _why_none # noqa: PLW0603 + import pytest + + if _BUILD_KEY is None: + _BUILD_KEY = pytest.StashKey[HttpdInfo | None]() + if _BUILD_KEY in config.stash: + return config.stash[_BUILD_KEY] + + info: HttpdInfo | None = None + _why_none = "" + if config.getoption("--apxs") or config.getoption("--httpd"): + info = _probe_options(config) + else: + ini = config_ini_path() + defines = [d for d in config.getoption("--defines").split() if d] + if ini.is_file(): + try: + info = _probe_config_ini(ini, defines) + except NoServerError as exc: + _why_none = f"no httpd: {exc}" + else: + _why_none = f"no httpd: pass --apxs or --httpd, or configure the tree ({ini})" + config.stash[_BUILD_KEY] = info + _current = info + return info diff -Nru apache2-2.4.69/test/pyhttpd/classic_errorlog.py apache2-2.4.71/test/pyhttpd/classic_errorlog.py --- apache2-2.4.69/test/pyhttpd/classic_errorlog.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/classic_errorlog.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,243 @@ +"""Error-log check for the classic (pytest_suite) tests. + +Like the modules/ suite, an unexpected error or warning in the httpd error log +fails the test that was running (as a teardown error), and a crash or a +sanitizer report always does. The classic tests share one long-running server +and many of them provoke errors on purpose, so what counts as expected is +declared here, not in the test files, which stay identical to upstream's +httpd-tests: + +* ``BACKGROUND_*`` -- logged by timers of the shared server (proxy health + checks of a dead backend, mod_proxy_beacon announcements), whatever test + happens to be running. Ignored everywhere. +* ``PER_FILE`` -- logged because a test file exercises that error path. + Ignored in that file only, by AH number, or by regex for lines without one. + +The log is read after each test and again when the file's last test is done. +A line logged just after a test returns is attributed to the next one, which +is why the lists are per file, not per test. What the server logs while it +starts, and while it stops after the last test (a child crashing on the way +out shows nowhere else), is checked by check_startup() and check_shutdown(), +which the classic suite's server fixture calls. + +``--classic-elog-report=FILE`` measures instead of checking: no ignores, no +failures, one JSON line per test with what it was attributed, plus a summary. +That is how the tables below were built. +""" +import json +import os +import re +from collections import Counter + +import pytest + +from .log import HttpdErrorLog + +# AH02683/AH02704: mod_ssl_ct's SCT maintenance daemon, every 30 seconds. Not +# built on macOS, found by a Linux run. +# AH01871: mod_optional_fn_export logs every request. At APLOG_DEBUG on trunk +# (git mirror c649960592) but at APLOG_ERR on a branch without that change, +# i.e. 2.4.x for now. +BACKGROUND_LOGNOS = ["AH10577", "AH10164", "AH10569", "AH02683", "AH02704", + "AH01871"] +BACKGROUND_MATCHES = [r".*\bHCOH: attempt to connect to .* failed"] + +_SSL_NOEXT =r".*SSL Library Error: error:0A000117:SSL routines::extension not received" + +# file name -> (AH numbers, regexes for lines without one) +PER_FILE = { + "test_CVE_2005_2700.py": (["AH10158"], [_SSL_NOEXT]), + "test_CVE_2008_2364.py": (["AH00898"], []), + "test_CVE_2009_1195.py": (["AH01228", "AH01374"], []), + "test_CVE_2019_0215.py": (["AH10158"], [_SSL_NOEXT]), + "test_aaa.py": (["AH01617", "AH01631", "AH01794", "AH01807"], []), + "test_acceptpathinfo.py": (["AH01266"], []), + "test_access.py": (["AH01797"], []), + "test_actions.py": (["AH01599"], []), + "test_alias.py": (["AH00674", "AH01264"], []), + "test_allowmethods.py": (["AH01623"], []), + "test_authz_core.py": (["AH01630", "AH01631"], []), + "test_basicauth.py": (["AH01618", "AH10096", "AH10158"], [_SSL_NOEXT]), + "test_cgi.py": (["AH01215", "AH01266", "AH10599"], [ + r".*End of script output before headers: bogus", + r".*malformed header from script 'bogus-sh.sh'"]), + # 400: test_dav_mswdv_null_deref on a server without r1939426 (2.4.x) + "test_dav.py": (["AH10484", "AH10619"], [ + r".*\[dav:error\].* \[(400|404|423), #0\]"]), + "test_deflate.py": (["AH01387", "AH01392"], []), + "test_digest.py": (["AH01786", "AH01794"], []), + "test_dir.py": (["AH01276", "AH01599"], []), + "test_expr.py": (["AH01630"], []), + "test_ext_filter.py": (["AH01270"], []), # LimitRequestBody 6: ENOSPC -> 413 + "test_fakeauth.py": (["AH10158"], [_SSL_NOEXT]), + "test_hostcheck.py": (["AH10157"], []), + "test_http_strict.py": (["AH00135", "AH01215", "AH02428", "AH02429", "AH02430"], [ + r".*malformed header from script 'send_hdr.pl'"]), + "test_include.py": ([ + "AH01228", "AH01329", "AH01330", "AH01337", "AH01346", "AH01352", + "AH01369", "AH01371", "AH01372", "AH01374", "AH01412"], [ + r".*\(20023\)The given path was above the root path:.*unable to include file", + r".*\[include:error\].* unable to include \""]), + "test_ldap.py": (["AH01617", "AH01631"], []), + "test_mergeslashes.py": (["AH01630"], []), + "test_negotiation.py": (["AH00690"], []), + "test_ocsp.py": (["AH03239", "AH10373"], []), + "test_pha.py": (["AH02018", "AH10158", "AH10228"], [_SSL_NOEXT]), + "test_proxy.py": (["AH02229"], []), + "test_proxy_balancer.py": ([ + "AH00957", "AH00959", "AH01114", "AH01170", "AH01171", "AH10187"], []), + "test_proxy_beacon.py": (["AH01170"], []), + "test_proxy_beacon_input.py": (["AH10187"], []), + "test_proxy_hcheck.py": (["AH10187"], []), + "test_remoteip.py": (["AH03499"], []), + "test_require.py": (["AH02229", "AH02262", "AH10158"], [_SSL_NOEXT]), + "test_rewrite.py": (["AH10410", "AH10411", "AH10508"], [ + r".*RewriteRule '.*' and URI '.*' exceeded maximum length"]), + "test_session.py": (["AH01817"], []), + "test_verify.py": (["AH02262", "AH10158"], [_SSL_NOEXT]), +} + +_state = {"log": None, "tests": 0, "with_lines": 0, "counts": Counter(), "samples": {}} +_RE_LOGNO = re.compile(r'\[(?P[^:\]]+):(?Perror|warn)\].* (?PAH\d+):') +_RE_LEVEL = re.compile(r'\[(?P[^:\]]+):(?Perror|warn)\]') + + +def pytest_addoption(parser): + parser.getgroup("httpd").addoption( + "--classic-elog-report", action="store", default=None, metavar="FILE", + help="instead of failing on unexpected errors/warnings in the error log " + "during classic tests, write what each test was attributed to FILE " + "(JSON lines, no ignores applied) and summarize it") + + +def pytest_configure(config): + path = config.getoption("--classic-elog-report", default=None) + if path: + open(path, "w").close() + + +def _key(line: str) -> str: + m = _RE_LOGNO.search(line) + if m: + return f"{m['mod']}:{m['level']} {m['logno']}" + m = _RE_LEVEL.search(line) + if m: + return f"{m['mod']}:{m['level']} (no AH number)" + return "other (crash/sanitizer)" + + +def _uses_server(item) -> bool: + return "framework" in getattr(item, "fixturenames", ()) + + +def _log_for(request) -> HttpdErrorLog: + if _state["log"] is None: + config = request.getfixturevalue("config") + # starts at the end of the log: server start-up is not a test's doing + _state["log"] = HttpdErrorLog(f"{config.vars['t_logs']}/error_log") + return _state["log"] + + +def _fail_on(errors, warnings, what): + if errors or warnings: + lines = "\n".join(l.rstrip() for l in (errors + warnings)[:20]) + more = len(errors) + len(warnings) - 20 + pytest.fail(f"apache logged {len(errors)} errors and {len(warnings)} warnings " + f"during {what}:\n{lines}" + (f"\n... and {more} more" if more > 0 else ""), + pytrace=False) + + +@pytest.fixture(autouse=True, scope="module") +def _classic_elog_module(request): + """Sets the module's ignores, and checks what is left when it is done.""" + module = request.module + if not any(_uses_server(i) and getattr(i, "module", None) is module + for i in request.session.items): + yield + return + report = request.config.getoption("--classic-elog-report", default=None) + log = _log_for(request) + log.clear_ignored_matches() + log.clear_ignored_lognos() + if not report: + nos, matches = PER_FILE.get(os.path.basename(module.__file__), ([], [])) + log.add_ignored_lognos(BACKGROUND_LOGNOS + nos) + log.add_ignored_matches(BACKGROUND_MATCHES + matches) + yield + if not report: + _fail_on(*log.get_missed(), f"the tests of {os.path.basename(module.__file__)}") + + +@pytest.fixture(autouse=True) +def _classic_elog(request): + if not _uses_server(request.node): + yield + return + path = request.config.getoption("--classic-elog-report", default=None) + log = _log_for(request) + yield + errors, warnings = log.get_missed() + if not path: + _fail_on(errors, warnings, "this test") + return + _state["tests"] += 1 + if not (errors or warnings): + return + _state["with_lines"] += 1 + for line in errors + warnings: + key = _key(line) + _state["counts"][key] += 1 + _state["samples"].setdefault(key, line.rstrip()) + with open(path, "a") as fd: + fd.write(json.dumps({"test": request.node.nodeid, + "errors": [l.rstrip() for l in errors], + "warnings": [l.rstrip() for l in warnings]}) + "\n") + + +def _check_window(config, log: HttpdErrorLog, what: str, warnings_count=True) -> None: + path = config.getoption("--classic-elog-report", default=None) + log.clear_ignored_matches() + log.clear_ignored_lognos() + if not path: + log.add_ignored_lognos(BACKGROUND_LOGNOS) + log.add_ignored_matches(BACKGROUND_MATCHES) + errors, warnings = log.get_missed() + if not path: + _fail_on(errors, warnings if warnings_count else [], what) + elif errors or warnings: + with open(path, "a") as fd: + fd.write(json.dumps({"test": f"<{what}>", + "errors": [l.rstrip() for l in errors], + "warnings": [l.rstrip() for l in warnings]}) + "\n") + + +def check_startup(config, error_log: str, since: int) -> None: + """Fail on errors (and crashes) the server logged from *since*, its log + size before it was started, up to now: its start. Call once it is + serving. Not on warnings: the shared t/conf makes httpd warn at every + start (a CRL without SSLCARevocationCheck, a revoked proxy client + certificate, SSLRandomSeed with OpenSSL 3, ...), and which ones depends + on the httpd and the TLS library.""" + log = HttpdErrorLog(error_log) + log.start_at(since) + _check_window(config, log, "the start of the server", warnings_count=False) + + +def check_shutdown(config) -> None: + """Fail on what the server logged after the last test was checked: its + shutdown. Call once it has stopped.""" + if _state["log"] is not None: + _check_window(config, _state["log"], "the shutdown of the server") + + +def pytest_terminal_summary(terminalreporter, config): + if not config.getoption("--classic-elog-report", default=None) or not _state["tests"]: + return + tr = terminalreporter + total = sum(_state["counts"].values()) + tr.section("classic error-log report (report only)") + tr.write_line(f"{_state['with_lines']} of {_state['tests']} classic tests were " + f"attributed {total} error/warning lines") + for key, n in _state["counts"].most_common(25): + tr.write_line(f"{n:6d} {key}") + tr.write_line(f" e.g. {_state['samples'][key][:200]}") diff -Nru apache2-2.4.69/test/pyhttpd/cmodules.py apache2-2.4.71/test/pyhttpd/cmodules.py --- apache2-2.4.69/test/pyhttpd/cmodules.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/cmodules.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,154 @@ +"""Build the C modules the tests load, with apxs. + +Both suites carry small modules that are not part of httpd: mod_aptest, +mod_h1test and mod_h2test of the modules/ suite, and the c-modules/ of +pytest_suite. They are built in place, ``mod_x.c`` in a directory of its own +giving ``.libs/mod_x.so``, and built once: next to the result goes a stamp +recording what the build depended on, and a module is rebuilt when + +* the .so is missing, +* a source or header of the module changed, +* the httpd it is built against changed: another apxs, or the same apxs after + the httpd it belongs to was rebuilt with another module magic number or + version (a module built for 2.4 is refused by 2.5 with "expected signature + 41503234 but saw 41503235"), or +* the defines or include directories differ. + +``--clean-modules`` removes the results, see clean_module_dir(). +""" + +import hashlib +import json +import logging +import os +import shutil +import subprocess +from pathlib import Path +from typing import Dict, Iterable, List, Optional, Sequence, Union + +log = logging.getLogger(__name__) + +LIB_SUBDIR = ".libs" # where apxs/libtool leave the result + +_include_fingerprints: Dict[str, str] = {} + + +def _sha256_files(paths: Iterable[Path]) -> str: + h = hashlib.sha256() + for p in sorted(paths): + h.update(p.name.encode()) + try: + h.update(p.read_bytes()) + except OSError: + h.update(b"") + return h.hexdigest() + + +def _apxs_command(apxs: Union[str, Path]) -> List[str]: + # apxs is a script; run it directly when it can be, as its shebang names + # the perl it was built for, else through the perl on the PATH. + apxs = str(apxs) + return [apxs] if os.access(apxs, os.X_OK) else ["perl", apxs] + + +def apxs_query(apxs: Union[str, Path], var: str) -> str: + proc = subprocess.run([*_apxs_command(apxs), "-q", var], + capture_output=True, text=True, check=True) + return proc.stdout.strip() + + +def httpd_fingerprint(apxs: Union[str, Path]) -> str: + """Identifies the httpd that modules built with *apxs* are made for.""" + key = os.path.realpath(str(apxs)) + if key not in _include_fingerprints: + try: + include_dir = Path(apxs_query(apxs, "INCLUDEDIR")) + sha = _sha256_files([include_dir / "ap_mmn.h", + include_dir / "ap_release.h"]) + except (OSError, subprocess.CalledProcessError): + sha = "unknown" + _include_fingerprints[key] = f"{key}:{sha}" + return _include_fingerprints[key] + + +def module_so(src_dir: Union[str, Path], name: str) -> Path: + return Path(src_dir) / LIB_SUBDIR / f"mod_{name}.so" + + +def _stamp_data(src_dir: Path, apxs, defines: Sequence[str], + include_dirs: Sequence[Path], extra_inputs: Sequence[Path]) -> dict: + sources = [p for p in src_dir.iterdir() + if p.is_file() and p.suffix in (".c", ".h")] + return { + "httpd": httpd_fingerprint(apxs), + "sources": _sha256_files(sources), + "extra": _sha256_files(extra_inputs), + "defines": list(defines), + "includes": [str(p) for p in include_dirs], + } + + +def ensure_built(src_dir: Union[str, Path], name: str, apxs: Union[str, Path], *, + defines: Sequence[str] = (), + include_dirs: Sequence[Union[str, Path]] = (), + extra_inputs: Sequence[Union[str, Path]] = (), + force: bool = False) -> Path: + """Build ``mod_.c`` in *src_dir* unless the result is up to date. + + :param defines: ``-D`` flags for the compile + :param include_dirs: ``-I`` directories + :param extra_inputs: further files the result depends on, e.g. a generated + header in one of *include_dirs* + :returns: the path of the .so + :raises RuntimeError: if apxs fails, with its output + """ + src_dir = Path(src_dir) + so = module_so(src_dir, name) + stamp_file = so.with_suffix(".stamp") + include_dirs = [Path(p) for p in include_dirs] + stamp = _stamp_data(src_dir, apxs, defines, include_dirs, + [Path(p) for p in extra_inputs]) + if not force and so.is_file() and stamp_file.is_file(): + try: + if json.loads(stamp_file.read_text()) == stamp: + return so + except (OSError, ValueError): + pass + cmd = _apxs_command(apxs) + for d in defines: + cmd += ["-D", d] + for d in include_dirs: + cmd += ["-I", str(d)] + cmd += ["-c", f"mod_{name}.c"] + log.debug("building mod_%s: %s", name, cmd) + stamp_file.unlink(missing_ok=True) + proc = subprocess.run(cmd, cwd=src_dir, capture_output=True, text=True, + check=False) + if proc.returncode != 0 or not so.is_file(): + raise RuntimeError( + f"apxs failed building mod_{name} (exit {proc.returncode}):\n" + f"$ {' '.join(cmd)}\n{proc.stdout}\n{proc.stderr}") + stamp_file.write_text(json.dumps(stamp, indent=1)) + return so + + +def clean_module_dir(src_dir: Union[str, Path]) -> None: + """Remove the build results of the module in *src_dir*, like ``make clean``: + the ``.libs`` directory and libtool's intermediates.""" + src_dir = Path(src_dir) + libs = src_dir / LIB_SUBDIR + if libs.is_dir(): + shutil.rmtree(libs) + for pattern in ("*.o", "*.lo", "*.slo", "*.la"): + for f in src_dir.glob(pattern): + f.unlink(missing_ok=True) + + +def clean_test_modules(test_dir: Union[str, Path]) -> None: + """Clean every module of the modules/ suite under test/: mod_aptest of the + framework, and the mod_* directories next to the tests.""" + test_dir = Path(test_dir) + for pattern in ("pyhttpd/mod_*", "modules/*/mod_*"): + for d in test_dir.glob(pattern): + if d.is_dir() and any(d.glob("mod_*.c")): + clean_module_dir(d) diff -Nru apache2-2.4.69/test/pyhttpd/conf/httpd.conf.template apache2-2.4.71/test/pyhttpd/conf/httpd.conf.template --- apache2-2.4.69/test/pyhttpd/conf/httpd.conf.template 2023-06-01 14:01:51.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/conf/httpd.conf.template 2026-11-09 03:07:05.000000000 +0000 @@ -1,6 +1,9 @@ ServerName localhost ServerRoot "${server_dir}" +DefaultRuntimeDir logs +PidFile httpd.pid + Include "conf/modules.conf" DocumentRoot "${server_dir}/htdocs" @@ -57,4 +60,15 @@ Options +ExecCGI + + + ScriptInterpreterSource Script + + + + + + ScriptInterpreterSource Script + + diff -Nru apache2-2.4.69/test/pyhttpd/conf/mime.types apache2-2.4.71/test/pyhttpd/conf/mime.types --- apache2-2.4.69/test/pyhttpd/conf/mime.types 2025-12-18 16:07:35.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/conf/mime.types 2026-11-09 03:07:05.000000000 +0000 @@ -1309,6 +1309,7 @@ chemical/x-csml csml # chemical/x-pdb chemical/x-xyz xyz +image/avif avif image/bmp bmp image/cgm cgm # image/example diff -Nru apache2-2.4.69/test/pyhttpd/conf/stop.conf.template apache2-2.4.71/test/pyhttpd/conf/stop.conf.template --- apache2-2.4.69/test/pyhttpd/conf/stop.conf.template 2021-12-14 11:26:52.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/conf/stop.conf.template 2026-11-09 03:07:05.000000000 +0000 @@ -5,6 +5,11 @@ ServerName localhost ServerRoot "${server_dir}" +# Must agree with httpd.conf, or the running server cannot be found: +# the built-in default varies between httpd versions. +DefaultRuntimeDir logs +PidFile httpd.pid + Include "conf/modules.conf" DocumentRoot "${server_dir}/htdocs" diff -Nru apache2-2.4.69/test/pyhttpd/depends.py apache2-2.4.71/test/pyhttpd/depends.py --- apache2-2.4.69/test/pyhttpd/depends.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/depends.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,255 @@ +"""Optional-dependency gating for the httpd integration test suite. + +Many test cases need something that is not part of httpd itself: the 'curl' +or 'nghttp' clients, the 'websockets' python package, an ACME test server, +a shared module that was not built, ... By default such a test is skipped +when its dependency is missing, which is the right thing for a developer +working on an unrelated part of the tree. + +In a CI environment that is *supposed* to provide those dependencies, a skip +instead hides a broken configuration: the run stays green while the coverage +silently disappears. Strict mode exists for those environments. Enable it +with --strict-optional or PYHTTPD_STRICT_OPTIONAL=1 and a missing dependency +becomes a failure naming what was expected and what was found instead. + +Only *missing dependencies* are affected. A test skipped because it does not +apply here - wrong mpm, httpd too old, STRESS_TEST not set - keeps skipping +in strict mode. Those keep using a plain pytest.mark.skipif, and when a test +carries both kinds of gate the "does not apply" one wins. + +Usage: + + from pyhttpd.depends import needs_dependency + + @needs_dependency("a2md", MDTestEnv.has_a2md(), reason="no a2md available") + class TestStore: + ... + +Note the polarity: skipif takes "skip when True", needs_dependency takes +"available when True". + +This module is also a pytest plugin, loaded from test/conftest.py. It +deliberately imports nothing from pyhttpd.env so that it can be loaded on +its own with '-p pyhttpd.depends' by a test that has no httpd installed. +""" + +import os + +import pytest + +STRICT_ENV_VAR = "PYHTTPD_STRICT_OPTIONAL" +MARKER_NAME = "needs_dependency" + +_FALSEY = ("", "0", "false", "no", "off") + +_HOWTO = (f"To go back to skipping missing optional dependencies, drop " + f"--strict-optional and unset {STRICT_ENV_VAR}.") + +# set at collection time on items that must hard-fail in strict mode +strict_fail_key = pytest.StashKey[str]() + + +def strict_optional_enabled(config=None) -> bool: + """Whether a missing optional dependency must fail instead of skip. + + *config* may be a pytest Config, or None for an HttpdTestEnv built + outside a pytest run. The environment variable wins when set to anything + truthy, so that CI can enable strict mode without a command line. + """ + if os.environ.get(STRICT_ENV_VAR, "").strip().lower() not in _FALSEY: + return True + if config is None: + return False + # passing a default keeps this quiet when the plugin is not registered + return bool(config.getoption("strict_optional", False)) + + +def strict_failure_message(dep, expected=None, detected=None, where=None) -> str: + """Format a strict-optional failure. The one place that does so. + + The dependency name carries what was expected ("h2load >= 1.41.0"), so + *expected* - the reason the test would otherwise have skipped - is + labelled for what it is. Those reasons are worded as an absence ("no + a2md available"), which under an "expected:" label read backwards. + """ + lines = [f"[strict-optional] missing optional dependency: {dep}"] + if expected: + lines.append(f" reason: {expected}") + if detected is not None: + lines.append(f" detected: {detected}") + if where: + lines.append(f" test: {where}") + lines.append(f" {_HOWTO}") + return "\n".join(lines) + + +def needs_dependency(dep, available, reason=None, detected=None): + """Declare that a test, class or module needs optional dependency *dep*. + + :param dep: short name for reports, e.g. "a2md", "h2load >= 1.41.0" + :param available: truthy if the dependency was found. May be a zero-arg + callable, evaluated once at collection rather than at import. + :param reason: skip reason used in non-strict mode. Keep the wording of + the skipif being replaced, so that -rs output does not change. + :param detected: what was found instead, e.g. "curl 7.88.1". Only used + in the strict-mode message. May also be a zero-arg callable. + """ + return pytest.mark.needs_dependency( + dep=dep, available=available, reason=reason, detected=detected, + ) + + +def _resolve(mark): + """Return (dep, available, reason, detected) for one marker. + + Positional arguments are tolerated, so a bare + pytest.mark.needs_dependency("x", cond) works as well. + """ + if "dep" in mark.kwargs: + dep = mark.kwargs["dep"] + elif mark.args: + dep = mark.args[0] + else: + dep = "" + + if "available" in mark.kwargs: + available = mark.kwargs["available"] + elif len(mark.args) > 1: + available = mark.args[1] + else: + # a marker carrying only a name is metadata, it never gates + available = True + if callable(available): + available = available() + + detected = mark.kwargs.get("detected") + if callable(detected): + detected = detected() + + reason = mark.kwargs.get("reason") or f"missing optional dependency: {dep}" + return dep, bool(available), reason, detected + + +def apply_gate(item, missing, strict): + """Skip *item*, or arm it to fail, for the dependencies in *missing*. + + *missing* is a list of (dep, reason, detected) in the order the gates + were declared. Shared by the needs_dependency marker below and by the + whole-suite gates in test/conftest.py, so that a dependency of the + framework itself reports exactly like a dependency of a single test, + with the same "does not apply here" precedence. + + Calling this twice for one item accumulates, so a test missing two + different dependencies is told about both. + """ + if not missing: + return + skip_reason = "; ".join(r for _, r, _ in missing) + + if not strict: + item.add_marker(pytest.mark.skip(reason=skip_reason)) + return + + gate = _other_gate(item) + if gate is _GATE_ACTIVE: + # does not apply here, let pytest skip it with its own reason + return + if gate is _GATE_UNKNOWN: + # a string condition we cannot evaluate, keep the old behaviour + item.add_marker(pytest.mark.skip(reason=skip_reason)) + return + + msg = "\n\n".join( + strict_failure_message(dep, expected=reason, detected=detected, + where=item.nodeid) + for dep, reason, detected in missing) + previous = item.stash.get(strict_fail_key, None) + item.stash[strict_fail_key] = ( + msg if previous is None else f"{previous}\n\n{msg}") + + +_GATE_ACTIVE = "active" +_GATE_UNKNOWN = "unknown" + + +def _other_gate(item): + """Whether this item is already skipped by something else. + + That is the "does not apply here" case: an httpd version gate, an mpm + gate, a STRESS_TEST gate, an unconditional skip. Those keep skipping in + strict mode, so they take precedence over a missing dependency. + + Returns _GATE_ACTIVE when a skip is certain, _GATE_UNKNOWN for a string + condition only pytest can eval() in the module namespace, and None when + nothing else gates the item. This covers the marker forms the suite + actually uses, without reaching into pytest's private skipping API. + + Must be called before we inject a skip marker of our own. + """ + for mark in item.iter_markers(): + if mark.name == "skip": + return _GATE_ACTIVE + if mark.name != "skipif": + continue + if "condition" in mark.kwargs: + conditions = [mark.kwargs["condition"]] + else: + conditions = list(mark.args) + for cond in conditions: + if isinstance(cond, str): + return _GATE_UNKNOWN + if cond: + return _GATE_ACTIVE + return None + + +def pytest_addoption(parser): + group = parser.getgroup("httpd", "apache httpd test suite") + group.addoption( + "--strict-optional", + dest="strict_optional", + action="store_true", + default=False, + help="Fail instead of skip when an optional dependency declared with " + "@needs_dependency() or env.require() is missing. Also enabled " + f"by {STRICT_ENV_VAR}=1. Tests that do not apply here (mpm, " + "httpd version, STRESS_TEST) keep skipping.", + ) + + +def pytest_configure(config): + config.addinivalue_line( + "markers", + "needs_dependency(dep, available, reason=None, detected=None): " + "declare an optional dependency. Missing means skip by default and " + "fail under --strict-optional. Use the needs_dependency() helper " + "from pyhttpd.depends rather than this marker directly.", + ) + + +def pytest_collection_modifyitems(config, items): + strict = strict_optional_enabled(config) + for item in items: + missing = [(dep, reason, detected) + for dep, ok, reason, detected + in (_resolve(m) for m in item.iter_markers(name=MARKER_NAME)) + if not ok] + if not missing: + continue + # markers arrive closest-first (function, class, module), report + # them in the order they are written instead + missing.reverse() + apply_gate(item, missing, strict) + + +@pytest.hookimpl(tryfirst=True) +def pytest_runtest_setup(item): + """Fail before any fixture runs, so that a test about to fail for a + missing dependency does not first go and start an httpd. + + Only ever armed for items carrying no active skip of their own, see + pytest_collection_modifyitems, so this cannot race pytest's own skipping. + """ + msg = item.stash.get(strict_fail_key, None) + if msg is not None: + pytest.fail(msg, pytrace=False) diff -Nru apache2-2.4.69/test/pyhttpd/env.py apache2-2.4.71/test/pyhttpd/env.py --- apache2-2.4.69/test/pyhttpd/env.py 2026-09-28 20:21:42.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/env.py 2026-11-09 03:07:05.000000000 +0000 @@ -3,11 +3,13 @@ import logging import re import os +import signal import shutil import stat import subprocess import sys import time +import platform from datetime import datetime, timedelta from string import Template from typing import List, Optional @@ -15,8 +17,11 @@ from configparser import ConfigParser, ExtendedInterpolation from urllib.parse import urlparse +from .cmodules import ensure_built from .certs import Credentials, HttpdTestCA, CertificateSpec +from .instance import HttpdInstance, HttpdStartError from .log import HttpdErrorLog +from .ports import PORT_KEYS, resolve_ports from .nghttp import Nghttp from .result import ExecResult @@ -30,6 +35,9 @@ class HttpdTestSetup: + # modules which exist only on Unix + UNIX_ONLY_MODULES = ("unixd", "suexec") + # the modules we want to load MODULES = [ "log_config", @@ -82,6 +90,20 @@ def add_optional_modules(self, modules: List[str]): self._optional_modules.extend(modules) + def _cgi_module(self): + """The name of a CGI module this build has: mod_cgid for preference, + mod_cgi otherwise. There is no mod_cgid on Windows.""" + if sys.platform != "win32" \ + and os.path.isfile(os.path.join(self.env.libexec_dir, + "mod_cgid.so")): + return "cgid" + return "cgi" + + def add_cgi_module(self): + """Load a CGI module, for a suite which needs CGI but does not care + which of mod_cgid and mod_cgi runs it.""" + self.add_modules([self._cgi_module()]) + def make(self): self._make_dirs() self._make_conf() @@ -92,6 +114,7 @@ self.add_modules([self.env.ssl_module]) self._make_modules_conf() self._make_htdocs() + self._add_aptest() self._build_clients() self.env.clear_curl_headerfiles() @@ -99,8 +122,11 @@ def _make_dirs(self): if not os.path.exists(self.env.gen_dir): os.makedirs(self.env.gen_dir) - if not os.path.exists(self.env.server_logs_dir): - os.makedirs(self.env.server_logs_dir) + # wipe logs from any previous module run to avoid stale files in the archive + if os.path.isdir(self.env.server_logs_dir): + shutil.rmtree(self.env.server_logs_dir, + ignore_errors=(sys.platform == "win32")) + os.makedirs(self.env.server_logs_dir, exist_ok=True) def _make_conf(self): # remove anything from another run/test suite @@ -130,14 +156,7 @@ fd.write(t.substitute(var_map)) def _get_builtin_modules(self) -> set: - httpd_bin = os.path.join(self.env.bin_dir, 'httpd') - p = subprocess.run([httpd_bin, '-l'], capture_output=True, text=True) - builtin = set() - for line in p.stdout.splitlines(): - match = re.match(r'^\s+mod_(.+)\.c$', line) - if match: - builtin.add(match.group(1)) - return builtin + return HttpdTestEnv.static_modules() def _make_modules_conf(self): loaded = set() @@ -146,21 +165,25 @@ with open(modules_conf, 'w') as fd: # issue load directives for all modules we want that are shared missing_mods = list() - for m in self._modules: + modules = self._modules + modules = [self._cgi_module() if m == "cgid" else m + for m in modules] + for m in modules: match = re.match(r'^mod_(.+)$', m) if match: m = match.group(1) if m in loaded: continue + if self.env.isWindows and m in HttpdTestSetup.UNIX_ONLY_MODULES: + continue mod_path = os.path.join(self.env.libexec_dir, f"mod_{m}.so") if m in builtin_modules: fd.write(f"#built-in: LoadModule {m}_module \"{mod_path}\"\n") elif os.path.isfile(mod_path): fd.write(f"LoadModule {m}_module \"{mod_path}\"\n") - elif m in self.env.dso_modules: - missing_mods.append(m) else: - fd.write(f"#built static: LoadModule {m}_module \"{mod_path}\"\n") + # neither compiled in nor built as a DSO + missing_mods.append(m) loaded.add(m) for m in self._optional_modules: match = re.match(r'^mod_(.+)$', m) @@ -187,30 +210,55 @@ srcdocs = os.path.join(d, 'htdocs') if os.path.isdir(srcdocs): shutil.copytree(srcdocs, dest_dir, dirs_exist_ok=True) - # make all contained .py scripts executable for dirpath, _dirnames, filenames in os.walk(dest_dir): for fname in filenames: - if re.match(r'.+\.py', fname): - py_file = os.path.join(dirpath, fname) - st = os.stat(py_file) - os.chmod(py_file, st.st_mode | stat.S_IEXEC) + fpath = os.path.join(dirpath, fname) + if sys.platform == "win32": + # normalize line endings to LF so test assertions match + try: + data = open(fpath, 'rb').read() + if b'\r\n' in data and b'\x00' not in data: + open(fpath, 'wb').write(data.replace(b'\r\n', b'\n')) + except OSError: + pass + if re.search(r'\.(py|cgi)$', fname): + if sys.platform == "win32": + self._fix_shebang(fpath) + st = os.stat(fpath) + os.chmod(fpath, st.st_mode | stat.S_IEXEC) + + @staticmethod + def _fix_shebang(py_file): + python_path = sys.executable + python_path = python_path.replace("\\", "/") + with open(py_file, 'r') as f: + lines = f.readlines() + if lines and re.match(r'#!\s*/usr/bin/env\s+python', lines[0]): + lines[0] = f"#!{python_path}\n" + with open(py_file, 'w', newline='\n') as f: + f.writelines(lines) def _add_aptest(self): - local_dir = os.path.dirname(inspect.getfile(HttpdTestSetup)) - p = subprocess.run([self.env.apxs, '-c', 'mod_aptest.c'], - capture_output=True, - cwd=os.path.join(local_dir, 'mod_aptest')) - rv = p.returncode - if rv != 0: - log.error(f"compiling mod_aptest failed: {p.stderr}") - raise Exception(f"compiling mod_aptest failed: {p.stderr}") + module_dir = self.env.test_modules_dir + if not self.env.isWindows: + local_dir = os.path.dirname(inspect.getfile(HttpdTestSetup)) + try: + ensure_built(os.path.join(local_dir, 'mod_aptest'), 'aptest', + self.env.apxs) + except RuntimeError as e: + log.error(f"compiling mod_aptest failed: {e}") + raise Exception(f"compiling mod_aptest failed: {e}") + + module_dir = os.path.join(local_dir, "mod_aptest", ".libs") modules_conf = os.path.join(self.env.server_dir, 'conf/modules.conf') with open(modules_conf, 'a') as fd: - # load our test module which is not installed - fd.write(f"LoadModule aptest_module \"{local_dir}/mod_aptest/.libs/mod_aptest.so\"\n") + # load our test module + fd.write(f"LoadModule aptest_module \"{module_dir}/mod_aptest.so\"\n") def _build_clients(self): + if self.env.test_modules_dir: + return clients_dir = os.path.join( os.path.dirname(os.path.dirname(inspect.getfile(HttpdTestSetup))), 'clients') @@ -224,30 +272,157 @@ class HttpdTestEnv: LIBEXEC_DIR = None + # entries shared across all modules, archived once in _shared + SHARED_SERVER_ENTRIES = {'ca', 'ca1', 'ca2', 'md', 'acme-ca.pem', + 'eab.json'} + + @staticmethod + def has_tool(name: str) -> bool: + return bool(shutil.which(name)) @classmethod def has_python_package(cls, name: str) -> bool: - if name in sys.modules: - # already loaded - return True - elif (spec := importlib.util.find_spec(name)) is not None: - module = importlib.util.module_from_spec(spec) - sys.modules[name] = module - spec.loader.exec_module(module) - return True - else: - return False + return name in sys.modules or importlib.util.find_spec(name) is not None @classmethod def get_ssl_module(cls): return os.environ['SSL'] if 'SSL' in os.environ else 'mod_ssl' + _static_modules = {} + + @classmethod + def static_modules(cls) -> set: + """The modules compiled into the httpd under test, by the name + LoadModule and add_modules() use: "so", "http2", "mpm_event"...""" + env = HttpdTestEnv() + httpd_bin = os.path.join(env.bin_dir, 'httpd') + if httpd_bin not in cls._static_modules: + names = set() + try: + p = subprocess.run([httpd_bin, '-l'], capture_output=True, text=True) + out = p.stdout + except OSError: + out = "" + for line in out.splitlines(): + m = re.match(r'^\s+(mod_|mpm_)?(.+)\.c$', line) + if m and m.group(1) == 'mod_': + names.add(m.group(2)) + elif m and m.group(1) == 'mpm_': # mpm_winnt.c + names.add(f"mpm_{m.group(2)}") + elif m and m.group(2) in ('event', 'worker', 'prefork', 'motorz'): + names.add(f"mpm_{m.group(2)}") + cls._static_modules[httpd_bin] = names + return cls._static_modules[httpd_bin] + @classmethod def has_shared_module(cls, name): + """Whether mod_ is there to be loaded: built as a DSO, or + compiled into httpd, when there is nothing to load.""" if cls.LIBEXEC_DIR is None: env = HttpdTestEnv() # will initialized it path = os.path.join(cls.LIBEXEC_DIR, f"mod_{name}.so") - return os.path.isfile(path) + return os.path.isfile(path) or name in cls.static_modules() + + @classmethod + def has_mod_ssl(cls): + """Whether mod_ssl is installed. + + Not the same question as get_ssl_module(), which only reports which + TLS module $SSL selected for this run. A test that is "only for + mod_ssl" is not applicable under SSL=mod_tls and should skip; a test + that needs mod_ssl to be installed at all has a real dependency. + """ + return cls.has_shared_module("ssl") + + _directives = None + + def has_directive(self, name: str) -> bool: + if HttpdTestEnv._directives is None: + httpd_bin = os.path.join(self._bin_dir, 'httpd') + try: + p = subprocess.run([httpd_bin, '-L'], + capture_output=True, text=True) + HttpdTestEnv._directives = p.stdout + except Exception: + HttpdTestEnv._directives = "" + return name in HttpdTestEnv._directives + + _nghttp_gate = {} + + @classmethod + def needs_nghttp(cls, assets: bool = False): + """A marker gating a test, class or module on the nghttp client. + + nghttp is the only client that can drive PUSH, trailers and explicit + frame padding, so the tests that use it have no fallback. It is named + unconditionally in config.ini, which says nothing about whether it is + installed, so the binary has to be run to find out. + + :param assets: also require the '-a' asset-fetching support, which + nghttp only has when built against libxml2 + """ + from .depends import needs_dependency + if assets not in cls._nghttp_gate: + env = HttpdTestEnv() + ok = env.has_nghttp_get_assets() if assets else env.has_nghttp() + if assets: + reason = "nghttp not available or misses -a (needs libxml2)" + else: + reason = "nghttp not available" + cls._nghttp_gate[assets] = needs_dependency( + "nghttp", ok, reason=reason, + detected=f"tried to run {env._nghttp}") + return cls._nghttp_gate[assets] + + _h2load_gate = {} + + @classmethod + def needs_h2load(cls, minv: str = None, reason: str = None): + """A marker gating a test, class or module on the h2load client. + + :param minv: also require at least this h2load version. 1.41.0 is + the one that matters here, it brought --connect-to. + :param reason: skip reason, when the default wording is too vague + about what the version is needed for. + """ + from .depends import needs_dependency + key = (minv, reason) + if key not in cls._h2load_gate: + env = HttpdTestEnv() + if minv is None: + ok = env.has_h2load() + dep = "h2load" + default_reason = "h2load not available" + else: + ok = env.h2load_is_at_least(minv) + dep = f"h2load >= {minv}" + default_reason = f"h2load unavailable or older than {minv}" + found = env.h2load_version_str() + cls._h2load_gate[key] = needs_dependency( + dep, ok, reason=reason or default_reason, + detected=(f"h2load {found}" if found is not None + else f"tried to run {env._h2load}")) + return cls._h2load_gate[key] + + _multipart_gate = None + + @classmethod + def needs_multipart(cls): + """A marker gating a test, class or module on the multipart package. + + The CGI helpers in htdocs/cgi/ parse file uploads with it. They + import it only for a multipart/ body, so this belongs on the tests + that upload a file, not on everything that touches a CGI. + """ + from .depends import needs_dependency + if cls._multipart_gate is None: + cls._multipart_gate = needs_dependency( + "multipart", cls.has_python_package('python_multipart') + or cls.has_python_package('multipart'), + reason="no multipart python package available", + detected="python package 'multipart' not installed " + "(pip install python-multipart)") + return cls._multipart_gate def __init__(self, pytestconfig=None): self._our_dir = os.path.dirname(inspect.getfile(Dummy)) @@ -260,8 +435,15 @@ self._apxs = self.config.get('global', 'apxs') self._prefix = self.config.get('global', 'prefix') self._apachectl = self.config.get('global', 'apachectl') + + self._libexec_dir = self.config.get('global', 'libexecdir') + HttpdTestEnv.LIBEXEC_DIR = self._libexec_dir + if HttpdTestEnv.LIBEXEC_DIR is None: HttpdTestEnv.LIBEXEC_DIR = self._libexec_dir = self.get_apxs_var('LIBEXECDIR') + + self._httpd_ver = self.config.get('httpd', 'version') + self._curl = self.config.get('global', 'curl_bin') if 'CURL' in os.environ: self._curl = os.environ['CURL'] @@ -271,27 +453,46 @@ self._h2load = self.config.get('global', 'h2load') if self._h2load is None: self._h2load = 'h2load' + self._test_modules_dir = self.config.get('global', 'pre_built_test_binaries_dir', + fallback=None) - self._http_port = int(self.config.get('test', 'http_port')) - self._http_port2 = int(self.config.get('test', 'http_port2')) - self._https_port = int(self.config.get('test', 'https_port')) - self._proxy_port = int(self.config.get('test', 'proxy_port')) - self._ws_port = int(self.config.get('test', 'ws_port')) + ports = resolve_ports( + {key: int(self.config.get('test', key)) for key in PORT_KEYS}, + base_port=(pytestconfig.getoption('--base-port', default=None) + if pytestconfig is not None else None), + fixed=(bool(pytestconfig.getoption('--fixed-ports', default=False)) + if pytestconfig is not None else True)) + self._http_port = ports['http_port'] + self._http_port2 = ports['http_port2'] + self._https_port = ports['https_port'] + self._proxy_port = ports['proxy_port'] + self._ws_port = ports['ws_port'] self._http_tld = self.config.get('test', 'http_tld') self._test_dir = self.config.get('test', 'test_dir') + + # In cmake determining the test directory is a headache and this is a simple workaround + if not self._test_dir: + self._test_dir = os.getcwd() + "/pyhttpd" if "pyhttpd" not in os.getcwd() else os.getcwd() + self._clients_dir = os.path.join(os.path.dirname(self._test_dir), 'clients') self._gen_dir = self.config.get('test', 'gen_dir') + self._server_dir = os.path.join(self._gen_dir, 'apache') self._server_conf_dir = os.path.join(self._server_dir, "conf") self._server_docs_dir = os.path.join(self._server_dir, "htdocs") - self._server_logs_dir = os.path.join(self.server_dir, "logs") + self._server_logs_dir = os.path.join(self._server_dir, "logs") + self._server_run_dir = os.path.join(self._server_dir, "run") self._server_access_log = os.path.join(self._server_logs_dir, "access_log") - self._error_log = HttpdErrorLog(os.path.join(self._server_logs_dir, "error_log")) + error_log_sep = "." if self.isWindows else "_" + self._error_log = HttpdErrorLog(os.path.join(self._server_logs_dir, f"error{error_log_sep}log")) self._apachectl_stderr = None + self._instance = None + + self._apply_target() self._dso_modules = self.config.get('httpd', 'dso_modules').split(' ') self._mpm_modules = self.config.get('httpd', 'mpm_modules').split(' ') - self._mpm_module = f"mpm_{os.environ['MPM']}" if 'MPM' in os.environ else 'mpm_event' + self._mpm_module = f"mpm_{os.environ['MPM']}" if 'MPM' in os.environ else 'mpm_winnt' if self.isWindows else 'mpm_event' self._ssl_module = self.get_ssl_module() if len(self._ssl_module.strip()) == 0: self._ssl_module = None @@ -301,8 +502,13 @@ self._https_base = f"https://{self._httpd_addr}:{self.https_port}" self._verbosity = pytestconfig.option.verbose if pytestconfig is not None else 0 + # imported here, not at module scope, to keep env.py free of a + # top-level pytest dependency: it is also built outside pytest runs + from .depends import strict_optional_enabled + self._strict_optional = strict_optional_enabled(pytestconfig) self._test_conf = os.path.join(self._server_conf_dir, "test.conf") self._httpd_base_conf = [] + self._httpd_env = {} self._httpd_log_modules = ['aptest'] self._log_interesting = None self._setup = None @@ -319,14 +525,73 @@ self._curl_headerfiles_n = 0 self._curl_version = None self._h2load_version = None + self._nghttp_version = None self._current_test = None + def _apply_target(self): + """Point this environment at the httpd of the session. + + config.ini, generated by the build system from config.ini.in, names + the build of this tree. When another one is tested (--apxs, --httpd), + pyhttpd.build.resolve_build() found it before collection, and the + server, its version and its modules come from there, whether or not + the environment was created with a pytest config. Ports, clients and + directories stay as config.ini has them. + """ + from .build import current + target = current() + if target is None or target.source == "config.ini": + return + httpd_path = os.path.abspath(str(target.httpd)) + self._bin_dir = os.path.dirname(httpd_path) + self._prefix = (str(target.prefix) if target.prefix is not None + else os.path.dirname(self._bin_dir)) + self._libexec_dir = str(target.libexecdir) + HttpdTestEnv.LIBEXEC_DIR = self._libexec_dir + if self.isWindows: + self._apachectl = httpd_path + else: + ctl = os.path.join(self._bin_dir, "apachectl") + self._apachectl = ctl if os.path.isfile(ctl) else httpd_path + self._apxs = (str(target.apxs) if target.apxs is not None + else os.path.join(self._bin_dir, "apxs")) + self._httpd_ver = target.version_str + dso = [] + mpm = [] + if target.apxs is None and target.inherited_conf is not None: + # --httpd with --conf, as on Windows: the modules the conf loads + for d in target.load_directives: + name = d.symbol[:-len("_module")] if d.symbol.endswith("_module") \ + else d.symbol + (mpm if name.startswith("mpm_") else dso).append(name) + elif os.path.isdir(self._libexec_dir): + # the shared modules that were built + for so in sorted(os.listdir(self._libexec_dir)): + m = re.match(r'^mod_(.+)\.so$', so) + if m: + (mpm if m.group(1).startswith("mpm_") else dso).append(m.group(1)) + self.config.set('httpd', 'dso_modules', ' '.join(dso)) + self.config.set('httpd', 'mpm_modules', ' '.join(mpm)) + def add_httpd_conf(self, lines: List[str]): self._httpd_base_conf.extend(lines) def add_httpd_log_modules(self, modules: List[str]): self._httpd_log_modules.extend(modules) + def set_httpd_env(self, name: str, value: Optional[str]): + """Add a variable to the environment httpd is started with, or + remove it again when passed None. + + Used by tests for modules which take their input from the + environment rather than from the configuration, such as + mod_systemd reading $NOTIFY_SOCKET. + """ + if value is None: + self._httpd_env.pop(name, None) + else: + self._httpd_env[name] = value + def issue_certs(self): if self._ca is None: self._ca = HttpdTestCA.create_root(name=self.http_tld, @@ -356,6 +621,81 @@ f"apache logged {len(errors)} errors and {len(warnings)} warnings: \n"\ "{0}\n{1}\n".format("\n".join(errors), "\n".join(warnings)) + # combines httpd version & revision to identify the archive folder + def _archive_version(self): + if not hasattr(self, '_cached_archive_version'): + version = self.get_httpd_version() + r = subprocess.run(['git', 'rev-parse', '--short', 'HEAD'], + capture_output=True, text=True, + cwd=self._our_dir) + if r.returncode == 0: + version = f"{version}-{r.stdout.strip()}" + else: + r = subprocess.run(['svn', 'info', '--show-item', 'revision'], + capture_output=True, text=True, + cwd=self._our_dir) + + if r.returncode == 0: + version = f"{version}-r{r.stdout.strip()}" + self._cached_archive_version = version + return self._cached_archive_version + + def archive_logs(self, package_name, archive_dir): + version = self._archive_version() + dest = os.path.join(archive_dir, version, package_name) + os.makedirs(dest, exist_ok=True) + + # merge into existing dest to preserve per-test confs + shutil.copytree(self._server_dir, dest, ignore=self.ignore_files, + dirs_exist_ok=True) + + shared_dest = os.path.join(archive_dir, version, '_shared') + + if os.path.isdir(shared_dest): + shutil.rmtree(shared_dest) + os.makedirs(shared_dest) + + for entry in self.SHARED_SERVER_ENTRIES: + src = os.path.join(self._server_dir, entry) + entry_dest = os.path.join(shared_dest, entry) + if os.path.isdir(src): + shutil.copytree(src, entry_dest) + elif os.path.isfile(src): + shutil.copy2(src, entry_dest) + + shared_conf_dest = os.path.join(shared_dest, 'conf') + os.makedirs(shared_conf_dest) + + # copy them once + for f in ['httpd.conf', 'mime.types', 'stop.conf']: + src = os.path.join(self._server_conf_dir, f) + if os.path.isfile(src): + shutil.copy2(src, shared_conf_dest) + + def archive_test_conf(self, test_name, package_name, archive_dir): + version = self._archive_version() + dest = os.path.join(archive_dir, version, package_name, 'conf') + if not os.path.isdir(dest): + os.makedirs(dest) + test_conf = os.path.join(self._server_conf_dir, 'test.conf') + if os.path.isfile(test_conf): + safe_name = test_name.replace('/', '_').replace('\\', '_') + dest_file = os.path.join(dest, f"{safe_name}.conf") + shutil.copy(test_conf, dest_file) + + # return files to ignore + def ignore_files(self, d, entries): + ignored = [e for e in entries if + e.endswith('.sock') or e in self.SHARED_SERVER_ENTRIES] + # shared confs goes to _shared + # test.conf is captured per-test + if os.path.basename(d) == 'conf': + ignored += ['httpd.conf', 'mime.types', 'stop.conf', 'test.conf'] + # htdocs are static test fixtures not useful in the archive + if os.path.basename(d) == os.path.basename(self._server_dir): + ignored += ['htdocs'] + return ignored + @property def curl(self) -> str: return self._curl @@ -441,6 +781,10 @@ return self._server_logs_dir @property + def server_run_dir(self) -> str: + return self._server_run_dir + + @property def libexec_dir(self) -> str: return HttpdTestEnv.LIBEXEC_DIR @@ -482,10 +826,43 @@ def set_current_test_name(self, val) -> None: self._current_test = val + def require(self, condition: bool, reason: str, + dep: str = None, detected=None) -> None: + """Skip, or fail under strict-optional mode, on a missing dependency. + + The in-test counterpart of the @needs_dependency decorator, for + checks that can only be made once a test is running. Use it instead + of a bare pytest.skip() whenever the reason is "something we expected + to be installed is not", and a plain pytest.skip() when the reason is + "this test does not apply here". + + :param condition: truthy if the dependency is present + :param reason: skip reason, also the "reason" line when strict + :param dep: short dependency name for the strict message + :param detected: what was found instead, for the strict message + """ + import pytest + if condition: + return + if self._strict_optional: + from .depends import strict_failure_message + pytest.fail(strict_failure_message( + dep or reason, expected=reason, detected=detected, + where=self.current_test_name), pytrace=False) + pytest.skip(reason) + @property - def apachectl_stderr(self): + def apachectl_stderr(self) -> str: return self._apachectl_stderr + @property + def test_modules_dir(self) -> str: + return self._test_modules_dir + + @property + def isWindows(self) -> bool: + return platform.system().lower() == "windows" + def add_cert_specs(self, specs: List[CertificateSpec]): self._cert_specs.extend(specs) @@ -503,64 +880,118 @@ hv = self._versiontuple(self.get_httpd_version()) return hv >= self._versiontuple(minv) + @staticmethod + def _run_version(args): + """Run a --version style command, returning its stdout or None. + + None means the binary is not there or would not run, which callers + must distinguish from "ran fine but is too old". config.ini/default + may just be the bare command name ("h2load"), not a verified path, + so catching OSError here is what keeps an uninstalled tool from + crashing collection with FileNotFoundError. + """ + try: + p = subprocess.run(args, capture_output=True, text=True) + except OSError: + return None + return p.stdout if p.returncode == 0 else None + + def _probe_h2load(self): + """The h2load version tuple, or None when h2load is unusable.""" + if self._h2load_version is None: + self._h2load_version = False # probed, not found + out = self._run_version([self._h2load, '--version']) if self._h2load else None + if out is not None: + m = re.match(r'h2load nghttp2/(\S+)', out.strip()) + if m: + self._h2load_version = self._versiontuple(m.group(1)) + return self._h2load_version or None + def has_h2load(self): - return self._h2load != "" + return self._probe_h2load() is not None + + def h2load_version_str(self): + """'1.41.0', or None when h2load is absent. For strict messages.""" + v = self._probe_h2load() + return None if v is None else ".".join(str(n) for n in v) def h2load_is_at_least(self, minv): - if not self.has_h2load(): - return False - if self._h2load_version is None: - p = subprocess.run([self._h2load, '--version'], capture_output=True, text=True) - if p.returncode != 0: - return False - s = p.stdout.strip() - m = re.match(r'h2load nghttp2/(\S+)', s) - if m: - self._h2load_version = self._versiontuple(m.group(1)) - if self._h2load_version is not None: - return self._h2load_version >= self._versiontuple(minv) - return False + v = self._probe_h2load() + return v is not None and v >= self._versiontuple(minv) - def curl_is_at_least(self, minv): + def _probe_curl(self): + """The curl version tuple, or None when curl is unusable.""" if self._curl_version is None: - p = subprocess.run([self._curl, '-V'], capture_output=True, text=True) - if p.returncode != 0: - return False - for l in p.stdout.splitlines(): - m = re.match(r'curl ([0-9.]+)[- ].*', l) - if m: - self._curl_version = self._versiontuple(m.group(1)) - break - if self._curl_version is not None: - return self._curl_version >= self._versiontuple(minv) - return False + self._curl_version = False # probed, not found + out = self._run_version([self._curl, '-V']) if self._curl else None + if out is not None: + for l in out.splitlines(): + m = re.match(r'curl ([0-9.]+)[- ].*', l) + if m: + self._curl_version = self._versiontuple(m.group(1)) + break + return self._curl_version or None + + def has_curl(self): + return self._probe_curl() is not None + + def curl_version_str(self): + """'8.4.0', or None when curl is absent. For strict messages.""" + v = self._probe_curl() + return None if v is None else ".".join(str(n) for n in v) + + def curl_is_at_least(self, minv): + v = self._probe_curl() + return v is not None and v >= self._versiontuple(minv) def curl_is_less_than(self, version): - if self._curl_version is None: - p = subprocess.run([self._curl, '-V'], capture_output=True, text=True) - if p.returncode != 0: - return False - for l in p.stdout.splitlines(): - m = re.match(r'curl ([0-9.]+)[- ].*', l) + v = self._probe_curl() + return v is not None and v < self._versiontuple(version) + + def curl_is_8_1_x(self): + """Whether curl is one of the 8.1.x releases. + + Those mishandle TLS renegotiation; 8.0.x and 8.2.0+ are both fine. + Returns False when curl is absent, so combine it with has_curl() when + the test also needs curl to exist at all. + """ + v = self._probe_curl() + return (v is not None + and self._versiontuple('8.1.0') <= v < self._versiontuple('8.2.0')) + + def _probe_nghttp(self): + """The nghttp version tuple, or None when nghttp is unusable. + + config.ini always names a binary, so the name alone proves nothing + about whether nghttp is installed: it has to be run. 'nghttp + --version' prints 'nghttp2/1.64.0', the nghttp2 library version. + """ + if self._nghttp_version is None: + self._nghttp_version = False # probed, not found + out = self._run_version([self._nghttp, '--version']) if self._nghttp else None + if out is not None: + m = re.search(r'nghttp2/(\S+)', out.strip()) if m: - self._curl_version = self._versiontuple(m.group(1)) - break - if self._curl_version is not None: - return self._curl_version < self._versiontuple(version) - return False + self._nghttp_version = self._versiontuple(m.group(1)) + return self._nghttp_version or None def has_nghttp(self): - return self._nghttp != "" + return self._probe_nghttp() is not None + + def nghttp_version_str(self): + """'1.64.0', or None when nghttp is absent. For strict messages.""" + v = self._probe_nghttp() + return None if v is None else ".".join(str(n) for n in v) + + def nghttp_is_at_least(self, minv): + v = self._probe_nghttp() + return v is not None and v >= self._versiontuple(minv) def has_nghttp_get_assets(self): if not self.has_nghttp(): return False - args = [self._nghttp, "-a"] - p = subprocess.run(args, stderr=subprocess.PIPE, stdout=subprocess.PIPE) - rv = p.returncode - if rv != 0: - return False - return p.stderr == "" + p = subprocess.run([self._nghttp, "-a"], capture_output=True, text=True) + return p.returncode == 0 and p.stderr == "" def get_apxs_var(self, name: str) -> str: p = subprocess.run([self._apxs, "-q", name], capture_output=True, text=True) @@ -569,7 +1000,7 @@ return p.stdout.strip() def get_httpd_version(self) -> str: - return self.get_apxs_var("HTTPD_VERSION") + return self._httpd_ver def mkpath(self, path): if not os.path.exists(path): @@ -685,61 +1116,118 @@ p for p in os.environ.get('PATH', '').split(os.pathsep) if '/shims' not in p ] - venv_bin = os.path.join(self._our_dir, '.venv', 'bin') + venv_bin = os.path.join(os.path.dirname(self._our_dir), '.venv', 'bin') if os.path.isdir(venv_bin) and venv_bin not in parts: parts.insert(0, venv_bin) env = os.environ.copy() env['PATH'] = os.pathsep.join(parts) + env.update(self._httpd_env) + return env + + @property + def instance(self) -> HttpdInstance: + """The httpd of this environment; see pyhttpd.instance.""" + if self._instance is None: + self._instance = HttpdInstance( + os.path.join(self._bin_dir, 'httpd'), + ["-d", self._server_dir, + "-f", os.path.join(self._server_conf_dir, 'httpd.conf')], + ports=[self._http_port], + error_log=self._error_log, + pid_files=[os.path.join(d, 'httpd.pid') + for d in (self._server_logs_dir, self._server_dir)], + env=self._server_env(), + stderr_file=os.path.join(self._server_dir, 'start.stderr')) + return self._instance + + def _server_env(self) -> dict: + env = self._clean_path_env() + # what apachectl did: the messages of the server in a known language + env.setdefault('LANG', 'C') return env def _run_apachectl(self, cmd) -> ExecResult: + """Run "httpd -k " and return the result. Only for tests that + look at what httpd itself reports; use apache_restart() and friends + to control the server.""" conf_file = 'stop.conf' if cmd == 'stop' else 'httpd.conf' - args = [self._apachectl, + args = [os.path.join(self._bin_dir, 'httpd'), "-d", self.server_dir, - "-f", os.path.join(self._server_dir, f'conf/{conf_file}'), + "-f", os.path.join(self._server_dir, 'conf', conf_file), "-k", cmd] - r = self.run(args, env=self._clean_path_env()) + r = self.run(args, env=self._server_env()) self._apachectl_stderr = r.stderr if r.exit_code != 0: log.warning(f"failed: {r}") return r + def _start_instance(self) -> int: + """Start the server, and wait for it to answer. 0 if it does.""" + self._apachectl_stderr = None + # tests change the environment of the server between starts + self.instance.env = self._server_env() + try: + self.instance.start() + except HttpdStartError as e: + self._apachectl_stderr = e.stderr + log.debug(f"httpd did not start: {e}") + return e.returncode if e.returncode else -1 + self._apachectl_stderr = self.instance.stderr_text() + return 0 if self.is_live(self._http_base, timeout=timedelta(seconds=10)) else -1 + def apache_reload(self): - r = self._run_apachectl("graceful") - if r.exit_code == 0: - timeout = timedelta(seconds=10) - return 0 if self.is_live(self._http_base, timeout=timeout) else -1 - return r.exit_code + return 0 if self.instance.restart(graceful=True) else -1 def apache_restart(self): self.apache_stop() - r = self._run_apachectl("start") - if r.exit_code == 0: - timeout = timedelta(seconds=10) - return 0 if self.is_live(self._http_base, timeout=timeout) else -1 - return r.exit_code - + return self._start_instance() + def apache_stop(self): - r = self._run_apachectl("stop") - if r.exit_code == 0: - timeout = timedelta(seconds=10) - return 0 if self.is_dead(self._http_base, timeout=timeout) else -1 - return r + return 0 if self.instance.stop() else -1 def apache_graceful_stop(self): log.debug("stop apache") - self._run_apachectl("graceful-stop") - return 0 if self.is_dead() else -1 + return 0 if self.instance.graceful_stop() else -1 def apache_fail(self): + """Start the server with a configuration it has to reject. 0 if it + did: httpd exited with an error before serving. -1 if it started + after all, or neither started nor exited in time (that is a stuck + server, not a rejected configuration). The server is stopped either + way, and apachectl_stderr has what it reported. + """ log.debug("expect apache fail") - self._run_apachectl("stop") - rv = self._run_apachectl("start") - if rv == 0: - rv = 0 if self.is_dead() else -1 - else: - rv = 0 - return rv + self.apache_stop() + self._apachectl_stderr = None + self.instance.env = self._server_env() + try: + self.instance.start() + except HttpdStartError as e: + self._apachectl_stderr = e.stderr + if e.returncode > 0: + return 0 + log.error(f"httpd neither started nor failed: {e}") + return -1 + self._apachectl_stderr = self.instance.stderr_text() + log.error("httpd started where it was expected to fail") + self.instance.stop() + return -1 + + def apache_hard_restart(self) -> int: + """Restart without the "graceful" flag, so the MPM starts over.""" + return 0 if self.instance.restart(graceful=False) else -1 + + def read_pid_file(self, name: str = 'httpd.pid') -> Optional[int]: + # Where PidFile lands depends on how the httpd under test resolves + # a relative path against DefaultRuntimeDir, which has differed + # between versions; look in both places rather than assume. + for d in (self._server_logs_dir, self._server_dir): + try: + with open(os.path.join(d, name)) as fd: + return int(fd.read().strip()) + except (OSError, ValueError): + continue + return None def apache_access_log_clear(self): if os.path.isfile(self._server_access_log): @@ -902,7 +1390,8 @@ def curl_protocol_version(self, url, timeout=5, options=None): if not options: options = [] - options.extend(["-w", "%{http_version}\n", "-o", "/dev/null"]) + devnull = "NUL" if sys.platform == "win32" else "/dev/null" + options.extend(["-w", "%{http_version}\n", "-o", devnull]) r = self.curl_raw(url, timeout=timeout, options=options) if r.exit_code == 0 and r.response: return r.response["body"].decode('utf-8').rstrip() @@ -940,7 +1429,7 @@ fpath = os.path.join(indir, fname) s10 = "0123456789" s = (101 * s10) + s10[0:3] - with open(fpath, 'w') as fd: + with open(fpath, 'w', newline='') as fd: for i in range(int(fsize / 1024)): fd.write(f"{i:09d}-{s}\n") remain = int(fsize % 1024) diff -Nru apache2-2.4.69/test/pyhttpd/gating.py apache2-2.4.71/test/pyhttpd/gating.py --- apache2-2.4.69/test/pyhttpd/gating.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/gating.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,164 @@ +"""Collection time gating shared by both suites. + +One vocabulary of markers, evaluated once per session against the httpd that +:func:`pyhttpd.build.resolve_build` found: + + need_module(*names) every named module is available + need_cgi() mod_cgi or mod_cgid + need_php() a PHP SAPI module, or php-fpm via mod_proxy_fcgi + need_ssl() mod_ssl + need_lwp() always satisfied (kept from the Perl vocabulary) + need_min_apache_version(v) httpd >= v + not_backported(rev, what, backported_in=None, platform=None) + expected to fail on httpd without the trunk change + (the test itself: errors in fixtures stay errors) + skip_not_backported(rev, what, backported_in=None, platform=None) + not run there (hangs, crashes, or leaves errors + in the error log) + +and, from pyhttpd.depends, ``needs_dependency`` for everything that is not +httpd itself (a client tool, a python package, an ACME server, ...). + +A missing module, CGI/PHP/SSL support or dependency is a *missing dependency*: +the test is skipped, or fails under --strict-optional. A test that does not +apply here (httpd too old, change not backported) always skips or xfails. The +"does not apply" gates are added first, so in strict mode they win, exactly as +for ``needs_dependency`` (see pyhttpd.depends). + +On trunk the backport marks do nothing; see pyhttpd.backport for backported_in +and platform. + +This is a pytest plugin, loaded from test/conftest.py. The markers themselves +are registered in test/pyproject.toml. +""" + +import pytest + +from .backport import lacks_change, reason +from .build import HttpdInfo, resolve_build +from .depends import apply_gate, strict_optional_enabled + +GATE_MARKERS = ( + "need_module", "need_cgi", "need_php", "need_ssl", "need_lwp", + "need_min_apache_version", "not_backported", "skip_not_backported", +) + +_PHP_SAPI_MODULES = ("sapi_apache2", "mod_php4", "mod_php5", "mod_php7", "mod_php") + +# callables (HttpdInfo) -> None that may add to info.modules, for modules the +# suite builds itself and which therefore exist at test time, not yet now +_module_providers = [] +_augmented = [] + + +def add_module_provider(provider) -> None: + """Let a suite declare modules it will build before it runs its tests. + + The classic suite compiles its c-modules/ in a session fixture, so + need_module("authany") has to be satisfied at collection time already. + """ + if provider not in _module_providers: + _module_providers.append(provider) + + +def _augment(info: HttpdInfo) -> None: + if any(i is info for i in _augmented): + return + _augmented.append(info) + for provider in _module_providers: + provider(info) + + +def _parse_version(ver) -> tuple: + parts = tuple(int(x) for x in str(ver).split(".")) + return parts + (0,) * (3 - len(parts)) + + +def _applies(item, info: HttpdInfo) -> None: + """Add the "does not apply to this httpd" skips and xfails.""" + for mark in item.iter_markers(name="need_min_apache_version"): + ver = mark.args[0] + if info.version < _parse_version(ver): + item.add_marker(pytest.mark.skip( + reason=f"httpd {info.version_str} < required {ver}")) + return + for mark in item.iter_markers(name="skip_not_backported"): + if lacks_change(info.version, mark.kwargs.get("backported_in"), + mark.kwargs.get("platform")): + rev, what = mark.args + item.add_marker(pytest.mark.skip( + reason=reason(rev, what, mark.kwargs.get("platform")))) + return + for mark in item.iter_markers(name="not_backported"): + if lacks_change(info.version, mark.kwargs.get("backported_in"), + mark.kwargs.get("platform")): + rev, what = mark.args + item.stash[_EXPECTED_FAILURE] = reason(rev, what, mark.kwargs.get("platform")) + return + + +# not_backported is an xfail of the test itself, not of its fixtures: a +# pytest.mark.xfail would also turn a broken server setup, a missing client or +# an error log check failing in teardown into an expected failure. +_EXPECTED_FAILURE = pytest.StashKey[str]() + + +@pytest.hookimpl(wrapper=True) +def pytest_runtest_makereport(item, call): + rep = yield + why = item.stash.get(_EXPECTED_FAILURE, None) + if why is None or call.when != "call" or rep.skipped \ + or item.config.option.runxfail: + return rep + # what pytest reports for a non-strict xfail: XFAIL, or XPASS + if rep.failed: + rep.outcome = "skipped" + rep.wasxfail = why + return rep + + +def _missing(item, info: HttpdInfo, php_fpm: bool) -> list: + """The unmet dependencies of *item* as (dep, reason, detected) tuples.""" + where = f"httpd {info.version_str}" + (f" in {info.prefix}" if info.prefix else "") + missing = [] + for mark in item.iter_markers(name="need_module"): + for name in mark.args: + if not info.has_module(name): + missing.append((f"module {name}", f"module {name} not available", + f"not built or loaded: {where}")) + if any(item.iter_markers(name="need_cgi")) and not ( + info.has_module("mod_cgi") or info.has_module("mod_cgid") + ): + missing.append(("mod_cgi or mod_cgid", + "no CGI module (mod_cgi/mod_cgid) available", where)) + if any(item.iter_markers(name="need_php")): + have_sapi = any(info.has_module(m) for m in _PHP_SAPI_MODULES) + have_fpm = php_fpm and info.has_module("mod_proxy_fcgi") + if not (have_sapi or have_fpm): + missing.append(("PHP", "no PHP SAPI module or php-fpm available", + f"no SAPI module, no --php-fpm: {where}")) + if any(item.iter_markers(name="need_ssl")) and not info.has_module("mod_ssl"): + missing.append(("mod_ssl", "mod_ssl not available", where)) + # need_lwp is always satisfied (httpx is always present); no check. + return missing + + +# Runs before pyhttpd.depends' hook: the skips added here are what "does not +# apply" means to it in strict mode. +@pytest.hookimpl(tryfirst=True) +def pytest_collection_modifyitems(config, items): + gated = [item for item in items + if any(item.get_closest_marker(name) for name in GATE_MARKERS)] + if not gated: + return + info = resolve_build(config) + if info is None: + # no httpd to evaluate against: leave the tests to fail or skip + # naturally at fixture setup + return + _augment(info) + strict = strict_optional_enabled(config) + php_fpm = bool(config.getoption("--php-fpm", default=None)) + for item in gated: + _applies(item, info) + apply_gate(item, _missing(item, info, php_fpm), strict) diff -Nru apache2-2.4.69/test/pyhttpd/install_tools.bat apache2-2.4.71/test/pyhttpd/install_tools.bat --- apache2-2.4.69/test/pyhttpd/install_tools.bat 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/install_tools.bat 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,277 @@ +@echo off +rem +rem install_tools.bat -- install curl CLI and nghttp2 for pyhttpd tests. +rem +rem Usage: +rem install_tools.bat C:\path\to\httpd\prefix +rem +rem Uses vcpkg to install curl (with CLI tool) and nghttp2, then copies +rem binaries into the httpd prefix bin/ directory so the test framework +rem can find them. +rem + +setlocal enabledelayedexpansion + +set "HERE=%~dp0" +if "%HERE:~-1%"=="\" set "HERE=%HERE:~0,-1%" +pushd "%HERE%\.." +set "TEST_DIR=%CD%" +popd + +rem --- target prefix (where httpd is installed) ------------------------------- +if not "%~1"=="" ( + set "PREFIX=%~1" +) else ( + echo install_tools.bat: ERROR: specify the httpd install prefix as argument. >&2 + echo Example: install_tools.bat "C:\Program Files (x86)\APR" >&2 + exit /b 1 +) + +if not exist "%PREFIX%\bin\httpd.exe" ( + echo install_tools.bat: ERROR: httpd.exe not found in "%PREFIX%\bin". >&2 + echo Is "%PREFIX%" the correct httpd install prefix? >&2 + exit /b 1 +) + +rem --- python detection and install -------------------------------------------- +set PYTHON= +for /f "delims=" %%P in ('where python.exe 2^>nul') do ( + if not defined PYTHON ( + echo %%P | findstr /i "hostedtoolcache" >nul + if not errorlevel 1 set "PYTHON=%%P" + ) +) +if not defined PYTHON ( + for /f "delims=" %%D in ('dir /b /o-n "%LOCALAPPDATA%\Programs\Python\Python3*" 2^>nul') do ( + if not defined PYTHON set "PYTHON=%LOCALAPPDATA%\Programs\Python\%%D\python.exe" + ) +) +if not defined PYTHON ( + for /f "delims=" %%D in ('dir /b /o-n "%PROGRAMFILES%\Python3*" 2^>nul') do ( + if not defined PYTHON set "PYTHON=%PROGRAMFILES%\%%D\python.exe" + ) +) +if defined PYTHON ( + echo install_tools.bat: Python found at "!PYTHON!" >&2 + goto :python_ok +) + +echo install_tools.bat: Python not found, installing... >&2 +rem Query the Python API for the latest stable release and extract the version +set "PY_VERSION=" +curl -s -o "%TEMP%\pyrel.json" "https://www.python.org/api/v2/downloads/release/?is_published=true&pre_release=false&version=3" +for /f "delims=" %%V in ('powershell -NoProfile -Command "$ProgressPreference='SilentlyContinue'; $j = Get-Content '%TEMP%\pyrel.json' -Raw | ConvertFrom-Json; ($j | Where-Object { $_.is_latest -eq $true }).name -replace 'Python ', ''" 2^>nul') do ( + set "PY_VERSION=%%V" +) +del "%TEMP%\pyrel.json" 2>nul +if not defined PY_VERSION ( + echo install_tools.bat: ERROR: could not detect latest Python version. >&2 + echo Install Python manually from https://www.python.org/downloads/windows/ >&2 + exit /b 1 +) +echo install_tools.bat: latest Python version: %PY_VERSION% >&2 + +set "PY_INSTALLER=python-%PY_VERSION%-amd64.exe" +set "PY_URL=https://www.python.org/ftp/python/%PY_VERSION%/%PY_INSTALLER%" +echo install_tools.bat: downloading %PY_URL%... >&2 +curl -L -o "%TEMP%\%PY_INSTALLER%" "%PY_URL%" +if errorlevel 1 ( + echo install_tools.bat: ERROR: download failed. >&2 + exit /b 1 +) + +echo install_tools.bat: installing Python %PY_VERSION%... >&2 +"%TEMP%\%PY_INSTALLER%" /quiet InstallAllUsers=0 PrependPath=0 Include_launcher=0 +if errorlevel 1 ( + echo install_tools.bat: ERROR: Python installation failed. >&2 + exit /b 1 +) +del "%TEMP%\%PY_INSTALLER%" 2>nul + +rem Re-detect after install +set PYTHON= +for /f "delims=" %%D in ('dir /b /o-n "%LOCALAPPDATA%\Programs\Python\Python3*" 2^>nul') do ( + if not defined PYTHON set "PYTHON=%LOCALAPPDATA%\Programs\Python\%%D\python.exe" +) +if not defined PYTHON ( + echo install_tools.bat: ERROR: Python still not found after install. >&2 + exit /b 1 +) +echo install_tools.bat: Python installed at "!PYTHON!" >&2 + +:python_ok + +rem --- uv detection and install ------------------------------------------------ +"!PYTHON!" -m uv --version >nul 2>&1 +if not errorlevel 1 ( + echo install_tools.bat: uv already installed. >&2 + goto :uv_ok +) +echo install_tools.bat: uv not found, installing via pip... >&2 +"!PYTHON!" -m pip install uv +if errorlevel 1 ( + echo install_tools.bat: ERROR: failed to install uv. >&2 + exit /b 1 +) +"!PYTHON!" -m uv --version >nul 2>&1 +if errorlevel 1 ( + echo install_tools.bat: ERROR: uv still not working after install. >&2 + exit /b 1 +) +echo install_tools.bat: uv installed successfully. >&2 + +:uv_ok + +rem --- perl detection and install (Strawberry Perl) ---------------------------- +set PERL= +for /f "delims=" %%P in ('where perl.exe 2^>nul') do ( + if not defined PERL set "PERL=%%P" +) +if not defined PERL ( + if exist "C:\Strawberry\perl\bin\perl.exe" set "PERL=C:\Strawberry\perl\bin\perl.exe" +) +if defined PERL ( + echo install_tools.bat: Perl found at "!PERL!" >&2 + goto :perl_ok +) + +echo install_tools.bat: Perl not found, installing Strawberry Perl... >&2 +rem Get the latest 64bit.msi download URL from GitHub releases API +set "PERL_URL=" +for /f "tokens=2 delims= " %%U in ('curl -s https://api.github.com/repos/StrawberryPerl/Perl-Dist-Strawberry/releases/latest ^| findstr /i "browser_download_url.*64bit.msi"') do ( + if not defined PERL_URL set "PERL_URL=%%~U" +) +if not defined PERL_URL ( + echo install_tools.bat: ERROR: could not detect latest Strawberry Perl installer. >&2 + echo Install Perl manually from https://strawberryperl.com >&2 + exit /b 1 +) +for %%F in ("%PERL_URL%") do set "PERL_MSI=%%~nxF" +echo install_tools.bat: downloading %PERL_URL%... >&2 +curl -L -o "%TEMP%\%PERL_MSI%" "%PERL_URL%" +if errorlevel 1 ( + echo install_tools.bat: ERROR: download failed. >&2 + exit /b 1 +) + +echo install_tools.bat: installing Strawberry Perl %PERL_VERSION%... >&2 +msiexec /i "%TEMP%\%PERL_MSI%" /quiet /norestart INSTALLDIR="C:\Strawberry" +if errorlevel 1 ( + echo install_tools.bat: ERROR: Strawberry Perl installation failed. >&2 + exit /b 1 +) +del "%TEMP%\%PERL_MSI%" 2>nul + +rem Re-detect after install +set PERL= +if exist "C:\Strawberry\perl\bin\perl.exe" ( + set "PERL=C:\Strawberry\perl\bin\perl.exe" + set "PATH=C:\Strawberry\perl\bin;C:\Strawberry\c\bin;!PATH!" +) +if not defined PERL ( + echo install_tools.bat: ERROR: Perl still not found after install. >&2 + exit /b 1 +) +echo install_tools.bat: Strawberry Perl installed at "!PERL!" >&2 + +:perl_ok + +rem --- vcpkg location --------------------------------------------------------- +if not defined VCPKG_TRIPLET set "VCPKG_TRIPLET=x64-windows-release" + +rem Honor VCPKG_DIR from the environment (e.g. set by CI) +if defined VCPKG_DIR ( + if not defined VCPKG_INSTALLED set "VCPKG_INSTALLED=%VCPKG_DIR%\installed\%VCPKG_TRIPLET%" + if exist "%VCPKG_DIR%\vcpkg.exe" ( + set "VCPKG_EXE=%VCPKG_DIR%\vcpkg.exe" + echo install_tools.bat: using VCPKG_DIR from environment: "!VCPKG_DIR!" >&2 + goto :vcpkg_ok + ) +) + +rem Try vcpkg on PATH (e.g. GitHub Actions has C:\vcpkg) +set "VCPKG_EXE=" +for /f "delims=" %%V in ('where vcpkg.exe 2^>nul') do ( + if not defined VCPKG_EXE set "VCPKG_EXE=%%V" +) +if defined VCPKG_EXE ( + echo install_tools.bat: using vcpkg from PATH: "!VCPKG_EXE!" >&2 + for %%F in ("!VCPKG_EXE!") do set "VCPKG_DIR=%%~dpF" + if "!VCPKG_DIR:~-1!"=="\" set "VCPKG_DIR=!VCPKG_DIR:~0,-1!" + if not defined VCPKG_INSTALLED set "VCPKG_INSTALLED=!VCPKG_DIR!\installed\%VCPKG_TRIPLET%" + goto :vcpkg_ok +) + +rem Fall back to cloning vcpkg locally +set "VCPKG_DIR=%TEST_DIR%\vcpkg" +if not defined VCPKG_INSTALLED set "VCPKG_INSTALLED=%VCPKG_DIR%\installed\%VCPKG_TRIPLET%" + +rem --- bootstrap vcpkg if needed ---------------------------------------------- +if not exist "%VCPKG_DIR%\" ( + echo install_tools.bat: cloning vcpkg... >&2 + git clone https://github.com/microsoft/vcpkg.git "%VCPKG_DIR%" + if errorlevel 1 ( + echo install_tools.bat: ERROR: git clone failed. >&2 + exit /b 1 + ) +) else ( + echo install_tools.bat: vcpkg already present, updating... >&2 + pushd "%VCPKG_DIR%" + git pull + popd +) + +if not exist "%VCPKG_DIR%\vcpkg.exe" ( + echo install_tools.bat: bootstrapping vcpkg... >&2 + pushd "%VCPKG_DIR%" + icacls scripts\tls12-download.exe /grant "%USERNAME%":RX >nul 2>&1 + call bootstrap-vcpkg.bat + if errorlevel 1 ( + popd + echo install_tools.bat: ERROR: vcpkg bootstrap failed. >&2 + exit /b 1 + ) + popd +) + +icacls "%VCPKG_DIR%\vcpkg.exe" /grant "%USERNAME%":RX >nul 2>&1 +set "VCPKG_EXE=%VCPKG_DIR%\vcpkg.exe" + +:vcpkg_ok + +rem --- install packages ------------------------------------------------------- +echo install_tools.bat: installing curl[core,tool,http2,openssl] and nghttp2 via vcpkg... >&2 +"%VCPKG_EXE%" install --recurse --triplet %VCPKG_TRIPLET% curl[core,tool,http2,openssl] nghttp2 +if errorlevel 1 ( + echo install_tools.bat: ERROR: vcpkg install failed. >&2 + exit /b 1 +) + +rem --- copy binaries to httpd prefix ------------------------------------------ +echo install_tools.bat: copying binaries to "%PREFIX%\bin"... >&2 + +if exist "%VCPKG_INSTALLED%\tools\curl\curl.exe" ( + copy /y "%VCPKG_INSTALLED%\tools\curl\curl.exe" "%PREFIX%\bin\" >nul + echo curl.exe installed >&2 +) else ( + echo WARNING: curl.exe not found in vcpkg output >&2 + dir /s /b "%VCPKG_INSTALLED%\*curl*.exe" 2>nul +) + +rem Copy all supporting DLLs (curl needs libcurl, openssl, zlib, nghttp2, etc.) +if exist "%VCPKG_INSTALLED%\bin\*.dll" ( + copy /y "%VCPKG_INSTALLED%\bin\*.dll" "%PREFIX%\bin\" >nul + echo vcpkg DLLs copied >&2 +) + +rem --- verify ----------------------------------------------------------------- +if exist "%PREFIX%\bin\curl.exe" ( + echo install_tools.bat: verifying curl: >&2 + "%PREFIX%\bin\curl.exe" --version +) else ( + echo install_tools.bat: WARNING: curl.exe was not installed. >&2 + exit /b 1 +) + +echo install_tools.bat: done. >&2 +exit /b 0 diff -Nru apache2-2.4.69/test/pyhttpd/instance.py apache2-2.4.71/test/pyhttpd/instance.py --- apache2-2.4.69/test/pyhttpd/instance.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/instance.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,430 @@ +"""One running httpd: start, stop, restart, reap. + +The server-control core shared by both suites: HttpdTestEnv (modules/), +HttpdServer (pytest_suite) and PrivateServer (throwaway servers) all drive an +HttpdInstance, and differ only in how they build the command line. + +The server runs in the foreground (``-DFOREGROUND``) under our control, in its +own session on Unix, so that + +* the whole process group, MPM children included, can be signalled and no + child is left holding a port after a failed or interrupted run, +* startup problems show on the stderr we captured, whether the configuration + is rejected or a module fails in post_config, +* graceful and hard restarts are the signals ``httpd -k`` would send to the + parent, and we wait for the new generation to be serving: "httpd -k + restart" only sends the signal, and a request answered straight afterwards + may have been served by the old generation, with the old config, about to + be killed. + +On Windows there are no process groups and no signals; the parent is told to +shut down or restart through the named events of the winnt MPM. + +An instance knows nothing about tests, curl or certificates. Callers that +want a stricter notion of "live" than "the ports accept connections" check +it themselves once start() returns. +""" + +import contextlib +import errno +import logging +import os +import re +import signal +import socket +import subprocess +import sys +import tempfile +import time +from pathlib import Path +from typing import Dict, Iterable, List, Optional, Sequence, Union + +from .log import HttpdErrorLog + +log = logging.getLogger(__name__) + +IS_WINDOWS = sys.platform == "win32" + +# Logged by every MPM once the new generation is serving. The startup flag +# makes it appear whatever the LogLevel is. +RE_RESUMING = re.compile(r'.* configured -- resuming normal operations$') + + +class HttpdStartError(RuntimeError): + """httpd did not come up. ``returncode`` is its exit code, or -1 when it + was still running but never became ready; ``stderr`` is what it wrote.""" + + def __init__(self, message: str, returncode: int = -1, stderr: str = ""): + super().__init__(message) + self.returncode = returncode + self.stderr = stderr + + +def _win_pid_alive(pid: int) -> bool: + # os.kill(pid, 0) is no probe on Windows: 0 is CTRL_C_EVENT, which it + # sends. Ask for the exit code instead. + import ctypes + from ctypes import wintypes + PROCESS_QUERY_LIMITED_INFORMATION = 0x1000 + STILL_ACTIVE = 259 + ERROR_ACCESS_DENIED = 5 + kernel32 = ctypes.WinDLL("kernel32", use_last_error=True) + kernel32.OpenProcess.restype = wintypes.HANDLE + kernel32.OpenProcess.argtypes = (wintypes.DWORD, wintypes.BOOL, wintypes.DWORD) + kernel32.GetExitCodeProcess.argtypes = (wintypes.HANDLE, ctypes.POINTER(wintypes.DWORD)) + kernel32.CloseHandle.argtypes = (wintypes.HANDLE,) + handle = kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pid) + if not handle: + # exists, but is not ours to query + return ctypes.get_last_error() == ERROR_ACCESS_DENIED + try: + code = wintypes.DWORD() + if not kernel32.GetExitCodeProcess(handle, ctypes.byref(code)): + return False + return code.value == STILL_ACTIVE + finally: + kernel32.CloseHandle(handle) + + +def pid_alive(pid: int) -> bool: + """Whether a process with this pid exists (``kill(0)``).""" + if pid <= 0: + return False + if IS_WINDOWS: + return _win_pid_alive(pid) + try: + os.kill(pid, 0) + except OSError as exc: + # ESRCH: no such process. EPERM: exists but not ours, still alive. + return exc.errno == errno.EPERM + return True + + +def read_pid(pid_file: Union[str, Path]) -> int: + """The pid in a pid file; 0 if missing, empty or garbage.""" + try: + return int(Path(pid_file).read_text().strip()) + except (OSError, ValueError): + return 0 + + +def signal_group(pid: int, sig: int) -> None: + """Send *sig* to the process group led by *pid*, else to *pid* alone. + + Children of an httpd started with start_new_session share the parent's + group id (== its pid), so signalling the group reaches the parent and + all its workers. On Windows there are no groups; signal the pid. + """ + if pid <= 0: + return + if IS_WINDOWS: + with contextlib.suppress(OSError): + os.kill(pid, sig) + return + try: + os.killpg(pid, sig) + except OSError as exc: + if exc.errno == errno.ESRCH: + return + with contextlib.suppress(OSError): + os.kill(pid, sig) + + +def port_open(port: int, host: str = "127.0.0.1", timeout: float = 0.25) -> bool: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.settimeout(timeout) + return s.connect_ex((host, port)) == 0 + + +def free_port() -> int: + """A port nobody listens on right now.""" + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.bind(("127.0.0.1", 0)) + return s.getsockname()[1] + + +class HttpdInstance: + """A single httpd, started, stopped and restarted by the test code. + + :param httpd: the httpd executable + :param args: the command line after the executable, e.g. + ``["-d", root, "-f", conf, "-DFOREGROUND"]``. -DFOREGROUND is added + when missing. + :param ports: ports that must accept connections once started + :param error_log: the ErrorLog of the server, as a path or an + HttpdErrorLog. Needed to wait for restarts to complete. + :param pid_files: pid files that may name a server left behind by an + earlier run; whatever they name is stopped before a start. + :param env: environment of the server, default is ours + :param stderr_file: where the stderr of the server goes (default: a + temporary file) + :param configtest_first: run ``httpd -t`` before starting. Costs a + second parse; the foreground start reports the same errors. + """ + + def __init__(self, httpd: Union[str, Path], args: Sequence[str], *, + ports: Iterable[int] = (), + error_log: Union[None, str, Path, HttpdErrorLog] = None, + pid_files: Iterable[Union[str, Path]] = (), + env: Optional[Dict[str, str]] = None, + stderr_file: Union[None, str, Path] = None, + configtest_first: bool = False): + self.httpd = str(httpd) + self.args: List[str] = list(args) + if "-DFOREGROUND" not in self.args: + self.args.append("-DFOREGROUND") + self.ports: List[int] = list(ports) + if isinstance(error_log, (str, Path)): + error_log = HttpdErrorLog(str(error_log)) + self.error_log: Optional[HttpdErrorLog] = error_log + self.pid_files = [Path(p) for p in pid_files] + self.env = env + self._stderr_file = Path(stderr_file) if stderr_file else None + self.configtest_first = configtest_first + self.proc: Optional[subprocess.Popen] = None + + # -- state ------------------------------------------------------------ + + @property + def pid(self) -> int: + """Pid of the parent process, 0 if not running.""" + return self.proc.pid if self.is_running() else 0 + + def is_running(self) -> bool: + return self.proc is not None and self.proc.poll() is None + + def all_ports_open(self) -> bool: + return all(port_open(p) for p in self.ports) + + def stderr_text(self) -> str: + """What the server wrote to stderr since it was last started.""" + if self._stderr_file is None: + return "" + try: + return self._stderr_file.read_text(errors="replace") + except OSError: + return "" + + # -- command line ----------------------------------------------------- + + def argv(self, *extra: str) -> List[str]: + return [self.httpd, *self.args, *extra] + + def configtest(self) -> subprocess.CompletedProcess: + """Run ``httpd ... -t``.""" + return subprocess.run(self.argv("-t"), capture_output=True, text=True, + env=self.env, check=False) + + # -- stale servers ---------------------------------------------------- + + def kill_stale(self, timeout: float = 10.0) -> None: + """Stop whatever the pid files name: a server left behind by a crashed + or interrupted run. Only those pids and their groups are touched.""" + for pid_file in self.pid_files: + pid = read_pid(pid_file) + if pid and pid_alive(pid) and not (self.proc and pid == self.proc.pid): + # the parent first, as "httpd -k stop" does: it shuts down its + # children, and whatever they started, in order + with contextlib.suppress(OSError): + os.kill(pid, signal.SIGTERM) + if not self._wait_gone(pid, timeout): + signal_group(pid, signal.SIGTERM if IS_WINDOWS else signal.SIGKILL) + self._wait_gone(pid, timeout) + self._reap_group(pid) + with contextlib.suppress(OSError): + if pid_file.exists() and not pid_alive(pid): + pid_file.unlink() + + @staticmethod + def _reap_group(pgid: int, timeout: float = 2.0) -> None: + """Make sure nothing of the process group of a stopped parent is left: + a helper that outlived it would hold ports or files. Normally the + group is empty by now and this does nothing.""" + if IS_WINDOWS or pgid <= 0: + return + for sig in (signal.SIGTERM, signal.SIGKILL): + try: + os.killpg(pgid, 0) # any member left? + except OSError: + return + signal_group(pgid, sig) + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + try: + os.killpg(pgid, 0) + except OSError: + return + time.sleep(0.05) + + @staticmethod + def _wait_gone(pid: int, timeout: float) -> bool: + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + if not pid_alive(pid): + return True + time.sleep(0.05) + return not pid_alive(pid) + + # -- start ------------------------------------------------------------ + + def start(self, timeout: float = 30.0) -> None: + """Start the server and wait until it is serving. + + Raises HttpdStartError if it does not come up, with its exit code + and the stderr it wrote; nothing is left running in that case. + """ + self.stop() + self.kill_stale() + if self.configtest_first: + check = self.configtest() + if check.returncode != 0: + raise HttpdStartError( + f"httpd config test failed:\n{check.stdout}\n{check.stderr}", + check.returncode, check.stderr) + + if self._stderr_file is None: + fd, name = tempfile.mkstemp(prefix="httpd-stderr-") + os.close(fd) + self._stderr_file = Path(name) + log_pos = self.error_log.current_pos() if self.error_log else 0 + popen_kwargs = {} + if not IS_WINDOWS: + popen_kwargs["start_new_session"] = True + argv = self.argv() + log.debug("starting httpd: %s", argv) + with open(self._stderr_file, "wb") as err: + self.proc = subprocess.Popen( # noqa: S603 - trusted paths + argv, env=self.env, stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, stderr=err, **popen_kwargs) + + deadline = time.monotonic() + timeout + resumed = self.error_log is None + while time.monotonic() < deadline: + rc = self.proc.poll() + if rc is not None: + self.stop() + raise HttpdStartError( + f"httpd exited early (code {rc})", rc, self.stderr_text()) + if not resumed: + resumed = self.error_log.wait_for(RE_RESUMING, log_pos, timeout=0.1) + if resumed and self.all_ports_open(): + return + if resumed: + time.sleep(0.05) + stderr = self.stderr_text() + self.stop() + raise HttpdStartError( + f"httpd not serving {self.ports or ''} within {timeout}s", -1, stderr) + + # -- stop ------------------------------------------------------------- + + def stop(self, timeout: float = 10.0) -> bool: + """Stop the server, the whole process group, now. Idempotent. Returns + True when nothing of it is left, and its ports are closed.""" + proc = self.proc + if proc is not None: + if IS_WINDOWS: + self._win_shutdown(proc, timeout) + elif proc.poll() is None: + # The parent only, as "httpd -k stop" does: it shuts down its + # children, and the processes they started (mod_md runs + # commands, for one), in order. Signalling the whole group at + # once makes such helpers die with SIGTERM and report errors. + with contextlib.suppress(OSError): + os.kill(proc.pid, signal.SIGTERM) + try: + proc.wait(timeout=timeout) + except subprocess.TimeoutExpired: + signal_group(proc.pid, signal.SIGKILL) + with contextlib.suppress(subprocess.TimeoutExpired): + proc.wait(timeout=timeout) + else: + with contextlib.suppress(Exception): + proc.wait(timeout=0) + if not IS_WINDOWS: + self._reap_group(proc.pid) + self.proc = None + self.kill_stale(timeout) + return self._wait_ports_closed(timeout) + + def graceful_stop(self, timeout: float = 30.0) -> bool: + """Ask the parent to stop once its children are done, and wait for + that. Escalates to a hard stop when it takes longer than *timeout*.""" + proc = self.proc + if proc is None or proc.poll() is not None: + return self.stop(timeout) + if not IS_WINDOWS: # there is no graceful stop signal on Windows + with contextlib.suppress(OSError): + os.kill(proc.pid, signal.SIGWINCH) + with contextlib.suppress(subprocess.TimeoutExpired): + proc.wait(timeout=timeout) + # reaps the group, and hard-stops when the wait above ran out + return self.stop(timeout) + + def _wait_ports_closed(self, timeout: float) -> bool: + deadline = time.monotonic() + timeout + while any(port_open(p) for p in self.ports): + if time.monotonic() > deadline: + log.warning("ports %s still open after stop", self.ports) + return False + time.sleep(0.05) + return True + + # -- restart ---------------------------------------------------------- + + def restart(self, graceful: bool = True, timeout: float = 10.0) -> bool: + """Have the running parent restart and wait for the new generation to + be serving. Graceful lets in-flight requests finish; otherwise the + MPM starts over. False if there is no server, or the restart did not + show up in the error log within *timeout*.""" + if not self.is_running(): + log.error("no httpd process to restart") + return False + log_pos = self.error_log.current_pos() if self.error_log else 0 + if IS_WINDOWS: + if not self._win_signal(f"ap{self.proc.pid}_restart"): + return False + else: + os.kill(self.proc.pid, signal.SIGUSR1 if graceful else signal.SIGHUP) + if self.error_log is not None and not self.error_log.wait_for( + RE_RESUMING, log_pos, timeout=timeout): + log.warning("no restart logged after %s within %ss", + "graceful" if graceful else "restart", timeout) + return False + deadline = time.monotonic() + timeout + while not self.all_ports_open(): + if time.monotonic() > deadline: + return False + time.sleep(0.05) + return True + + def send_signal(self, sig: int) -> None: + """Signal the parent process only.""" + if self.is_running(): + os.kill(self.proc.pid, sig) + + # -- Windows ---------------------------------------------------------- + + @staticmethod + def _win_signal(event_name: str) -> bool: + import ctypes + handle = ctypes.windll.kernel32.OpenEventW(0x0002, False, event_name) + if not handle: + log.error("cannot open event %s", event_name) + return False + ctypes.windll.kernel32.SetEvent(handle) + ctypes.windll.kernel32.CloseHandle(handle) + return True + + def _win_shutdown(self, proc: subprocess.Popen, timeout: float) -> None: + if proc.poll() is not None: + return + log.debug("stopping httpd (signal shutdown event)") + if not self._win_signal(f"ap{proc.pid}_shutdown"): + log.warning("falling back to terminate") + proc.terminate() + try: + proc.wait(timeout=max(timeout, 30)) + except subprocess.TimeoutExpired: + proc.kill() + proc.wait(timeout=5) diff -Nru apache2-2.4.69/test/pyhttpd/log.py apache2-2.4.71/test/pyhttpd/log.py --- apache2-2.4.69/test/pyhttpd/log.py 2026-09-28 20:21:42.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/log.py 2026-11-09 03:07:05.000000000 +0000 @@ -41,6 +41,11 @@ def path(self) -> str: return self._path + def start_at(self, pos: int): + """Check from file position *pos* on, e.g. the size of the log before + the server was started.""" + self._start_pos = self._recent_pos = pos + def clear_log(self): if os.path.isfile(self.path): os.truncate(self.path, 0) @@ -146,6 +151,31 @@ self._caught_matches = set() return errors, warnings + def current_pos(self) -> int: + """The end of the log as it stands now, to wait_for() lines + logged after it. + """ + if not os.path.isfile(self._path): + return 0 + with open(self._path) as fd: + return fd.seek(0, SEEK_END) + + def wait_for(self, pattern: re.Pattern, start_pos: int, timeout=10) -> bool: + """Wait for a line matching pattern to be logged after start_pos, + as returned by current_pos(). + """ + end = datetime.now() + timedelta(seconds=timeout) + while True: + if os.path.isfile(self._path): + with open(self._path) as fd: + fd.seek(start_pos, os.SEEK_SET) + for line in fd: + if pattern.match(line): + return True + if datetime.now() > end: + return False + time.sleep(.1) + def scan_recent(self, pattern: re.Pattern, timeout=10): if not os.path.isfile(self.path): return False diff -Nru apache2-2.4.69/test/pyhttpd/mod_aptest/mod_aptest.c apache2-2.4.71/test/pyhttpd/mod_aptest/mod_aptest.c --- apache2-2.4.69/test/pyhttpd/mod_aptest/mod_aptest.c 2023-01-09 07:35:18.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/mod_aptest/mod_aptest.c 2026-11-09 03:07:05.000000000 +0000 @@ -51,6 +51,85 @@ return DECLINED; } +/* + * Register the methods named in an "AP-Test-Allow-Methods" request header + * via ap_allow_methods(), so a test can check how they are reflected in the + * Allow response header. + */ +static int aptest_allow_methods(request_rec *r) +{ + const char *methods = apr_table_get(r->headers_in, "AP-Test-Allow-Methods"); + char *list, *name, *last; + + if (methods == NULL) { + return DECLINED; + } + + list = apr_pstrdup(r->pool, methods); + for (name = apr_strtok(list, ", ", &last); name != NULL; + name = apr_strtok(NULL, ", ", &last)) { + ap_allow_methods(r, MERGE_ALLOW, name, NULL); + } + + return DECLINED; +} + +/* + * Handler "aptest-getline-echo": echo the request body back, reading it + * with AP_MODE_GETLINE rather than AP_MODE_READBYTES. The Content-Length + * request header left once the body has been read, if any, is returned in + * an "AP-Test-Content-Length" response header. + */ +static int aptest_getline_echo(request_rec *r) +{ + apr_bucket_brigade *bb, *body; + const char *clen; + char *data; + apr_size_t len; + apr_status_t rv; + int seen_eos = 0; + + if (strcmp(r->handler, "aptest-getline-echo")) { + return DECLINED; + } + + bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); + body = apr_brigade_create(r->pool, r->connection->bucket_alloc); + while (!seen_eos) { + rv = ap_get_brigade(r->input_filters, bb, AP_MODE_GETLINE, + APR_BLOCK_READ, HUGE_STRING_LEN); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "aptest-getline-echo: reading body failed"); + return ap_map_http_request_error(rv, HTTP_BAD_REQUEST); + } + if (!APR_BRIGADE_EMPTY(bb) + && APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) { + seen_eos = 1; + } + rv = apr_brigade_pflatten(bb, &data, &len, r->pool); + if (rv == APR_SUCCESS) { + rv = apr_brigade_write(body, NULL, NULL, data, len); + } + if (rv != APR_SUCCESS) { + return HTTP_INTERNAL_SERVER_ERROR; + } + apr_brigade_cleanup(bb); + } + + clen = apr_table_get(r->headers_in, "Content-Length"); + if (clen) { + apr_table_setn(r->headers_out, "AP-Test-Content-Length", clen); + } + ap_set_content_type(r, "text/plain"); + if (apr_brigade_pflatten(body, &data, &len, r->pool) != APR_SUCCESS) { + return HTTP_INTERNAL_SERVER_ERROR; + } + ap_rwrite(data, len, r); + + return OK; +} + /* Install this module into the apache2 infrastructure. */ static void aptest_hooks(apr_pool_t *pool) @@ -61,6 +140,8 @@ /* test case monitoring */ ap_hook_post_read_request(aptest_post_read_request, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_fixups(aptest_allow_methods, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(aptest_getline_echo, NULL, NULL, APR_HOOK_MIDDLE); } diff -Nru apache2-2.4.69/test/pyhttpd/plugin.py apache2-2.4.71/test/pyhttpd/plugin.py --- apache2-2.4.69/test/pyhttpd/plugin.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/plugin.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,93 @@ +"""Command-line options for the test suites under test/. + +Registered here, in a plugin the root conftest loads, rather than in the +conftests of modules/ or pytest_suite/: pytest only sees options from conftests +on the invocation path, so options defined in a subdirectory conftest are +"unrecognized" when pytest is run from test/ with no path arguments. A single +registration point also settles the options both suites share (--httpd, --conf), +since pytest rejects a duplicate addoption. + +It also settles, once and before anything is collected, which httpd the +session tests: see pyhttpd.build.resolve_build. +""" +import pytest + + +@pytest.hookimpl(tryfirst=True) +def pytest_configure(config): + from .build import TargetError, resolve_build + try: + resolve_build(config) + except TargetError as exc: + raise pytest.UsageError(f"httpd under test: {exc}") from exc + + +def pytest_addoption(parser): + # modules/ (pyhttpd) + parser.addoption("--repeat", action="store", type=int, default=1, + help='Number of times to repeat each test') + parser.addoption("--all", action="store_true") + parser.addoption("--archive", action="store", default=None, + help='Archive the server dir after each test package ' + 'to the specified folder') + + group = parser.getgroup("httpd") + group.addoption("--base-port", action="store", type=int, default=None, + help="modules/: listen on this port and the next four instead of " + "free ports picked per test environment") + group.addoption("--fixed-ports", action="store_true", default=False, + help="modules/: listen on the ports of the build's config.ini " + "(5001 ...) instead of free ports") + group.addoption("--dynamic-ports", action="store_true", default=False, + help="modules/: free ports; this is the default, the option is " + "only kept for existing scripts") + # shared by both suites + group.addoption("--httpd", action="store", default=None, + help="path to httpd binary (modules/: used to derive prefix, " + "version, etc.; pytest_suite: default is derived from --apxs)") + group.addoption("--conf", action="store", default=None, + help="path to the installed httpd.conf, for LoadModule discovery " + "(default: the one of --apxs; needed where apxs is not, e.g. " + "on Windows)") + # pytest_suite + group.addoption("--apxs", action="store", default=None, help="path to apxs") + group.addoption( + "--defines", + action="store", + default="", + help="space-separated extra -D defines (e.g. 'LDAP')", + ) + group.addoption( + "--php-fpm", + action="store", + default=None, + help="path to a php-fpm binary; enables the t/php tests via mod_proxy_fcgi " + "(any version/location -- the path is the only PHP-specific input)", + ) + group.addoption( + "--php-fpm-port", + action="store", + type=int, + default=8999, + help="TCP port for the managed php-fpm pool (default 8999)", + ) + group.addoption( + "--clean-modules", + action="store_true", + default=False, + help="remove all compiled C-module artifacts before building (emulate make clean)", + ) + group.addoption( + "--prefix", + action="store", + default=None, + help="server install prefix for resolving relative module paths " + "(default: derived from --conf path)", + ) + group.addoption( + "--with-perl", + action="store", + default=None, + help="path to the perl interpreter for generated scripts and " + "rewrite prg-maps (default: /usr/bin/perl or APACHE_TEST_PERL env)", + ) diff -Nru apache2-2.4.69/test/pyhttpd/ports.py apache2-2.4.71/test/pyhttpd/ports.py --- apache2-2.4.69/test/pyhttpd/ports.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/ports.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,60 @@ +"""The ports the modules/ suite's httpd listens on. + +By default the five test ports are free ports, picked per test environment +(full runs of both suites on event and worker showed no difference from the +fixed ports). Two options change that: + + --base-port N use N, N+1, ... instead, e.g. to know the ports in advance + --fixed-ports use the build's config.ini ports (5001 ...), which is what + the suite used before; also what an environment created + without a pytest config gets + +Tests read their ports from the environment (env.http_port etc.) and the +generated configuration, pebble's included, is built from the same values, so +any of the three works. Dynamic ports, and one directory per instance, are +what running several servers side by side (pytest-xdist) needs. +""" + +import random +import socket +from typing import Dict, List + +# config.ini key, in the order --base-port hands out consecutive numbers +PORT_KEYS = ("https_port", "http_port", "proxy_port", "http_port2", "ws_port") + +# Below the ephemeral ranges of Linux (32768-) and macOS/BSD (49152-), so that +# a port is not taken by an outgoing connection between picking and binding. +DYNAMIC_RANGE = (20000, 30000) + + +def free_ports(count: int, host: str = "127.0.0.1") -> List[int]: + """*count* distinct ports that nobody listens on right now.""" + socks, ports = [], [] + try: + while len(ports) < count: + port = random.randrange(*DYNAMIC_RANGE) + if port in ports: + continue + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + s.bind((host, port)) + except OSError: + s.close() + continue + socks.append(s) + ports.append(port) + finally: + # held until all are chosen, so they are distinct + for s in socks: + s.close() + return ports + + +def resolve_ports(defaults: Dict[str, int], base_port: int = None, + fixed: bool = False) -> Dict[str, int]: + """The ports to use, by PORT_KEYS name; *defaults* are the config.ini ones.""" + if base_port: + return {key: base_port + i for i, key in enumerate(PORT_KEYS)} + if fixed: + return {key: defaults[key] for key in PORT_KEYS} + return dict(zip(PORT_KEYS, free_ports(len(PORT_KEYS)))) diff -Nru apache2-2.4.69/test/pyhttpd/pyproject.toml apache2-2.4.71/test/pyhttpd/pyproject.toml --- apache2-2.4.69/test/pyhttpd/pyproject.toml 2026-09-28 20:21:42.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/pyproject.toml 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -[project] -name = "httpd-pyhttpd-tests" -version = "0.1.0" -requires-python = ">=3.9" -dependencies = [ - "pytest>=7.0", - "cryptography", - "filelock", - "python-multipart", - "pyopenssl", - "packaging", - "websockets", -] - -[tool.uv] -package = false diff -Nru apache2-2.4.69/test/pyhttpd/runtests.sh apache2-2.4.71/test/pyhttpd/runtests.sh --- apache2-2.4.69/test/pyhttpd/runtests.sh 2026-07-18 15:32:22.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/runtests.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -#!/bin/sh -# -# runtests.sh -- run the pyhttpd modules/ test suite. -# -# Manages a local .venv under pyhttpd/ so that pytest and the CGI helper scripts -# (which httpd forks) both use the same Python with all required packages -# (cryptography, python-multipart, websockets, etc.) available. -# -# Usage: -# ./runtests.sh # run all modules/ tests -# ./runtests.sh modules/http1 # run a specific suite -# ./runtests.sh -k post -v # any pytest args pass through -# -# Environment: -# PYHTTPD_TARGETS space-separated list of test paths (default: modules) -# -set -eu - -here="$(cd "$(dirname "$0")" && pwd)" - -# --- ensure the venv exists and is current ---------------------------------- -# The suite baselines on uv (https://docs.astral.sh/uv/) as its dependency and -# venv manager: it reads pyproject.toml + uv.lock, so there is a single source -# of truth for dependencies. We invoke .venv/bin/pytest directly rather than -# `uv run` so the suite works even where `uv run` is shimmed/unavailable. -# -# Create $here/.venv on first run, and rebuild it when pyproject.toml is newer -# than the venv (i.e. dependencies changed). Absolute paths throughout, so this -# behaves identically regardless of the caller's cwd. This block is kept -# byte-for-byte identical in pytest_suite/runtests.sh and pyhttpd/runtests.sh -# -- edit both together. -PYTEST="$here/.venv/bin/pytest" -if [ ! -x "$PYTEST" ] || [ "$here/pyproject.toml" -nt "$here/.venv" ]; then - if ! command -v uv >/dev/null 2>&1; then - echo "runtests.sh: ERROR: 'uv' is required but not installed." >&2 - echo " Install it from https://docs.astral.sh/uv/ and re-run." >&2 - exit 1 - fi - echo "runtests.sh: (re)creating $here/.venv via 'uv sync'..." >&2 - uv sync --project "$here" - # Mark the venv as freshly built so the staleness check above won't retrigger - # until pyproject.toml changes again. - touch "$here/.venv" -fi - -# Prepend the venv's bin dir so that CGI scripts forked by httpd also resolve -# python3 to the venv's interpreter (which has all packages installed), and so -# that any shim wrappers earlier on PATH are shadowed. -export PATH="$here/.venv/bin:$PATH" - -# The modules/ test suite lives in test/, a sibling of this script's directory -# (test/pyhttpd/) -- cd there so both the default target and any -# PYHTTPD_TARGETS/positional path the caller supplies resolve the same way -# regardless of where runtests.sh was invoked from. -cd "$(dirname "$here")" - -# Only fall back to the "modules" default when the caller gave no positional -# test path of their own -- otherwise it would always tag along after theirs -# (`pytest modules modules/http1`), silently widening any subset selection -# back out to the full suite. A positional path is recognized by actually -# existing on disk (relative to test/, our cwd at this point) -- this avoids -# both having to enumerate every pytest flag that takes a separate-word value -# (-k, -m, -p, --tb, --maxfail, -n from pytest-xdist, ...) and misdetecting a -# -k/-m expression that happens to contain '/' (this suite's own parametrize -# IDs look like "/006/006.css", so "-k 006/006" is a realistic selector, and -# it does not exist as a path). -have_path=0 -for arg in "$@"; do - case "$arg" in - -*) ;; - # Strip a trailing ::nodeid (pytest's file::Class::test node-selector - # syntax) before checking existence -- only the file/dir part is real. - *) [ -e "${arg%%::*}" ] && have_path=1 ;; - esac -done - -if [ -n "${PYHTTPD_TARGETS:-}" ]; then - targets="$PYHTTPD_TARGETS" -elif [ "$have_path" = 1 ]; then - targets="" -else - targets="modules" -fi - -echo "runtests.sh: $PYTEST $targets $*" >&2 -# shellcheck disable=SC2086 # $targets is an intentional word-split path list -exec "$PYTEST" $targets "$@" diff -Nru apache2-2.4.69/test/pyhttpd/sslca.py apache2-2.4.71/test/pyhttpd/sslca.py --- apache2-2.4.69/test/pyhttpd/sslca.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/sslca.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,331 @@ +"""The certificate authority tree of the classic (Apache::Test) SSL tests. + +Python port of Apache::TestSSLCA on the ``cryptography`` package, like the +rest of the certificate code of this suite (pyhttpd.certs); no openssl command +line tool is needed to build it. It writes, under ``/asf``, a +self-signed CA plus server and client certificates in the layout and under +the file names of the Perl framework, so that the ``t/conf/ssl/*.conf.in`` +files work unchanged: + + certs/ca.crt, keys/ca.pem the CA + certs/.crt, keys/.pem server, server2, server_des3, + server2_des3 (+ _dsa variants), + client_ok, client_snakeoil, + client_colon, client_revoked + proxy/.pem certificate and key of each client + crl/ca-bundle.crl the CRL, with client_revoked in it + index.txt, serial the database of "openssl ca" / + "openssl ocsp -index", which + t/htdocs/modules/cgi/ocsp.pl uses + +Differences from the Perl original: +* RSA keys are 2048 bit, digest sha256 (what modern openssl defaults to). +* The CA certificate carries keyCertSign and cRLSign: OpenSSL 3 refuses to + verify a chain through a CA without them. +* DSA server variants (``server_dsa`` etc.) are generated too, for config files + that may reference them. +* Keys are PKCS#8 (openssl 3 writes that too); the _des3 ones and the CA key + are encrypted with the passphrase ``httpd``. +""" + +import datetime +import hashlib +from pathlib import Path +from typing import Dict, List, Optional, Tuple + +from cryptography import x509 +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import dsa, rsa +from cryptography.x509.name import _ASN1Type +from cryptography.x509.oid import (ExtendedKeyUsageOID, NameOID, + ObjectIdentifier) + +PASS = "httpd" +DAYS = 365 +EMAIL_FIELD = "emailAddress" + +# CA distinguished name (ca_dn{asf}). +CA_DN = { + "C": "US", + "ST": "California", + "L": "San Francisco", + "O": "ASF", + "OU": "httpd-test", + "CN": "", + EMAIL_FIELD: "test-dev@httpd.apache.org", +} + +# Per-cert DN overrides (cert_dn). CN for server* is set to the servername at +# generation time (matching generate() in the Perl module). +CERT_DN: Dict[str, Dict[str, str]] = { + "client_snakeoil": { + "C": "AU", "ST": "Queensland", "L": "Mackay", + "O": "Snake Oil, Ltd.", "OU": "Staff", + }, + "client_ok": {}, + "client_colon": {"CN": "user:colon"}, + "client_revoked": {}, + "server": {"CN": "localhost", "OU": "httpd-test/rsa-test"}, + "server2": {"CN": "localhost", "OU": "httpd-test/rsa-test-2"}, + "server_des3": {"CN": "localhost", "OU": "httpd-test/rsa-des3-test"}, + "server2_des3": {"CN": "localhost", "OU": "httpd-test/rsa-des3-test-2"}, +} +# DSA variants of every server cert (Perl derives these dynamically). +for _k in list(CERT_DN): + if _k.startswith("server"): + _dsa = dict(CERT_DN[_k]) + _dsa["OU"] = _dsa.get("OU", "").replace("rsa", "dsa") + CERT_DN[f"{_k}_dsa"] = _dsa + +# DN attributes in the order "openssl req" and the policy of "openssl ca" put them +_DN_ATTRS: List[Tuple[str, ObjectIdentifier]] = [ + ("C", NameOID.COUNTRY_NAME), + ("ST", NameOID.STATE_OR_PROVINCE_NAME), + ("L", NameOID.LOCALITY_NAME), + ("O", NameOID.ORGANIZATION_NAME), + ("OU", NameOID.ORGANIZATIONAL_UNIT_NAME), + ("CN", NameOID.COMMON_NAME), + (EMAIL_FIELD, NameOID.EMAIL_ADDRESS), +] + +# nsComment, and the custom OID of the client_ok certificate: the DER encoded +# UTF8String "Lemons" (0c=UTF8String, 06=length, 4c656d6f6e73="Lemons"), which +# t/ssl/require.t's certext location asserts: "Lemons" in PeerExtList(). +_NS_COMMENT_OID = ObjectIdentifier("2.16.840.1.113730.1.13") +_CLIENT_OK_OID = ObjectIdentifier("1.3.6.1.4.1.18060.12.0") +_NS_COMMENT = b"This Is A Comment" + + +def _openssl_time(t: datetime.datetime) -> str: + return t.strftime("%y%m%d%H%M%SZ") + + +def x509_name_hash(name: x509.Name) -> str: + """The hash OpenSSL names certificate and CRL files in a directory by + (``openssl x509 -hash``, ``openssl crl -hash``): the first four bytes, + little endian, of the SHA-1 of the canonical form of the name. In that + form every value is a UTF8String, with leading and trailing whitespace + removed, runs of whitespace collapsed to one space, and ASCII upper case + made lower case. + """ + canon = [] + for rdn in name.rdns: + canon.append(x509.RelativeDistinguishedName([ + x509.NameAttribute( + a.oid, " ".join(a.value.split()).translate(_ASCII_LOWER), + _type=_ASN1Type.UTF8String) + for a in rdn])) + der = x509.Name(canon).public_bytes() + # the canonical encoding is the RDNs without the SEQUENCE around them + if der[1] < 0x80: + body = der[2:] + else: + body = der[2 + (der[1] & 0x7F):] + return f"{int.from_bytes(hashlib.sha1(body).digest()[:4], 'little'):08x}" + + +_ASCII_LOWER = {c: c + 32 for c in range(ord("A"), ord("Z") + 1)} + + +class SSLCA: + def __init__(self, ca_root: Path, servername: str, *, + openssl: Optional[str] = None) -> None: + # ca_root is e.g. t/conf/ssl/ca ; the CA tree lives under ca_root/asf. + # *openssl* is accepted for compatibility and unused. + self.root = Path(ca_root) + self.ca = "asf" + self.servername = servername + self.dir = self.root / self.ca + self._serial = 1 + self._index: List[str] = [] + self._ca_key = None + self._ca_cert: Optional[x509.Certificate] = None + self._dsa_params = None + + # -- helpers ---------------------------------------------------------- + + def _dn(self, name: str) -> Dict[str, str]: + dn = dict(CA_DN) + dn["CN"] = dn["CN"] or name + dn.update(CERT_DN.get(name, {})) + if name.startswith("server"): + dn["CN"] = self.servername + return dn + + @staticmethod + def _x509_name(dn: Dict[str, str]) -> x509.Name: + return x509.Name([x509.NameAttribute(oid, dn[key]) + for key, oid in _DN_ATTRS if dn.get(key)]) + + @staticmethod + def _slash_dn(dn: Dict[str, str]) -> str: + # as in the database of "openssl ca": a "/" in a value is escaped + return "".join(f"/{key}={dn[key].replace('/', chr(92) + '/')}" + for key, _ in _DN_ATTRS if dn.get(key)) + + @staticmethod + def _key_pem(key, encrypted: bool) -> bytes: + enc = (serialization.BestAvailableEncryption(PASS.encode()) + if encrypted else serialization.NoEncryption()) + return key.private_bytes(serialization.Encoding.PEM, + serialization.PrivateFormat.PKCS8, enc) + + def _write(self, rel: str, data: bytes) -> None: + (self.dir / rel).write_bytes(data) + + # -- generation steps ------------------------------------------------- + + def _init_dirs(self) -> None: + for d in ("keys", "newcerts", "certs", "crl", "export", "csr", "conf", "proxy"): + (self.dir / d).mkdir(parents=True, exist_ok=True) + + def _new_ca(self, now: datetime.datetime) -> None: + dn = self._dn("ca") + name = self._x509_name(dn) + self._ca_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) + ski = x509.SubjectKeyIdentifier.from_public_key(self._ca_key.public_key()) + self._ca_cert = ( + x509.CertificateBuilder() + .subject_name(name).issuer_name(name) + .public_key(self._ca_key.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(now).not_valid_after(now + datetime.timedelta(days=DAYS)) + .add_extension(ski, critical=False) + .add_extension(x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ski), + critical=False) + .add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True) + # OpenSSL 3's verifier rejects a CA cert used to sign certs unless + # it carries keyCertSign (and cRLSign for the CRL). + .add_extension(x509.KeyUsage( + digital_signature=False, content_commitment=False, + key_encipherment=False, data_encipherment=False, + key_agreement=False, key_cert_sign=True, crl_sign=True, + encipher_only=False, decipher_only=False), critical=True) + .sign(self._ca_key, hashes.SHA256()) + ) + self._write("keys/ca.pem", self._key_pem(self._ca_key, True)) + self._write("certs/ca.crt", self._ca_cert.public_bytes(serialization.Encoding.PEM)) + + def _new_key(self, name: str): + if "dsa" in name: + if self._dsa_params is None: + self._dsa_params = dsa.generate_parameters(key_size=2048) + key = self._dsa_params.generate_private_key() + else: + key = rsa.generate_private_key(public_exponent=65537, key_size=2048) + self._write(f"keys/{name}.pem", self._key_pem(key, "_des3" in name)) + return key + + def _extensions(self, name: str, dn: Dict[str, str], pub) -> List[Tuple[x509.ExtensionType, bool]]: + exts: List[Tuple[x509.ExtensionType, bool]] = [] + mail = dn[EMAIL_FIELD] + if "client_ok" in name: # client_ok_ext + exts.append((x509.UnrecognizedExtension( + _NS_COMMENT_OID, bytes([0x16, len(_NS_COMMENT)]) + _NS_COMMENT), False)) + exts.append((x509.UnrecognizedExtension( + _CLIENT_OK_OID, bytes.fromhex("0c064c656d6f6e73")), False)) + exts.append((x509.SubjectAlternativeName([x509.RFC822Name(mail)]), False)) + elif "client" in name: # client_ext + exts.append((x509.ExtendedKeyUsage([ExtendedKeyUsageOID.CLIENT_AUTH]), False)) + if "server" in name: # server_ext + exts.append((x509.SubjectAlternativeName([x509.DNSName(dn["CN"])]), False)) + exts.append((x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH]), False)) + return exts + + def _new_cert(self, name: str, key, now: datetime.datetime) -> x509.Certificate: + dn = self._dn(name) + ca_ski = x509.SubjectKeyIdentifier.from_public_key(self._ca_key.public_key()) + builder = ( + x509.CertificateBuilder() + .subject_name(self._x509_name(dn)) + .issuer_name(self._ca_cert.subject) + .public_key(key.public_key()) + .serial_number(self._serial) + .not_valid_before(now).not_valid_after(now + datetime.timedelta(days=DAYS)) + ) + if "server" in name: + # the key identifiers are part of server_ext; the client + # certificates never had them + builder = ( + builder + .add_extension(x509.SubjectKeyIdentifier.from_public_key(key.public_key()), + critical=False) + .add_extension( + x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ca_ski), + critical=False) + ) + for ext, critical in self._extensions(name, dn, key.public_key()): + builder = builder.add_extension(ext, critical=critical) + cert = builder.sign(self._ca_key, hashes.SHA256()) + pem = cert.public_bytes(serialization.Encoding.PEM) + self._write(f"certs/{name}.crt", pem) + self._write(f"newcerts/{self._serial:02X}.pem", pem) + # the "openssl ca" database: valid, expires, no revocation, serial, + # file "unknown", subject + self._index.append( + f"V\t{_openssl_time(cert.not_valid_after_utc)}\t\t{self._serial:02X}" + f"\tunknown\t{self._slash_dn(dn)}") + self._serial += 1 + return cert + + def _make_proxy_cert(self, name: str, key) -> None: + """Certificate and key in one file, proxy/.pem (make_proxy_cert).""" + crt = (self.dir / "certs" / f"{name}.crt").read_bytes() + self._write(f"proxy/{name}.pem", crt + self._key_pem(key, False)) + + def _revoke(self, name: str, cert: x509.Certificate, now: datetime.datetime, + revoked: List[Tuple[x509.Certificate, datetime.datetime]]) -> None: + # the database line of the certificate becomes an "R" line: + # R, expires, revoked at, serial, file, subject + for i, line in enumerate(self._index): + fields = line.split("\t") + if fields[3] == f"{cert.serial_number:02X}": + fields[0] = "R" + fields[2] = _openssl_time(now) + self._index[i] = "\t".join(fields) + revoked.append((cert, now)) + + def _write_crl(self, now: datetime.datetime, + revoked: List[Tuple[x509.Certificate, datetime.datetime]]) -> None: + ca_ski = x509.SubjectKeyIdentifier.from_public_key(self._ca_key.public_key()) + builder = ( + x509.CertificateRevocationListBuilder() + .issuer_name(self._ca_cert.subject) + .last_update(now).next_update(now + datetime.timedelta(days=DAYS)) + .add_extension(x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ca_ski), + critical=False) + .add_extension(x509.CRLNumber(1), critical=False) + ) + for cert, when in revoked: + builder = builder.add_revoked_certificate( + x509.RevokedCertificateBuilder() + .serial_number(cert.serial_number).revocation_date(when).build()) + crl = builder.sign(self._ca_key, hashes.SHA256()) + self._write("crl/ca-bundle.crl", crl.public_bytes(serialization.Encoding.PEM)) + # SSLProxyCARevocationPath finds the CRL of an issuer by the hash of + # the issuer's name: .r0 + link = self.dir / "crl" / f"{x509_name_hash(self._ca_cert.subject)}.r0" + link.unlink(missing_ok=True) + link.symlink_to("ca-bundle.crl") + + def generate(self) -> Path: + """Build the full CA tree (idempotent). Returns the CA root dir.""" + if self.dir.is_dir() and (self.dir / "certs" / "ca.crt").exists(): + return self.root # already generated + now = (datetime.datetime.now(datetime.timezone.utc) + .replace(microsecond=0) - datetime.timedelta(minutes=1)) + self._init_dirs() + self._new_ca(now) + revoked: List[Tuple[x509.Certificate, datetime.datetime]] = [] + for name in CERT_DN: + key = self._new_key(name) + cert = self._new_cert(name, key, now) + if name.endswith("_revoked"): + self._revoke(name, cert, now, revoked) + if name.startswith("client_"): + self._make_proxy_cert(name, key) + # crl/ca-bundle.crl is referenced by SSLCARevocationFile + self._write_crl(now, revoked) + self._write("index.txt", ("\n".join(self._index) + "\n").encode()) + self._write("serial", f"{self._serial:02X}\n".encode()) + return self.root diff -Nru apache2-2.4.69/test/pyhttpd/uv.lock apache2-2.4.71/test/pyhttpd/uv.lock --- apache2-2.4.69/test/pyhttpd/uv.lock 2026-09-28 20:21:42.000000000 +0000 +++ apache2-2.4.71/test/pyhttpd/uv.lock 1970-01-01 00:00:00.000000000 +0000 @@ -1,679 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.9" -resolution-markers = [ - "python_full_version >= '3.10'", - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", version = "2.23", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and implementation_name != 'PyPy'" }, - { name = "pycparser", version = "3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' and implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, - { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, - { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, - { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, - { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, - { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, - { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, - { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, - { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, - { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, - { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, - { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, - { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, - { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, - { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, - { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, - { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, - { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, - { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, - { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, - { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, - { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, - { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, - { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, - { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cc/08ed5a43f2996a16b462f64a7055c6e962803534924b9b2f1371d8c00b7b/cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf", size = 184288, upload-time = "2025-09-08T23:23:48.404Z" }, - { url = "https://files.pythonhosted.org/packages/3d/de/38d9726324e127f727b4ecc376bc85e505bfe61ef130eaf3f290c6847dd4/cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7", size = 180509, upload-time = "2025-09-08T23:23:49.73Z" }, - { url = "https://files.pythonhosted.org/packages/9b/13/c92e36358fbcc39cf0962e83223c9522154ee8630e1df7c0b3a39a8124e2/cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c", size = 208813, upload-time = "2025-09-08T23:23:51.263Z" }, - { url = "https://files.pythonhosted.org/packages/15/12/a7a79bd0df4c3bff744b2d7e52cc1b68d5e7e427b384252c42366dc1ecbc/cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165", size = 216498, upload-time = "2025-09-08T23:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/a3/ad/5c51c1c7600bdd7ed9a24a203ec255dccdd0ebf4527f7b922a0bde2fb6ed/cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534", size = 203243, upload-time = "2025-09-08T23:23:53.836Z" }, - { url = "https://files.pythonhosted.org/packages/32/f2/81b63e288295928739d715d00952c8c6034cb6c6a516b17d37e0c8be5600/cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f", size = 203158, upload-time = "2025-09-08T23:23:55.169Z" }, - { url = "https://files.pythonhosted.org/packages/1f/74/cc4096ce66f5939042ae094e2e96f53426a979864aa1f96a621ad128be27/cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63", size = 216548, upload-time = "2025-09-08T23:23:56.506Z" }, - { url = "https://files.pythonhosted.org/packages/e8/be/f6424d1dc46b1091ffcc8964fa7c0ab0cd36839dd2761b49c90481a6ba1b/cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2", size = 218897, upload-time = "2025-09-08T23:23:57.825Z" }, - { url = "https://files.pythonhosted.org/packages/f7/e0/dda537c2309817edf60109e39265f24f24aa7f050767e22c98c53fe7f48b/cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65", size = 211249, upload-time = "2025-09-08T23:23:59.139Z" }, - { url = "https://files.pythonhosted.org/packages/2b/e7/7c769804eb75e4c4b35e658dba01de1640a351a9653c3d49ca89d16ccc91/cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322", size = 218041, upload-time = "2025-09-08T23:24:00.496Z" }, - { url = "https://files.pythonhosted.org/packages/aa/d9/6218d78f920dcd7507fc16a766b5ef8f3b913cc7aa938e7fc80b9978d089/cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a", size = 172138, upload-time = "2025-09-08T23:24:01.7Z" }, - { url = "https://files.pythonhosted.org/packages/54/8f/a1e836f82d8e32a97e6b29cc8f641779181ac7363734f12df27db803ebda/cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9", size = 182794, upload-time = "2025-09-08T23:24:02.943Z" }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "cryptography" -version = "47.0.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version <= '3.9'", -] -dependencies = [ - { name = "cffi", marker = "python_full_version <= '3.9' and platform_python_implementation != 'PyPy'" }, - { name = "typing-extensions", marker = "python_full_version <= '3.9'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ef/b2/7ffa7fe8207a8c42147ffe70c3e360b228160c1d85dc3faff16aaa3244c0/cryptography-47.0.0.tar.gz", hash = "sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb", size = 830863, upload-time = "2026-04-24T19:54:57.056Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/98/40dfe932134bdcae4f6ab5927c87488754bf9eb79297d7e0070b78dd58e9/cryptography-47.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:160ad728f128972d362e714054f6ba0067cab7fb350c5202a9ae8ae4ce3ef1a0", size = 7912214, upload-time = "2026-04-24T19:53:03.864Z" }, - { url = "https://files.pythonhosted.org/packages/34/c6/2733531243fba725f58611b918056b277692f1033373dcc8bd01af1c05d4/cryptography-47.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b9a8943e359b7615db1a3ba587994618e094ff3d6fa5a390c73d079ce18b3973", size = 4644617, upload-time = "2026-04-24T19:53:06.909Z" }, - { url = "https://files.pythonhosted.org/packages/00/e3/b27be1a670a9b87f855d211cf0e1174a5d721216b7616bd52d8581d912ed/cryptography-47.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f5c15764f261394b22aef6b00252f5195f46f2ca300bec57149474e2538b31f8", size = 4668186, upload-time = "2026-04-24T19:53:09.053Z" }, - { url = "https://files.pythonhosted.org/packages/81/b9/8443cfe5d17d482d348cee7048acf502bb89a51b6382f06240fd290d4ca3/cryptography-47.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9c59ab0e0fa3a180a5a9c59f3a5abe3ef90d474bc56d7fadfbe80359491b615b", size = 4651244, upload-time = "2026-04-24T19:53:11.217Z" }, - { url = "https://files.pythonhosted.org/packages/5d/5e/13ed0cdd0eb88ba159d6dd5ebfece8cb901dbcf1ae5ac4072e28b55d3153/cryptography-47.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:34b4358b925a5ea3e14384ca781a2c0ef7ac219b57bb9eacc4457078e2b19f92", size = 5252906, upload-time = "2026-04-24T19:53:13.532Z" }, - { url = "https://files.pythonhosted.org/packages/64/16/ed058e1df0f33d440217cd120d41d5dda9dd215a80b8187f68483185af82/cryptography-47.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0024b87d47ae2399165a6bfb20d24888881eeab83ae2566d62467c5ff0030ce7", size = 4701842, upload-time = "2026-04-24T19:53:15.618Z" }, - { url = "https://files.pythonhosted.org/packages/02/e0/3d30986b30fdbd9e969abbdf8ba00ed0618615144341faeb57f395a084fe/cryptography-47.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:1e47422b5557bb82d3fff997e8d92cff4e28b9789576984f08c248d2b3535d93", size = 4289313, upload-time = "2026-04-24T19:53:17.755Z" }, - { url = "https://files.pythonhosted.org/packages/df/fd/32db38e3ad0cb331f0691cb4c7a8a6f176f679124dee746b3af6633db4d9/cryptography-47.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:6f29f36582e6151d9686235e586dd35bb67491f024767d10b842e520dc6a07ac", size = 4650964, upload-time = "2026-04-24T19:53:20.062Z" }, - { url = "https://files.pythonhosted.org/packages/86/53/5395d944dfd48cb1f67917f533c609c34347185ef15eb4308024c876f274/cryptography-47.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a9b761f012a943b7de0e828843c5688d0de94a0578d44d6c85a1bae32f87791f", size = 5207817, upload-time = "2026-04-24T19:53:22.498Z" }, - { url = "https://files.pythonhosted.org/packages/34/4f/e5711b28e1901f7d480a2b1b688b645aa4c77c73f10731ed17e7f7db3f0d/cryptography-47.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4e1de79e047e25d6e9f8cea71c86b4a53aced64134f0f003bbcbf3655fd172c8", size = 4701544, upload-time = "2026-04-24T19:53:24.356Z" }, - { url = "https://files.pythonhosted.org/packages/22/22/c8ddc25de3010fc8da447648f5a092c40e7a8fadf01dd6d255d9c0b9373d/cryptography-47.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef6b3634087f18d2155b1e8ce264e5345a753da2c5fa9815e7d41315c90f8318", size = 4783536, upload-time = "2026-04-24T19:53:26.665Z" }, - { url = "https://files.pythonhosted.org/packages/66/b6/d4a68f4ea999c6d89e8498579cba1c5fcba4276284de7773b17e4fa69293/cryptography-47.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:11dbb9f50a0f1bb9757b3d8c27c1101780efb8f0bdecfb12439c22a74d64c001", size = 4926106, upload-time = "2026-04-24T19:53:28.686Z" }, - { url = "https://files.pythonhosted.org/packages/54/ed/5f524db1fade9c013aa618e1c99c6ed05e8ffc9ceee6cda22fed22dda3f4/cryptography-47.0.0-cp311-abi3-win32.whl", hash = "sha256:7fda2f02c9015db3f42bb8a22324a454516ed10a8c29ca6ece6cdbb5efe2a203", size = 3258581, upload-time = "2026-04-24T19:53:31.058Z" }, - { url = "https://files.pythonhosted.org/packages/b2/dc/1b901990b174786569029f67542b3edf72ac068b6c3c8683c17e6a2f5363/cryptography-47.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:f5c3296dab66202f1b18a91fa266be93d6aa0c2806ea3d67762c69f60adc71aa", size = 3775309, upload-time = "2026-04-24T19:53:33.054Z" }, - { url = "https://files.pythonhosted.org/packages/14/88/7aa18ad9c11bc87689affa5ce4368d884b517502d75739d475fc6f4a03c7/cryptography-47.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:be12cb6a204f77ed968bcefe68086eb061695b540a3dd05edac507a3111b25f0", size = 7904299, upload-time = "2026-04-24T19:53:35.003Z" }, - { url = "https://files.pythonhosted.org/packages/07/55/c18f75724544872f234678fdedc871391722cb34a2aee19faa9f63100bb2/cryptography-47.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2ebd84adf0728c039a3be2700289378e1c164afc6748df1a5ed456767bef9ba7", size = 4631180, upload-time = "2026-04-24T19:53:37.517Z" }, - { url = "https://files.pythonhosted.org/packages/ee/65/31a5cc0eaca99cec5bafffe155d407115d96136bb161e8b49e0ef73f09a7/cryptography-47.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f68d6fbc7fbbcfb0939fea72c3b96a9f9a6edfc0e1b1d29778a2066030418b1", size = 4653529, upload-time = "2026-04-24T19:53:39.775Z" }, - { url = "https://files.pythonhosted.org/packages/e5/bc/641c0519a495f3bfd0421b48d7cd325c4336578523ccd76ea322b6c29c7a/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:6651d32eff255423503aa276739da98c30f26c40cbeffcc6048e0d54ef704c0c", size = 4638570, upload-time = "2026-04-24T19:53:42.129Z" }, - { url = "https://files.pythonhosted.org/packages/2b/f2/300327b0a47f6dc94dd8b71b57052aefe178bb51745073d73d80604f11ab/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3fb8fa48075fad7193f2e5496135c6a76ac4b2aa5a38433df0a539296b377829", size = 5238019, upload-time = "2026-04-24T19:53:44.577Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5a/5b5cf994391d4bf9d9c7efd4c66aabe4d95227256627f8fea6cff7dfadbd/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:11438c7518132d95f354fa01a4aa2f806d172a061a7bed18cf18cbdacdb204d7", size = 4686832, upload-time = "2026-04-24T19:53:47.015Z" }, - { url = "https://files.pythonhosted.org/packages/dc/2c/ae950e28fd6475c852fc21a44db3e6b5bcc1261d1e370f2b6e42fa800fef/cryptography-47.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8c1a736bbb3288005796c3f7ccb9453360d7fed483b13b9f468aea5171432923", size = 4269301, upload-time = "2026-04-24T19:53:48.97Z" }, - { url = "https://files.pythonhosted.org/packages/67/fb/6a39782e150ffe5cc1b0018cb6ddc48bf7ca62b498d7539ffc8a758e977d/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:f1557695e5c2b86e204f6ce9470497848634100787935ab7adc5397c54abd7ab", size = 4638110, upload-time = "2026-04-24T19:53:51.011Z" }, - { url = "https://files.pythonhosted.org/packages/8e/d7/0b3c71090a76e5c203164a47688b697635ece006dcd2499ab3a4dbd3f0bd/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:f9a034b642b960767fb343766ae5ba6ad653f2e890ddd82955aef288ffea8736", size = 5194988, upload-time = "2026-04-24T19:53:52.962Z" }, - { url = "https://files.pythonhosted.org/packages/63/33/63a961498a9df51721ab578c5a2622661411fc520e00bd83b0cc64eb20c4/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:b1c76fca783aa7698eb21eb14f9c4aa09452248ee54a627d125025a43f83e7a7", size = 4686563, upload-time = "2026-04-24T19:53:55.274Z" }, - { url = "https://files.pythonhosted.org/packages/b7/bf/5ee5b145248f92250de86145d1c1d6edebbd57a7fe7caa4dedb5d4cf06a1/cryptography-47.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4f7722c97826770bab8ae92959a2e7b20a5e9e9bf4deae68fd86c3ca457bab52", size = 4770094, upload-time = "2026-04-24T19:53:57.753Z" }, - { url = "https://files.pythonhosted.org/packages/92/43/21d220b2da5d517773894dacdcdb5c682c28d3fffce65548cb06e87d5501/cryptography-47.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:09f6d7bf6724f8db8b32f11eccf23efc8e759924bc5603800335cf8859a3ddbd", size = 4913811, upload-time = "2026-04-24T19:54:00.236Z" }, - { url = "https://files.pythonhosted.org/packages/31/98/dc4ad376ac5f1a1a7d4a83f7b0c6f2bcad36b5d2d8f30aeb482d3a7d9582/cryptography-47.0.0-cp314-cp314t-win32.whl", hash = "sha256:6eebcaf0df1d21ce1f90605c9b432dd2c4f4ab665ac29a40d5e3fc68f51b5e63", size = 3237158, upload-time = "2026-04-24T19:54:02.606Z" }, - { url = "https://files.pythonhosted.org/packages/bc/da/97f62d18306b5133468bc3f8cc73a3111e8cdc8cf8d3e69474d6e5fd2d1b/cryptography-47.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:51c9313e90bd1690ec5a75ed047c27c0b8e6c570029712943d6116ef9a90620b", size = 3758706, upload-time = "2026-04-24T19:54:04.433Z" }, - { url = "https://files.pythonhosted.org/packages/e0/34/a4fae8ae7c3bc227460c9ae43f56abf1b911da0ec29e0ebac53bb0a4b6b7/cryptography-47.0.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:14432c8a9bcb37009784f9594a62fae211a2ae9543e96c92b2a8e4c3cd5cd0c4", size = 7904072, upload-time = "2026-04-24T19:54:06.411Z" }, - { url = "https://files.pythonhosted.org/packages/01/64/d7b1e54fdb69f22d24a64bb3e88dc718b31c7fb10ef0b9691a3cf7eeea6e/cryptography-47.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:07efe86201817e7d3c18781ca9770bc0db04e1e48c994be384e4602bc38f8f27", size = 4635767, upload-time = "2026-04-24T19:54:08.519Z" }, - { url = "https://files.pythonhosted.org/packages/8b/7b/cca826391fb2a94efdcdfe4631eb69306ee1cff0b22f664a412c90713877/cryptography-47.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b45761c6ec22b7c726d6a829558777e32d0f1c8be7c3f3480f9c912d5ee8a10", size = 4654350, upload-time = "2026-04-24T19:54:10.795Z" }, - { url = "https://files.pythonhosted.org/packages/4c/65/4b57bcc823f42a991627c51c2f68c9fd6eb1393c1756aac876cba2accae2/cryptography-47.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:edd4da498015da5b9f26d38d3bfc2e90257bfa9cbed1f6767c282a0025ae649b", size = 4643394, upload-time = "2026-04-24T19:54:13.275Z" }, - { url = "https://files.pythonhosted.org/packages/f4/c4/2c5fbeea70adbbca2bbae865e1d605d6a4a7f8dbd9d33eaf69645087f06c/cryptography-47.0.0-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9af828c0d5a65c70ec729cd7495a4bf1a67ecb66417b8f02ff125ab8a6326a74", size = 5225777, upload-time = "2026-04-24T19:54:15.18Z" }, - { url = "https://files.pythonhosted.org/packages/7e/b8/ac57107ef32749d2b244e36069bb688792a363aaaa3acc9e3cf84c130315/cryptography-47.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:256d07c78a04d6b276f5df935a9923275f53bd1522f214447fdf365494e2d515", size = 4688771, upload-time = "2026-04-24T19:54:17.835Z" }, - { url = "https://files.pythonhosted.org/packages/56/fc/9f1de22ff8be99d991f240a46863c52d475404c408886c5a38d2b5c3bb26/cryptography-47.0.0-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:5d0e362ff51041b0c0d219cc7d6924d7b8996f57ce5712bdcef71eb3c65a59cc", size = 4270753, upload-time = "2026-04-24T19:54:19.963Z" }, - { url = "https://files.pythonhosted.org/packages/00/68/d70c852797aa68e8e48d12e5a87170c43f67bb4a59403627259dd57d15de/cryptography-47.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:1581aef4219f7ca2849d0250edaa3866212fb74bf5667284f46aa92f9e65c1ca", size = 4642911, upload-time = "2026-04-24T19:54:21.818Z" }, - { url = "https://files.pythonhosted.org/packages/a5/51/661cbee74f594c5d97ff82d34f10d5551c085ca4668645f4606ebd22bd5d/cryptography-47.0.0-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a49a3eb5341b9503fa3000a9a0db033161db90d47285291f53c2a9d2cd1b7f76", size = 5181411, upload-time = "2026-04-24T19:54:24.376Z" }, - { url = "https://files.pythonhosted.org/packages/94/87/f2b6c374a82cf076cfa1416992ac8e8ec94d79facc37aec87c1a5cb72352/cryptography-47.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2207a498b03275d0051589e326b79d4cf59985c99031b05bb292ac52631c37fe", size = 4688262, upload-time = "2026-04-24T19:54:26.946Z" }, - { url = "https://files.pythonhosted.org/packages/14/e2/8b7462f4acf21ec509616f0245018bb197194ab0b65c2ea21a0bdd53c0eb/cryptography-47.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7a02675e2fabd0c0fc04c868b8781863cbf1967691543c22f5470500ff840b31", size = 4775506, upload-time = "2026-04-24T19:54:28.926Z" }, - { url = "https://files.pythonhosted.org/packages/70/75/158e494e4c08dc05e039da5bb48553826bd26c23930cf8d3cd5f21fa8921/cryptography-47.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80887c5cbd1774683cb126f0ab4184567f080071d5acf62205acb354b4b753b7", size = 4912060, upload-time = "2026-04-24T19:54:30.869Z" }, - { url = "https://files.pythonhosted.org/packages/06/bd/0a9d3edbf5eadbac926d7b9b3cd0c4be584eeeae4a003d24d9eda4affbbd/cryptography-47.0.0-cp38-abi3-win32.whl", hash = "sha256:ed67ea4e0cfb5faa5bc7ecb6e2b8838f3807a03758eec239d6c21c8769355310", size = 3248487, upload-time = "2026-04-24T19:54:33.494Z" }, - { url = "https://files.pythonhosted.org/packages/60/80/5681af756d0da3a599b7bdb586fac5a1540f1bcefd2717a20e611ddade45/cryptography-47.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:835d2d7f47cdc53b3224e90810fb1d36ca94ea29cc1801fb4c1bc43876735769", size = 3755737, upload-time = "2026-04-24T19:54:35.408Z" }, - { url = "https://files.pythonhosted.org/packages/1b/a0/928c9ce0d120a40a81aa99e3ba383e87337b9ac9ef9f6db02e4d7822424d/cryptography-47.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f1207974a904e005f762869996cf620e9bf79ecb4622f148550bb48e0eb35a7", size = 3909893, upload-time = "2026-04-24T19:54:38.334Z" }, - { url = "https://files.pythonhosted.org/packages/81/75/d691e284750df5d9569f2b1ce4a00a71e1d79566da83b2b3e5549c84917f/cryptography-47.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:1a405c08857258c11016777e11c02bacbe7ef596faf259305d282272a3a05cbe", size = 4587867, upload-time = "2026-04-24T19:54:40.619Z" }, - { url = "https://files.pythonhosted.org/packages/07/d6/1b90f1a4e453009730b4545286f0b39bb348d805c11181fc31544e4f9a65/cryptography-47.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:20fdbe3e38fb67c385d233c89371fa27f9909f6ebca1cecc20c13518dae65475", size = 4627192, upload-time = "2026-04-24T19:54:42.849Z" }, - { url = "https://files.pythonhosted.org/packages/dc/53/cb358a80e9e359529f496870dd08c102aa8a4b5b9f9064f00f0d6ed5b527/cryptography-47.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:f7db373287273d8af1414cf95dc4118b13ffdc62be521997b0f2b270771fef50", size = 4587486, upload-time = "2026-04-24T19:54:44.908Z" }, - { url = "https://files.pythonhosted.org/packages/8b/57/aaa3d53876467a226f9a7a82fd14dd48058ad2de1948493442dfa16e2ffd/cryptography-47.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:9fe6b7c64926c765f9dff301f9c1b867febcda5768868ca084e18589113732ab", size = 4626327, upload-time = "2026-04-24T19:54:47.813Z" }, - { url = "https://files.pythonhosted.org/packages/ab/9c/51f28c3550276bcf35660703ba0ab829a90b88be8cd98a71ef23c2413913/cryptography-47.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:cffbba3392df0fa8629bb7f43454ee2925059ee158e23c54620b9063912b86c8", size = 3698916, upload-time = "2026-04-24T19:54:49.782Z" }, -] - -[[package]] -name = "cryptography" -version = "48.0.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", - "python_full_version > '3.9' and python_full_version < '3.10'", -] -dependencies = [ - { name = "cffi", marker = "python_full_version > '3.9' and platform_python_implementation != 'PyPy'" }, - { name = "typing-extensions", marker = "python_full_version > '3.9' and python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/3d/01f6dd9190170a5a241e0e98c2d04be3664a9e6f5b9b872cde63aff1c3dd/cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6", size = 8001587, upload-time = "2026-05-04T22:57:36.803Z" }, - { url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433, upload-time = "2026-05-04T22:57:40.373Z" }, - { url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620, upload-time = "2026-05-04T22:57:42.935Z" }, - { url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283, upload-time = "2026-05-04T22:57:45.294Z" }, - { url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573, upload-time = "2026-05-04T22:57:47.933Z" }, - { url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677, upload-time = "2026-05-04T22:57:50.067Z" }, - { url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808, upload-time = "2026-05-04T22:57:52.301Z" }, - { url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941, upload-time = "2026-05-04T22:57:54.603Z" }, - { url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579, upload-time = "2026-05-04T22:57:57.207Z" }, - { url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326, upload-time = "2026-05-04T22:57:59.535Z" }, - { url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672, upload-time = "2026-05-04T22:58:01.996Z" }, - { url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574, upload-time = "2026-05-04T22:58:03.968Z" }, - { url = "https://files.pythonhosted.org/packages/04/70/e5a1b41d325f797f39427aa44ef8baf0be500065ab6d8e10369d850d4a4f/cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4", size = 3294868, upload-time = "2026-05-04T22:58:06.467Z" }, - { url = "https://files.pythonhosted.org/packages/f4/ac/8ac51b4a5fc5932eb7ee5c517ba7dc8cd834f0048962b6b352f00f41ebf9/cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7", size = 3817107, upload-time = "2026-05-04T22:58:08.845Z" }, - { url = "https://files.pythonhosted.org/packages/6b/84/70e3feea9feea87fd7cbe77efb2712ae1e3e6edf10749dc6e95f4e60e455/cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec", size = 7986556, upload-time = "2026-05-04T22:58:11.172Z" }, - { url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776, upload-time = "2026-05-04T22:58:13.712Z" }, - { url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121, upload-time = "2026-05-04T22:58:16.448Z" }, - { url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042, upload-time = "2026-05-04T22:58:18.544Z" }, - { url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526, upload-time = "2026-05-04T22:58:20.75Z" }, - { url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116, upload-time = "2026-05-04T22:58:23.627Z" }, - { url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030, upload-time = "2026-05-04T22:58:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640, upload-time = "2026-05-04T22:58:27.747Z" }, - { url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657, upload-time = "2026-05-04T22:58:29.848Z" }, - { url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362, upload-time = "2026-05-04T22:58:32.009Z" }, - { url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580, upload-time = "2026-05-04T22:58:34.254Z" }, - { url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283, upload-time = "2026-05-04T22:58:36.376Z" }, - { url = "https://files.pythonhosted.org/packages/75/54/cc6d0f3deac3e81c7f847e8a189a12b6cdd65059b43dad25d4316abd849a/cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f", size = 3270954, upload-time = "2026-05-04T22:58:38.791Z" }, - { url = "https://files.pythonhosted.org/packages/49/67/cc947e288c0758a4e5473d1dcb743037ab7785541265a969240b8885441a/cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12", size = 3797313, upload-time = "2026-05-04T22:58:40.746Z" }, - { url = "https://files.pythonhosted.org/packages/f2/63/61d4a4e1c6b6bab6ce1e213cd36a24c415d90e76d78c5eb8577c5541d2e8/cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86", size = 7983482, upload-time = "2026-05-04T22:58:43.769Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266, upload-time = "2026-05-04T22:58:46.064Z" }, - { url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228, upload-time = "2026-05-04T22:58:48.22Z" }, - { url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097, upload-time = "2026-05-04T22:58:50.9Z" }, - { url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582, upload-time = "2026-05-04T22:58:53.017Z" }, - { url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479, upload-time = "2026-05-04T22:58:55.611Z" }, - { url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481, upload-time = "2026-05-04T22:58:57.607Z" }, - { url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713, upload-time = "2026-05-04T22:59:00.077Z" }, - { url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165, upload-time = "2026-05-04T22:59:02.317Z" }, - { url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947, upload-time = "2026-05-04T22:59:05.255Z" }, - { url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059, upload-time = "2026-05-04T22:59:07.802Z" }, - { url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575, upload-time = "2026-05-04T22:59:09.851Z" }, - { url = "https://files.pythonhosted.org/packages/b8/23/6e6f32143ab5d8b36ca848a502c4bcd477ae75b9e1677e3530d669062578/cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd", size = 3279117, upload-time = "2026-05-04T22:59:12.019Z" }, - { url = "https://files.pythonhosted.org/packages/9d/9a/0fea98a70cf1749d41d738836f6349d97945f7c89433a259a6c2642eefeb/cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8", size = 3792100, upload-time = "2026-05-04T22:59:14.884Z" }, - { url = "https://files.pythonhosted.org/packages/be/d2/024b5e06be9d44cb021fb0e1a03d34d63989cf56a0fe62f3dfbab695b9b4/cryptography-48.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:84cf79f0dc8b36ac5da873481716e87aef31fcfa0444f9e1d8b4b2cece142855", size = 3950391, upload-time = "2026-05-04T22:59:17.415Z" }, - { url = "https://files.pythonhosted.org/packages/bc/17/3861e17c56fa0fd37491a14a8673fdb77c57fc5693cafe745ea8b06dba75/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fdfef35d751d510fcef5252703621574364fec16418c4a1e5e1055248401054b", size = 4637126, upload-time = "2026-05-04T22:59:20.197Z" }, - { url = "https://files.pythonhosted.org/packages/f0/0a/7e226dbff530f21480727eb764973a7bff2b912f8e15cd4f129e71b56d1d/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0890f502ddf7d9c6426129c3f49f5c0a39278ed7cd6322c8755ffca6ee675a13", size = 4667270, upload-time = "2026-05-04T22:59:22.647Z" }, - { url = "https://files.pythonhosted.org/packages/3b/f2/5a72274ca9f1b2a8b44a662ee0bf1b435909deb473d6f97bcd035bcdbc71/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:ecde28a596bead48b0cfd2a1b4416c3d43074c2d785e3a398d7ec1fc4d0f7fbb", size = 4636797, upload-time = "2026-05-04T22:59:24.912Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e1/48cedb2fe63626e91ded1edad159e2a4fb8b6906c4425eb7749673077ce7/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:4defde8685ae324a9eb9d818717e93b4638ef67070ac9bc15b8ca85f63048355", size = 4666800, upload-time = "2026-05-04T22:59:27.474Z" }, - { url = "https://files.pythonhosted.org/packages/a2/ca/7e8365deec19afb2b2c7be7c1c0aa8f99633b54e90c570999acda93260fc/cryptography-48.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:db63bf618e5dea46c07de12e900fe1cdd2541e6dc9dbae772a70b7d4d4765f6a", size = 3739536, upload-time = "2026-05-04T22:59:29.61Z" }, -] - -[[package]] -name = "exceptiongroup" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "filelock" -version = "3.19.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] -sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, -] - -[[package]] -name = "filelock" -version = "3.29.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/1f/f9/f38573ed5844586db374d085911740a501ccfa373b455fc9413f09f85237/filelock-3.29.1.tar.gz", hash = "sha256:d97e6b1b9757569626c58caa07dc4beb1613f4a2938b1e8cc81afca398906c9e", size = 59335, upload-time = "2026-06-03T15:19:04.053Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/a0/614c5fe402fd88951df45f4dda2fa3b4e17a99ecd92340771929169b3b95/filelock-3.29.1-py3-none-any.whl", hash = "sha256:85199dfd706869641b72b2e8955d5416a4b2b7dc4b0e8e6d97b4cc1299a6983b", size = 40750, upload-time = "2026-06-03T15:19:02.959Z" }, -] - -[[package]] -name = "httpd-pyhttpd-tests" -version = "0.1.0" -source = { virtual = "." } -dependencies = [ - { name = "cryptography", version = "47.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version <= '3.9'" }, - { name = "cryptography", version = "48.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version > '3.9'" }, - { name = "filelock", version = "3.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "filelock", version = "3.29.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "packaging" }, - { name = "pyopenssl" }, - { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "pytest", version = "9.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "python-multipart", version = "0.0.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "python-multipart", version = "0.0.30", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "websockets", version = "15.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "websockets", version = "16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, -] - -[package.metadata] -requires-dist = [ - { name = "cryptography" }, - { name = "filelock" }, - { name = "packaging" }, - { name = "pyopenssl" }, - { name = "pytest", specifier = ">=7.0" }, - { name = "python-multipart" }, - { name = "websockets" }, -] - -[[package]] -name = "iniconfig" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "packaging" -version = "26.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "pycparser" -version = "2.23" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] -sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pygments" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, -] - -[[package]] -name = "pyopenssl" -version = "26.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography", version = "47.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version <= '3.9'" }, - { name = "cryptography", version = "48.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version > '3.9'" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1a/51/27a5ad5f939d08f690a326ef9582cda7140555180db71695f6fb747d6a36/pyopenssl-26.2.0.tar.gz", hash = "sha256:8c6fcecd1183a7fc897548dfe388b0cdb7f37e018200d8409cf33959dbe35387", size = 182195, upload-time = "2026-05-04T23:06:09.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/73/b8/a0e2790ae249d6f38c9f66de7a211621a7ab2650217bcd04e1262f578a56/pyopenssl-26.2.0-py3-none-any.whl", hash = "sha256:4f9d971bc5298b8bc1fab282803da04bf000c755d4ad9d99b52de2569ca19a70", size = 55823, upload-time = "2026-05-04T23:06:08.395Z" }, -] - -[[package]] -name = "pytest" -version = "8.4.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.10'" }, - { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "packaging", marker = "python_full_version < '3.10'" }, - { name = "pluggy", marker = "python_full_version < '3.10'" }, - { name = "pygments", marker = "python_full_version < '3.10'" }, - { name = "tomli", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, -] - -[[package]] -name = "pytest" -version = "9.0.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version == '3.10.*'" }, - { name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "packaging", marker = "python_full_version >= '3.10'" }, - { name = "pluggy", marker = "python_full_version >= '3.10'" }, - { name = "pygments", marker = "python_full_version >= '3.10'" }, - { name = "tomli", marker = "python_full_version == '3.10.*'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, -] - -[[package]] -name = "python-multipart" -version = "0.0.20" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, -] - -[[package]] -name = "python-multipart" -version = "0.0.30" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/4b/82/c8cd43a6e0719bf5a3b034f6726dd701f75829c08944c83d4b95d02ed0e8/python_multipart-0.0.30.tar.gz", hash = "sha256:0edfe0475c1f46ddd3ff7785a626f6118af32bdcf359bb21260367313bb32118", size = 46316, upload-time = "2026-05-31T19:24:55.198Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/fd/0318007beb234790993d3ec5afd051d1dbceb733e81e3afe2b981ece3f37/python_multipart-0.0.30-py3-none-any.whl", hash = "sha256:830964def8c90607ac5daa00514e3987815865713ade8d20febc9177ac0c3c5b", size = 29730, upload-time = "2026-05-31T19:24:53.814Z" }, -] - -[[package]] -name = "tomli" -version = "2.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, - { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, - { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, - { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, - { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, - { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, - { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, - { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, - { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, - { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, - { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, - { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, - { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, - { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, - { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, - { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, - { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, - { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, - { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, - { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, - { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, - { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, - { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, - { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, - { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, - { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, - { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, - { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, - { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, - { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, - { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, - { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, - { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, - { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, - { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, - { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, - { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, - { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, - { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, - { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, - { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, - { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, - { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, - { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] - -[[package]] -name = "websockets" -version = "15.0.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version > '3.9' and python_full_version < '3.10'", - "python_full_version <= '3.9'", -] -sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423, upload-time = "2025-03-05T20:01:35.363Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080, upload-time = "2025-03-05T20:01:37.304Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329, upload-time = "2025-03-05T20:01:39.668Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312, upload-time = "2025-03-05T20:01:41.815Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319, upload-time = "2025-03-05T20:01:43.967Z" }, - { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631, upload-time = "2025-03-05T20:01:46.104Z" }, - { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016, upload-time = "2025-03-05T20:01:47.603Z" }, - { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426, upload-time = "2025-03-05T20:01:48.949Z" }, - { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360, upload-time = "2025-03-05T20:01:50.938Z" }, - { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388, upload-time = "2025-03-05T20:01:52.213Z" }, - { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830, upload-time = "2025-03-05T20:01:53.922Z" }, - { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" }, - { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" }, - { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" }, - { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" }, - { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" }, - { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" }, - { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" }, - { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" }, - { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" }, - { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, - { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, - { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, - { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, - { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, - { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, - { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, - { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, - { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, - { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, - { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, - { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, - { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, - { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, - { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, - { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, - { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, - { url = "https://files.pythonhosted.org/packages/36/db/3fff0bcbe339a6fa6a3b9e3fbc2bfb321ec2f4cd233692272c5a8d6cf801/websockets-15.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f4c04ead5aed67c8a1a20491d54cdfba5884507a48dd798ecaf13c74c4489f5", size = 175424, upload-time = "2025-03-05T20:02:56.505Z" }, - { url = "https://files.pythonhosted.org/packages/46/e6/519054c2f477def4165b0ec060ad664ed174e140b0d1cbb9fafa4a54f6db/websockets-15.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abdc0c6c8c648b4805c5eacd131910d2a7f6455dfd3becab248ef108e89ab16a", size = 173077, upload-time = "2025-03-05T20:02:58.37Z" }, - { url = "https://files.pythonhosted.org/packages/1a/21/c0712e382df64c93a0d16449ecbf87b647163485ca1cc3f6cbadb36d2b03/websockets-15.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a625e06551975f4b7ea7102bc43895b90742746797e2e14b70ed61c43a90f09b", size = 173324, upload-time = "2025-03-05T20:02:59.773Z" }, - { url = "https://files.pythonhosted.org/packages/1c/cb/51ba82e59b3a664df54beed8ad95517c1b4dc1a913730e7a7db778f21291/websockets-15.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d591f8de75824cbb7acad4e05d2d710484f15f29d4a915092675ad3456f11770", size = 182094, upload-time = "2025-03-05T20:03:01.827Z" }, - { url = "https://files.pythonhosted.org/packages/fb/0f/bf3788c03fec679bcdaef787518dbe60d12fe5615a544a6d4cf82f045193/websockets-15.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47819cea040f31d670cc8d324bb6435c6f133b8c7a19ec3d61634e62f8d8f9eb", size = 181094, upload-time = "2025-03-05T20:03:03.123Z" }, - { url = "https://files.pythonhosted.org/packages/5e/da/9fb8c21edbc719b66763a571afbaf206cb6d3736d28255a46fc2fe20f902/websockets-15.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac017dd64572e5c3bd01939121e4d16cf30e5d7e110a119399cf3133b63ad054", size = 181397, upload-time = "2025-03-05T20:03:04.443Z" }, - { url = "https://files.pythonhosted.org/packages/2e/65/65f379525a2719e91d9d90c38fe8b8bc62bd3c702ac651b7278609b696c4/websockets-15.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4a9fac8e469d04ce6c25bb2610dc535235bd4aa14996b4e6dbebf5e007eba5ee", size = 181794, upload-time = "2025-03-05T20:03:06.708Z" }, - { url = "https://files.pythonhosted.org/packages/d9/26/31ac2d08f8e9304d81a1a7ed2851c0300f636019a57cbaa91342015c72cc/websockets-15.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363c6f671b761efcb30608d24925a382497c12c506b51661883c3e22337265ed", size = 181194, upload-time = "2025-03-05T20:03:08.844Z" }, - { url = "https://files.pythonhosted.org/packages/98/72/1090de20d6c91994cd4b357c3f75a4f25ee231b63e03adea89671cc12a3f/websockets-15.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2034693ad3097d5355bfdacfffcbd3ef5694f9718ab7f29c29689a9eae841880", size = 181164, upload-time = "2025-03-05T20:03:10.242Z" }, - { url = "https://files.pythonhosted.org/packages/2d/37/098f2e1c103ae8ed79b0e77f08d83b0ec0b241cf4b7f2f10edd0126472e1/websockets-15.0.1-cp39-cp39-win32.whl", hash = "sha256:3b1ac0d3e594bf121308112697cf4b32be538fb1444468fb0a6ae4feebc83411", size = 176381, upload-time = "2025-03-05T20:03:12.77Z" }, - { url = "https://files.pythonhosted.org/packages/75/8b/a32978a3ab42cebb2ebdd5b05df0696a09f4d436ce69def11893afa301f0/websockets-15.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7643a03db5c95c799b89b31c036d5f27eeb4d259c798e878d6937d71832b1e4", size = 176841, upload-time = "2025-03-05T20:03:14.367Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109, upload-time = "2025-03-05T20:03:17.769Z" }, - { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343, upload-time = "2025-03-05T20:03:19.094Z" }, - { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599, upload-time = "2025-03-05T20:03:21.1Z" }, - { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207, upload-time = "2025-03-05T20:03:23.221Z" }, - { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155, upload-time = "2025-03-05T20:03:25.321Z" }, - { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884, upload-time = "2025-03-05T20:03:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/b7/48/4b67623bac4d79beb3a6bb27b803ba75c1bdedc06bd827e465803690a4b2/websockets-15.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7f493881579c90fc262d9cdbaa05a6b54b3811c2f300766748db79f098db9940", size = 173106, upload-time = "2025-03-05T20:03:29.404Z" }, - { url = "https://files.pythonhosted.org/packages/ed/f0/adb07514a49fe5728192764e04295be78859e4a537ab8fcc518a3dbb3281/websockets-15.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:47b099e1f4fbc95b701b6e85768e1fcdaf1630f3cbe4765fa216596f12310e2e", size = 173339, upload-time = "2025-03-05T20:03:30.755Z" }, - { url = "https://files.pythonhosted.org/packages/87/28/bd23c6344b18fb43df40d0700f6d3fffcd7cef14a6995b4f976978b52e62/websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67f2b6de947f8c757db2db9c71527933ad0019737ec374a8a6be9a956786aaf9", size = 174597, upload-time = "2025-03-05T20:03:32.247Z" }, - { url = "https://files.pythonhosted.org/packages/6d/79/ca288495863d0f23a60f546f0905ae8f3ed467ad87f8b6aceb65f4c013e4/websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d08eb4c2b7d6c41da6ca0600c077e93f5adcfd979cd777d747e9ee624556da4b", size = 174205, upload-time = "2025-03-05T20:03:33.731Z" }, - { url = "https://files.pythonhosted.org/packages/04/e4/120ff3180b0872b1fe6637f6f995bcb009fb5c87d597c1fc21456f50c848/websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b826973a4a2ae47ba357e4e82fa44a463b8f168e1ca775ac64521442b19e87f", size = 174150, upload-time = "2025-03-05T20:03:35.757Z" }, - { url = "https://files.pythonhosted.org/packages/cb/c3/30e2f9c539b8da8b1d76f64012f3b19253271a63413b2d3adb94b143407f/websockets-15.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:21c1fa28a6a7e3cbdc171c694398b6df4744613ce9b36b1a498e816787e28123", size = 176877, upload-time = "2025-03-05T20:03:37.199Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, -] - -[[package]] -name = "websockets" -version = "16.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/74/221f58decd852f4b59cc3354cccaf87e8ef695fede361d03dc9a7396573b/websockets-16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04cdd5d2d1dacbad0a7bf36ccbcd3ccd5a30ee188f2560b7a62a30d14107b31a", size = 177343, upload-time = "2026-01-10T09:22:21.28Z" }, - { url = "https://files.pythonhosted.org/packages/19/0f/22ef6107ee52ab7f0b710d55d36f5a5d3ef19e8a205541a6d7ffa7994e5a/websockets-16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8ff32bb86522a9e5e31439a58addbb0166f0204d64066fb955265c4e214160f0", size = 175021, upload-time = "2026-01-10T09:22:22.696Z" }, - { url = "https://files.pythonhosted.org/packages/10/40/904a4cb30d9b61c0e278899bf36342e9b0208eb3c470324a9ecbaac2a30f/websockets-16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:583b7c42688636f930688d712885cf1531326ee05effd982028212ccc13e5957", size = 175320, upload-time = "2026-01-10T09:22:23.94Z" }, - { url = "https://files.pythonhosted.org/packages/9d/2f/4b3ca7e106bc608744b1cdae041e005e446124bebb037b18799c2d356864/websockets-16.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7d837379b647c0c4c2355c2499723f82f1635fd2c26510e1f587d89bc2199e72", size = 183815, upload-time = "2026-01-10T09:22:25.469Z" }, - { url = "https://files.pythonhosted.org/packages/86/26/d40eaa2a46d4302becec8d15b0fc5e45bdde05191e7628405a19cf491ccd/websockets-16.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df57afc692e517a85e65b72e165356ed1df12386ecb879ad5693be08fac65dde", size = 185054, upload-time = "2026-01-10T09:22:27.101Z" }, - { url = "https://files.pythonhosted.org/packages/b0/ba/6500a0efc94f7373ee8fefa8c271acdfd4dca8bd49a90d4be7ccabfc397e/websockets-16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2b9f1e0d69bc60a4a87349d50c09a037a2607918746f07de04df9e43252c77a3", size = 184565, upload-time = "2026-01-10T09:22:28.293Z" }, - { url = "https://files.pythonhosted.org/packages/04/b4/96bf2cee7c8d8102389374a2616200574f5f01128d1082f44102140344cc/websockets-16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:335c23addf3d5e6a8633f9f8eda77efad001671e80b95c491dd0924587ece0b3", size = 183848, upload-time = "2026-01-10T09:22:30.394Z" }, - { url = "https://files.pythonhosted.org/packages/02/8e/81f40fb00fd125357814e8c3025738fc4ffc3da4b6b4a4472a82ba304b41/websockets-16.0-cp310-cp310-win32.whl", hash = "sha256:37b31c1623c6605e4c00d466c9d633f9b812ea430c11c8a278774a1fde1acfa9", size = 178249, upload-time = "2026-01-10T09:22:32.083Z" }, - { url = "https://files.pythonhosted.org/packages/b4/5f/7e40efe8df57db9b91c88a43690ac66f7b7aa73a11aa6a66b927e44f26fa/websockets-16.0-cp310-cp310-win_amd64.whl", hash = "sha256:8e1dab317b6e77424356e11e99a432b7cb2f3ec8c5ab4dabbcee6add48f72b35", size = 178685, upload-time = "2026-01-10T09:22:33.345Z" }, - { url = "https://files.pythonhosted.org/packages/f2/db/de907251b4ff46ae804ad0409809504153b3f30984daf82a1d84a9875830/websockets-16.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:31a52addea25187bde0797a97d6fc3d2f92b6f72a9370792d65a6e84615ac8a8", size = 177340, upload-time = "2026-01-10T09:22:34.539Z" }, - { url = "https://files.pythonhosted.org/packages/f3/fa/abe89019d8d8815c8781e90d697dec52523fb8ebe308bf11664e8de1877e/websockets-16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:417b28978cdccab24f46400586d128366313e8a96312e4b9362a4af504f3bbad", size = 175022, upload-time = "2026-01-10T09:22:36.332Z" }, - { url = "https://files.pythonhosted.org/packages/58/5d/88ea17ed1ded2079358b40d31d48abe90a73c9e5819dbcde1606e991e2ad/websockets-16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af80d74d4edfa3cb9ed973a0a5ba2b2a549371f8a741e0800cb07becdd20f23d", size = 175319, upload-time = "2026-01-10T09:22:37.602Z" }, - { url = "https://files.pythonhosted.org/packages/d2/ae/0ee92b33087a33632f37a635e11e1d99d429d3d323329675a6022312aac2/websockets-16.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:08d7af67b64d29823fed316505a89b86705f2b7981c07848fb5e3ea3020c1abe", size = 184631, upload-time = "2026-01-10T09:22:38.789Z" }, - { url = "https://files.pythonhosted.org/packages/c8/c5/27178df583b6c5b31b29f526ba2da5e2f864ecc79c99dae630a85d68c304/websockets-16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7be95cfb0a4dae143eaed2bcba8ac23f4892d8971311f1b06f3c6b78952ee70b", size = 185870, upload-time = "2026-01-10T09:22:39.893Z" }, - { url = "https://files.pythonhosted.org/packages/87/05/536652aa84ddc1c018dbb7e2c4cbcd0db884580bf8e95aece7593fde526f/websockets-16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6297ce39ce5c2e6feb13c1a996a2ded3b6832155fcfc920265c76f24c7cceb5", size = 185361, upload-time = "2026-01-10T09:22:41.016Z" }, - { url = "https://files.pythonhosted.org/packages/6d/e2/d5332c90da12b1e01f06fb1b85c50cfc489783076547415bf9f0a659ec19/websockets-16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c1b30e4f497b0b354057f3467f56244c603a79c0d1dafce1d16c283c25f6e64", size = 184615, upload-time = "2026-01-10T09:22:42.442Z" }, - { url = "https://files.pythonhosted.org/packages/77/fb/d3f9576691cae9253b51555f841bc6600bf0a983a461c79500ace5a5b364/websockets-16.0-cp311-cp311-win32.whl", hash = "sha256:5f451484aeb5cafee1ccf789b1b66f535409d038c56966d6101740c1614b86c6", size = 178246, upload-time = "2026-01-10T09:22:43.654Z" }, - { url = "https://files.pythonhosted.org/packages/54/67/eaff76b3dbaf18dcddabc3b8c1dba50b483761cccff67793897945b37408/websockets-16.0-cp311-cp311-win_amd64.whl", hash = "sha256:8d7f0659570eefb578dacde98e24fb60af35350193e4f56e11190787bee77dac", size = 178684, upload-time = "2026-01-10T09:22:44.941Z" }, - { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, - { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, - { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, - { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, - { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, - { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, - { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, - { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, - { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, - { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, - { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, - { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, - { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, - { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, - { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, - { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, - { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, - { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, - { url = "https://files.pythonhosted.org/packages/72/07/c98a68571dcf256e74f1f816b8cc5eae6eb2d3d5cfa44d37f801619d9166/websockets-16.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:349f83cd6c9a415428ee1005cadb5c2c56f4389bc06a9af16103c3bc3dcc8b7d", size = 174947, upload-time = "2026-01-10T09:23:36.166Z" }, - { url = "https://files.pythonhosted.org/packages/7e/52/93e166a81e0305b33fe416338be92ae863563fe7bce446b0f687b9df5aea/websockets-16.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:4a1aba3340a8dca8db6eb5a7986157f52eb9e436b74813764241981ca4888f03", size = 175260, upload-time = "2026-01-10T09:23:37.409Z" }, - { url = "https://files.pythonhosted.org/packages/56/0c/2dbf513bafd24889d33de2ff0368190a0e69f37bcfa19009ef819fe4d507/websockets-16.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f4a32d1bd841d4bcbffdcb3d2ce50c09c3909fbead375ab28d0181af89fd04da", size = 176071, upload-time = "2026-01-10T09:23:39.158Z" }, - { url = "https://files.pythonhosted.org/packages/a5/8f/aea9c71cc92bf9b6cc0f7f70df8f0b420636b6c96ef4feee1e16f80f75dd/websockets-16.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0298d07ee155e2e9fda5be8a9042200dd2e3bb0b8a38482156576f863a9d457c", size = 176968, upload-time = "2026-01-10T09:23:41.031Z" }, - { url = "https://files.pythonhosted.org/packages/9a/3f/f70e03f40ffc9a30d817eef7da1be72ee4956ba8d7255c399a01b135902a/websockets-16.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a653aea902e0324b52f1613332ddf50b00c06fdaf7e92624fbf8c77c78fa5767", size = 178735, upload-time = "2026-01-10T09:23:42.259Z" }, - { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, -] diff -Nru apache2-2.4.69/test/pyproject.toml apache2-2.4.71/test/pyproject.toml --- apache2-2.4.69/test/pyproject.toml 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pyproject.toml 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,41 @@ +[project] +name = "httpd-tests" +version = "0.1.0" +description = "Python/pytest test suites for Apache httpd (modules/ and pytest_suite/)" +requires-python = ">=3.11" +dependencies = [ + "pytest>=8.0", + "httpx>=0.27", + "cryptography", + "filelock", + "python-multipart", + "pyopenssl", + "packaging", + "websockets", +] + +[tool.uv] +package = false + +[tool.pytest.ini_options] +python_files = ["test_*.py", "*_test.py"] +# test_*.py: the self-tests of the framework, first, as they take seconds +testpaths = ["test_*.py", "modules", "pytest_suite/tests"] +# "." makes the pyhttpd framework package importable; pytest_suite makes the +# apache_pytest framework package importable. +pythonpath = [".", "pytest_suite"] +# importlib import mode lets same-named test modules coexist across category +# dirs (e.g. ssl/test_all.py and php/test_all.py) without __init__.py files or +# import-file-mismatch collisions. +addopts = "-ra --import-mode=importlib --strict-markers" +markers = [ + "need_module(*names): skip unless all named httpd modules are available", + "need_min_apache_version(ver): skip unless the server is at least ver", + "need_cgi(): skip unless a CGI module (mod_cgi or mod_cgid) is available", + "need_php(): skip unless a PHP SAPI module or php-fpm is available", + "need_ssl(): skip unless mod_ssl is available", + "need_lwp(): always satisfied (the httpx client is always present)", + "needs_dependency(dep, available, reason=None, detected=None): optional dependency; skip if missing, fail under --strict-optional. Use pyhttpd.depends.needs_dependency()", + "not_backported(rev, what, backported_in=None, platform=None): expected to fail on httpd without the trunk change rev (2.4.x, or older than backported_in). Use pyhttpd.backport.not_backported()", + "skip_not_backported(rev, what, backported_in=None, platform=None): like not_backported but not run (hangs, crashes, error log). Use pyhttpd.backport.skip_not_backported()", +] diff -Nru apache2-2.4.69/test/pytest_suite/.gitignore apache2-2.4.71/test/pytest_suite/.gitignore --- apache2-2.4.69/test/pytest_suite/.gitignore 2026-09-28 16:36:07.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/.gitignore 2026-11-09 03:07:05.000000000 +0000 @@ -31,7 +31,6 @@ t/conf/cacheroot/ t/logs/ t/state/ -t/md/ # Generated htpasswd / htdigest files t/basic1 diff -Nru apache2-2.4.69/test/pytest_suite/README.md apache2-2.4.71/test/pytest_suite/README.md --- apache2-2.4.69/test/pytest_suite/README.md 2026-07-18 15:32:22.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/README.md 2026-11-09 03:07:05.000000000 +0000 @@ -1,16 +1,27 @@ # Apache httpd test suite — Python / pytest > **Provenance / how this fits in `test/`.** This `pytest_suite/` is a -> self-contained snapshot ported from the `httpd-tests` repository (the Python -> port of the classic Perl `Apache::Test` suite). It **coexists alongside** -> `test/pyhttpd/` and `test/modules/` — it does *not* use pyhttpd's framework -> or its root `conftest.py`. It has its own `conftest.py` and `pyproject.toml`, -> so pytest treats this directory as its own rootdir; run it from here (or via -> `test/run-all-tests.sh`), never by pointing pytest at `test/` as a whole. -> The two suites are complementary: this one covers the classic core/modules/ -> security/SSL/PHP tests; pyhttpd covers HTTP/2, mod_md (ACME), HTTP/1 and proxy. -> To re-sync from upstream, re-copy the source tree (source files only — exclude -> `.venv/`, caches, and generated artifacts; see `.gitignore`). +> snapshot of the `httpd-tests` repository (the Python port of the classic Perl +> `Apache::Test` suite). It is one half of a single pytest project rooted in +> `test/`, alongside `test/modules/` (the pyhttpd suites). Both halves share the +> framework in `test/pyhttpd/` (finding and probing the httpd build, `need_*` and +> `not_backported` gating, starting and stopping httpd, ports, the test CA, the +> C-module builder); `apache_pytest/` here is a thin layer on top of it that keeps +> the test files' imports working, so they can stay identical to upstream's. +> The Python environment (`test/pyproject.toml`, `test/uv.lock`, `test/.venv`), +> the pytest configuration and the command-line options (registered by +> `test/pyhttpd/plugin.py`) are `test/`'s. Run it with `test/runtests.sh`, +> alone (`./runtests.sh pytest_suite/tests`) or together with `modules/`. +> The two halves are complementary: this one covers the classic core/modules/ +> security/SSL/PHP tests; `modules/` covers HTTP/2, mod_md (ACME), HTTP/1 and proxy. +> +> **Re-syncing from upstream `httpd-tests`:** copy the test files and assets +> only: `tests/t/`, `t/` and `c-modules/` (not `.venv/`, caches or generated +> artifacts; see `.gitignore`). Never copy upstream's `conftest.py` or +> `apache_pytest/` over this directory's: the framework here is the shared one. +> After a copy, run `pytest pytest_suite/tests` from `test/`. A new upstream test +> that needs a framework API missing from `apache_pytest/` fails at import, and +> the API is added to `pyhttpd/` and re-exported there. A self-contained [pytest](https://pytest.org) port of the Apache httpd integration test suite (historically the Perl `Apache::Test` framework). It @@ -28,25 +39,28 @@ ```sh # 1. Create the virtualenv (pytest + httpx). Needs `uv` (https://docs.astral.sh/uv/); -# reads pyproject.toml + uv.lock. runtests.sh also does this for you on first run. -uv sync - -# 2. Run the whole suite against your httpd build. -./runtests.sh --apxs /path/to/your/httpd/bin/apxs +# reads ../pyproject.toml + ../uv.lock. ../runtests.sh also does this for you +# on first run. +uv sync --project .. + +# 2. Run the whole suite against your httpd build, from test/. +cd .. +./runtests.sh --apxs=/path/to/your/httpd/bin/apxs pytest_suite/tests # ...or let it auto-detect apxs / httpd / php-fpm from $PATH or env vars: -APXS=$HOME/root/httpd/bin/apxs PHP_FPM=/usr/sbin/php-fpm8.3 ./runtests.sh +APXS=$HOME/root/httpd/bin/apxs PHP_FPM=/usr/sbin/php-fpm8.3 ./runtests.sh pytest_suite/tests ``` -`runtests.sh` is the self-evident entry point: it locates the venv's `pytest`, -fills in `--apxs` / `--php-fpm` from environment variables or `$PATH`, clears a -stale CGI socket, and forwards any extra arguments to pytest. +`test/runtests.sh` is the entry point: it locates the venv's `pytest`, fills in +`--apxs` / `--php-fpm` from environment variables or `$PATH`, clears a stale CGI +socket, and forwards any extra arguments to pytest. Paths are relative to +`test/`; a path relative to this directory (`tests/t/php`) works as well. ```sh ./runtests.sh tests/t/modules # run one category ./runtests.sh tests/t/modules/test_alias.py # run one file -./runtests.sh -k rewrite -v # any pytest args pass through -./runtests.sh --php-fpm /opt/local/sbin/php-fpm83 tests/t/php # PHP tests +./runtests.sh pytest_suite/tests -k rewrite -v # any pytest args pass through +./runtests.sh --php-fpm=/opt/local/sbin/php-fpm83 tests/t/php # PHP tests ``` @@ -54,10 +68,10 @@ | Requirement | How it's provided | Notes | |---|---|---| -| Python ≥ 3.11 + pytest + httpx | `uv sync` (uses `pyproject.toml` / `uv.lock`) | The only Python deps. | +| Python ≥ 3.11 + pytest + httpx | `uv sync` (uses `test/pyproject.toml` / `test/uv.lock`) | The only Python deps. | | A built Apache httpd | `--apxs` / `--httpd` (or `APXS` / `HTTPD` env) | The server under test. Built with shared modules so the suite can load them. | | `php-fpm` (optional) | `--php-fpm` (or `PHP_FPM` env) | Any PHP 7/8 version. Without it, the PHP tests skip. | -| `openssl` CLI | auto (on `$PATH`) | Used to generate the test SSL CA/certs. | +| `openssl` CLI | auto (on `$PATH`) | Optional. The test SSL CA/certs are generated in Python (`pyhttpd/sslca.py`, `cryptography`); the CLI is only run by a few tests (the OCSP responder, `proxy_hcheck`), which skip without it. | The suite probes the httpd build (`httpd -v/-V/-l` and its installed `httpd.conf`) to learn its version, MPM, and available modules, then **skips** @@ -81,17 +95,15 @@ ## Layout ``` -python/ -├── runtests.sh # entry point (this is how you run the suite) -├── pyproject.toml # deps + pytest config -├── conftest.py # fixtures: server lifecycle, the `http` client, need_* gating -├── apache_pytest/ # the framework (port of Apache::Test internals) -│ ├── probe.py # inspect the httpd build (version, MPM, modules) +pytest_suite/ # (the runner, pyproject.toml and uv.lock are in test/) +├── conftest.py # fixtures: server lifecycle, the `http` client +├── apache_pytest/ # classic-suite layer over test/pyhttpd (port of Apache::Test internals) +│ ├── probe.py # re-exports pyhttpd.build (inspect the httpd build) │ ├── config.py # generate httpd.conf from t/conf/*.conf.in -│ ├── cmodules.py # compile c-modules/ via apxs -│ ├── sslca.py # generate the SSL test CA + certs +│ ├── cmodules.py # which c-modules/ to build (built by pyhttpd.cmodules) +│ ├── sslca.py # re-exports pyhttpd.sslca (the SSL test CA + certs) │ ├── fpm.py # manage a php-fpm daemon -│ ├── server.py # start/stop httpd +│ ├── server.py # the generated config's httpd command line (runs on pyhttpd.instance) │ ├── client.py # the `http` fixture (HTTP/HTTPS + raw sockets) │ ├── rawsocket.py # raw-socket helper for protocol tests │ └── testapi.py # t_cmp() + need_* markers used by tests @@ -318,12 +330,19 @@ ## Notes & gotchas -* **`uv run` vs the venv.** `runtests.sh` invokes `.venv/bin/pytest` directly so +* **`uv run` vs the venv.** `runtests.sh` invokes `test/.venv/bin/pytest` directly so it works in environments where `uv run` is shimmed/unavailable. If you call - pytest yourself, use `.venv/bin/pytest` (or activate the venv). + pytest yourself, use `../.venv/bin/pytest` (or activate the venv). +* **Write options as `--apxs=PATH`**, not `--apxs PATH`, when calling pytest + yourself: pytest's early argument scan cannot yet tell that the second form's + value is not a test path, and picks the wrong rootdir. `runtests.sh` joins them. +* **The error log is checked.** An unexpected error or warning logged while a + test runs fails it (as a teardown error). Tests that provoke errors on purpose + are covered by the lists in `test/pyhttpd/classic_errorlog.py`, not in the test + files; a new test with a deliberate error path adds its AH number there. * **Same-named test modules** across categories (e.g. `ssl/test_all.py` and `php/test_all.py`) coexist thanks to `--import-mode=importlib` + - `pythonpath=["."]` in `pyproject.toml`. + `pythonpath` in `test/pyproject.toml`. * **Stale CGI socket.** A killed run can leave `t/logs/cgisock*`, which breaks a fresh start; `runtests.sh` removes it automatically. * **No orphans.** The server is launched in its own process group and reaped on diff -Nru apache2-2.4.69/test/pytest_suite/apache_pytest/__init__.py apache2-2.4.71/test/pytest_suite/apache_pytest/__init__.py --- apache2-2.4.69/test/pytest_suite/apache_pytest/__init__.py 2026-06-03 17:21:22.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/apache_pytest/__init__.py 2026-11-09 03:07:05.000000000 +0000 @@ -16,6 +16,7 @@ need_module, need_php, need_ssl, + not_backported, t_cmp, ) @@ -32,6 +33,7 @@ "need_module", "need_php", "need_ssl", + "not_backported", "probe", "t_cmp", ] diff -Nru apache2-2.4.69/test/pytest_suite/apache_pytest/cmodules.py apache2-2.4.71/test/pytest_suite/apache_pytest/cmodules.py --- apache2-2.4.69/test/pytest_suite/apache_pytest/cmodules.py 2026-06-03 17:21:22.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/apache_pytest/cmodules.py 2026-11-09 03:07:05.000000000 +0000 @@ -21,14 +21,13 @@ from __future__ import annotations import re -import shutil -import subprocess from dataclasses import dataclass from pathlib import Path -from .probe import HttpdInfo +from pyhttpd.cmodules import LIB_SUBDIR as _LIB_SUBDIR +from pyhttpd.cmodules import clean_module_dir, ensure_built -_LIB_SUBDIR = ".libs" # apxs output dir for Apache 2.x (TestConfigC %lib_dir) +from .probe import HttpdInfo # Hook phases registered by APACHE_HTTPD_TEST_MODULE for Apache 2.x # (TestConfigC @cmodule_phases). @@ -190,16 +189,6 @@ return mods, skipped -def _apxs_cmd(apxs: Path, defines: list[str], include_dir: Path, src: Path) -> list[str]: - # apxs is a Perl script; invoke via perl to sidestep exec-bit issues. - # Mirrors: $(APXS) -D APACHE2 -D APACHE2_4 -I -c mod_NAME.c - cmd = ["perl", str(apxs)] - for d in defines: - cmd += ["-D", d] - cmd += ["-I", str(include_dir), "-c", str(src)] - return cmd - - def clean_modules(cmodules_dir: Path) -> None: """Remove all apxs build artifacts from ``cmodules_dir`` (emulate ``make clean``). @@ -209,12 +198,7 @@ The next ``compile_all`` call will rebuild everything from scratch. """ for sub in sorted(p for p in cmodules_dir.iterdir() if p.is_dir()): - libs = sub / _LIB_SUBDIR - if libs.is_dir(): - shutil.rmtree(libs) - for pattern in ("*.o", "*.lo", "*.slo", "*.la"): - for f in sub.glob(pattern): - f.unlink(missing_ok=True) + clean_module_dir(sub) header = cmodules_dir / "apache_httpd_test.h" header.unlink(missing_ok=True) @@ -233,26 +217,16 @@ :raises RuntimeError: if apxs fails for a module (captured output included). """ defines = defines if defines is not None else ["APACHE2", "APACHE2_4"] - generate_header(cmodules_dir / "apache_httpd_test.h") + header = cmodules_dir / "apache_httpd_test.h" + generate_header(header) mods, skipped = discover(cmodules_dir, info) - apxs_mtime = apxs.stat().st_mtime loads: list[tuple[str, Path]] = [] for mod in mods: - needs = force or not mod.so.exists() or ( - mod.so.stat().st_mtime < mod.src.stat().st_mtime - ) or ( - mod.so.stat().st_mtime < apxs_mtime - ) - if needs: - cmd = _apxs_cmd(apxs, defines, cmodules_dir, mod.src) - proc = subprocess.run( # noqa: S603 - trusted paths - cmd, cwd=mod.src_dir, capture_output=True, text=True, check=False - ) - if proc.returncode != 0 or not mod.so.exists(): - raise RuntimeError( - f"apxs failed building mod_{mod.name} (exit {proc.returncode}):\n" - f"$ {' '.join(cmd)}\n{proc.stdout}\n{proc.stderr}" - ) + # rebuilt when the source, the generated header, the defines or the + # httpd it is built against change (see pyhttpd.cmodules) + ensure_built(mod.src_dir, mod.name, apxs, defines=defines, + include_dirs=[cmodules_dir], extra_inputs=[header], + force=force) loads.append((mod.symbol, mod.so)) return loads, skipped diff -Nru apache2-2.4.69/test/pytest_suite/apache_pytest/private_server.py apache2-2.4.71/test/pytest_suite/apache_pytest/private_server.py --- apache2-2.4.69/test/pytest_suite/apache_pytest/private_server.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/apache_pytest/private_server.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,155 @@ +"""A throwaway private httpd for tests that must restart or reconfigure the +server (the suite's own server is shared, so they cannot use it). + +It serves one small and one large static file. Module and path settings are +taken from the suite's generated config so that they match the build under test. +""" + +import re +import socket +import subprocess +import time + +from pyhttpd.instance import HttpdInstance, free_port # noqa: F401 (free_port re-exported) + +BODY_LENGTH = 64 * 1024 * 1024 +SMALL_LENGTH = 1000 + +# What the private server needs, taken from the suite's generated config so that +# paths and module names are the ones this build uses. +WANTED_MODULES = ("mpm_", "unixd_module", "authz_core_module", + "unique_id_module", "headers_module", "echo_module") + + +def load_module_lines(config): + text = open(config.vars["t_conf_file"], encoding="utf-8").read() + lines = [] + for m in re.finditer(r"^\s*(LoadModule\s+(\S+)\s+.*)$", text, re.M): + if m.group(2).startswith(WANTED_MODULES[0]) or m.group(2) in WANTED_MODULES: + lines.append(m.group(1).strip()) + return lines + + +def read_headers(sock): + data = b"" + while b"\r\n\r\n" not in data: + chunk = sock.recv(4096) + if not chunk: + break + data += chunk + head, _, body = data.partition(b"\r\n\r\n") + return head, body + + +def child_pids(parent): + """Pids of the direct children of parent (portable: macOS and Linux).""" + out = subprocess.run(["ps", "-A", "-o", "pid=,ppid="], + capture_output=True, text=True).stdout + pids = set() + for line in out.splitlines(): + fields = line.split() + if len(fields) == 2 and int(fields[1]) == parent: + pids.add(int(fields[0])) + return pids + + +def get(port, path, rcvbuf=None): + """Send an HTTP/1.0 GET; return the open socket (the body ends at close).""" + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + if rcvbuf: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, rcvbuf) + sock.settimeout(30) + sock.connect(("127.0.0.1", port)) + sock.sendall(f"GET {path} HTTP/1.0\r\n\r\n".encode()) + return sock + + +class PrivateServer: + """A throwaway httpd with one small and one large static file.""" + + def __init__(self, config, tmp_path, extra=(), big_length=BODY_LENGTH): + self.config = config + self.tmp_path = tmp_path + self.extra = list(extra) + self.big_length = big_length + self.port = free_port() + self.instance = None + + def start(self): + tmp_path = self.tmp_path + docroot = tmp_path / "htdocs" + docroot.mkdir() + (docroot / "big.bin").write_bytes(b"x" * self.big_length) + (docroot / "small.txt").write_bytes(b"y" * SMALL_LENGTH) + + conf = tmp_path / "httpd.conf" + conf.write_text( + "\n".join( + [ + f'ServerRoot "{tmp_path}"', + "ServerName localhost", + f"Listen 127.0.0.1:{self.port}", + f'PidFile "{tmp_path}/httpd.pid"', + f'ErrorLog "{tmp_path}/error_log"', + "LogLevel info", + *load_module_lines(self.config), + "StartServers 1", + "ThreadsPerChild 10", + f'DocumentRoot "{docroot}"', + "", + " TypesConfig /dev/null", + "", + *self.extra, + "", + ] + ) + ) + # Its own process group, so that the children go away with the parent. + self.instance = HttpdInstance( + self.config.info.httpd, ["-f", str(conf)], + ports=[self.port], error_log=tmp_path / "error_log", + stderr_file=tmp_path / "stderr", configtest_first=True) + self.instance.start(timeout=20) + + @property + def proc(self): + return self.instance.proc if self.instance else None + + def stop(self): + if self.instance is not None: + self.instance.stop(timeout=15) + + def __enter__(self): + self.start() + return self + + def __exit__(self, *exc): + self.stop() + + def graceful(self, timeout=30): + """Ask for a graceful restart and return once the parent has done it. A + slow (instrumented) parent can take seconds to act on the signal, and a + test that goes on before that is not restarting the server when it + thinks it is.""" + self.instance.restart(graceful=True, timeout=timeout) + + def error_log(self): + return (self.tmp_path / "error_log").read_text(errors="replace") + + def small_ok(self, timeout): + """True if /small.txt is served completely within timeout seconds.""" + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + try: + s = get(self.port, "/small.txt") + s.settimeout(1) + h, b = read_headers(s) + while chunk := s.recv(4096): + b += chunk + s.close() + if b" 200 " in h.split(b"\r\n")[0] and len(b) == SMALL_LENGTH: + return True + except OSError: + pass + time.sleep(0.1) + return False diff -Nru apache2-2.4.69/test/pytest_suite/apache_pytest/probe.py apache2-2.4.71/test/pytest_suite/apache_pytest/probe.py --- apache2-2.4.69/test/pytest_suite/apache_pytest/probe.py 2026-06-03 16:20:30.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/apache_pytest/probe.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,249 +1,12 @@ -"""Toolchain probing for httpd. +"""Compatibility re-export: the probe now lives in :mod:`pyhttpd.build`.""" -Mirrors the discovery logic in Apache::TestConfigParse (perl-apache-test): -run ``httpd -v`` / ``-V`` / ``-l`` and parse the inherited install ``httpd.conf`` -to learn the server version, MPM, compile-time ``-D`` defines, and the full set -of available modules (static + dynamically loaded). - -The merged module set drives ```` blocks, the module-name token -resolution in :mod:`apache_pytest.config`, and (eventually) the ``need_module`` -test marks. -""" - -from __future__ import annotations - -import re -import subprocess -from dataclasses import dataclass, field -from pathlib import Path - -# httpd -V emits lines like: -D APACHE_MPM_DIR="server/mpm/prefork" -_DEFINE_RE = re.compile(r"^\s*-D\s+([A-Z0-9_]+)(?:=(.*))?\s*$") -# httpd -l / module conf lines end in ".c"; -l indents them two spaces. -_STATIC_MOD_RE = re.compile(r"^\s*(\w+\.c)\s*$") -# LoadModule foo_module modules/mod_foo.so (optionally commented with leading #) -_LOADMODULE_RE = re.compile(r"^\s*(#?)\s*LoadModule\s+(\S+)\s+(\S+)") -_VERSION_RE = re.compile(r"Apache/(\d+)\.(\d+)\.(\d+)") - - -@dataclass -class LoadDirective: - """One ``LoadModule symbol file`` mapping from an install conf.""" - - symbol: str # e.g. "session_module" - so: str # path as written, e.g. "modules/mod_session.so" - active: bool # False if the line was commented out - - @property - def cname(self) -> str: - """Module source name, e.g. "mod_session.c" (used as the modules-set key).""" - stem = Path(self.so).name - for suffix in (".so", ".dll"): - if stem.endswith(suffix): - stem = stem[: -len(suffix)] - break - if stem.startswith("lib"): # libphp.so -> mod_php.c - stem = "mod_" + stem[3:] - return f"{stem}.c" - - @property - def is_mpm(self) -> bool: - return self.symbol.startswith("mpm_") or "mpm_" in Path(self.so).name - - -@dataclass -class HttpdInfo: - """Everything probed from an httpd binary + its inherited config.""" - - httpd: Path - version: tuple[int, int, int] - mpm: str - defines: dict[str, str | bool] = field(default_factory=dict) - # Set of module source-file names, e.g. {"core.c", "mod_proxy.c", ...}. - # This is the union of statically-compiled modules and modules available - # via LoadModule in the inherited httpd.conf (active OR commented) — matching - # Apache::Test's $self->{modules} hash keyed on ".c". Membership here - # means "this module exists and can be loaded", which is what drives the - # blocks and the need_module marks. - modules: set[str] = field(default_factory=set) - # Every LoadModule directive discovered in the inherited conf, with absolute - # .so paths. Re-emitted into the generated httpd.conf so the test server - # actually loads them (the generated conf does not Include the install conf). - load_directives: list[LoadDirective] = field(default_factory=list) - # TAKE1 directives inherited from the install conf (wanted_config in - # TestConfigParse.pm:34): ServerAdmin, TypesConfig, DocumentRoot, ServerRoot. - # Keyed by directive name (as written), value is the single argument. - inherited: dict[str, str] = field(default_factory=dict) - - @property - def version_str(self) -> str: - return ".".join(str(p) for p in self.version) - - def has_module(self, name: str) -> bool: - """True if module ``name`` is available. - - Accepts every form the Perl tests use: a bare name (``status``), a - ``mod_``-prefixed name (``mod_status``), or any of those with a ``.c``/ - ``.so`` suffix. Modules are stored as source names (``mod_status.c``), - so a bare name is also tried with the ``mod_`` prefix -- matching - Apache::Test's need_module/have_module, which accept ``need_module 'status'``. - """ - stem = name - for suffix in (".c", ".so"): - if stem.endswith(suffix): - stem = stem[: -len(suffix)] - break - candidates = {f"{stem}.c"} - if not stem.startswith("mod_"): - candidates.add(f"mod_{stem}.c") - return bool(candidates & self.modules) - - def version_at_least(self, major: int, minor: int, patch: int = 0) -> bool: - return self.version >= (major, minor, patch) - - -def _run(cmd: list[str]) -> str: - """Run a probe command, returning combined stdout (httpd prints to stdout).""" - proc = subprocess.run( # noqa: S603 - trusted, caller-provided httpd path - cmd, - capture_output=True, - text=True, - check=False, - ) - # httpd -V can exit non-zero if it wants a config; we only need the banner. - return proc.stdout + proc.stderr - - -def probe_version(httpd: Path) -> tuple[int, int, int]: - out = _run([str(httpd), "-v"]) - m = _VERSION_RE.search(out) - if not m: - raise RuntimeError(f"could not parse version from `{httpd} -v`:\n{out}") - return (int(m.group(1)), int(m.group(2)), int(m.group(3))) - - -def probe_defines_and_mpm(httpd: Path) -> tuple[dict[str, str | bool], str]: - """Parse ``httpd -V`` for ``-D`` defines, server MPM, and module magic info.""" - out = _run([str(httpd), "-V"]) - defines: dict[str, str | bool] = {} - mpm = "" - for line in out.splitlines(): - m = _DEFINE_RE.match(line) - if m: - key, val = m.group(1), m.group(2) - defines[key] = val.strip('"') if val else True - continue - # "Server MPM: event" (2.4+/2.5) - sm = re.match(r"\s*Server MPM:\s*(.+)", line, re.IGNORECASE) - if sm: - mpm = sm.group(1).strip().lower() - if not mpm: - # 2.0-era fallback: derive from APACHE_MPM_DIR=server/mpm/ - mpm_dir = defines.get("APACHE_MPM_DIR") - if isinstance(mpm_dir, str): - mpm = Path(mpm_dir).name.lower() - return defines, mpm - - -def probe_static_modules(httpd: Path) -> set[str]: - """Modules compiled statically into the binary (``httpd -l``).""" - out = _run([str(httpd), "-l"]) - mods: set[str] = set() - for line in out.splitlines(): - m = _STATIC_MOD_RE.match(line) - if m: - mods.add(m.group(1)) - return mods - - -def parse_loadmodules(conf: Path, server_root: Path) -> list[LoadDirective]: - """Parse ``LoadModule`` lines (active and commented) from an install httpd.conf. - - The suite is built with ``--enable-load-all-modules`` upstream; locally the - stock install conf lists most modules commented out. We capture both so the - generated config can re-emit them and so module-presence checks succeed. - Relative ``.so`` paths are resolved against ``server_root`` (the install - prefix, ``apxs -q PREFIX``). ``Include`` directives are not followed — the - stock conf lists modules directly, sufficient for the local build target. - """ - directives: list[LoadDirective] = [] - if not conf.is_file(): - return directives - for raw in conf.read_text(errors="replace").splitlines(): - m = _LOADMODULE_RE.match(raw) - if not m: - continue - commented, symbol, so = m.group(1), m.group(2), m.group(3) - so_path = Path(so) - if not so_path.is_absolute(): - so_path = server_root / so_path - directives.append( - LoadDirective(symbol=symbol, so=str(so_path), active=(commented == "")) - ) - return directives - - -# TAKE1 directives inherited from the install conf (wanted_config TAKE1). -# ServerRoot is intentionally NOT applied (spec_apply maps it to a no-op so the -# test serverroot wins); we still record it for completeness but config.py -# ignores it. -_INHERIT_TAKE1 = ("ServerAdmin", "TypesConfig", "DocumentRoot", "ServerRoot") -_TAKE1_RE = re.compile( - r"^\s*(" + "|".join(_INHERIT_TAKE1) + r")\s+(.+?)\s*$" +from pyhttpd.build import ( # noqa: F401 + HttpdInfo, + LoadDirective, + parse_inherited_take1, + parse_loadmodules, + probe, + probe_defines_and_mpm, + probe_static_modules, + probe_version, ) - - -def parse_inherited_take1(conf: Path) -> dict[str, str]: - """Parse last-wins TAKE1 directives from an install conf (wanted_config).""" - found: dict[str, str] = {} - if not conf.is_file(): - return found - for raw in conf.read_text(errors="replace").splitlines(): - if raw.lstrip().startswith("#"): - continue - m = _TAKE1_RE.match(raw) - if m: - found[m.group(1)] = m.group(2).strip().strip('"') - return found - - -def probe( - httpd: Path, - inherited_conf: Path | None = None, - server_root: Path | None = None, -) -> HttpdInfo: - """Full probe of an httpd binary and (optionally) its install config. - - :param httpd: path to the httpd executable. - :param inherited_conf: install ``httpd.conf`` to scan for LoadModule lines. - Typically ``/conf/httpd.conf`` derived from ``apxs -q SYSCONFDIR``. - :param server_root: install prefix used to resolve relative ``.so`` paths - (``apxs -q PREFIX``). Defaults to the parent of ``inherited_conf``'s dir. - """ - version = probe_version(httpd) - defines, mpm = probe_defines_and_mpm(httpd) - modules = probe_static_modules(httpd) - load_directives: list[LoadDirective] = [] - inherited: dict[str, str] = {} - if inherited_conf is not None: - root = server_root or inherited_conf.parent.parent - inherited = parse_inherited_take1(inherited_conf) - all_directives = parse_loadmodules(inherited_conf, root) - # Inherit only ACTIVE LoadModule lines, matching Apache::Test's - # inherit_load_module (which parses the live config, not commented - # lines). A build intended for the suite activates all modules - # (--enable-load-all-modules); locally the install conf is activated to - # match. Commented lines are ignored entirely. - load_directives = [d for d in all_directives if d.active] - for d in load_directives: - if Path(d.so).exists(): - modules.add(d.cname) - return HttpdInfo( - httpd=httpd, - version=version, - mpm=mpm, - defines=defines, - modules=modules, - load_directives=load_directives, - inherited=inherited, - ) diff -Nru apache2-2.4.69/test/pytest_suite/apache_pytest/server.py apache2-2.4.71/test/pytest_suite/apache_pytest/server.py --- apache2-2.4.69/test/pytest_suite/apache_pytest/server.py 2026-09-28 16:36:07.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/apache_pytest/server.py 2026-11-09 03:07:05.000000000 +0000 @@ -2,94 +2,56 @@ Port of the lifecycle bits of Apache::TestServer. Builds the same command line (``httpd -d -f -D APACHE2 -D APACHE2_4 ``), starts -the server, waits for its port to accept connections, and stops it cleanly. +the server, waits for it to serve, and stops it cleanly. -Orphan/stale-state handling mirrors Apache::TestServer's ``sub start`` (which -calls ``sub stop`` up front to clear any prior server) and ``sub stop`` (which -reads the pid file, sends ``SIGTERM``, polls, and finally unlinks the pid -file). Because the event MPM parent forks children, we launch httpd in its own -session (``start_new_session=True``/``setsid``) so the whole process group can -be signalled with ``os.killpg`` -- escalating to ``SIGKILL`` if needed -- and -no children are left holding ports after a failed or interrupted run. +The process handling -- stale servers cleared up front like Apache::TestServer's +``sub start`` does via ``sub stop``, its own session so the whole process group +can be signalled and no children are left holding ports after a failed or +interrupted run -- is :class:`pyhttpd.instance.HttpdInstance`, shared with the +modules/ suite. This class only knows the command line of the generated config. """ from __future__ import annotations -import contextlib -import errno -import os -import sys -import signal -import socket import subprocess -import time from pathlib import Path -from .config import TestConfig - +from pyhttpd.instance import HttpdInstance, HttpdStartError -def _pid_alive(pid: int) -> bool: - """Return True if a process with ``pid`` exists (``kill(0)`` probe). - - Mirrors Apache::TestServer's ``kill 0, $pid`` liveness check in ``sub - ping``/``sub stop``. - """ - if pid <= 0: - return False - try: - os.kill(pid, 0) - except OSError as exc: - # ESRCH: no such process. EPERM: exists but not ours (still alive). - return exc.errno == errno.EPERM - return True - - -def _read_pid(pid_file: Path) -> int: - """Read the PID from ``pid_file``; return 0 if missing/empty/garbage. - - Mirrors Apache::TestServer ``sub pid``, including tolerance for a pid file - that exists but has not been written to yet (returns 0). - """ - try: - text = pid_file.read_text().strip() - except OSError: - return 0 - try: - return int(text) - except ValueError: - return 0 - - -def _killpg_or_pid(pid: int, sig: int) -> None: - """Send ``sig`` to the process group led by ``pid``, falling back to ``pid``. - - Children of an httpd parent launched with ``start_new_session=True`` share - the parent's process group id (== parent pid), so signalling the group with - ``os.killpg`` reaches the parent and all its workers. If the process is not - a group leader (no such pgid) we fall back to signalling the bare pid. - - On Windows there are no process groups; use ``os.kill`` directly. - """ - if pid <= 0: - return - if sys.platform == "win32": - with contextlib.suppress(OSError): - os.kill(pid, sig) - return - try: - os.killpg(pid, sig) - except OSError as exc: - if exc.errno == errno.ESRCH: - return # already gone - # Not a group leader (or no permission for the group): try the pid. - with contextlib.suppress(OSError): - os.kill(pid, sig) +from .config import TestConfig class HttpdServer: def __init__(self, config: TestConfig) -> None: self.config = config - self.proc: subprocess.Popen[bytes] | None = None + self._instance: HttpdInstance | None = None + + @property + def instance(self) -> HttpdInstance: + if self._instance is None: + vars_ = self.config.vars + self._instance = HttpdInstance( + self.config.info.httpd, + [ + "-d", + vars_["serverroot"], + "-f", + vars_["t_conf_file"], + "-DFOREGROUND", + *[a for d in self.dversion_defines() for a in ("-D", d)], + ], + ports=[int(vars_["port"])], + error_log=Path(vars_["t_logs"]) / "error_log", + pid_files=[Path(vars_["t_pid_file"])], + stderr_file=Path(vars_["t_logs"]) / "httpd.stderr", + # Like Apache::TestServer, validate the config before launch + configtest_first=True, + ) + return self._instance + + @property + def proc(self) -> subprocess.Popen[bytes] | None: + return self.instance.proc # -- command line ----------------------------------------------------- @@ -108,26 +70,13 @@ return defs def args(self) -> list[str]: - vars_ = self.config.vars - argv = [ - str(self.config.info.httpd), - "-d", - vars_["serverroot"], - "-f", - vars_["t_conf_file"], - "-DFOREGROUND", - ] - for d in self.dversion_defines(): - argv += ["-D", d] - return argv + return self.instance.argv() # -- syntax check ----------------------------------------------------- def configtest(self) -> subprocess.CompletedProcess[str]: """Run ``httpd ... -t`` to validate the generated config before launch.""" - return subprocess.run( # noqa: S603 - trusted paths - [*self.args(), "-t"], capture_output=True, text=True, check=False - ) + return self.instance.configtest() # -- lifecycle -------------------------------------------------------- @@ -135,107 +84,18 @@ def port(self) -> int: return int(self.config.vars["port"]) - @property - def _pid_file(self) -> Path: - return Path(self.config.vars["t_pid_file"]) - - def _port_open(self) -> bool: - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - s.settimeout(0.25) - return s.connect_ex(("127.0.0.1", self.port)) == 0 - - @classmethod - def kill_stale(cls, pid_file: Path, *, timeout: float = 10.0) -> int: - """Defensively clear a server left behind by a prior/crashed run. - - Mirrors the up-front ``$self->stop`` that Apache::TestServer's ``sub - start`` performs: if ``pid_file`` names a live process, signal its - process group with ``SIGTERM`` (escalating to ``SIGKILL`` after - ``timeout``), then unlink the (now stale) pid file. Targets only the - PID recorded in the file and that PID's process group -- never a broad - pattern match. Safe to call when no pid file exists. - - Returns the PID that was found in the file (0 if none). - """ - pid = _read_pid(pid_file) - if pid and _pid_alive(pid): - _killpg_or_pid(pid, signal.SIGTERM) - deadline = time.monotonic() + timeout - while time.monotonic() < deadline: - if not _pid_alive(pid): - break - time.sleep(0.1) - if _pid_alive(pid): - kill_sig = signal.SIGTERM if sys.platform == "win32" else signal.SIGKILL - _killpg_or_pid(pid, kill_sig) - if sys.platform != "win32": - with contextlib.suppress(OSError): - os.waitpid(pid, 0) # reap if it happens to be our child - if pid_file.exists(): - with contextlib.suppress(OSError): - pid_file.unlink() - return pid - def start(self, *, timeout: float = 30.0) -> None: - # Like Apache::TestServer::start, clear any server (and stale pid file) - # left over from a prior run before launching a new one. - self.kill_stale(self._pid_file) - - check = self.configtest() - if check.returncode != 0: + try: + self.instance.start(timeout=timeout) + except HttpdStartError as exc: raise RuntimeError( - f"httpd config test failed:\n{check.stdout}\n{check.stderr}" - ) - # start_new_session=True (setsid) puts httpd in its own process group so - # the parent and all forked MPM children can be signalled together via - # os.killpg, guaranteeing no orphaned children survive a failed start. - popen_kwargs = {} - if sys.platform != "win32": - popen_kwargs["start_new_session"] = True - self.proc = subprocess.Popen( # noqa: S603 - trusted paths - self.args(), **popen_kwargs - ) - deadline = time.monotonic() + timeout - while time.monotonic() < deadline: - if self.proc.poll() is not None: - code = self.proc.returncode - self.stop() # reap any children that did spawn - raise RuntimeError( - f"httpd exited early (code {code}); " - f"see {self.config.vars['t_logs']}/error_log" - ) - if self._port_open(): - return - time.sleep(0.1) - self.stop() - raise TimeoutError(f"httpd did not start within {timeout}s on port {self.port}") + f"{exc}; see {self.config.vars['t_logs']}/error_log\n{exc.stderr}" + ) from exc def stop(self, *, timeout: float = 10.0) -> None: """Stop the server, reaping the whole process group, idempotently. - Mirrors Apache::TestServer ``sub stop``: ``SIGTERM`` the server, wait, - then unlink the pid file. We signal the launched process *group* so the - event MPM children die with the parent, escalating to ``SIGKILL`` if - the group does not exit within ``timeout``. Safe to call when the - server was never started; also clears any matching stale pid file. + Safe to call when the server was never started; also clears any + matching stale pid file. """ - proc = self.proc - if proc is not None and proc.poll() is None: - pgid_pid = proc.pid - _killpg_or_pid(pgid_pid, signal.SIGTERM) - try: - proc.wait(timeout=timeout) - except subprocess.TimeoutExpired: - kill_sig = signal.SIGTERM if sys.platform == "win32" else signal.SIGKILL - _killpg_or_pid(pgid_pid, kill_sig) - with contextlib.suppress(subprocess.TimeoutExpired): - proc.wait(timeout=timeout) - elif proc is not None: - # Process exited on its own; reap it (no-op if already reaped). - with contextlib.suppress(Exception): - proc.wait(timeout=0) - - # Whether or not we had a live Popen handle, defensively clear anything - # the pid file still points at and remove the file. - self.kill_stale(self._pid_file, timeout=timeout) - self.proc = None + self.instance.stop(timeout=timeout) diff -Nru apache2-2.4.69/test/pytest_suite/apache_pytest/sslca.py apache2-2.4.71/test/pytest_suite/apache_pytest/sslca.py --- apache2-2.4.69/test/pytest_suite/apache_pytest/sslca.py 2026-06-03 16:20:30.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/apache_pytest/sslca.py 2026-11-09 03:07:05.000000000 +0000 @@ -1,262 +1,10 @@ -"""Generate the SSL Certificate Authority and test certificates. +"""Compatibility re-export: the CA generator now lives in :mod:`pyhttpd.sslca`.""" -Python port of Apache::TestSSLCA. Builds, under ``t/conf/ssl/ca/asf``, a self- -signed CA plus server and client certificates that the SSL test configs reference -(``server.crt``/``server.pem``, ``server_des3.*``, ``client_ok``/``client_snakeoil``/ -``client_revoked``/``client_colon``, proxy client PEMs, and a CRL). The layout and -filenames match the Perl framework so the existing ``t/conf/ssl/*.conf.in`` files -work unchanged. - -Notes vs. the Perl original: -* RSA keys are 2048-bit, digest sha256 (matches modern openssl defaults). -* DSA server variants (``server_dsa`` etc.) are generated too, for config files - that may reference them; on OpenSSL 3.x DSA keygen still works. -* All passphrases are ``httpd`` (``$pass`` in the original). -""" - -from __future__ import annotations - -import subprocess -from pathlib import Path - -PASS = "httpd" -DAYS = "365" -DGST = "sha256" -EMAIL_FIELD = "emailAddress" - -# CA distinguished name (ca_dn{asf}). -CA_DN = { - "C": "US", - "ST": "California", - "L": "San Francisco", - "O": "ASF", - "OU": "httpd-test", - "CN": "", - EMAIL_FIELD: "test-dev@httpd.apache.org", -} - -# Per-cert DN overrides (cert_dn). CN for server* is set to the servername at -# generation time (matching generate() in the Perl module). -CERT_DN: dict[str, dict[str, str]] = { - "client_snakeoil": { - "C": "AU", "ST": "Queensland", "L": "Mackay", - "O": "Snake Oil, Ltd.", "OU": "Staff", - }, - "client_ok": {}, - "client_colon": {"CN": "user:colon"}, - "client_revoked": {}, - "server": {"CN": "localhost", "OU": "httpd-test/rsa-test"}, - "server2": {"CN": "localhost", "OU": "httpd-test/rsa-test-2"}, - "server_des3": {"CN": "localhost", "OU": "httpd-test/rsa-des3-test"}, - "server2_des3": {"CN": "localhost", "OU": "httpd-test/rsa-des3-test-2"}, -} -# DSA variants of every server cert (Perl derives these dynamically). -for _k in list(CERT_DN): - if _k.startswith("server"): - _dsa = dict(CERT_DN[_k]) - _dsa["OU"] = _dsa.get("OU", "").replace("rsa", "dsa") - CERT_DN[f"{_k}_dsa"] = _dsa - - -class SSLCA: - def __init__(self, ca_root: Path, servername: str, *, openssl: str = "openssl") -> None: - # ca_root is e.g. t/conf/ssl/ca ; the CA tree lives under ca_root/asf. - self.root = ca_root - self.ca = "asf" - self.servername = servername - self.openssl = openssl - self.dir = ca_root / self.ca - - # -- helpers ---------------------------------------------------------- - - def _run(self, *args: str, cwd: Path) -> None: - cmd = [self.openssl, *args] - proc = subprocess.run( # noqa: S603 - trusted openssl invocation - cmd, cwd=cwd, capture_output=True, text=True, check=False - ) - if proc.returncode != 0: - raise RuntimeError( - f"openssl failed: {' '.join(cmd)}\n{proc.stdout}\n{proc.stderr}" - ) - - def _dn(self, name: str) -> dict[str, str]: - dn = dict(CA_DN) - dn["CN"] = dn["CN"] or name - dn.update(CERT_DN.get(name, {})) - if name.startswith("server"): - dn["CN"] = self.servername - return dn - - def _config_file(self, name: str) -> Path: - """Write conf/.cnf (openssl config) and return its path.""" - dn = self._dn(name) - cnf = self.dir / "conf" / f"{name}.cnf" - cnf.write_text( - f"mail = {dn[EMAIL_FIELD]}\n" - f"CN = {dn['CN']}\n\n" - "[ req ]\n" - "distinguished_name = req_distinguished_name\n" - "attributes = req_attributes\n" - "prompt = no\n" - "default_bits = 2048\n" - f"output_password = {PASS}\n\n" - "[ req_distinguished_name ]\n" - f"C = {dn['C']}\n" - f"ST = {dn['ST']}\n" - f"L = {dn['L']}\n" - f"O = {dn['O']}\n" - f"OU = {dn['OU']}\n" - "CN = $CN\n" - f"{EMAIL_FIELD} = $mail\n\n" - "[ req_attributes ]\n" - f"challengePassword = {PASS}\n\n" - "[ ca ]\n" - "default_ca = CA_default\n\n" - "[ CA_default ]\n" - "certs = certs\n" - "new_certs_dir = newcerts\n" - "crl_dir = crl\n" - "database = index.txt\n" - "serial = serial\n" - "certificate = certs/ca.crt\n" - "crl = crl/ca-bundle.crl\n" - "private_key = keys/ca.pem\n" - "default_days = 365\n" - "default_crl_days = 365\n" - f"default_md = {DGST}\n" - "preserve = no\n\n" - "[ policy_anything ]\n" - "countryName = optional\n" - "stateOrProvinceName = optional\n" - "localityName = optional\n" - "organizationName = optional\n" - "organizationalUnitName = optional\n" - "commonName = supplied\n" - f"{EMAIL_FIELD} = optional\n\n" - "[ client_ok_ext ]\n" - "nsComment = This Is A Comment\n" - # Custom OID extension carrying the DER-encoded UTF8String "Lemons" - # (0c=UTF8String, 06=len 6, 4c656d6f6e73="Lemons"). t/ssl/require.t's - # certext location asserts: "Lemons" in PeerExtList(). - # Faithful to Apache::TestSSLCA's client_ok_ext. - "1.3.6.1.4.1.18060.12.0 = DER:0c064c656d6f6e73\n" - "subjectAltName = email:$mail\n\n" - "[ client_ext ]\n" - "extendedKeyUsage = clientAuth\n\n" - "[ server_ext ]\n" - "subjectAltName = DNS:$CN\n" - "extendedKeyUsage = serverAuth\n" - "subjectKeyIdentifier = hash\n" - "authorityKeyIdentifier = keyid,issuer\n\n" - "[ ca_ext ]\n" - "subjectKeyIdentifier = hash\n" - "authorityKeyIdentifier = keyid:always,issuer\n" - "basicConstraints = critical,CA:true\n" - # OpenSSL 3.x's verifier rejects a CA cert used to sign certs unless - # it carries keyCertSign (and cRLSign for the CRL). The Perl-era - # certs predate this strictness; add it so modern clients (httpx via - # OpenSSL 3) can verify the chain. - "keyUsage = critical,keyCertSign,cRLSign\n" - ) - return cnf - - # -- generation steps ------------------------------------------------- - - def _init_dirs(self) -> None: - for d in ("keys", "newcerts", "certs", "crl", "export", "csr", "conf", "proxy"): - (self.dir / d).mkdir(parents=True, exist_ok=True) - (self.dir / "index.txt").write_text("") - (self.dir / "serial").write_text("01\n") - - def _new_ca(self) -> None: - self._config_file("ca") - self._run( - "req", "-new", "-x509", "-extensions", "ca_ext", - "-keyout", "keys/ca.pem", "-out", "certs/ca.crt", - "-days", DAYS, "-config", "conf/ca.cnf", - "-passout", f"pass:{PASS}", - cwd=self.dir, - ) - - def _new_key(self, name: str) -> None: - out = f"keys/{name}.pem" - if "dsa" in name: - param = self.dir / "dsa-param" - if not param.exists(): - self._run("dsaparam", "-out", "dsa-param", "2048", cwd=self.dir) - args = ["gendsa", "-out", out] - if "_des3" in name: - args += ["-des3", "-passout", f"pass:{PASS}"] - args += ["dsa-param"] - self._run(*args, cwd=self.dir) - else: - args = ["genrsa", "-out", out] - if "_des3" in name: - args += ["-des3", "-passout", f"pass:{PASS}"] - args += ["2048"] - self._run(*args, cwd=self.dir) - - def _new_cert(self, name: str) -> None: - self._config_file(name) - self._run( - "req", "-new", "-key", f"keys/{name}.pem", "-out", f"csr/{name}.csr", - "-passin", f"pass:{PASS}", "-passout", f"pass:{PASS}", - "-config", f"conf/{name}.cnf", - cwd=self.dir, - ) - exts: list[str] = [] - if "client" in name: - exts = ["-extensions", "client_ext"] - if "client_ok" in name: - exts = ["-extensions", "client_ok_ext"] - if "server" in name: - exts = ["-extensions", "server_ext"] - self._run( - "ca", "-policy", "policy_anything", - "-in", f"csr/{name}.csr", "-out", f"certs/{name}.crt", - "-passin", f"pass:{PASS}", "-config", f"conf/{name}.cnf", - "-batch", *exts, - cwd=self.dir, - ) - - def _make_proxy_cert(self, name: str) -> None: - """Concatenate cert + key into proxy/.pem (make_proxy_cert).""" - crt = (self.dir / "certs" / f"{name}.crt").read_text() - key = (self.dir / "keys" / f"{name}.pem").read_text() - (self.dir / "proxy" / f"{name}.pem").write_text(crt + key) - - def _revoke_cert(self, name: str) -> None: - self._run( - "ca", "-revoke", f"certs/{name}.crt", - "-config", "conf/ca.cnf", "-passin", f"pass:{PASS}", - cwd=self.dir, - ) - self._run( - "ca", "-gencrl", "-out", "crl/ca-bundle.crl", - "-config", "conf/ca.cnf", "-passin", f"pass:{PASS}", - cwd=self.dir, - ) - - def generate(self) -> Path: - """Build the full CA tree (idempotent). Returns the CA root dir.""" - if self.dir.is_dir() and (self.dir / "certs" / "ca.crt").exists(): - return self.root # already generated - self._init_dirs() - self._new_ca() - # Ensure a CRL exists even if nothing is revoked. - for name in CERT_DN: - self._new_key(name) - self._new_cert(name) - if name.endswith("_revoked"): - self._revoke_cert(name) - if name.startswith("client_"): - self._make_proxy_cert(name) - # Guarantee crl/ca-bundle.crl exists (referenced by SSLCARevocationFile). - crl = self.dir / "crl" / "ca-bundle.crl" - if not crl.exists(): - self._run( - "ca", "-gencrl", "-out", "crl/ca-bundle.crl", - "-config", "conf/ca.cnf", "-passin", f"pass:{PASS}", - cwd=self.dir, - ) - return self.root +from pyhttpd.sslca import ( # noqa: F401 + CA_DN, + CERT_DN, + DAYS, + EMAIL_FIELD, + PASS, + SSLCA, +) diff -Nru apache2-2.4.69/test/pytest_suite/apache_pytest/testapi.py apache2-2.4.71/test/pytest_suite/apache_pytest/testapi.py --- apache2-2.4.69/test/pytest_suite/apache_pytest/testapi.py 2026-06-03 16:20:30.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/apache_pytest/testapi.py 2026-11-09 03:07:05.000000000 +0000 @@ -10,9 +10,9 @@ * ``have_min_apache_version()`` -> :func:`have_min_apache_version` (runtime gate) * ``GET`` / ``GET_BODY`` / ... -> methods on the ``http`` fixture (client.py) -The requirement markers (need_*) are evaluated at collection time by a -``pytest_collection_modifyitems`` hook in conftest.py, which probes the target -httpd once and skips tests whose requirements aren't met -- the analog of +The requirement markers (need_*) are evaluated at collection time by +pyhttpd.gating, which probes the target httpd once and skips tests whose +requirements aren't met -- the analog of Apache::Test's plan-time skipping. """ @@ -23,6 +23,9 @@ import pytest +from pyhttpd.backport import has_change as has_trunk_change # noqa: F401 +from pyhttpd.backport import not_backported # noqa: F401 (one definition, both suites) + # --------------------------------------------------------------------------- # # Assertions / comparison # --------------------------------------------------------------------------- # @@ -60,8 +63,8 @@ # --------------------------------------------------------------------------- # # Requirement markers (need_*) -- thin wrappers over pytest.mark # --------------------------------------------------------------------------- # -# Each marker records its requirement as marker args; the collection hook in -# conftest.py reads them against the probed HttpdInfo and skips as needed. +# Each marker records its requirement as marker args; pyhttpd.gating reads them +# against the probed HttpdInfo at collection time and skips as needed. def need_module(*names: str) -> pytest.MarkDecorator: diff -Nru apache2-2.4.69/test/pytest_suite/conftest.py apache2-2.4.71/test/pytest_suite/conftest.py --- apache2-2.4.69/test/pytest_suite/conftest.py 2026-09-28 16:36:07.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/conftest.py 2026-11-09 03:07:05.000000000 +0000 @@ -18,10 +18,13 @@ from __future__ import annotations -import subprocess from pathlib import Path import pytest +from pyhttpd.build import resolve_build as _resolve_build +from pyhttpd.build import why_no_build as _why_no_build +from pyhttpd.classic_errorlog import check_shutdown, check_startup +from pyhttpd.gating import add_module_provider from apache_pytest import HttpdServer, TestClient, TestConfig, compile_all, probe from apache_pytest.cmodules import clean_modules @@ -35,200 +38,27 @@ REPO_ROOT = SUITE_ROOT -def pytest_addoption(parser: pytest.Parser) -> None: - group = parser.getgroup("httpd") - group.addoption("--httpd", action="store", default=None, help="path to httpd binary") - group.addoption("--apxs", action="store", default=None, help="path to apxs") - group.addoption( - "--defines", - action="store", - default="", - help="space-separated extra -D defines (e.g. 'LDAP')", - ) - group.addoption( - "--php-fpm", - action="store", - default=None, - help="path to a php-fpm binary; enables the t/php tests via mod_proxy_fcgi " - "(any version/location -- the path is the only PHP-specific input)", - ) - group.addoption( - "--php-fpm-port", - action="store", - type=int, - default=8999, - help="TCP port for the managed php-fpm pool (default 8999)", - ) - group.addoption( - "--clean-modules", - action="store_true", - default=False, - help="remove all compiled C-module artifacts before building (emulate make clean)", - ) - group.addoption( - "--conf", - action="store", - default=None, - help="path to the installed httpd.conf (for LoadModule discovery " - "when --apxs is not available, e.g. on Windows)", - ) - group.addoption( - "--prefix", - action="store", - default=None, - help="server install prefix for resolving relative module paths " - "(default: derived from --conf path)", - ) - group.addoption( - "--with-perl", - action="store", - default=None, - help="path to the perl interpreter for generated scripts and " - "rewrite prg-maps (default: /usr/bin/perl or APACHE_TEST_PERL env)", - ) - - -def pytest_configure(config: pytest.Config) -> None: - for marker in ( - "need_module(*names): skip unless all named httpd modules are available", - "need_min_apache_version(ver): skip unless server >= ver", - "need_cgi(): skip unless a CGI module is available", - "need_php(): skip unless a PHP SAPI module is available", - "need_ssl(): skip unless mod_ssl is available", - "need_lwp(): always satisfied (httpx client always present)", - ): - config.addinivalue_line("markers", marker) - - -def _apxs_query(apxs: Path, var: str) -> str: - proc = subprocess.run( # noqa: S603 - trusted path - ["perl", str(apxs), "-q", var], capture_output=True, text=True, check=True - ) - return proc.stdout.strip() - - -class _NoServerError(Exception): - """Raised when neither --httpd nor --apxs was provided.""" - - -def _resolve_paths( - config: pytest.Config, -) -> tuple[Path, Path | None, Path | None, Path | None, list[str]]: - """Resolve (httpd, apxs, inherited_conf, install_prefix, defines) from options. - - Raises :class:`_NoServerError` if neither --httpd nor --apxs is given. (A - plain exception, not pytest.fail, so the collection-time probe can catch it - without turning into an INTERNALERROR; the fixture converts it to a clean - skip/fail at setup time.) - """ - apxs_opt = config.getoption("--apxs") - httpd_opt = config.getoption("--httpd") - defines = [d for d in config.getoption("--defines").split() if d] - - apxs = Path(apxs_opt) if apxs_opt else None - inherited_conf: Path | None = None - install_prefix: Path | None = None - if apxs is not None: - sbindir = Path(_apxs_query(apxs, "SBINDIR")) - sysconfdir = Path(_apxs_query(apxs, "SYSCONFDIR")) - install_prefix = Path(_apxs_query(apxs, "PREFIX")) - inherited_conf = sysconfdir / "httpd.conf" - if httpd_opt is None: - httpd_opt = str(sbindir / "httpd") - - conf_opt = config.getoption("--conf") - prefix_opt = config.getoption("--prefix") - if conf_opt is not None and inherited_conf is None: - inherited_conf = Path(conf_opt) - if prefix_opt is not None: - install_prefix = Path(prefix_opt) - elif inherited_conf is not None and install_prefix is None: - install_prefix = inherited_conf.parent.parent - - if httpd_opt is None: - raise _NoServerError("must pass --httpd or --apxs") - return Path(httpd_opt), apxs, inherited_conf, install_prefix, defines - - # --------------------------------------------------------------------------- # # Collection-time requirement gating (need_* markers) # --------------------------------------------------------------------------- # -_probe_cache: HttpdInfo | None = None - +# The markers are evaluated by pyhttpd.gating, against the build that +# pyhttpd.build found. The bundled C test modules (authany, echo_post, +# input_body_filter, ...) are compiled and LoadModule'd into the generated +# test config by the session fixture below, so need_module("authany") etc. +# should be satisfied at collection time too. Declare the C modules that WILL +# be built (honoring the same HTTPD_TEST_REQUIRE_APACHE gating discover() +# applies). -def _probed_info(config: pytest.Config) -> HttpdInfo | None: - """Probe the target httpd once for collection-time requirement checks. - Returns None if no httpd was specified (so requirement markers can't be - evaluated -- tests are left to fail/skip naturally at fixture setup). - """ - global _probe_cache # noqa: PLW0603 - simple memo across the collection hook - if _probe_cache is not None: - return _probe_cache - try: - httpd, _apxs, inherited_conf, install_prefix, _defines = _resolve_paths(config) - except Exception: - return None - info = probe(httpd, inherited_conf, install_prefix) - # The bundled C test modules (authany, echo_post, input_body_filter, ...) are - # compiled and LoadModule'd into the generated test config by the session - # fixture, so need_module("authany") etc. should be satisfied at collection - # time too. Augment the probed set with the C modules that WILL be built - # (honoring the same HTTPD_TEST_REQUIRE_APACHE gating discover() applies). +def _provide_cmodules(info: HttpdInfo) -> None: from apache_pytest.cmodules import discover cmods, _skipped = discover(REPO_ROOT / "c-modules", info) for mod in cmods: info.modules.add(f"mod_{mod.name}.c") - _probe_cache = info - return _probe_cache -def _unmet_requirement( - item: pytest.Item, info: HttpdInfo, *, php_fpm: bool = False -) -> str | None: - """Return a skip reason if any need_* marker on ``item`` is unmet, else None. - - ``php_fpm`` indicates a php-fpm binary was provided (and mod_proxy_fcgi is - available), which satisfies need_php even without an in-process PHP SAPI. - """ - for mark in item.iter_markers(name="need_module"): - for name in mark.args: - if not info.has_module(name): - return f"module {name} not available" - for mark in item.iter_markers(name="need_min_apache_version"): - ver = mark.args[0] - parts = tuple(int(x) for x in str(ver).split(".")) - parts += (0,) * (3 - len(parts)) - if info.version < parts: - return f"httpd {info.version_str} < required {ver}" - if any(item.iter_markers(name="need_cgi")) and not ( - info.has_module("mod_cgi") or info.has_module("mod_cgid") - ): - return "no CGI module (mod_cgi/mod_cgid) available" - if any(item.iter_markers(name="need_php")): - php_mods = ("sapi_apache2", "mod_php4", "mod_php5", "mod_php7", "mod_php") - have_sapi = any(info.has_module(m) for m in php_mods) - have_fpm = php_fpm and info.has_module("mod_proxy_fcgi") - if not (have_sapi or have_fpm): - return "no PHP SAPI module or php-fpm available" - if any(item.iter_markers(name="need_ssl")) and not info.has_module("mod_ssl"): - return "mod_ssl not available" - # need_lwp is always satisfied (httpx is always present); no check. - return None - - -def pytest_collection_modifyitems( - config: pytest.Config, items: list[pytest.Item] -) -> None: - info = _probed_info(config) - if info is None: - return - php_fpm = bool(config.getoption("--php-fpm")) - for item in items: - reason = _unmet_requirement(item, info, php_fpm=php_fpm) - if reason: - item.add_marker(pytest.mark.skip(reason=reason)) +add_module_provider(_provide_cmodules) # --------------------------------------------------------------------------- # @@ -237,11 +67,15 @@ @pytest.fixture(scope="session") def framework(request: pytest.FixtureRequest): """Probe, configure, build C modules, start httpd; yield (config, server).""" - try: - httpd, apxs, inherited_conf, install_prefix, defines = _resolve_paths(request.config) - except _NoServerError as exc: - pytest.fail(str(exc)) + # the httpd of the session, the one the need_* marks were evaluated for + target = _resolve_build(request.config) + if target is None: + pytest.fail(_why_no_build()) + httpd, apxs = target.httpd, target.apxs + inherited_conf, install_prefix = target.inherited_conf, target.prefix + defines = target.extra_defines + # probed again: gating added the c-modules/ that will be built to target info = probe(httpd, inherited_conf, install_prefix) perl_opt = request.config.getoption("--with-perl") @@ -309,11 +143,14 @@ server = HttpdServer(config) server.start() try: + check_startup(request.config, str(error_log), + int(config.vars["session_log_start"])) yield config, server finally: server.stop() if fpm_mgr is not None: fpm_mgr.stop() + check_shutdown(request.config) @pytest.fixture(scope="session") diff -Nru apache2-2.4.69/test/pytest_suite/gen_test_header.py apache2-2.4.71/test/pytest_suite/gen_test_header.py --- apache2-2.4.69/test/pytest_suite/gen_test_header.py 2026-09-28 16:36:07.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/gen_test_header.py 2026-11-09 03:07:05.000000000 +0000 @@ -9,6 +9,10 @@ import types from pathlib import Path +# CMake runs this script directly, without pytest's pythonpath settings. +# cmodules.py also uses the shared, standard-library-only pyhttpd builder. +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) + # Stub out the apache_pytest package so cmodules.py can be loaded # without pulling in httpx and other test-only dependencies. pkg = types.ModuleType('apache_pytest') diff -Nru apache2-2.4.69/test/pytest_suite/pyproject.toml apache2-2.4.71/test/pytest_suite/pyproject.toml --- apache2-2.4.69/test/pytest_suite/pyproject.toml 2026-06-03 16:20:30.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/pyproject.toml 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -[project] -name = "httpd-pytest" -version = "0.1.0" -description = "Self-contained pytest port of the Apache httpd integration test suite" -requires-python = ">=3.11" -dependencies = [ - "pytest>=8.0", - "httpx>=0.27", -] - -[tool.pytest.ini_options] -# Eventually tests live under ../t/ per category (apache, modules, ssl, ...). -# Phase 1 ships the framework + smoke/parity tests under tests/. -testpaths = ["tests"] -python_files = ["test_*.py", "*_test.py"] -# Make the framework package (python/apache_pytest) importable from conftest and -# tests regardless of import mode. -pythonpath = ["."] -# importlib import mode lets same-named test modules coexist across category -# dirs (e.g. ssl/test_all.py and php/test_all.py) without __init__.py files or -# import-file-mismatch collisions. -addopts = "-ra --import-mode=importlib" - -[tool.ruff] -line-length = 100 -target-version = "py311" - -[tool.uv] -package = false diff -Nru apache2-2.4.69/test/pytest_suite/ruff.toml apache2-2.4.71/test/pytest_suite/ruff.toml --- apache2-2.4.69/test/pytest_suite/ruff.toml 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/ruff.toml 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,2 @@ +line-length = 100 +target-version = "py311" diff -Nru apache2-2.4.69/test/pytest_suite/runtests.bat apache2-2.4.71/test/pytest_suite/runtests.bat --- apache2-2.4.69/test/pytest_suite/runtests.bat 2026-09-28 16:36:07.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/runtests.bat 1970-01-01 00:00:00.000000000 +0000 @@ -1,164 +0,0 @@ -@echo off -rem -rem runtests.bat -- run the Apache httpd pytest suite on Windows. -rem -rem This is the Windows equivalent of runtests.sh. It needs a built -rem Apache httpd (the server under test) located via apxs, and -rem optionally a php-fpm binary to run the PHP tests. -rem -rem Usage: -rem runtests.bat -- auto-detect apxs/httpd/php-fpm/perl on PATH -rem runtests.bat --apxs C:\path\apxs -- point at a specific build -rem runtests.bat --with-perl C:\path\perl.exe -- point at a specific perl -rem runtests.bat tests\t\modules -- run a subset (any pytest args pass through) -rem runtests.bat -k status -v -- extra pytest args pass through too -rem -rem Environment overrides (used when the matching --flag is not supplied): -rem APXS path to apxs -rem HTTPD path to httpd -rem PHP_FPM path to php-fpm -rem APACHE_TEST_PERL path to perl -rem - -setlocal enabledelayedexpansion - -set "HERE=%~dp0" -rem Remove trailing backslash -if "%HERE:~-1%"=="\" set "HERE=%HERE:~0,-1%" - -cd /d "%HERE%" - -rem --- python location: find the newest native Windows Python 3.x -set PYTHON= -for /f "delims=" %%D in ('dir /b /o-n "%LOCALAPPDATA%\Programs\Python\Python3*" 2^>nul') do ( - if not defined PYTHON set "PYTHON=%LOCALAPPDATA%\Programs\Python\%%D\python.exe" -) -if not defined PYTHON ( - for /f "delims=" %%D in ('dir /b /o-n "%PROGRAMFILES%\Python3*" 2^>nul') do ( - if not defined PYTHON set "PYTHON=%PROGRAMFILES%\%%D\python.exe" - ) -) -if not defined PYTHON ( - echo runtests.bat: ERROR: Python interpreter not found. >&2 - echo Install Python from https://www.python.org or set PYTHON=... >&2 - exit /b 1 -) - -rem --- ensure the venv exists and is current ---------------------------------- -set "PYTEST=%HERE%\.venv\Scripts\pytest.exe" - -set "NEED_SYNC=0" -if not exist "%PYTEST%" set "NEED_SYNC=1" - -rem Check if pyproject.toml is newer than .venv (approximate: compare timestamps -rem via xcopy /D /L which lists files that are newer than the destination). -if exist "%HERE%\.venv" ( - for /f %%F in ('xcopy /D /L /Y "%HERE%\pyproject.toml" "%HERE%\.venv\" 2^>nul ^| C:\Windows\System32\find.exe /c "pyproject.toml"') do ( - if %%F gtr 0 set "NEED_SYNC=1" - ) -) else ( - set "NEED_SYNC=1" -) - -if "%NEED_SYNC%"=="1" ( - %PYTHON% -m uv --version >nul 2>&1 - if errorlevel 1 ( - echo runtests.bat: ERROR: 'uv' is required but not installed. >&2 - echo Install it from https://docs.astral.sh/uv/ and re-run. >&2 - echo %PYTHON% -m pip install uv >&2 - exit /b 1 - ) - echo runtests.bat: ^(re^)creating %HERE%\.venv via 'uv sync'... >&2 - %PYTHON% -m uv sync --project "%HERE%" - if errorlevel 1 exit /b 1 - rem Touch .venv so the staleness check won't retrigger - copy /b "%HERE%\.venv" +,, "%HERE%\.venv" >nul 2>&1 -) - -rem --- discover apxs / httpd / php-fpm ---------------------------------------- -if not defined APXS ( - for %%C in (apxs.exe) do ( - if "!APXS!"=="" ( - for /f "delims=" %%P in ('where %%C 2^>nul') do ( - if "!APXS!"=="" set "APXS=%%P" - ) - ) - ) -) - -if not defined PHP_FPM ( - for %%C in (php-fpm.exe php-fpm8.3.exe php-fpm83.exe php-fpm8.2.exe php-fpm82.exe) do ( - if "!PHP_FPM!"=="" ( - for /f "delims=" %%P in ('where %%C 2^>nul') do ( - if "!PHP_FPM!"=="" set "PHP_FPM=%%P" - ) - ) - ) -) - -if not defined APACHE_TEST_PERL ( - for %%C in (perl.exe) do ( - for /f "delims=" %%P in ('where %%C 2^>nul') do ( - if "!APACHE_TEST_PERL!"=="" set "APACHE_TEST_PERL=%%P" - ) - ) -) - -rem Build the default flag set only for values the user did not pass explicitly. -set "AUTO_ARGS=" - -rem Check if user passed --apxs or --httpd on the command line. -set "HAS_SERVER_FLAG=0" -for %%A in (%*) do ( - echo %%A | findstr /b /c:"--apxs" /c:"--httpd" >nul 2>&1 && set "HAS_SERVER_FLAG=1" -) - -if "%HAS_SERVER_FLAG%"=="0" ( - if defined APXS ( - set "AUTO_ARGS=--apxs=!APXS!" - ) else if defined HTTPD ( - set "AUTO_ARGS=--httpd=!HTTPD!" - ) else ( - echo runtests.bat: ERROR: no apxs/httpd found. >&2 - echo Pass --apxs=C:\path\to\apxs, or set APXS=... or HTTPD=... >&2 - exit /b 1 - ) -) - -rem Check if user passed --php-fpm on the command line. -set "HAS_PHP_FLAG=0" -for %%A in (%*) do ( - echo %%A | findstr /b /c:"--php-fpm" >nul 2>&1 && set "HAS_PHP_FLAG=1" -) - -if "%HAS_PHP_FLAG%"=="0" ( - if defined PHP_FPM ( - set "AUTO_ARGS=!AUTO_ARGS! --php-fpm=!PHP_FPM!" - ) else ( - echo runtests.bat: note: no php-fpm found; PHP tests will skip. >&2 - echo Set PHP_FPM=C:\path\to\php-fpm to run them. >&2 - ) -) - -rem Check if user passed --with-perl on the command line. -set "HAS_PERL_FLAG=0" -for %%A in (%*) do ( - echo %%A | findstr /b /c:"--with-perl" >nul 2>&1 && set "HAS_PERL_FLAG=1" -) - -if "%HAS_PERL_FLAG%"=="0" ( - if defined APACHE_TEST_PERL ( - set "AUTO_ARGS=!AUTO_ARGS! --with-perl=!APACHE_TEST_PERL!" - ) else ( - echo runtests.bat: note: no perl found; rewrite prg-map tests may fail. >&2 - echo Pass --with-perl=C:\path\to\perl.exe or set APACHE_TEST_PERL=... >&2 - ) -) - -rem A stale mod_cgid socket in t\logs can break a fresh run; clear it first. -del /q "%HERE%\t\logs\cgisock*" 2>nul - -rem --- run -------------------------------------------------------------------- -echo runtests.bat: %PYTEST% %AUTO_ARGS% %* >&2 -"%PYTEST%" %AUTO_ARGS% %* -exit /b %errorlevel% diff -Nru apache2-2.4.69/test/pytest_suite/runtests.sh apache2-2.4.71/test/pytest_suite/runtests.sh --- apache2-2.4.69/test/pytest_suite/runtests.sh 2026-07-18 15:32:22.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/runtests.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,104 +0,0 @@ -#!/bin/sh -# -# runtests.sh -- run the Apache httpd pytest suite. -# -# This is the self-contained Python/pytest port of the Apache httpd test suite. -# It needs a built Apache httpd (the server under test) located via apxs, and -# optionally a php-fpm binary to run the PHP tests. -# -# Usage: -# ./runtests.sh # auto-detect apxs/httpd/php-fpm on PATH -# ./runtests.sh --apxs /path/to/apxs # point at a specific build -# ./runtests.sh tests/t/modules # run a subset (any pytest args pass through) -# ./runtests.sh -k status -v # extra pytest args pass through too -# -# Environment overrides (used when the matching --flag is not supplied): -# APXS path to apxs (default: first 'apxs' on PATH) -# HTTPD path to httpd (default: derived from apxs, i.e. apxs -q SBINDIR/httpd) -# PHP_FPM path to php-fpm (default: first 'php-fpm'/'php-fpm8*'/'php-fpm83' on PATH; PHP tests skip if none) -# -# Examples: -# APXS=$HOME/root/httpd/bin/apxs ./runtests.sh -# PHP_FPM=/opt/local/sbin/php-fpm83 ./runtests.sh tests/t/php -# -set -eu - -here="$(cd "$(dirname "$0")" && pwd)" -cd "$here" - -# --- ensure the venv exists and is current ---------------------------------- -# The suite baselines on uv (https://docs.astral.sh/uv/) as its dependency and -# venv manager: it reads pyproject.toml + uv.lock, so there is a single source -# of truth for dependencies. We invoke .venv/bin/pytest directly rather than -# `uv run` so the suite works even where `uv run` is shimmed/unavailable. -# -# Create $here/.venv on first run, and rebuild it when pyproject.toml is newer -# than the venv (i.e. dependencies changed). Absolute paths throughout, so this -# behaves identically regardless of the caller's cwd. This block is kept -# byte-for-byte identical in pytest_suite/runtests.sh and pyhttpd/runtests.sh -# -- edit both together. -PYTEST="$here/.venv/bin/pytest" -if [ ! -x "$PYTEST" ] || [ "$here/pyproject.toml" -nt "$here/.venv" ]; then - if ! command -v uv >/dev/null 2>&1; then - echo "runtests.sh: ERROR: 'uv' is required but not installed." >&2 - echo " Install it from https://docs.astral.sh/uv/ and re-run." >&2 - exit 1 - fi - echo "runtests.sh: (re)creating $here/.venv via 'uv sync'..." >&2 - uv sync --project "$here" - # Mark the venv as freshly built so the staleness check above won't retrigger - # until pyproject.toml changes again. - touch "$here/.venv" -fi - -# --- discover apxs / httpd / php-fpm ---------------------------------------- -# Any of these may be overridden by passing the matching --flag through to -# pytest (those take precedence); here we only fill in defaults via env/PATH. -discover() { - # discover VAR cmd1 cmd2 ... -> echo first one found on PATH - shift - for c in "$@"; do - if command -v "$c" >/dev/null 2>&1; then command -v "$c"; return 0; fi - done - return 1 -} - -APXS="${APXS:-$(discover APXS apxs || true)}" -PHP_FPM="${PHP_FPM:-$(discover PHP_FPM php-fpm php-fpm8.3 php-fpm83 php-fpm8.2 php-fpm82 || true)}" - -# Build the default flag set only for values the user did not pass explicitly. -auto_args="" -case " $* " in - *" --apxs"*|*" --httpd"*) : ;; # user specified a server; don't auto-add - *) - if [ -n "${APXS:-}" ]; then - auto_args="--apxs=$APXS" - elif [ -n "${HTTPD:-}" ]; then - auto_args="--httpd=$HTTPD" - else - echo "runtests.sh: ERROR: no apxs/httpd found." >&2 - echo " Pass --apxs=/path/to/apxs, or set APXS=... or HTTPD=..." >&2 - exit 1 - fi - ;; -esac - -case " $* " in - *" --php-fpm"*) : ;; # user specified php-fpm; don't auto-add - *) - if [ -n "${PHP_FPM:-}" ]; then - auto_args="$auto_args --php-fpm=$PHP_FPM" - else - echo "runtests.sh: note: no php-fpm found; PHP tests will skip." >&2 - echo " Set PHP_FPM=/path/to/php-fpm (any version) to run them." >&2 - fi - ;; -esac - -# A stale mod_cgid socket in t/logs can break a fresh run; clear it first. -rm -f "$here/t/logs/cgisock"* 2>/dev/null || true - -# --- run -------------------------------------------------------------------- -echo "runtests.sh: $PYTEST $auto_args $*" >&2 -# shellcheck disable=SC2086 # auto_args is an intentional word-split flag list -exec "$PYTEST" $auto_args "$@" diff -Nru apache2-2.4.69/test/pytest_suite/t/conf/extra.conf.in apache2-2.4.71/test/pytest_suite/t/conf/extra.conf.in --- apache2-2.4.69/test/pytest_suite/t/conf/extra.conf.in 2026-09-28 16:36:07.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/t/conf/extra.conf.in 2026-11-09 03:07:05.000000000 +0000 @@ -135,6 +135,15 @@ LanguagePriority zh-TW fr fu en de + + Options +MultiViews + AddType text/html;qs=0.9 .html + AddType application/xhtml+xml;qs=1.0 .xhtml + AddType "text/html;charset=utf-8;qs=0.8" .qsafter + AddType "text/html;QS=0.8;charset=utf-8" .qsbefore + AddType "application/example;foo=\"a;b\";q=0.7;qs=0.5;bar=\"c, d\"" .quoted + + @@ -647,6 +656,9 @@ DAV On + = 2.5.1> + DAVMSext All + @@ -884,6 +896,8 @@ Alias /modules/ldap/group @DocumentRoot@ Alias /modules/ldap/refer @DocumentRoot@ Alias /modules/ldap/search @DocumentRoot@ + Alias /modules/ldap/subreq @DocumentRoot@ + Alias /modules/ldap/target @DocumentRoot@ # Simple user lookup @@ -924,6 +938,40 @@ Require ldap-search cn=Subgroup + # Subrequest issued from the fixups phase (mod_rewrite -U) into a second + # per-dir config with identical LDAP settings. A subrequest only re-runs + # authn/authz when its per-dir config differs from the parent's, and at + # mod_rewrite's fixup the parent still holds the LDAP connection it used + # for authz. On platforms where APR_THREAD_MUTEX_DEFAULT is recursive + # (Windows) the subrequest used to be handed the parent's connection, and + # the parent's cleanup then ran against an already released connection. + + + AuthLDAPURL "ldap://localhost:8389/dc=example,dc=com?uid" + AuthLDAPBindDN "cn=httpd,dc=example,dc=com" + AuthLDAPBindPassword mod_authnz_ldap + AuthType Basic + AuthName ldap-subreq@httpd.apache.org + AuthBasicProvider ldap + Require ldap-group cn=Group One,dc=example,dc=com + RewriteEngine On + RewriteCond /modules/ldap/target/foobar.html -U + RewriteRule ^ - [E=LDAP_SUBREQ:ok] + + Header set X-LDAP-Subreq "%{LDAP_SUBREQ}e" + + + + AuthLDAPURL "ldap://localhost:8389/dc=example,dc=com?uid" + AuthLDAPBindDN "cn=httpd,dc=example,dc=com" + AuthLDAPBindPassword mod_authnz_ldap + AuthType Basic + AuthName ldap-target@httpd.apache.org + AuthBasicProvider ldap + Require ldap-group cn=Group One,dc=example,dc=com + + + ## @@ -1538,6 +1586,10 @@ CheckSpelling on CheckCaseOnly on + # Point a short URI at a longer, nonexistent base name in a CheckSpelling + # directory. This exercised the r->uri + (urlen - pglen) underflow in + # check_speling(); the server must answer cleanly rather than read OOB. + Alias /sp-oob @SERVERROOT@/htdocs/modules/speling/nocase/nonexistent-long-name.html diff -Nru apache2-2.4.69/test/pytest_suite/t/conf/proxy_beacon.conf.in apache2-2.4.71/test/pytest_suite/t/conf/proxy_beacon.conf.in --- apache2-2.4.69/test/pytest_suite/t/conf/proxy_beacon.conf.in 2026-06-05 08:02:50.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/t/conf/proxy_beacon.conf.in 2026-11-09 03:07:05.000000000 +0000 @@ -27,6 +27,11 @@ + # Let mod_watchdog's singleton move to another child when its owner dies. + # The default lock is not released by a SIGKILLed holder, so the hand-off + # tests in test_proxy_beacon_input.py would never see a new owner. + Mutex sysvsem watchdog-callback + # Received-beacon + balancer add/route logging in the error_log. LogLevel proxy_beacon:info proxy_balancer:debug @@ -49,7 +54,7 @@ # An initially-empty balancer with spare slots for runtime members. - ProxySet growth=10 + ProxySet growth=60 ProxyPass /beacon balancer://beacon @@ -72,7 +77,7 @@ ProxyBeaconAddress 127.0.0.1:@PORT@ ProxyBeaconAdvertise http://127.0.0.1:1 - ProxyBeaconSecret wrong-secret + ProxyBeaconSecret wrong-secret-0123456789 ProxyBeaconInterval 1 @@ -97,6 +102,118 @@ ProxyPass /cap balancer://cap + # + # Ownership / hand-off scenario (third receiver). balancer://handoff holds one + # STATIC member (configured by the admin, never announces) plus room for + # dynamic ones. The tests announce a member once and then fall silent, kill + # the watchdog-owning child, and expect the NEW owner to evict the silent + # member after ProxyBeaconTimeout -- and to leave the static member alone. + # + Define BEACON_HANDOFF_PORT @NextAvailablePort@ + + + ProxyBeaconListen 127.0.0.1:${BEACON_HANDOFF_PORT} + ProxyBeaconBalancer handoff + ProxyBeaconSecret beacon-test-shared-secret + ProxyBeaconTimeout 3 + + BalancerMember http://127.0.0.11:19000 + ProxySet growth=5 + + ProxyPass /handoff balancer://handoff + + SetHandler balancer-manager + Require all granted + + + + # + # Sub-second freshness window (fourth receiver): ProxyBeaconMaxSkew 500ms must + # mean 500ms, not "rounds to 0, so fall back to the 30s default". + # + Define BEACON_SKEW_PORT @NextAvailablePort@ + + + ProxyBeaconListen 127.0.0.1:${BEACON_SKEW_PORT} + ProxyBeaconBalancer skew + ProxyBeaconSecret beacon-test-shared-secret + ProxyBeaconTimeout 0 + ProxyBeaconMaxSkew 500ms + + ProxySet growth=5 + + ProxyPass /skew balancer://skew + + + # + # Table reclamation (fifth receiver): members are evicted after 1s of silence + # and their records reclaimed once a replay of their last message would be + # stale anyway (2 * MaxSkew + 1s = 5s here). growth=100 is the most a balancer + # accepts, so announcing 256 urls leaves 100 members and 156 failed adds: a + # full tracking table. + # + Define BEACON_RECLAIM_PORT @NextAvailablePort@ + + + ProxyBeaconListen 127.0.0.1:${BEACON_RECLAIM_PORT} + ProxyBeaconBalancer reclaim + ProxyBeaconSecret beacon-test-shared-secret + ProxyBeaconTimeout 1 + ProxyBeaconMaxSkew 2 + + ProxySet growth=100 + + ProxyPass /reclaim balancer://reclaim + + + # + # Random-key secret (sixth receiver): "hex:" is the SipHash key itself, with + # no MD5 step, so only a sender signing with the raw bytes is accepted. + # + Define BEACON_HEXKEY_PORT @NextAvailablePort@ + + + ProxyBeaconListen 127.0.0.1:${BEACON_HEXKEY_PORT} + ProxyBeaconBalancer hexkey + ProxyBeaconSecret hex:000102030405060708090a0b0c0d0e0f + ProxyBeaconTimeout 0 + + ProxySet growth=5 + + ProxyPass /hexkey balancer://hexkey + + + # + # A listener whose bind always fails (192.0.2.1 is TEST-NET-1, never a local + # address): the open must be retried with a growing delay, not on every tick + # and not never. + # + + ProxyBeaconListen 192.0.2.1:9 + ProxyBeaconSecret beacon-test-shared-secret + + + # + # Ownership against administrator-added members: workers are added through + # this server's balancer-manager (any child may handle that), then announced. + # + Define BEACON_OWNSYNC_PORT @NextAvailablePort@ + + + ProxyBeaconListen 127.0.0.1:${BEACON_OWNSYNC_PORT} + ProxyBeaconBalancer ownsync + ProxyBeaconSecret beacon-test-shared-secret + ProxyBeaconTimeout 0 + + ProxySet growth=40 + + ProxyPass /ownsync balancer://ownsync + + SetHandler balancer-manager + Require all granted + + + # Two backends competing for the single cap slot (announce every 1s). ProxyBeaconAddress 127.0.0.1:${BEACON_CAP_PORT} diff -Nru apache2-2.4.69/test/pytest_suite/t/conf/proxy_hcheck.conf.in apache2-2.4.71/test/pytest_suite/t/conf/proxy_hcheck.conf.in --- apache2-2.4.69/test/pytest_suite/t/conf/proxy_hcheck.conf.in 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/t/conf/proxy_hcheck.conf.in 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,107 @@ +#t/TEST -proxy_hcheck +# +# Health-checked balancers for t/modules/test_proxy_hcheck.py. The backends are +# not httpd: the tests start small socket servers on the ports allocated here, +# so that they control exactly what each health check sees. hcinterval=1 with +# one pass / one fail makes a worker change state within a couple of seconds. + + + + + + Define HC_URI_PORT @NextAvailablePort@ + Define HC_HDR_PORT @NextAvailablePort@ + Define HC_MANYHDR_PORT @NextAvailablePort@ + Define HC_SLOW_PORT @NextAvailablePort@ + Define HC_BIGEXPR_PORT @NextAvailablePort@ + Define HC_BIGPLAIN_PORT @NextAvailablePort@ + Define HC_PATHA_PORT @NextAvailablePort@ + Define HC_PATHB_PORT @NextAvailablePort@ + Define HC_PATHC_PORT @NextAvailablePort@ + Define HC_PATHD_PORT @NextAvailablePort@ + Define HC_PATHE_PORT @NextAvailablePort@ + Define HC_PATHF_PORT @NextAvailablePort@ + Define HC_PATHG_PORT @NextAvailablePort@ + Define HC_TLSBAD_PORT @NextAvailablePort@ + Define HC_TLSGOOD_PORT @NextAvailablePort@ + + + LogLevel proxy_hcheck:debug + + # The probe path is changed at run time through the balancer-manager. + + BalancerMember http://127.0.0.1:${HC_URI_PORT} hcmethod=GET hcuri=/old hcinterval=1 hcpasses=1 hcfails=1 + + ProxyPass /hcuri balancer://hcuri + + # A HEAD probe against a backend that sends a truncated response. + + BalancerMember http://127.0.0.1:${HC_HDR_PORT} hcmethod=HEAD hcuri=/ hcinterval=1 hcpasses=1 hcfails=1 + + ProxyPass /hchdr balancer://hchdr + + # HTTPS probes. Certificates are not chain-verified (SSLProxyVerify none) + # so that only the host name check is in play: the "bad" backend presents a + # certificate for another name, the "good" one for localhost. + + SSLProxyEngine on + SSLProxyVerify none + SSLProxyCheckPeerName on + + BalancerMember https://localhost:${HC_TLSBAD_PORT} hcmethod=GET hcuri=/ hcinterval=1 hcpasses=1 hcfails=1 + + ProxyPass /hctlsbad balancer://hctlsbad + + BalancerMember https://localhost:${HC_TLSGOOD_PORT} hcmethod=GET hcuri=/ hcinterval=1 hcpasses=1 hcfails=1 + + ProxyPass /hctlsgood balancer://hctlsgood + + + # How the probe path is put together from the worker URL and hcuri. + + BalancerMember http://127.0.0.1:${HC_PATHA_PORT} hcmethod=GET hcuri=ping hcinterval=1 hcpasses=1 hcfails=1 + + + BalancerMember http://127.0.0.1:${HC_PATHB_PORT}/app hcmethod=GET hcuri=/ping hcinterval=1 hcpasses=1 hcfails=1 + + + BalancerMember http://127.0.0.1:${HC_PATHC_PORT}/app/ hcmethod=GET hcuri=ping hcinterval=1 hcpasses=1 hcfails=1 + + + BalancerMember http://127.0.0.1:${HC_PATHD_PORT} hcmethod=GET hcinterval=1 hcpasses=1 hcfails=1 + + + BalancerMember http://127.0.0.1:${HC_PATHE_PORT}/app hcmethod=GET hcinterval=1 hcpasses=1 hcfails=1 + + + BalancerMember http://127.0.0.1:${HC_PATHF_PORT}/app/ hcmethod=GET hcinterval=1 hcpasses=1 hcfails=1 + + + BalancerMember http://127.0.0.1:${HC_PATHG_PORT}/app hcmethod=GET hcuri=/ hcinterval=1 hcpasses=1 hcfails=1 + + + # Limits on what a probe will take from a backend. + ProxyHCExpr nomaint {hc('body') !~ /UNDER-MAINTENANCE/} + + BalancerMember http://127.0.0.1:${HC_MANYHDR_PORT} hcmethod=GET hcuri=/ hcinterval=1 hcpasses=1 hcfails=1 + + # timeout=2: also the overall budget of one probe + + BalancerMember http://127.0.0.1:${HC_SLOW_PORT} hcmethod=GET hcuri=/ hcinterval=1 hcpasses=1 hcfails=1 timeout=2 + + + BalancerMember http://127.0.0.1:${HC_BIGEXPR_PORT} hcmethod=GET hcuri=/ hcinterval=1 hcpasses=1 hcfails=1 hcexpr=nomaint + + + BalancerMember http://127.0.0.1:${HC_BIGPLAIN_PORT} hcmethod=GET hcuri=/ hcinterval=1 hcpasses=1 hcfails=1 + + + + SetHandler balancer-manager + Require all granted + + + + + + diff -Nru apache2-2.4.69/test/pytest_suite/t/htdocs/modules/negotiation/qs/after.qsafter apache2-2.4.71/test/pytest_suite/t/htdocs/modules/negotiation/qs/after.qsafter --- apache2-2.4.69/test/pytest_suite/t/htdocs/modules/negotiation/qs/after.qsafter 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/t/htdocs/modules/negotiation/qs/after.qsafter 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +after diff -Nru apache2-2.4.69/test/pytest_suite/t/htdocs/modules/negotiation/qs/before.qsbefore apache2-2.4.71/test/pytest_suite/t/htdocs/modules/negotiation/qs/before.qsbefore --- apache2-2.4.69/test/pytest_suite/t/htdocs/modules/negotiation/qs/before.qsbefore 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/t/htdocs/modules/negotiation/qs/before.qsbefore 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +before diff -Nru apache2-2.4.69/test/pytest_suite/t/htdocs/modules/negotiation/qs/doc.html apache2-2.4.71/test/pytest_suite/t/htdocs/modules/negotiation/qs/doc.html --- apache2-2.4.69/test/pytest_suite/t/htdocs/modules/negotiation/qs/doc.html 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/t/htdocs/modules/negotiation/qs/doc.html 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +html diff -Nru apache2-2.4.69/test/pytest_suite/t/htdocs/modules/negotiation/qs/doc.xhtml apache2-2.4.71/test/pytest_suite/t/htdocs/modules/negotiation/qs/doc.xhtml --- apache2-2.4.69/test/pytest_suite/t/htdocs/modules/negotiation/qs/doc.xhtml 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/t/htdocs/modules/negotiation/qs/doc.xhtml 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +xhtml diff -Nru apache2-2.4.69/test/pytest_suite/t/htdocs/modules/negotiation/qs/quoted.quoted apache2-2.4.71/test/pytest_suite/t/htdocs/modules/negotiation/qs/quoted.quoted --- apache2-2.4.69/test/pytest_suite/t/htdocs/modules/negotiation/qs/quoted.quoted 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/t/htdocs/modules/negotiation/qs/quoted.quoted 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +quoted diff -Nru apache2-2.4.69/test/pytest_suite/t/htdocs/modules/negotiation/qs/tm1.htm apache2-2.4.71/test/pytest_suite/t/htdocs/modules/negotiation/qs/tm1.htm --- apache2-2.4.69/test/pytest_suite/t/htdocs/modules/negotiation/qs/tm1.htm 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/t/htdocs/modules/negotiation/qs/tm1.htm 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +html diff -Nru apache2-2.4.69/test/pytest_suite/t/htdocs/modules/negotiation/qs/tm2.xht apache2-2.4.71/test/pytest_suite/t/htdocs/modules/negotiation/qs/tm2.xht --- apache2-2.4.69/test/pytest_suite/t/htdocs/modules/negotiation/qs/tm2.xht 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/t/htdocs/modules/negotiation/qs/tm2.xht 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1 @@ +xht diff -Nru apache2-2.4.69/test/pytest_suite/t/htdocs/modules/negotiation/qs/typemap.var apache2-2.4.71/test/pytest_suite/t/htdocs/modules/negotiation/qs/typemap.var --- apache2-2.4.69/test/pytest_suite/t/htdocs/modules/negotiation/qs/typemap.var 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/t/htdocs/modules/negotiation/qs/typemap.var 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,5 @@ +URI: tm1.htm +Content-Type: text/html; qs=0.9 + +URI: tm2.xht +Content-Type: application/xhtml+xml; qs=1.0 diff -Nru apache2-2.4.69/test/pytest_suite/tests/t/apache/test_motorz_connections.py apache2-2.4.71/test/pytest_suite/tests/t/apache/test_motorz_connections.py --- apache2-2.4.69/test/pytest_suite/tests/t/apache/test_motorz_connections.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/tests/t/apache/test_motorz_connections.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,134 @@ +r"""mpm_motorz: connection ids. + +c->id names the worker slot running a connection (child * ThreadLimit + slot), as +in the worker and event MPMs. mod_cgid keeps one entry per id and assumes the +ids stay within the number of worker slots. The child also holds a limited +number of connections, whether or not they use a worker. + +The id is read back from mod_unique_id, whose last four bytes are c->id. +""" + +import base64 +import socket +import sys +import time + +import pytest + +from apache_pytest.private_server import PrivateServer + +THREADS = 4 +CHILDREN = 2 + + +def unique_id_conn_id(uid): + raw = base64.urlsafe_b64decode(uid + "=" * (-len(uid) % 4)) + return int.from_bytes(raw[-4:], "big") + + +def request_id(port, path="/small.txt"): + with socket.create_connection(("127.0.0.1", port), timeout=10) as s: + s.sendall(f"GET {path} HTTP/1.0\r\n\r\n".encode()) + data = b"" + while chunk := s.recv(4096): + data += chunk + head = data.partition(b"\r\n\r\n")[0].decode() + for line in head.split("\r\n"): + if line.lower().startswith("x-uid:"): + return unique_id_conn_id(line.split(":", 1)[1].strip()) + raise AssertionError(f"no X-Uid header in {head!r}") + + +@pytest.mark.skipif(sys.platform == "win32", reason="forking MPM") +def test_connection_ids_stay_within_the_worker_slots(config, tmp_path): + if config.info.mpm != "motorz": + pytest.skip("motorz only") + if not (config.info.has_module("unique_id") and config.info.has_module("headers")): + pytest.skip("needs mod_unique_id and mod_headers") + + extra = [ + f"StartServers {CHILDREN}", + f"ThreadLimit {THREADS}", + f"ThreadsPerChild {THREADS}", + "ServerLimit 4", + 'Header always set X-Uid "%{UNIQUE_ID}e"', + ] + with PrivateServer(config, tmp_path, extra=extra, big_length=1024) as server: + ids = [request_id(server.port) for _ in range(200)] + + limit = 4 * THREADS # ServerLimit * ThreadLimit + assert max(ids) < limit, f"id {max(ids)} outside the {limit} worker slots" + + +def served(sock, wait): + """True once a 200 response to the request already sent on sock arrives.""" + sock.settimeout(wait) + try: + return sock.recv(4096).startswith(b"HTTP/1.1 200") + except OSError: + return False + + +@pytest.mark.skipif(sys.platform == "win32", reason="forking MPM") +def test_connections_are_limited_per_child(config, tmp_path): + """Idle keep-alive connections use no worker, so the workers cannot limit + them. With 4 workers and AsyncRequestWorkerFactor 1 the child holds about + 4 + 4 connections; the rest wait in the listen backlog until room is made.""" + if config.info.mpm != "motorz": + pytest.skip("motorz only") + + extra = ["StartServers 1", "ThreadsPerChild 4", "AsyncRequestWorkerFactor 1", + "KeepAliveTimeout 120"] + total = 40 + socks = [] + with PrivateServer(config, tmp_path, extra=extra, big_length=1024) as server: + try: + for _ in range(total): + s = socket.create_connection(("127.0.0.1", server.port), timeout=10) + s.sendall(b"GET /small.txt HTTP/1.1\r\nHost: localhost\r\n\r\n") + socks.append(s) + time.sleep(1) + first = [s for s in socks if served(s, 0.2)] + assert 4 <= len(first) <= 12, f"{len(first)} of {total} connections accepted" + + # Room is made when some of them go away, and is filled again. + rest = [s for s in socks if s not in first] + for s in first: + s.close() + deadline = time.monotonic() + 15 + second = [] + while time.monotonic() < deadline and len(second) < len(first): + second += [s for s in rest if s not in second and served(s, 0.2)] + assert len(second) >= len(first) // 2, \ + f"{len(second)} waiting connections served after {len(first)} closed" + time.sleep(1) + second += [s for s in rest if s not in second and served(s, 0.2)] + assert len(second) <= 12, f"{len(second)} connections held after the restart of accepting" + finally: + for s in socks: + s.close() + + +@pytest.mark.skipif(sys.platform == "win32", reason="forking MPM") +def test_connections_of_a_protocol_that_sets_no_state_are_closed(config, tmp_path): + """mod_echo returns OK from process_connection without setting a connection + state. Such a connection must be closed when the client leaves; if it is + not, it counts against the connection limit for ever and the child stops + accepting.""" + if config.info.mpm != "motorz": + pytest.skip("motorz only") + if not config.info.has_module("echo"): + pytest.skip("needs mod_echo") + + extra = ["StartServers 1", "ThreadsPerChild 4", "AsyncRequestWorkerFactor 1", + "ProtocolEcho On"] + with PrivateServer(config, tmp_path, extra=extra, big_length=1024) as server: + results = [] + for _ in range(20): # more than the limit of 8 + with socket.create_connection(("127.0.0.1", server.port), timeout=3) as s: + s.sendall(b"hi\r\n") + try: + results.append(s.recv(100) == b"hi\r\n") + except OSError: + results.append(False) + assert all(results), "".join("Y" if r else "n" for r in results) diff -Nru apache2-2.4.69/test/pytest_suite/tests/t/apache/test_motorz_graceful.py apache2-2.4.71/test/pytest_suite/tests/t/apache/test_motorz_graceful.py --- apache2-2.4.69/test/pytest_suite/tests/t/apache/test_motorz_graceful.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/tests/t/apache/test_motorz_graceful.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,175 @@ +r"""mpm_motorz: a child told to stop gracefully finishes the connections it has. + +The server delivers a large static file. The client reads a little of it and +stops, so that the server fills the socket buffers and the connection is parked +in the MPM waiting to write. Then the parent is sent the graceful restart +signal. The old child must keep serving that connection until the whole body +has been delivered, while the new generation answers other requests. + +The body has to come from the core handler: a handler that writes it itself +(mod_test_rwrite) blocks a worker thread for the whole transfer, and the +connection is then never parked, so the test passes even without the drain. + +The tests run a private httpd: restarting the suite's own server would change +its state (its worker set, the log, ...) for the tests that run after this one. + +A peer that keeps writing to a connection the server is closing must not keep +the old child alive: the lingering close has one fixed deadline. + +A draining child keeps its process slot, and no replacement can start while +every slot is taken. The other tests open a download, restart, and repeat, so +that several old children are draining at once, then check that new connections +are still served (default ServerLimit) and that the parent says so when they are +not (ServerLimit 2). +""" + +import socket +import sys +import time + +import pytest + +from apache_pytest.private_server import (BODY_LENGTH, PrivateServer, + child_pids, get, read_headers) + + +@pytest.mark.skipif(sys.platform == "win32", reason="signals") +def test_graceful_restart_does_not_cut_a_download(config, tmp_path): + if config.info.mpm != "motorz": + pytest.skip("motorz only") + + sock = None + with PrivateServer(config, tmp_path) as server: + try: + sock = get(server.port, "/big.bin", rcvbuf=4096) + head, body = read_headers(sock) + assert b" 200 " in head.split(b"\r\n")[0], head + received = len(body) + + # Let the server run into the closed window, then restart gracefully. + time.sleep(1) + server.graceful() + + # The new generation answers other requests while the old child drains. + assert server.small_ok(20), "no response after the graceful restart" + + # The old child has had time to exit if it had dropped the connection. + time.sleep(1) + while chunk := sock.recv(65536): + received += len(chunk) + finally: + if sock: + sock.close() + + assert received == BODY_LENGTH + + +def open_downloads_across_restarts(server, rounds): + """Park a download, restart gracefully, and repeat; return the sockets and + whether /small.txt was served after the last restart.""" + socks = [] + served = True + for _ in range(rounds): + sock = get(server.port, "/big.bin", rcvbuf=4096) + head, body = read_headers(sock) + assert b" 200 " in head.split(b"\r\n")[0], head + socks.append([sock, len(body)]) + time.sleep(1) # the server fills the buffers and parks it + server.graceful() + served = server.small_ok(5) + return socks, served + + +def finish_downloads(socks, big_length): + for entry in socks: + sock = entry[0] + while chunk := sock.recv(65536): + entry[1] += len(chunk) + sock.close() + assert entry[1] == big_length + + +@pytest.mark.skipif(sys.platform == "win32", reason="signals") +def test_restarts_with_open_downloads_do_not_starve_new_connections(config, tmp_path): + """Each old child keeps its slot while its download runs. With the default + ServerLimit there is still room for the next generation, so a new request + is served after every restart.""" + if config.info.mpm != "motorz": + pytest.skip("motorz only") + + big = 16 * 1024 * 1024 + socks = [] + with PrivateServer(config, tmp_path, big_length=big) as server: + try: + socks, served = open_downloads_across_restarts(server, 3) + assert served, "no child accepted a new connection after the restarts" + assert "scoreboard is full" not in server.error_log() + finally: + for entry in socks: + entry[0].close() + + +@pytest.mark.skipif(sys.platform == "win32", reason="signals") +def test_full_scoreboard_is_reported_once_and_recovers(config, tmp_path): + """With ServerLimit 2 the second restart finds both slots held by draining + children, so nothing can start. The parent must say so, once, and serve + again as soon as a draining child is done.""" + if config.info.mpm != "motorz": + pytest.skip("motorz only") + + big = 16 * 1024 * 1024 + socks = [] + with PrivateServer(config, tmp_path, extra=["ServerLimit 2"], + big_length=big) as server: + try: + socks, _ = open_downloads_across_restarts(server, 2) + # The parent checks the slots once a second; give it a few rounds. + time.sleep(4) + log = server.error_log() + assert log.count("scoreboard is full") == 1, log + + finish_downloads(socks, big) + socks = [] + assert server.small_ok(20), "no recovery after the downloads finished" + finally: + for entry in socks: + entry[0].close() + + +@pytest.mark.skipif(sys.platform == "win32", reason="signals") +def test_trickle_input_does_not_extend_lingering_close(config, tmp_path): + """The client sends one byte every 250 ms to a connection the server has + finished with. The linger deadline is fixed when the lingering close + starts, so once a graceful restart shortens it the old child must be gone + after a couple of seconds although the client never stops writing.""" + if config.info.mpm != "motorz": + pytest.skip("motorz only") + + with PrivateServer(config, tmp_path) as server: + sock = socket.create_connection(("127.0.0.1", server.port), timeout=10) + try: + sock.sendall(b"GET /small.txt HTTP/1.1\r\nHost: localhost\r\n" + b"Connection: close\r\n\r\n") + while sock.recv(4096): # until the server shuts its side down + pass + + old = child_pids(server.proc.pid) + assert old, "no child process found" + server.graceful() + + start = time.monotonic() + gone = None + while time.monotonic() - start < 15: + try: + sock.send(b"x") + except OSError: + pass # the server may have closed already + if not (old & child_pids(server.proc.pid)): + gone = time.monotonic() - start + break + time.sleep(0.25) + assert gone is not None, "old child still alive while the peer trickles" + assert gone < 8, f"old child needed {gone:.1f}s to exit" + assert server.small_ok(5), "new generation does not answer" + finally: + sock.close() diff -Nru apache2-2.4.69/test/pytest_suite/tests/t/apache/test_motorz_hooks.py apache2-2.4.71/test/pytest_suite/tests/t/apache/test_motorz_hooks.py --- apache2-2.4.69/test/pytest_suite/tests/t/apache/test_motorz_hooks.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/tests/t/apache/test_motorz_hooks.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,149 @@ +r"""mpm_motorz: suspend_connection / resume_connection hooks. + +A connection that is parked between requests is not tied to a worker thread: the +next request may be run by another one. mpm_common.h says the suspend hook runs +on the thread that was processing the connection and the resume hook on the +thread that will continue it, so a module that keeps per-thread state can follow +the connection. + +A small module, built with apxs, logs both hooks and each request with the +thread it ran on. For every connection the log must read +request, (suspend, resume, request)*, with each suspend on the thread of the +request before it and each resume on the thread of the request after it. +""" + +import re +import socket +import subprocess +import sys +import threading +import time + +import pytest + +from apache_pytest.private_server import PrivateServer + +MODULE_SOURCE = r''' +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" +#include "http_main.h" +#include "http_protocol.h" +#include "mpm_common.h" +#include "apr_portable.h" +#include + +static unsigned long tid(void) +{ + return (unsigned long)(apr_uintptr_t)apr_os_thread_current(); +} + +static void hook_suspend(conn_rec *c, request_rec *r) +{ + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, + "HOOKTEST suspend c=%pp tid=%lu", c, tid()); +} + +static void hook_resume(conn_rec *c, request_rec *r) +{ + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, + "HOOKTEST resume c=%pp tid=%lu", c, tid()); +} + +static int hooks_handler(request_rec *r) +{ + if (strcmp(r->handler, "conn-hooks")) { + return DECLINED; + } + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, + "HOOKTEST request c=%pp tid=%lu", r->connection, tid()); + ap_set_content_type(r, "text/plain"); + ap_rputs("ok\n", r); + return OK; +} + +static void register_hooks(apr_pool_t *p) +{ + ap_hook_suspend_connection(hook_suspend, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_resume_connection(hook_resume, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(hooks_handler, NULL, NULL, APR_HOOK_MIDDLE); +} + +AP_DECLARE_MODULE(conn_hooks) = { + STANDARD20_MODULE_STUFF, NULL, NULL, NULL, NULL, NULL, register_hooks +}; +''' + +EVENT = re.compile(r"HOOKTEST (suspend|resume|request) c=(\S+) tid=(\d+)") + + +def build_module(config, tmp_path): + src = tmp_path / "mod_conn_hooks.c" + src.write_text(MODULE_SOURCE) + proc = subprocess.run(["perl", str(config.apxs), "-c", str(src)], + cwd=tmp_path, capture_output=True, text=True) + so = tmp_path / ".libs" / "mod_conn_hooks.so" + assert proc.returncode == 0 and so.exists(), proc.stdout + proc.stderr + return so + + +def keepalive_requests(port, count, gap): + with socket.create_connection(("127.0.0.1", port), timeout=10) as s: + for _ in range(count): + s.sendall(b"GET /hooks HTTP/1.1\r\nHost: localhost\r\n\r\n") + data = b"" + while not data.endswith(b"ok\n"): + chunk = s.recv(4096) + assert chunk, "connection closed" + data += chunk + time.sleep(gap) + + +@pytest.mark.skipif(sys.platform == "win32", reason="forking MPM") +def test_hooks_run_on_the_threads_that_hand_over_and_take_over(config, tmp_path): + if config.info.mpm != "motorz": + pytest.skip("motorz only") + if config.apxs is None: + pytest.skip("needs --apxs to build a module") + + so = build_module(config, tmp_path) + extra = [f"LoadModule conn_hooks_module {so}", + "", " SetHandler conn-hooks", "", + "KeepAliveTimeout 30"] + (tmp_path / "srv").mkdir() + with PrivateServer(config, tmp_path / "srv", extra=extra, + big_length=1024) as server: + threads = [threading.Thread(target=keepalive_requests, + args=(server.port, 25, 0.02)) + for _ in range(4)] + for t in threads: + t.start() + for t in threads: + t.join() + time.sleep(0.5) + log = server.error_log() + + per_conn = {} + for kind, conn, tid in EVENT.findall(log): + per_conn.setdefault(conn, []).append((kind, tid)) + assert len(per_conn) >= 4, per_conn.keys() + + resumes = 0 + moved = 0 + for conn, events in per_conn.items(): + kinds = [k for k, _ in events] + # request, then repeated suspend, resume, request; it may end parked. + expected = ["request"] + while len(expected) < len(kinds): + expected += ["suspend", "resume", "request"] + assert kinds == expected[:len(kinds)], f"{conn}: {kinds}" + for (k1, t1), (k2, t2) in zip(events, events[1:]): + if k1 == "request" and k2 == "suspend": + assert t1 == t2, f"{conn}: suspended on another thread" + if k1 == "resume" and k2 == "request": + assert t1 == t2, f"{conn}: resumed on another thread" + if k1 == "suspend" and k2 == "resume": + resumes += 1 + moved += t1 != t2 + assert resumes >= 4 * 20, f"only {resumes} resumes were logged" + assert moved > 0, "no connection ever moved to another thread; weak test" diff -Nru apache2-2.4.69/test/pytest_suite/tests/t/apache/test_motorz_limits.py apache2-2.4.71/test/pytest_suite/tests/t/apache/test_motorz_limits.py --- apache2-2.4.69/test/pytest_suite/tests/t/apache/test_motorz_limits.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/tests/t/apache/test_motorz_limits.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,43 @@ +r"""mpm_motorz: the default ServerLimit is the larger of 16 and twice StartServers, +capped at the compile-time maximum, and must not overflow for a huge StartServers. + +Each case runs `httpd -t` on the generated test config plus one StartServers. +""" + +import subprocess + +import pytest + +MAX_SERVER_LIMIT = 200000 + + +def configtest(server, tmp_path, start_servers): + conf = tmp_path / "startservers.conf" + conf.write_text( + f'Include "{server.config.vars["t_conf_file"]}"\n' + f"StartServers {start_servers}\n") + argv = [a for a in server.args() if a != "-DFOREGROUND"] + argv[argv.index("-f") + 1] = str(conf) + return subprocess.run([*argv, "-t"], capture_output=True, text=True) + + +@pytest.mark.parametrize("start_servers", [ + MAX_SERVER_LIMIT // 2, # twice this is exactly the maximum + MAX_SERVER_LIMIT // 2 + 1, # twice this is just over it + MAX_SERVER_LIMIT, + MAX_SERVER_LIMIT + 1, + 1073741824, # twice this overflows a 32-bit int + 2147483647, +]) +def test_default_server_limit_does_not_overflow(server, tmp_path, start_servers): + if server.config.info.mpm != "motorz": + pytest.skip("motorz only") + r = configtest(server, tmp_path, start_servers) + assert r.returncode == 0 and "Syntax OK" in r.stderr, r.stderr + assert "ServerLimit of -" not in r.stderr, r.stderr + warning = (f"StartServers of {start_servers} exceeds ServerLimit of " + f"{MAX_SERVER_LIMIT}, decreasing to {MAX_SERVER_LIMIT}") + if start_servers > MAX_SERVER_LIMIT: + assert warning in r.stderr, r.stderr + else: + assert "exceeds ServerLimit" not in r.stderr, r.stderr diff -Nru apache2-2.4.69/test/pytest_suite/tests/t/modules/test_dav.py apache2-2.4.71/test/pytest_suite/tests/t/modules/test_dav.py --- apache2-2.4.69/test/pytest_suite/tests/t/modules/test_dav.py 2026-06-05 17:48:53.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/tests/t/modules/test_dav.py 2026-11-09 03:07:05.000000000 +0000 @@ -19,7 +19,7 @@ import httpx import pytest -from apache_pytest import need_module, t_cmp +from apache_pytest import need_module, not_backported, t_cmp DIR = "modules/dav" URI = f"/{DIR}/dav.html" @@ -207,3 +207,42 @@ os.rmdir(p) except OSError: pass + + +@need_module("dav") +@not_backported("r1939426", "DAVMSext (ms_wdv) null dereference") +def test_dav_mswdv_null_deref(http): + """BZ 70051: a PUT/DELETE/MOVE/LOCK whose dav_get_resource() fails (e.g. an + extraneous path component under an existing file) must not crash the + worker. Under DAVMSext, check_locked_by_other() in ms_wdv.c used to + dereference its lockdb unconditionally in the out: label, even on the + paths that goto out before open_lockdb() runs and leave it NULL. + """ + htdocs = http.vars("documentroot") + os.makedirs(os.path.join(htdocs, DIR), exist_ok=True) + name = "mswdv-70051.html" + fpath = os.path.join(htdocs, DIR, name) + if os.path.exists(fpath): + os.unlink(fpath) + + base = http.base_url + url = f"{base}/{DIR}/{name}" + + with httpx.Client(timeout=30.0) as c: + resp = c.put(url, content=BODY) + assert resp.is_success, f"seed PUT failed (got {resp.status_code})" + + # dav_fs_get_resource() rejects the extra path component below the + # plain file with an error -- dav_get_resource() fails before any + # lockdb is opened. Pre-fix, check_locked_by_other()'s "out:" label + # dereferenced that still-NULL lockdb and crashed the worker. + resp = c.put(f"{url}/extra", content="boom") + assert resp.status_code in (403, 404, 409), \ + f"PUT past a file should be a clean DAV error, got {resp.status_code}" + + # the worker must still be alive: a normal request must succeed. + resp = c.get(url) + assert resp.status_code == 200, \ + f"server did not survive the request (got {resp.status_code})" + + os.unlink(fpath) diff -Nru apache2-2.4.69/test/pytest_suite/tests/t/modules/test_ldap.py apache2-2.4.71/test/pytest_suite/tests/t/modules/test_ldap.py --- apache2-2.4.69/test/pytest_suite/tests/t/modules/test_ldap.py 2026-09-28 13:16:28.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/tests/t/modules/test_ldap.py 2026-11-09 03:07:05.000000000 +0000 @@ -27,6 +27,8 @@ ("/modules/ldap/refer/", "alpha", "Alpha", 401), ("/modules/ldap/refer/", "beta", "Beta", 200), ("/modules/ldap/search/", "unchecked", "unchecked", 200), + ("/modules/ldap/subreq/foobar.html", "delta", "Delta", 200), + ("/modules/ldap/target/foobar.html", "delta", "Delta", 200), ] @@ -46,3 +48,18 @@ r = http.GET(url) creds = f"{username}/{password}" if username else "no credentials" assert t_cmp(r.status_code, expected), f"test for {url} with {creds}" + + +@need_module("authnz_ldap", "rewrite", "headers") +def test_ldap_fixups_subrequest(http): + """A mod_rewrite -U subrequest from the parent's fixups phase lands in a + second per-dir config with the same LDAP settings, so its own LDAP authn + and authz run while the parent still holds its authz connection. The + parent must complete, and the header proves the subrequest succeeded.""" + if not _ldap_configured(http): + pytest.skip("LDAP testing not configured (run with --defines LDAP)") + r = http.GET("/modules/ldap/subreq/foobar.html", + auth=httpx.BasicAuth("delta", "Delta")) + assert t_cmp(r.status_code, 200), "parent request with nested LDAP subrequest" + assert t_cmp(r.headers.get("x-ldap-subreq"), "ok"), \ + "subrequest into second LDAP per-dir config did not succeed" diff -Nru apache2-2.4.69/test/pytest_suite/tests/t/modules/test_negotiation.py apache2-2.4.71/test/pytest_suite/tests/t/modules/test_negotiation.py --- apache2-2.4.69/test/pytest_suite/tests/t/modules/test_negotiation.py 2026-09-28 16:36:07.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/tests/t/modules/test_negotiation.py 2026-11-09 03:07:05.000000000 +0000 @@ -3,15 +3,18 @@ Covers default-language selection, explicit variant requests, Accept-Language obedience (plain + compressed + typemap), quality-rating preferences, a non-existent highest-quality fallback, a typemap query-string case, and -Accept content-type negotiation (with 406 cases). +Accept content-type negotiation (with 406 cases), and the source quality +("qs") of a variant not showing up in the Content-Type of the response. Perl original used ``need_module('negotiation') && need_cgi && need_module('mime')``. """ +import re + import pytest -from apache_pytest import need_cgi, need_module, t_cmp +from apache_pytest import need_cgi, need_module, not_backported, t_cmp EN, FR, DE, FU, BU, ZH = "en", "fr", "de", "fu", "bu", "zh-TW" @@ -153,3 +156,45 @@ else: assert t_cmp(r.status_code, 406), \ f"expect Not Acceptable for Accept: {accept}" + + +XML = "application/xhtml+xml" + +# "qs" only decides between variants, the other parameters are kept +QS_PARAMS = [ + ("after", "text/html;charset=utf-8"), + ("before", "text/html;charset=utf-8"), + ("quoted", 'application/example;foo="a;b";q=0.7;bar="c, d"'), +] + + +def _content_type(resp): + assert resp.status_code == 200 + return re.sub(r";\s+", ";", resp.headers["content-type"]) + + +@need_module("negotiation", "mime") +# (a type map was right without the change, MultiViews was not) +@pytest.mark.parametrize("path", [ + pytest.param("doc", marks=not_backported( + "r1939268", "stripping qs from the negotiated Content-Type")), + "typemap.var", +]) +@pytest.mark.parametrize("accept,expected", [ + (f"text/html, {XML}", XML), + ("text/html", "text/html"), + (f"text/html, {XML};q=0.5", "text/html"), +]) +def test_source_quality(http, path, accept, expected): + r = http.GET(f"/modules/negotiation/qs/{path}", headers={"Accept": accept}) + assert t_cmp(_content_type(r), expected), \ + "qs selects the variant but is not sent in Content-Type" + + +@need_module("negotiation", "mime") +@pytest.mark.parametrize("name,expected", QS_PARAMS, ids=[n for n, _ in QS_PARAMS]) +@not_backported("r1939268", "stripping qs from the negotiated Content-Type") +def test_source_quality_parameters(http, name, expected): + r = http.GET(f"/modules/negotiation/qs/{name}", headers={"Accept": "*/*"}) + assert t_cmp(_content_type(r), expected), \ + "only qs is removed from the Content-Type" diff -Nru apache2-2.4.69/test/pytest_suite/tests/t/modules/test_proxy_beacon.py apache2-2.4.71/test/pytest_suite/tests/t/modules/test_proxy_beacon.py --- apache2-2.4.69/test/pytest_suite/tests/t/modules/test_proxy_beacon.py 2026-09-28 16:36:07.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/tests/t/modules/test_proxy_beacon.py 2026-11-09 03:07:05.000000000 +0000 @@ -91,7 +91,8 @@ # Qualify by balancer://beacon so the capacity-test balancer (below) doesn't # perturb these counts. added = [ln for ln in session_loglines - if "added backend" in ln and "balancer://beacon" in ln] + if f"added backend http://{vars_['servername']}:{vars_['port']} " + "to balancer://beacon" in ln] assert len(added) == 1, ( f"backend should be added exactly once; saw {len(added)}: {added}") assert not [ln for ln in loglines if "failed to add worker" in ln diff -Nru apache2-2.4.69/test/pytest_suite/tests/t/modules/test_proxy_beacon_input.py apache2-2.4.71/test/pytest_suite/tests/t/modules/test_proxy_beacon_input.py --- apache2-2.4.69/test/pytest_suite/tests/t/modules/test_proxy_beacon_input.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/tests/t/modules/test_proxy_beacon_input.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,908 @@ +r"""Hostile-input and member-identity tests for mod_proxy_beacon's receiver. + +test_proxy_beacon.py covers the happy-path lifecycle. These tests inject +hand-built, correctly *signed* announcements (a "valid MAC" sender that is not the +bundled sender, e.g. a buggy or compromised backend holding the shared secret) and +assert the receiver survives them. They use the same t/conf/proxy_beacon.conf.in +as test_proxy_beacon.py: the main server listens for beacons on its own UDP port +for balancer://beacon, authenticated with the secret below. + +Like test_proxy_beacon.py these are skipped under the prefork MPM (mod_watchdog's +singleton runs in a child process). +""" + +import os +import re +import subprocess +import urllib.parse +from datetime import datetime +import signal +import socket +import struct +import time +from pathlib import Path + +import pytest + +from apache_pytest import need_module + +SECRET = b"beacon-test-shared-secret" +M64 = (1 << 64) - 1 + + +def _siphash24(key: bytes, data: bytes) -> int: + """Reference SipHash-2-4 (what apr_siphash24_auth computes).""" + def rotl(x, b): + return ((x << b) | (x >> (64 - b))) & M64 + + k0, k1 = struct.unpack(" bytes: + import hashlib + return hashlib.md5(data).digest() + + +def signed_beacon(url: str, seq: int = 1, ts_us: int = None, key: bytes = None) -> bytes: + """A correctly signed announcement, as the bundled sender would build it. + + key is the 16-byte SipHash key; the default is what a passphrase secret + derives (MD5 of it).""" + if ts_us is None: + ts_us = int(time.time() * 1_000_000) + base = f"BEACON url={url} host=probe pid=1 seq={seq} ts={ts_us}" + mac = _siphash24(key or _md5(SECRET), base.encode()).to_bytes(8, "little").hex() + return f"{base} mac={mac}".encode() + + +def signed_raw(base: str, trailer: bytes = b"") -> bytes: + """Sign an arbitrary frame prefix (it need not be well-formed).""" + mac = _siphash24(_md5(SECRET), base.encode()).to_bytes(8, "little").hex() + return f"{base} mac={mac}".encode() + trailer + + +def send_udp(port: int, payload: bytes) -> None: + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: + s.sendto(payload, ("127.0.0.1", port)) + + +def log_since(error_log: Path, start: int) -> list: + with error_log.open("r", errors="replace") as fh: + fh.seek(start) + return fh.read().splitlines() + + +def test_siphash_reference_vector(): + """Guard the test helper itself: SipHash-2-4 paper vector (key 00..0f, empty).""" + key = bytes(range(16)) + assert _siphash24(key, b"") == 0x726FDB47DD0E0E31 + assert _siphash24(key, bytes(range(15))) == 0xA129CA6149BE45E5 + + +def inject(http, urls, probe_url, port=None, balancer="beacon"): + """Send one signed announcement per url, then a valid one for probe_url. + + The receiver handles datagrams in order, so once probe_url is reported as + added every earlier datagram has definitely been processed -- no timing + guesses and no reliance on a log line the hostile url itself may or may not + produce. Asserts that no child crashed; returns the log lines written since + the first datagram was sent. + """ + if http.have_module("mpm_prefork"): + pytest.skip("proxy_beacon not run under the prefork MPM") + + port = int(port or http.vars("port")) + error_log = Path(http.vars("t_logs")) / "error_log" + + # The listener socket is opened by the watchdog some time after startup, and + # a datagram sent before that is simply lost. The legit backend announces + # on its own; wait until the receiver has logged one of its beacons. + session_start = int(http.vars("session_log_start") or 0) + deadline = time.time() + 15 + while not any("received: BEACON" in ln + for ln in log_since(error_log, session_start)): + assert time.time() < deadline, "beacon receiver never came up" + time.sleep(0.25) + + start = error_log.stat().st_size + + now_us = int(time.time() * 1_000_000) + for seq, item in enumerate(list(urls) + [probe_url]): + if callable(item): # built at send time + item = item() + if isinstance(item, bytes): # a hand-built datagram, sent as is + send_udp(port, item) + else: + send_udp(port, signed_beacon(item, seq=seq, ts_us=now_us + seq)) + + added = f"added backend {probe_url} to balancer://{balancer}" + deadline = time.time() + 10 + lines = [] + while time.time() < deadline: + time.sleep(0.5) + lines = log_since(error_log, start) + if any(added in ln for ln in lines): + break + time.sleep(1) # let the parent report a child crash, if there was one + lines = log_since(error_log, start) + + crashed = [ln for ln in lines if "exit signal" in ln] + assert not crashed, f"a child crashed on a signed announcement: {crashed}" + assert any(added in ln for ln in lines), ( + "the valid announcement after the hostile one(s) was never processed; " + f"log tail: {lines[-10:]}") + + # Still serving. + assert http.GET("/index.html").status_code == 200 + return lines + + +def added_members(lines, balancer="beacon"): + """Members reported added to balancer://, as a list of urls.""" + out = [] + for ln in lines: + if "added backend " in ln and f" to balancer://{balancer}" in ln: + out.append(ln.split("added backend ", 1)[1].split(" ", 1)[0]) + return out + + +need = need_module("proxy_beacon", "watchdog", "proxy_balancer", "proxy_http") + + +@need +def test_overlong_hostname_does_not_corrupt_worker_array(http): + """A hostname the proxy cannot store makes worker definition fail *after* it + appended a half-built entry to the balancer's worker array; the next lookup + then dereferenced the NULL shared-worker pointer and killed the child.""" + inject(http, ["http://" + "a" * 300 + ".invalid:81"], + "http://127.0.0.3:18091") + + +@need +def test_overlong_uds_path_does_not_crash(http): + """unix:/|http://localhost -- the original reproducer.""" + inject(http, ["unix:/" + "b" * 300 + "|http://localhost"], + "http://127.0.0.3:18092") + + +# ---- member identity: an announced url names an origin, canonically ---------- + +@need +def test_default_port_is_dropped_from_identity(http): + """http://host:80 is the same origin as http://host. The proxy stores the + worker as http://host; the member must be tracked, added and enabled under + that name, not under the announced spelling (which then matches no worker, + leaves it 'Init Dis' and still logs a successful add).""" + lines = inject(http, ["http://127.0.0.4:80"], "http://127.0.0.3:18093") + assert "http://127.0.0.4" in added_members(lines) + assert not [ln for ln in lines if "127.0.0.4" in ln + and "failed to enable" in ln] + + +@need +def test_scheme_and_host_case_are_canonicalized(http): + lines = inject(http, ["HTTP://Canon.Invalid:8081"], "http://127.0.0.3:18094") + assert "http://canon.invalid:8081" in added_members(lines) + + +@need +def test_equivalent_spellings_are_one_member(http): + """Same origin spelled three ways: one member, one lease, one add.""" + lines = inject(http, ["http://127.0.0.7:8082", "http://127.0.0.7:8082/", + "HTTP://127.0.0.7:8082"], "http://127.0.0.3:18095") + assert added_members(lines).count("http://127.0.0.7:8082") == 1 + + +@need +@pytest.mark.parametrize("n,url", enumerate([ + "http://127.0.0.8:8083/one", # path: would alias /one and /one/two + "http://127.0.0.8:8083/one/two", + "http://127.0.0.8:8083?x=1", # query + "http://127.0.0.8:8083/#frag", # fragment + "http://user:pw@127.0.0.8:8083", # userinfo + "http://127.0.0.8:0", # port 0 + "http://127.0.0.8:-80", # strtol sign wraps to port 65456 + "http://127.0.0.8:+80", + "http://127.0.0.8:65536", # truncates to port 0 + "http://127.0.0.8:99999", # truncates to port 34463 + "http://127.0.0.8:4294967376", # 2^32 + 80: truncates to port 80 + "abcdefghijklmnop://127.0.0.8:8085", # 16 chars: does not fit the worker + "http://" + "h" * 256 + ":8086", # host does not fit the worker +])) +def test_non_origin_urls_are_rejected(http, n, url): + lines = inject(http, [url], f"http://127.0.0.3:{18100 + n}") + assert not [m for m in added_members(lines) if "127.0.0.8" in m], ( + f"{url!r} must not become a member") + + +@need +@pytest.mark.parametrize("n,url", enumerate([ + "https://127.0.0.9:8443", + "uwsgi://127.0.0.9:3031", # a mod_proxy scheme apr does not know + "fcgi://127.0.0.9:9000", +])) +def test_proxy_scheme_origins_are_accepted(http, n, url): + lines = inject(http, [url], f"http://127.0.0.3:{18121 + n}") + assert url in added_members(lines) + + +@need +def test_highest_port_is_accepted(http): + lines = inject(http, ["http://127.0.0.14:65535"], "http://127.0.0.3:18120") + assert "http://127.0.0.14:65535" in added_members(lines) + + +# ---- the log of received messages is bounded --------------------------------- + +@need +def test_received_log_lines_are_bounded(http): + """An authentic message can be replayed within the freshness window as often + as the sender likes. Its "received:" line is logged for the first twenty in + a second; the rest are counted, and the count is logged once.""" + if http.have_module("mpm_prefork"): + pytest.skip("proxy_beacon not run under the prefork MPM") + port = int(http.vars("port")) + error_log = Path(http.vars("t_logs")) / "error_log" + session_start = int(http.vars("session_log_start") or 0) + assert wait_for(error_log, session_start, "received: BEACON", 15) + + # A url with a path is refused after it was logged, so no member is added. + marker = "127.0.0.15:8300/replayed" + start = error_log.stat().st_size + now_us = int(time.time() * 1_000_000) + for i in range(300): + send_udp(port, signed_beacon(f"http://{marker}", seq=i, ts_us=now_us + i)) + time.sleep(1.5) + # The next message opens a new second, in which the count is logged. + send_udp(port, signed_beacon(f"http://{marker}", seq=1000)) + time.sleep(1.5) + + lines = log_since(error_log, start) + received = [ln for ln in lines if "received:" in ln and marker in ln] + assert 0 < len(received) < 70, f"{len(received)} lines for 301 messages" + assert any("received: lines left out" in ln for ln in lines), lines[-5:] + + +# ---- ownership and watchdog hand-off ----------------------------------------- + +STATIC_MEMBER = "http://127.0.0.11:19000" # BalancerMember in balancer://handoff + + +def conf_port(http, name): + conf = Path(http.vars("t_logs")).parent / "conf" / "proxy_beacon.conf" + m = re.search(rf"Define BEACON_{name}_PORT (\d+)", conf.read_text()) + assert m, f"BEACON_{name}_PORT not found in {conf}" + return int(m.group(1)) + + +def handoff_port(http): + return conf_port(http, "HANDOFF") + + +def wait_for(error_log, start, needle, timeout): + deadline = time.time() + timeout + while time.time() < deadline: + for ln in log_since(error_log, start): + if needle in ln: + return ln + time.sleep(0.2) + return None + + +@need +def test_beacon_does_not_manage_members_it_did_not_create(http): + """A BalancerMember from the configuration is not beacon's to enable or + evict, even if some backend announces exactly its url.""" + port = handoff_port(http) + error_log = Path(http.vars("t_logs")) / "error_log" + start = error_log.stat().st_size + inject(http, [STATIC_MEMBER], "http://127.0.0.3:18200", + port=port, balancer="handoff") + # Outlast ProxyBeaconTimeout (3s) plus the 1s sweep: had beacon adopted the + # static member it would be evicted by now. + time.sleep(6) + lines = log_since(error_log, start) + assert STATIC_MEMBER not in added_members(lines, "handoff") + assert not [ln for ln in lines if f"evicted backend {STATIC_MEMBER}" in ln] + + +@need +def test_new_watchdog_owner_evicts_silent_member(http): + """Leases live in the watchdog owner's private memory. When the owner dies + the replacement used to start with none, so a member that had gone silent + stayed enabled forever. It must adopt the beacon-created members and expire + the silent one after ProxyBeaconTimeout.""" + if http.have_module("mpm_prefork"): + pytest.skip("proxy_beacon not run under the prefork MPM") + + port = handoff_port(http) + error_log = Path(http.vars("t_logs")) / "error_log" + member = "http://127.0.0.3:18201" + + # Wait for the receiver, announce once, and never again. + session_start = int(http.vars("session_log_start") or 0) + assert wait_for(error_log, session_start, "received: BEACON", 15) + start = error_log.stat().st_size + send_udp(port, signed_beacon(member)) + + line = wait_for(error_log, start, f"added backend {member} to balancer://handoff", 10) + assert line, "member was never added" + owner = int(re.search(r"\[pid (\d+):", line).group(1)) + + # Kill the owner straight away -- well inside the 3s timeout, so the old + # owner never gets to evict anything. + os.kill(owner, signal.SIGKILL) + + evicted = wait_for(error_log, start, f"evicted backend {member} from balancer://handoff", 40) + assert evicted, "silent member was never evicted by the new watchdog owner" + new_owner = int(re.search(r"\[pid (\d+):", evicted).group(1)) + assert new_owner != owner + + lines = log_since(error_log, start) + assert any("took over" in ln and "balancer://handoff" in ln for ln in lines) + # The static member was not adopted along with it. + assert not [ln for ln in lines if f"evicted backend {STATIC_MEMBER}" in ln] + + +@need +def test_new_owner_reenables_adopted_evicted_member(http): + """A member that was already evicted when the owner died is adopted as + evicted; its next announcement must be handled by the new owner (which has + not run any other balancer operation yet) and re-enable it.""" + if http.have_module("mpm_prefork"): + pytest.skip("proxy_beacon not run under the prefork MPM") + + port = handoff_port(http) + error_log = Path(http.vars("t_logs")) / "error_log" + member = "http://127.0.0.3:18202" + + session_start = int(http.vars("session_log_start") or 0) + assert wait_for(error_log, session_start, "received: BEACON", 15) + start = error_log.stat().st_size + seq = 0 + + def announce(): + nonlocal seq + seq += 1 + send_udp(port, signed_beacon(member, seq=seq)) + + announce() + line = wait_for(error_log, start, f"added backend {member} to balancer://handoff", 10) + assert line, "member was never added" + owner = int(re.search(r"\[pid (\d+):", line).group(1)) + + # Let the old owner evict it (ProxyBeaconTimeout is 3s), then kill the owner. + assert wait_for(error_log, start, + f"evicted backend {member} from balancer://handoff", 10) + os.kill(owner, signal.SIGKILL) + + # Announce continuously: datagrams sent while no one is listening are lost, + # but one lands in the window between the new owner binding its socket and + # its first processing tick, which is when it knows nothing but the adopted + # (evicted) lease. + deadline = time.time() + 40 + reenabled = None + while time.time() < deadline and not reenabled: + announce() + time.sleep(0.02) + for ln in log_since(error_log, start): + if f"re-enabled backend {member} in balancer://handoff" in ln: + reenabled = ln + break + + lines = log_since(error_log, start) + crashed = [ln for ln in lines + if "exit signal" in ln and "Killed" not in ln] + assert not crashed, f"the new owner crashed: {crashed}" + assert reenabled, "adopted evicted member was never re-enabled" + assert int(re.search(r"\[pid (\d+):", reenabled).group(1)) != owner + + +# ---- wire grammar and timestamp strictness ----------------------------------- +# +# Each frame below is correctly signed but malformed. A parser that finds fields +# by searching the string accepts them; the receiver must not. The member url in +# each is unique, and the valid probe sent after it proves it was processed. + +def frame_cases(): + now = int(time.time() * 1_000_000) + stale = now - 10_000_000 + + def b(url, ts_part, marker="BEACON", sep=" "): + return f"{marker}{sep}url={url}{sep}host=probe{sep}pid=1{sep}seq=1{sep}{ts_part}" + + return [ + ("ts-trailing-garbage", lambda u: signed_raw(b(u, f"ts={now}x"))), + ("ts-signed", lambda u: signed_raw(b(u, f"ts=+{now}"))), + ("ts-too-long", lambda u: signed_raw(b(u, f"ts={now}0000000"))), + ("duplicate-ts", lambda u: signed_raw(b(u, f"ts={stale} ts={now}"))), + ("duplicate-url", lambda u: signed_raw(b(u, f"ts={now} url=http://127.0.0.99:1"))), + ("no-marker", lambda u: signed_raw(b(u, f"ts={now}", marker="XEACON"))), + ("double-space", lambda u: signed_raw(b(u, f"ts={now}", sep=" "))), + ("embedded-nul", lambda u: signed_raw(b(u, f"ts={now}"), b"\x00junk")), + ("oversize", lambda u: signed_raw(b(u, f"ts={now}") + " host=" + "h" * 3000)), + ] + + +@need +@pytest.mark.parametrize("n,case", enumerate(frame_cases()), + ids=[c[0] for c in frame_cases()]) +def test_malformed_frames_are_dropped(http, n, case): + name, build = case + member = f"http://127.0.0.12:{8100 + n}" + lines = inject(http, [build(member)], f"http://127.0.0.3:{18300 + n}") + assert member not in added_members(lines), ( + f"malformed frame {name!r} must not register a member") + + +@need +def test_wellformed_frame_still_accepted(http): + """Guard for the cases above: the same hand-built shape, but valid.""" + now = int(time.time() * 1_000_000) + member = "http://127.0.0.12:8199" + frame = signed_raw(f"BEACON url={member} host=probe pid=1 seq=1 ts={now}") + lines = inject(http, [frame], "http://127.0.0.3:18399") + assert member in added_members(lines) + + +@need +def test_subsecond_max_skew_is_honoured(http): + """ProxyBeaconMaxSkew 500ms: a timestamp 700ms old is stale. (It used to be + rounded down to 0 seconds, which then selected the 30 second default.)""" + port = conf_port(http, "SKEW") + old, fresh = "http://127.0.0.13:8201", "http://127.0.0.13:8202" + # Signed only when sent: inject() can take longer than the window to + # get there. + lines = inject( + http, + [lambda: signed_beacon(old, seq=1, + ts_us=int(time.time() * 1_000_000) - 700_000), + lambda: signed_beacon(fresh, seq=2)], + "http://127.0.0.13:8203", port=port, balancer="skew") + members = added_members(lines, "skew") + assert old not in members, "a 700ms-old timestamp passed a 500ms window" + assert fresh in members + + +# ---- tracking-table capacity ------------------------------------------------- + +TABLE_CAP = 256 # BEACON_MAX_MEMBERS + + +@need +def test_silent_members_are_reclaimed_so_new_ones_can_join(http): + """The receiver tracks at most 256 urls. Records of members that have been + evicted, and of adds that failed, used to stay forever, so once 256 distinct + urls had ever announced the table was full for good and every later + announcement -- from any new backend -- was dropped. They are reclaimed once + a replay of their last message could no longer be fresh.""" + if http.have_module("mpm_prefork"): + pytest.skip("proxy_beacon not run under the prefork MPM") + + port = conf_port(http, "RECLAIM") + error_log = Path(http.vars("t_logs")) / "error_log" + session_start = int(http.vars("session_log_start") or 0) + assert wait_for(error_log, session_start, "received: BEACON", 15) + + start = error_log.stat().st_size + members = [f"http://10.1.{i // 200}.{i % 200 + 1}:9000" for i in range(TABLE_CAP)] + now_us = int(time.time() * 1_000_000) + for i, url in enumerate(members): + send_udp(port, signed_beacon(url, seq=i, ts_us=now_us + i)) + + # A balancer takes at most 100 runtime members: 100 get added, the other 156 + # fail -- and all 256 occupy a tracking record. + deadline = time.time() + 30 + while time.time() < deadline: + added = set(added_members(log_since(error_log, start), "reclaim")) + if len(added) >= 100: + break + time.sleep(0.5) + assert len(added) == 100, f"{len(added)} members added, expected 100" + + # Everyone falls silent: members are evicted after 1s, and every record is + # reclaimable after 2 * MaxSkew + 1s. + time.sleep(10) + + # The table has room again: a newcomer is looked at (and fails, because the + # balancer really is full) instead of being dropped for want of a record. + mark = error_log.stat().st_size + send_udp(port, signed_beacon("http://10.2.0.1:9000", seq=1000)) + time.sleep(3) + lines = log_since(error_log, mark) + assert any("balancer add failed" in ln for ln in lines), lines[-5:] + assert not any("member table full" in ln for ln in lines) + + # An evicted member that comes back is re-adopted from the balancer. + again = sorted(added)[0] + mark = error_log.stat().st_size + send_udp(port, signed_beacon(again, seq=1001)) + assert wait_for(error_log, mark, + f"backend {again} already in balancer://reclaim", 10) + + +# ---- operator intent --------------------------------------------------------- + +def manager_post(http, query): + """POST to the handoff vhost's balancer-manager (nonce taken from its page).""" + r = http.GET("/balancer-manager") + assert r.status_code == 200, "balancer-manager not reachable" + m = re.search(r"b=handoff[^\"]*?nonce=([0-9a-fA-F-]+)", r.text.replace("&", "&")) + assert m, "no nonce for balancer://handoff" + vars_ = http.vars() + referer = {"Referer": f"http://{vars_['servername']}:{http.vhost_port('proxy_beacon_handoff')}/balancer-manager"} + return http.POST("/balancer-manager", content=(query + "&nonce=" + m.group(1)).encode(), + headers=referer) + + +def member_status(http, member): + """The balancer-manager's status text for one member of balancer://handoff.""" + r = http.GET("/balancer-manager") + host = member.split("://", 1)[1] + for row in re.split(r"", r.text): + if host in row and "balancer://" not in row.split(host)[0][-40:]: + cells = re.findall(r"]*>(.*?)", row, re.S) + for c in cells: + if re.search(r"\b(Init|Ok|Dis|Stop|Drn)\b", c): + return re.sub(r"<[^>]+>", "", c).strip() + return None + + +@need +def test_beacon_leaves_operator_flags_alone(http): + """Beacon owns a member's Disabled flag. An administrator who needs a member + held out of rotation regardless of announcements uses Stopped (or Drain), + which beacon never touches, so it survives an evict / re-enable cycle.""" + if http.have_module("mpm_prefork"): + pytest.skip("proxy_beacon not run under the prefork MPM") + http.module("proxy_beacon_handoff") + try: + port = handoff_port(http) + error_log = Path(http.vars("t_logs")) / "error_log" + member = "http://127.0.0.15:8300" + + session_start = int(http.vars("session_log_start") or 0) + assert wait_for(error_log, session_start, "received: BEACON", 15) + start = error_log.stat().st_size + seq = 0 + + def announce(): + nonlocal seq + seq += 1 + send_udp(port, signed_beacon(member, seq=seq)) + + announce() + assert wait_for(error_log, start, f"added backend {member} to balancer://handoff", 10) + + r = manager_post(http, f"w={member}&b=handoff&w_status_S=1") + assert r.status_code == 200 + assert "Stop" in (member_status(http, member) or ""), member_status(http, member) + + # Silent past ProxyBeaconTimeout (3s): beacon evicts it... + assert wait_for(error_log, start, f"evicted backend {member} from balancer://handoff", 10) + # ...then it announces again and beacon re-enables it. + announce() + assert wait_for(error_log, start, f"re-enabled backend {member} in balancer://handoff", 10) + + status = member_status(http, member) or "" + assert "Stop" in status, f"beacon cleared the administrator's Stop: {status!r}" + assert "Dis" not in status, f"member should be re-enabled: {status!r}" + finally: + http.module(None) + + +# ---- random-key secret ------------------------------------------------------- + +HEX_KEY = bytes(range(16)) # ProxyBeaconSecret hex:000102...0f + + +@need +def test_hex_secret_is_the_key_itself(http): + """hex:<32 digits> is used as the MAC key directly. A sender signing with the + raw bytes is accepted; one that signs with MD5 of the same text (what a + passphrase would give) is not.""" + if http.have_module("mpm_prefork"): + pytest.skip("proxy_beacon not run under the prefork MPM") + port = conf_port(http, "HEXKEY") + error_log = Path(http.vars("t_logs")) / "error_log" + session_start = int(http.vars("session_log_start") or 0) + assert wait_for(error_log, session_start, "received: BEACON", 15) + + bad, good = "http://127.0.0.16:8401", "http://127.0.0.16:8402" + start = error_log.stat().st_size + now = int(time.time() * 1_000_000) + send_udp(port, signed_beacon(bad, seq=1, ts_us=now, + key=_md5(b"hex:000102030405060708090a0b0c0d0e0f"))) + send_udp(port, signed_beacon(good, seq=2, ts_us=now + 1, key=HEX_KEY)) + assert wait_for(error_log, start, f"added backend {good} to balancer://hexkey", 10) + time.sleep(1) + assert bad not in added_members(log_since(error_log, start), "hexkey") + + +# ---- ProxyBeaconSecret validation (httpd -t on a wrapped config) -------------- + +def configtest(server, tmp_path, secret): + """`httpd -t` on the generated test config plus one vhost with this secret.""" + conf = tmp_path / "secret.conf" + conf.write_text( + f'Include "{server.config.vars["t_conf_file"]}"\n' + "\n" + " ProxyBeaconListen 127.0.0.1:65302\n" + f' ProxyBeaconSecret "{secret}"\n' + "\n") + argv = [a for a in server.args() if a != "-DFOREGROUND"] + argv[argv.index("-f") + 1] = str(conf) + return subprocess.run([*argv, "-t"], capture_output=True, text=True) + + +@need +@pytest.mark.parametrize("secret", [ + "hex:000102030405060708090a0b0c0d0e0f", + "hex:000102030405060708090A0B0C0D0E0F", + "x" * 16, # shortest passphrase +]) +def test_valid_secrets_are_accepted(server, tmp_path, secret): + r = configtest(server, tmp_path, secret) + assert r.returncode == 0 and "Syntax OK" in r.stderr, r.stderr + + +@need +@pytest.mark.parametrize("secret,message", [ + ("short", "at least 16"), + ("x" * 15, "at least 16"), # one short of the minimum + ("hex:abc", "exactly 32 hex digits"), + ("hex:" + "0" * 34, "exactly 32 hex digits"), + ("hex:zz" + "0" * 30, "non-hex digit"), +]) +def test_invalid_secrets_are_rejected(server, tmp_path, secret, message): + r = configtest(server, tmp_path, secret) + assert r.returncode != 0, "config with this secret should not load" + assert message in r.stderr, r.stderr + + +def advertise_configtest(server, tmp_path, url): + """`httpd -t` on the generated test config plus one vhost advertising url.""" + conf = tmp_path / "advertise.conf" + conf.write_text( + f'Include "{server.config.vars["t_conf_file"]}"\n' + "\n" + " ProxyBeaconAddress 127.0.0.1:65302\n" + f" ProxyBeaconAdvertise {url}\n" + f' ProxyBeaconSecret "{"x" * 16}"\n' + "\n") + argv = [a for a in server.args() if a != "-DFOREGROUND"] + argv[argv.index("-f") + 1] = str(conf) + return subprocess.run([*argv, "-t"], capture_output=True, text=True) + + +@need +def test_advertised_highest_port_is_accepted(server, tmp_path): + r = advertise_configtest(server, tmp_path, "http://127.0.0.14:65535") + assert r.returncode == 0 and "Syntax OK" in r.stderr, r.stderr + + +@need +@pytest.mark.parametrize("url", [ + "http://127.0.0.14:65536", + "http://127.0.0.14:99999", + "http://127.0.0.14:4294967376", +]) +def test_advertised_out_of_range_port_is_rejected(server, tmp_path, url): + r = advertise_configtest(server, tmp_path, url) + assert r.returncode != 0, "config advertising this url should not load" + assert "is not a valid scheme://host[:port] URL" in r.stderr, r.stderr + + +# ---- permanent configuration errors fail `httpd -t` -------------------------- + +def vhost_configtest(server, tmp_path, body): + """`httpd -t` on the generated test config plus one vhost with this body.""" + conf = tmp_path / "vhost.conf" + conf.write_text( + f'Include "{server.config.vars["t_conf_file"]}"\n' + "\n" + f"{body}\n" + "\n") + argv = [a for a in server.args() if a != "-DFOREGROUND"] + argv[argv.index("-f") + 1] = str(conf) + return subprocess.run([*argv, "-t"], capture_output=True, text=True) + + +SECRET_DIRECTIVE = 'ProxyBeaconSecret "xxxxxxxxxxxxxxxx"' + + +@need +def test_listener_with_a_balancer_passes_configtest(server, tmp_path): + """Control for the cases below: the same shape, but valid.""" + r = vhost_configtest(server, tmp_path, + f"ProxyBeaconListen 127.0.0.1:65302\n{SECRET_DIRECTIVE}\n" + "ProxyBeaconBalancer beacon") + assert r.returncode == 0 and "Syntax OK" in r.stderr, r.stderr + + +@need +@pytest.mark.parametrize("body", [ + "ProxyBeaconListen 127.0.0.1:65302", # listener + "ProxyBeaconAddress 127.0.0.1:65302", # sender +]) +def test_missing_secret_fails_configtest(server, tmp_path, body): + r = vhost_configtest(server, tmp_path, body) + assert r.returncode != 0, "a channel without a secret must not pass -t" + assert "ProxyBeaconSecret is required" in r.stderr, r.stderr + + +@need +@pytest.mark.parametrize("body,message", [ + (f"ProxyBeaconAddress justahost\n{SECRET_DIRECTIVE}", "must be host:port"), + (f"ProxyBeaconAddress 127.0.0.1\n{SECRET_DIRECTIVE}", "must be host:port"), + (f"ProxyBeaconAddress [::1\n{SECRET_DIRECTIVE}", "must be host:port"), + (f"ProxyBeaconListen [::1\n{SECRET_DIRECTIVE}", "must be [host][:port]"), + (f"ProxyBeaconListen 127.0.0.1:65302\n{SECRET_DIRECTIVE}\n" + "ProxyBeaconBalancer nosuchbalancer", "is not defined for this server"), +]) +def test_malformed_address_or_unknown_balancer_fails_configtest( + server, tmp_path, body, message): + r = vhost_configtest(server, tmp_path, body) + assert r.returncode != 0, "this configuration must not pass -t" + assert message in r.stderr, r.stderr + + +@need +def test_listener_shared_with_another_server_is_warned_about(server, tmp_path): + """The main server's ProxyBeaconListen takes no argument; so does this one.""" + r = vhost_configtest(server, tmp_path, f"ProxyBeaconListen\n{SECRET_DIRECTIVE}") + assert r.returncode == 0 and "Syntax OK" in r.stderr, r.stderr + assert "is also written for another server" in r.stderr, r.stderr + + +@need +@pytest.mark.parametrize("address", [":65302", "65302", "127.0.0.1", "[::1]:65302", + "udp://127.0.0.1:65302"]) +def test_listen_address_forms_pass_configtest(server, tmp_path, address): + r = vhost_configtest(server, tmp_path, + f"ProxyBeaconListen {address}\n{SECRET_DIRECTIVE}") + assert r.returncode == 0 and "Syntax OK" in r.stderr, r.stderr + + +@need +@pytest.mark.parametrize("address", [":", ":0", ":65536", ":80x", "[::1"]) +def test_bad_listen_address_forms_fail_configtest(server, tmp_path, address): + r = vhost_configtest(server, tmp_path, + f"ProxyBeaconListen {address}\n{SECRET_DIRECTIVE}") + assert r.returncode != 0, f"{address!r} must not pass -t" + assert "must be [host][:port]" in r.stderr, r.stderr + + +# ---- socket-open retry back-off ---------------------------------------------- + +LOG_TS = re.compile(r"^\[\w{3} (\w{3}\s+\d+ [\d:.]+ \d{4})\]") + + +def log_time(line): + m = LOG_TS.match(line) + return datetime.strptime(re.sub(r"\s+", " ", m.group(1)), "%b %d %H:%M:%S.%f %Y") + + +@need +def test_failed_socket_open_is_retried_with_backoff(http): + """The nobind vhost cannot bind. The open is retried after 30s, then after + 60s (doubling) -- not on every watchdog tick, and not never.""" + if http.have_module("mpm_prefork"): + pytest.skip("proxy_beacon not run under the prefork MPM") + error_log = Path(http.vars("t_logs")) / "error_log" + session_start = int(http.vars("session_log_start") or 0) + + def attempts(): + """Attempt times per watchdog-owning pid: a new owner (after a hand-off + test killed the old one) starts its own retry cycle.""" + by_pid = {} + for ln in log_since(error_log, session_start): + if "bind to 192.0.2.1:9 failed" in ln: + pid = re.search(r"\[pid (\d+)", ln).group(1) + by_pid.setdefault(pid, []).append(log_time(ln)) + return by_pid + + def best(): + return max(attempts().values(), key=len, default=[]) + + deadline = time.time() + 75 + while len(best()) < 2 and time.time() < deadline: + time.sleep(1) + assert len(best()) >= 2, f"never retried the failed open: {attempts()}" + + for pid, times in attempts().items(): + gaps = [(b - a).total_seconds() for a, b in zip(times, times[1:])] + for i, gap in enumerate(gaps): + expected = 30 * 2 ** i + assert expected - 3 <= gap <= expected + 8, ( + f"pid {pid}: retry {i + 1} came {gap:.1f}s after the previous " + f"attempt, expected about {expected}s: {gaps}") + + +# ---- administrator-added members --------------------------------------------- + +def ownsync_post(http, query): + """POST to the ownsync vhost's balancer-manager on a fresh connection, so + that different children get to handle it.""" + close = {"Connection": "close"} + r = http.GET("/balancer-manager", headers=close) + assert r.status_code == 200, "balancer-manager not reachable" + m = re.search(r"b=ownsync[^\"]*?nonce=([0-9a-fA-F-]+)", r.text.replace("&", "&")) + assert m, "no nonce for balancer://ownsync" + vars_ = http.vars() + referer = {"Referer": f"http://{vars_['servername']}:" + f"{http.vhost_port('proxy_beacon_ownsync')}/balancer-manager"} + return http.POST("/balancer-manager", + content=(query + "&nonce=" + m.group(1)).encode(), + headers={**close, **referer}) + + +@need +def test_admin_added_members_are_not_taken_over(http): + """Workers added through the balancer-manager (possibly by a child other than + the watchdog owner) are not beacon's, even when announced right away: the + owner must see them before deciding, and leave them alone.""" + if http.have_module("mpm_prefork"): + pytest.skip("proxy_beacon not run under the prefork MPM") + http.module("proxy_beacon_ownsync") + try: + port = conf_port(http, "OWNSYNC") + error_log = Path(http.vars("t_logs")) / "error_log" + session_start = int(http.vars("session_log_start") or 0) + assert wait_for(error_log, session_start, "received: BEACON", 15) + start = error_log.stat().st_size + + urls = [f"http://127.0.0.{30 + i}:8600" for i in range(12)] + for i, url in enumerate(urls): + r = ownsync_post(http, "b=ownsync&b_wyes=1&b_nwrkr=" + urllib.parse.quote(url, safe="")) + assert r.status_code == 200, r.status_code + send_udp(port, signed_beacon(url, seq=i + 1)) + + assert wait_for(error_log, start, "member not managed by beacon", 10) + time.sleep(2) + page = http.GET("/balancer-manager", headers={"Connection": "close"}).text + assert all(u.split("://", 1)[1] in page for u in urls), ( + "the administrator's workers were not all added") + + lines = log_since(error_log, start) + taken = [ln for ln in lines if any(u in ln for u in urls) + and ("added backend" in ln or "already in balancer" in ln + or "re-enabled" in ln or "evicted" in ln)] + assert not taken, f"beacon acted on an administrator's worker: {taken[:3]}" + finally: + http.module(None) diff -Nru apache2-2.4.69/test/pytest_suite/tests/t/modules/test_proxy_hcheck.py apache2-2.4.71/test/pytest_suite/tests/t/modules/test_proxy_hcheck.py --- apache2-2.4.69/test/pytest_suite/tests/t/modules/test_proxy_hcheck.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/tests/t/modules/test_proxy_hcheck.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,424 @@ +r"""Tests for mod_proxy_hcheck. + +The backends are plain socket servers started here (not httpd), so each test +controls exactly what the health check sees. Worker state is read from the +balancer-manager of the t/conf/proxy_hcheck.conf.in vhost: "Ok" while the +worker is healthy, "HcFl" once a health check has failed it. +""" + +import re +import shutil +import socketserver +import ssl +import subprocess +import tempfile +import threading +import time +from pathlib import Path + +import pytest + +from apache_pytest import need_module, not_backported + +need = need_module("proxy_hcheck", "proxy_balancer", "proxy_http", "watchdog") + + +def conf_port(http, name): + conf = Path(http.vars("t_logs")).parent / "conf" / "proxy_hcheck.conf" + m = re.search(rf"Define HC_{name}_PORT (\d+)", conf.read_text()) + assert m, f"HC_{name}_PORT not found in {conf}" + return int(m.group(1)) + + +class Backend: + """A TCP server that records each request head and answers via respond().""" + + def __init__(self, port, respond, tls=None): + self.requests = [] # [(method, target, {lower-case header: value})] + self.sni = [] # server names seen in TLS ClientHellos (None = none) + self.respond = respond # (backend, request) -> bytes or an iterable of them + self.stopping = False + self.tls = None + if tls: + self.tls = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + self.tls.load_cert_chain(*tls) + self.tls.sni_callback = lambda sock, name, ctx: self.sni.append(name) + outer = self + + class Handler(socketserver.BaseRequestHandler): + def handle(self): + data = b"" + conn = self.request + conn.settimeout(5) + if outer.tls: + try: + conn = outer.tls.wrap_socket(conn, server_side=True) + except (ssl.SSLError, OSError): + return + self.request = conn + try: + while b"\r\n\r\n" not in data: + chunk = self.request.recv(4096) + if not chunk: + return + data += chunk + except OSError: + return + lines = data.split(b"\r\n\r\n")[0].decode("latin-1").split("\r\n") + method, target, _ = lines[0].split(" ", 2) + headers = {} + for ln in lines[1:]: + k, _, v = ln.partition(":") + headers[k.strip().lower()] = v.strip() + req = (method, target, headers) + outer.requests.append(req) + try: + out = outer.respond(outer, req) + if isinstance(out, (bytes, bytearray)): + out = [out] + for chunk in out: # a generator may stream, slowly + if outer.stopping: + break + self.request.sendall(chunk) + except OSError: + pass + + class Server(socketserver.ThreadingTCPServer): + allow_reuse_address = True + daemon_threads = True + + self.server = Server(("127.0.0.1", port), Handler) + threading.Thread(target=self.server.serve_forever, daemon=True).start() + + def close(self): + self.stopping = True + self.server.shutdown() + self.server.server_close() + + def targets(self): + return [t for _, t, _ in self.requests] + + +def ok_response(backend, req): + return (b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: close\r\n\r\n") + + +def worker_status(http, port): + """The balancer-manager's status text for the worker on port .""" + r = http.GET("/balancer-manager") + assert r.status_code == 200, "balancer-manager not reachable" + needle = re.compile(rf":{port}\b") + for row in re.split(r"", r.text): + if needle.search(row): + for cell in re.findall(r"]*>(.*?)", row, re.S): + text = re.sub(r"<[^>]+>", "", cell).strip() + if re.search(r"\b(Init|Ok|Dis|Stop|HcFl|Err)\b", text): + return text + return None + + +def wait_until(fn, timeout, interval=0.3): + deadline = time.time() + timeout + last = None + while time.time() < deadline: + last = fn() + if last: + return last + time.sleep(interval) + return last + + +def is_ok(http, port): + st = worker_status(http, port) or "" + return "Ok" in st and "HcFl" not in st + + +def goes_bad(http, backend, port, bad): + """Start from a healthy worker, then make the backend misbehave. + + A worker whose backend is not up yet is already failing, so "it is failing" + proves nothing: first see it pass, and only then switch the response.""" + backend.respond = ok_response + assert wait_until(lambda: is_ok(http, port), 20), ( + f"worker never became healthy: {worker_status(http, port)!r}") + backend.respond = bad + return wait_until(lambda: "HcFl" in (worker_status(http, port) or ""), 20) + + +@pytest.fixture +def hc_http(http): + http.module("proxy_hcheck") + try: + yield http + finally: + http.module(None) + + +@need +def test_complete_response_keeps_worker_healthy(hc_http): + """Control: a proper response passes the health check.""" + port = conf_port(hc_http, "HDR") + b = Backend(port, ok_response) + try: + assert wait_until(lambda: b.requests, 15), "no health check arrived" + time.sleep(3) + assert "HcFl" not in (worker_status(hc_http, port) or "") + assert "Ok" in (worker_status(hc_http, port) or "") + finally: + b.close() + + +@need +@not_backported("r1939353, r1939390", "hcheck rejecting a truncated response") +def test_truncated_response_headers_fail_the_check(hc_http): + """A response whose headers end at EOF, without the blank line that + terminates them, is not a valid response. It used to be accepted, so a + backend that was cutting its responses short was reported healthy.""" + port = conf_port(hc_http, "HDR") + + def truncated(backend, req): + return b"HTTP/1.1 200 OK\r\nServer: broken\r\n" # ...and closes + + b = Backend(port, ok_response) + try: + assert goes_bad(hc_http, b, port, truncated), ( + f"truncated headers passed: {worker_status(hc_http, port)!r}") + finally: + b.close() + + +def manager_post(http, balancer, query): + """POST to the vhost's balancer-manager (the nonce comes from its page).""" + r = http.GET("/balancer-manager") + assert r.status_code == 200, "balancer-manager not reachable" + m = re.search(rf"b={balancer}[^\"]*?nonce=([0-9a-fA-F-]+)", r.text.replace("&", "&")) + assert m, f"no nonce for balancer://{balancer}" + v = http.vars() + port = http.vhost_port("proxy_hcheck") + referer = {"Referer": f"http://{v['servername']}:{port}/balancer-manager"} + return http.POST("/balancer-manager", + content=(query + "&nonce=" + m.group(1)).encode(), + headers=referer) + + +@need +@not_backported("r1939353, r1939390", "hcheck following a changed hcuri") +def test_changed_probe_uri_is_used(hc_http): + """The probe URI is read from the worker when the health check is first set + up, and was never looked at again, so changing it in the balancer-manager + had no effect on what the backend saw.""" + port = conf_port(hc_http, "URI") + b = Backend(port, ok_response) + try: + assert wait_until(lambda: "/old" in b.targets(), 15), "no /old probe" + + r = manager_post(hc_http, "hcuri", f"w=http://127.0.0.1:{port}&b=hcuri&w_hu=/new") + assert r.status_code == 200 + assert "

    " in hc_http.GET("/balancer-manager").text, ( + "the balancer-manager did not store the new URI") + + mark = len(b.requests) + assert wait_until(lambda: "/new" in b.targets()[mark:], 15), ( + f"probes after the change still went to {sorted(set(b.targets()[mark:]))}") + finally: + b.close() + + +def make_cert(directory, name): + """A self-signed certificate for `name`; returns (certfile, keyfile) or None.""" + for exe in (shutil.which("openssl"), "/opt/homebrew/opt/openssl@3/bin/openssl", + "/opt/local/bin/openssl"): + if not exe: + continue + cert, key = f"{directory}/{name}.crt", f"{directory}/{name}.key" + r = subprocess.run( + [exe, "req", "-x509", "-newkey", "rsa:2048", "-nodes", "-days", "2", + "-subj", f"/CN={name}", "-addext", f"subjectAltName=DNS:{name}", + "-keyout", key, "-out", cert], + capture_output=True) + if r.returncode == 0: + return cert, key + return None + + +@pytest.fixture +def certs(): + with tempfile.TemporaryDirectory() as d: + bad, good = make_cert(d, "wrong.invalid"), make_cert(d, "localhost") + if not bad or not good: + pytest.skip("no openssl able to make a test certificate") + yield {"bad": bad, "good": good} + + +def tls_need(http): + if not http.have_module("ssl"): + pytest.skip("mod_ssl is required") + + +@need +@not_backported("r1939353, r1939390", "hcheck SNI on HTTPS probes") +def test_https_probe_sends_sni_and_checks_the_name(hc_http, certs): + """A health check of an https worker used to send no SNI and skip the host + name check, so a backend presenting the certificate of another host passed + even though real requests to it are refused.""" + tls_need(hc_http) + port = conf_port(hc_http, "TLSBAD") + b = Backend(port, ok_response, tls=certs["bad"]) + try: + assert wait_until(lambda: b.sni, 15), "no TLS handshake reached the backend" + assert "localhost" in b.sni, f"SNI seen by the backend: {b.sni}" + # The worker starts out failing (nothing was listening yet), so what + # matters is that, with handshakes happening, it never turns healthy. + assert not wait_until(lambda: is_ok(hc_http, port), 8), ( + "wrong-host certificate passed the check: " + f"{worker_status(hc_http, port)!r}") + finally: + b.close() + + +@need +@not_backported("r1939353, r1939390", "hcheck SNI on HTTPS probes") +def test_https_probe_with_matching_certificate_passes(hc_http, certs): + """Control for the above: the right certificate keeps the worker healthy.""" + tls_need(hc_http) + port = conf_port(hc_http, "TLSGOOD") + b = Backend(port, ok_response, tls=certs["good"]) + try: + assert wait_until(lambda: b.requests, 15), "no health check request arrived" + time.sleep(3) + status = worker_status(hc_http, port) or "" + assert "Ok" in status and "HcFl" not in status, status + assert "localhost" in b.sni + finally: + b.close() + + +# ---- limits on what a probe accepts from a backend ---------------------------- + +@need +@not_backported("r1939353, r1939390", "hcheck response header limit") +def test_response_with_too_many_headers_fails_the_check(hc_http): + port = conf_port(hc_http, "MANYHDR") + + def many(backend, req): + hdrs = b"".join(b"X-Filler-%d: v\r\n" % i for i in range(2000)) + return b"HTTP/1.1 200 OK\r\n" + hdrs + b"Content-Length: 0\r\nConnection: close\r\n\r\n" + + b = Backend(port, ok_response) + try: + assert goes_bad(hc_http, b, port, many), ( + f"2000 response headers were accepted: {worker_status(hc_http, port)!r}") + finally: + b.close() + + +@need +@not_backported("r1939353, r1939390", "hcheck cutting off an endless response") +def test_endless_response_is_cut_off(hc_http): + """A backend that keeps sending (a byte every half second here) never trips + a per-read timeout. The probe needs an overall deadline (the worker's + timeout, 2s here) or it holds a checking thread, and keeps growing its + buffer, for as long as the backend cares to go on.""" + port = conf_port(hc_http, "SLOW") + + def endless(backend, req): + yield b"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n" + while True: + time.sleep(0.5) + yield b"x" + + b = Backend(port, ok_response) + try: + assert goes_bad(hc_http, b, port, endless), ( + f"an endless response was never cut off: {worker_status(hc_http, port)!r}") + finally: + b.close() + + +def big_ok(size): + def respond(backend, req): + yield (b"HTTP/1.1 200 OK\r\nContent-Length: %d\r\nConnection: close\r\n\r\n" % size) + sent = 0 + while sent < size: + n = min(65536, size - sent) + yield b"x" * n + sent += n + return respond + + +@need +@not_backported("r1939353, r1939390", "hcheck not keeping a large body") +def test_large_body_is_not_kept_for_a_condition(hc_http): + """With a condition on the body, a probe keeps the body in memory to evaluate + it. A 3MB body is more than that is allowed to take, and fails the check.""" + port = conf_port(hc_http, "BIGEXPR") + b = Backend(port, ok_response) + try: + assert goes_bad(hc_http, b, port, big_ok(3 * 1024 * 1024)), ( + f"a 3MB body was kept and evaluated: {worker_status(hc_http, port)!r}") + finally: + b.close() + + +@need +def test_large_body_without_a_condition_is_fine(hc_http): + """Control: with no condition the body is not needed, is read and dropped, + and the check passes as before.""" + port = conf_port(hc_http, "BIGPLAIN") + b = Backend(port, big_ok(3 * 1024 * 1024)) + try: + assert wait_until(lambda: len(b.requests) >= 2, 15), "probes stopped arriving" + status = worker_status(hc_http, port) or "" + assert "Ok" in status and "HcFl" not in status, status + finally: + b.close() + + +# ---- the probe's request line --------------------------------------------------- + +_JOIN = not_backported("r1939353, r1939390", "hcheck joining the worker path and hcuri") + + +@need +@pytest.mark.parametrize("name,expected", [ + pytest.param("PATHA", "/ping", marks=_JOIN), # http://host:port + hcuri "ping" + pytest.param("PATHB", "/app/ping", marks=_JOIN), # http://host:port/app + hcuri "/ping" + pytest.param("PATHC", "/app/ping", marks=_JOIN), # http://host:port/app/ + hcuri "ping" + pytest.param("PATHD", "/", marks=_JOIN), # http://host:port + no hcuri + ("PATHE", "/app"), # http://host:port/app + no hcuri + ("PATHF", "/app/"), # http://host:port/app/ + no hcuri: kept as is + pytest.param("PATHG", "/app/", marks=_JOIN), # http://host:port/app + hcuri "/" +]) +def test_probe_path_joins_worker_path_and_hcuri(hc_http, name, expected): + """One slash between the worker's path and hcuri, and a request target that + starts with one: a bare "ping" used to be sent as "GET ping HTTP/1.0", and a + path plus "/ping" as "/app//ping".""" + b = Backend(conf_port(hc_http, name), ok_response) + try: + assert wait_until(lambda: b.requests, 15), "no health check arrived" + assert set(b.targets()) == {expected}, b.targets() + finally: + b.close() + + +@need +def test_check_worker_is_created_once(hc_http): + """The cache of per-worker check state is looked up on every cycle; if the + lookup were to miss, a new long-lived check worker would be created each + time. (Guards the lifetime of the cache's keys.)""" + port = conf_port(hc_http, "URI") + error_log = Path(hc_http.vars("t_logs")) / "error_log" + b = Backend(port, ok_response) + try: + assert wait_until(lambda: len(b.requests) >= 5, 20), "too few health checks" + finally: + b.close() + session_start = int(hc_http.vars("session_log_start") or 0) + with error_log.open("r", errors="replace") as fh: + fh.seek(session_start) + created = [ln for ln in fh if "Creating hc worker" in ln + and f"127.0.0.1:{port}" in ln] + # Once per process: the cache is per process, and a child which takes + # over the singleton watchdog from one that exited builds its own. + pids = [re.search(r"\[pid (\d+)", ln).group(1) for ln in created] + assert created and len(pids) == len(set(pids)), created diff -Nru apache2-2.4.69/test/pytest_suite/tests/t/modules/test_speling.py apache2-2.4.71/test/pytest_suite/tests/t/modules/test_speling.py --- apache2-2.4.69/test/pytest_suite/tests/t/modules/test_speling.py 2026-09-28 16:36:07.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/tests/t/modules/test_speling.py 2026-11-09 03:07:05.000000000 +0000 @@ -59,3 +59,17 @@ # Only redirect responses carry a corrected-filename body. if expected not in (200, 404): assert t_cmp(r.text, _REDIRECT_BODY), "Redirect ok" + + +@need_module("mod_speling") +def test_speling_short_uri_alias(http): + """Regression for the check_speling() out-of-bounds read. + + ``Alias /sp-oob`` maps a short URI to a longer, nonexistent base name in + a CheckSpelling directory. postgood (the base name) is then longer than + r->uri, so r->uri + (urlen - pglen) underflowed and strcmp read before + the start of the buffer. The server must answer cleanly (404) instead of + crashing or reading out of bounds. + """ + r = http.GET("/sp-oob") + assert t_cmp(r.status_code, 404), "short-URI alias must not read OOB" diff -Nru apache2-2.4.69/test/pytest_suite/tests/test_backport_marks.py apache2-2.4.71/test/pytest_suite/tests/test_backport_marks.py --- apache2-2.4.69/test/pytest_suite/tests/test_backport_marks.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/tests/test_backport_marks.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,27 @@ +"""Self-test for the not_backported mark: which servers it expects a failure +on. Needs no httpd.""" +import pytest + +from apache_pytest.testapi import has_trunk_change, not_backported + + +@pytest.mark.parametrize("version,since,expected", [ + ((2, 4, 70), None, False), + ((2, 4, 70), "2.4.71", False), + ((2, 4, 71), "2.4.71", True), + ((2, 4, 72), "2.4.71", True), + # numeric, not textual, comparison + ((2, 4, 9), "2.4.10", False), + ((2, 4, 10), "2.4.9", True), + ((2, 5, 0), None, True), + ((2, 5, 1), None, True), + ((2, 5, 1), "2.4.71", True), +]) +def test_has_trunk_change(version, since, expected): + assert has_trunk_change(version, since) is expected + + +def test_mark_carries_backported_in(): + mark = not_backported("r1", "a change", backported_in="2.4.71").mark + assert mark.args == ("r1", "a change") + assert mark.kwargs == {"backported_in": "2.4.71"} diff -Nru apache2-2.4.69/test/pytest_suite/uv.lock apache2-2.4.71/test/pytest_suite/uv.lock --- apache2-2.4.69/test/pytest_suite/uv.lock 2026-06-03 16:20:30.000000000 +0000 +++ apache2-2.4.71/test/pytest_suite/uv.lock 1970-01-01 00:00:00.000000000 +0000 @@ -1,156 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.11" - -[[package]] -name = "anyio" -version = "4.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, -] - -[[package]] -name = "certifi" -version = "2026.5.20" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "h11" -version = "0.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, -] - -[[package]] -name = "httpcore" -version = "1.0.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, -] - -[[package]] -name = "httpd-pytest" -version = "0.1.0" -source = { virtual = "." } -dependencies = [ - { name = "httpx" }, - { name = "pytest" }, -] - -[package.metadata] -requires-dist = [ - { name = "httpx", specifier = ">=0.27" }, - { name = "pytest", specifier = ">=8.0" }, -] - -[[package]] -name = "httpx" -version = "0.28.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "certifi" }, - { name = "httpcore" }, - { name = "idna" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, -] - -[[package]] -name = "idna" -version = "3.18" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "packaging" -version = "26.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "pygments" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, -] - -[[package]] -name = "pytest" -version = "9.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] diff -Nru apache2-2.4.69/test/run-all-tests.sh apache2-2.4.71/test/run-all-tests.sh --- apache2-2.4.69/test/run-all-tests.sh 2026-07-17 18:15:55.000000000 +0000 +++ apache2-2.4.71/test/run-all-tests.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,196 +0,0 @@ -#!/bin/sh -# -# run-all-tests.sh -- unified runner for the two Python test suites under test/. -# -# Runs both pytest-based suites against the same built httpd: -# 1. pytest_suite/ -- the self-contained port of the classic Apache::Test -# suite (core HTTP, modules, security CVEs, SSL, PHP). Run FIRST. -# 2. pyhttpd tests under modules/ -- httpd's own HTTP/2, mod_md, HTTP/1, -# proxy and core tests (uses curl/nghttp/h2load; configured via -# pyhttpd/config.ini). -# -# Both run against the same httpd; pytest_suite locates it via --apxs, while -# the pyhttpd tests read pyhttpd/config.ini (generated by httpd's configure). -# -# Usage: -# ./run-all-tests.sh # run both suites (pytest_suite first) -# ./run-all-tests.sh --only=pysuite # run only pytest_suite -# ./run-all-tests.sh --only=pyhttpd # run only the pyhttpd modules/ tests -# ./run-all-tests.sh --apxs /path/to/apxs # override the httpd build -# ./run-all-tests.sh -k status -v # extra args pass through to BOTH pytests -# ./run-all-tests.sh --clean-modules # wipe compiled C-module artifacts before -# # building (emulate make clean; pytest_suite only) -# -# Environment overrides: -# APXS path to apxs (default: read from pyhttpd/config.ini, else $PATH) -# PHP_FPM path to php-fpm for pytest_suite's PHP tests (optional) -# PYHTTPD_TARGETS pyhttpd test paths (default: "modules") -# -set -eu - -usage() { - cat <<'EOF' -Usage: run-all-tests.sh [OPTIONS] [PATHS...] - -Run both Python test suites against a built httpd. - -Suites (run in order): - 1. pytest_suite/ -- classic Apache::Test port (HTTP, modules, SSL, CVEs) - 2. modules/ -- pyhttpd tests (HTTP/2, mod_md, proxy, core) - -Options: - --only=pysuite run only pytest_suite - --only=pyhttpd run only the pyhttpd modules/ tests - --apxs=PATH path to apxs for the pytest_suite build - --clean-modules wipe compiled C-module artifacts before building - (emulate make clean; pytest_suite only) - -k EXPR pytest keyword filter (passed to both suites) - -m EXPR pytest marker filter (passed to both suites) - -v, -x, ... other pytest flags (passed to both suites) - -h, --help show this help and exit - -Positional PATHS are forwarded to pytest_suite only (e.g. tests/t/modules/foo). -The pyhttpd suite selects tests via PYHTTPD_TARGETS or auto-detection. - -Environment: - APXS path to apxs (default: config.ini, then \$PATH) - PHP_FPM path to php-fpm for PHP tests in pytest_suite (optional) - PYHTTPD_TARGETS space-separated list of pyhttpd test paths (default: modules/*) -EOF -} - -here="$(cd "$(dirname "$0")" && pwd)" -suite_dir="$here/pytest_suite" -config_ini="$here/pyhttpd/config.ini" - -# Parse args once. Pull out --only and --apxs (space and = forms). Remaining -# arguments are split into: -# flags (start with '-', e.g. -v -k NAME -x): passed to BOTH pytests. -# paths (positional, e.g. tests/t/modules/...): these are pytest_suite -# paths and go ONLY to pytest_suite. The pyhttpd side selects its -# tests via PYHTTPD_TARGETS (or its auto-detected default), since a -# pytest_suite path is meaningless there. -# -# The hard part is telling a positional test path from the value of a flag that -# takes a separate word (e.g. `--tb short`, `--maxfail 3`, `-n 4`). We handle it -# two ways: (a) the common value-flags -k/-m/-p are known to consume the next -# word, and (b) any OTHER bare word is treated as a pysuite path only if it -# actually exists on disk -- a flag value like "short"/"3"/"no" never does, so -# it stays with `flags` (attached to its preceding flag) instead of being -# misrouted to pysuite-only paths and stripped from what pyhttpd receives. -only="" -apxs_opt="" -flags="" -pysuite_flags="" -paths="" -expect_apxs=0 -expect_flagval=0 -for arg in "$@"; do - if [ "$expect_apxs" = "1" ]; then apxs_opt="$arg"; expect_apxs=0; continue; fi - if [ "$expect_flagval" = "1" ]; then flags="$flags $arg"; expect_flagval=0; continue; fi - case "$arg" in - -h|--help) usage; exit 0 ;; - --only=*) only="${arg#--only=}" ;; - --apxs) expect_apxs=1 ;; - --apxs=*) apxs_opt="${arg#--apxs=}" ;; - --clean-modules) pysuite_flags="$pysuite_flags $arg" ;; # pysuite-only; pyhttpd has no C modules - -k|-m|-p) flags="$flags $arg"; expect_flagval=1 ;; # take a value next - -*) flags="$flags $arg" ;; - # A real pysuite path exists relative to pytest_suite/ (how users type - # it, e.g. "tests/t/php") or to our cwd; strip any ::nodeid suffix - # first. Anything else is a stray flag value -> keep it with the flags. - *) if [ -e "$suite_dir/${arg%%::*}" ] || [ -e "${arg%%::*}" ]; then - paths="$paths $arg" - else - flags="$flags $arg" - fi - ;; - esac -done - -# --- locate apxs (for pytest_suite) ----------------------------------------- -if [ -z "$apxs_opt" ]; then - if [ -n "${APXS:-}" ]; then - apxs_opt="$APXS" - elif [ -f "$config_ini" ]; then - # Resolve apxs = ${exec_prefix}/bin/apxs from config.ini (simple expand). - prefix=$(sed -n 's/^prefix = //p' "$config_ini" | head -1) - apxs_opt="$prefix/bin/apxs" - elif command -v apxs >/dev/null 2>&1; then - apxs_opt="$(command -v apxs)" - fi -fi - -php_args="" -[ -n "${PHP_FPM:-}" ] && php_args="--php-fpm=$PHP_FPM" - -rc=0 -skipped="" # names of suites that did NOT run (so we never report them "passed") - -run_pysuite() { - echo "==========================================================" - echo "[1/2] pytest_suite (classic Apache::Test port)" - echo "==========================================================" - if [ -z "$apxs_opt" ]; then - echo "run-all-tests.sh: ERROR: no apxs found for pytest_suite." >&2 - echo " Pass --apxs=/path/to/apxs or set APXS=..." >&2 - return 2 - fi - # runtests.sh handles venv + cgisock cleanup + flag assembly. - # pytest_suite gets both the shared flags and any positional paths. - # pysuite_flags holds options only meaningful to this suite (e.g. --clean-modules). - # shellcheck disable=SC2086 - ( cd "$suite_dir" && ./runtests.sh --apxs="$apxs_opt" $php_args $flags $pysuite_flags $paths ) || return $? -} - -run_pyhttpd() { - echo "==========================================================" - echo "[2/2] pyhttpd suites (modules/: http2, md, http1, proxy, core)" - echo "==========================================================" - if [ ! -f "$config_ini" ]; then - echo "run-all-tests.sh: note: pyhttpd/config.ini not found;" >&2 - echo " build httpd with its test config (configure) to run these." >&2 - skipped="$skipped pyhttpd" - return 0 - fi - # runtests.sh manages the venv, prepends its bin/ to PATH (so CGI - # scripts forked by httpd also use the venv's Python), and runs pytest. - # PYHTTPD_TARGETS can override which modules/ subdirs are run. - # shellcheck disable=SC2086 - ( cd "$here/pyhttpd" && ./runtests.sh $flags ) || return $? -} - -case "$only" in - pysuite) run_pysuite || rc=$? ;; - pyhttpd) run_pyhttpd || rc=$? ;; - "") - # Both, pytest_suite first. Keep going if the first fails so we get - # results from both; report a non-zero overall rc if either failed. - run_pysuite || rc=$? - run_pyhttpd || rc=$? - ;; - *) - echo "run-all-tests.sh: unknown --only=$only (use pysuite|pyhttpd)" >&2 - exit 2 - ;; -esac - -echo "==========================================================" -if [ "$rc" -ne 0 ]; then - echo "SOME TESTS FAILED (rc=$rc)" -elif [ -n "$skipped" ]; then - # Nothing failed, but at least one suite never ran -- don't claim success - # for a suite that was skipped (e.g. pyhttpd with no config.ini). - echo "PASSED, BUT SKIPPED:$skipped (not run -- see notes above)" -else - echo "ALL SUITES PASSED" -fi -echo "==========================================================" - -# If a suite was skipped and the user explicitly asked for ONLY that suite, -# treat "ran nothing" as a failure -- otherwise --only=pyhttpd could exit 0 -# having executed zero tests. -if [ -n "$skipped" ] && [ -n "$only" ] && [ "$rc" -eq 0 ]; then - exit 3 -fi -exit "$rc" diff -Nru apache2-2.4.69/test/run-one-test.sh apache2-2.4.71/test/run-one-test.sh --- apache2-2.4.69/test/run-one-test.sh 2026-09-29 09:09:07.000000000 +0000 +++ apache2-2.4.71/test/run-one-test.sh 2026-11-09 03:07:05.000000000 +0000 @@ -1,13 +1,13 @@ #!/bin/sh # # run-one-test.sh -- re-run a single test (file, directory or ::nodeid) via -# run-all-tests.sh, picking the right suite from the path. +# runtests.sh. The path can be given the way the test output prints it. # # Usage: # ./run-one-test.sh PATH[::nodeid] [pytest args...] # -# PATH may be given as printed in the run-all-tests.sh output, or as a real -# filesystem path from anywhere, e.g.: +# PATH may be given as printed in the pytest output, or as a real filesystem +# path from anywhere, e.g.: # ./run-one-test.sh tests/t/apache/test_404.py # pytest_suite # ./run-one-test.sh tests/t/apache/test_404.py::test_x -v # ./run-one-test.sh modules/http2/test_003_get.py # pyhttpd @@ -92,15 +92,8 @@ fi case "$abs" in - "$here/pytest_suite/"*) - exec "$here/run-all-tests.sh" --only=pysuite "$@" \ - "${abs#"$here/pytest_suite/"}$node" - ;; - "$here/modules/"*) - # pyhttpd paths are taken from PYHTTPD_TARGETS, relative to test/. - PYHTTPD_TARGETS="${abs#"$here/"}$node" - export PYHTTPD_TARGETS - exec "$here/run-all-tests.sh" --only=pyhttpd "$@" + "$here/pytest_suite/"*|"$here/modules/"*) + exec "$here/runtests.sh" "$@" "${abs#"$here/"}$node" ;; *) echo "run-one-test.sh: $abs is not under pytest_suite/ or modules/" >&2 diff -Nru apache2-2.4.69/test/runtests.bat apache2-2.4.71/test/runtests.bat --- apache2-2.4.69/test/runtests.bat 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/runtests.bat 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,179 @@ +@echo off +rem +rem runtests.bat -- run the Python test suites on Windows: modules/ and +rem pytest_suite/ in one pytest session against one built httpd. +rem +rem This is the Windows equivalent of runtests.sh. It manages the .venv in +rem test\ so that pytest and the CGI helper scripts (which httpd forks) use the +rem same Python with all required packages, finds apxs/php-fpm/perl when it +rem can, and runs pytest from test\. +rem +rem Usage: +rem runtests.bat --httpd C:\path\httpd.exe --conf C:\path\httpd.conf modules +rem runtests.bat --apxs=C:\path\apxs pytest_suite\tests -- the classic suite +rem runtests.bat --httpd ... modules\http2 -- a specific suite +rem runtests.bat --httpd ... modules -k post -v -- any pytest args pass through +rem +rem Name the suite to run: without a path both run, and pytest_suite\ has not +rem been exercised on Windows CI. Paths are relative to test\. +rem +rem Environment overrides (used when the matching --flag is not supplied): +rem PYTHON path to python (auto-detected if not set) +rem APXS path to apxs +rem HTTPD path to httpd +rem PHP_FPM path to php-fpm +rem APACHE_TEST_PERL path to perl +rem + +setlocal enabledelayedexpansion + +set "HERE=%~dp0" +rem Remove trailing backslash +if "%HERE:~-1%"=="\" set "HERE=%HERE:~0,-1%" +set "PROJ=%HERE%" + +rem --- python location: find the newest native Windows Python 3.x ------------- +if not defined PYTHON ( + set PYTHON= + for /f "delims=" %%P in ('where python.exe 2^>nul') do ( + if not defined PYTHON ( + echo %%P | findstr /i "hostedtoolcache" >nul + if not errorlevel 1 set "PYTHON=%%P" + ) + ) +) +if not defined PYTHON ( + for /f "delims=" %%D in ('dir /b /o-n "%LOCALAPPDATA%\Programs\Python\Python3*" 2^>nul') do ( + if not defined PYTHON set "PYTHON=%LOCALAPPDATA%\Programs\Python\%%D\python.exe" + ) +) +if not defined PYTHON ( + for /f "delims=" %%D in ('dir /b /o-n "%PROGRAMFILES%\Python3*" 2^>nul') do ( + if not defined PYTHON set "PYTHON=%PROGRAMFILES%\%%D\python.exe" + ) +) +if not defined PYTHON ( + echo runtests.bat: ERROR: Python interpreter not found. >&2 + echo Install Python from https://www.python.org or set PYTHON=... >&2 + exit /b 1 +) + +rem --- ensure the venv exists and is current ---------------------------------- +set "PYTEST=%PROJ%\.venv\Scripts\pytest.exe" + +set "NEED_SYNC=0" +if not exist "%PYTEST%" set "NEED_SYNC=1" + +rem Check if pyproject.toml is newer than .venv (approximate: compare timestamps +rem via xcopy /D /L which lists files that are newer than the destination). +if exist "%PROJ%\.venv" ( + for /f %%F in ('xcopy /D /L /Y "%PROJ%\pyproject.toml" "%PROJ%\.venv\" 2^>nul ^| C:\Windows\System32\find.exe /c "pyproject.toml"') do ( + if %%F gtr 0 set "NEED_SYNC=1" + ) +) else ( + set "NEED_SYNC=1" +) + +if "%NEED_SYNC%"=="1" ( + %PYTHON% -m uv --version >nul 2>&1 + if errorlevel 1 ( + echo runtests.bat: ERROR: 'uv' is required but not installed. >&2 + echo Install it from https://docs.astral.sh/uv/ and re-run. >&2 + echo %PYTHON% -m pip install uv >&2 + exit /b 1 + ) + echo runtests.bat: ^(re^)creating %PROJ%\.venv via 'uv sync'... >&2 + %PYTHON% -m uv sync --project "%PROJ%" + if errorlevel 1 exit /b 1 + rem Touch .venv so the staleness check won't retrigger + copy /b "%PROJ%\.venv" +,, "%PROJ%\.venv" >nul 2>&1 +) + +rem --- discover apxs / php-fpm / perl ----------------------------------------- +if not defined APXS ( + for %%C in (apxs.exe) do ( + if "!APXS!"=="" ( + for /f "delims=" %%P in ('where %%C 2^>nul') do ( + if "!APXS!"=="" set "APXS=%%P" + ) + ) + ) +) + +if not defined PHP_FPM ( + for %%C in (php-fpm.exe php-fpm8.3.exe php-fpm83.exe php-fpm8.2.exe php-fpm82.exe) do ( + if "!PHP_FPM!"=="" ( + for /f "delims=" %%P in ('where %%C 2^>nul') do ( + if "!PHP_FPM!"=="" set "PHP_FPM=%%P" + ) + ) + ) +) + +if not defined APACHE_TEST_PERL ( + for %%C in (perl.exe) do ( + for /f "delims=" %%P in ('where %%C 2^>nul') do ( + if "!APACHE_TEST_PERL!"=="" set "APACHE_TEST_PERL=%%P" + ) + ) +) + +rem Build the default flag set only for values the user did not pass explicitly. +set "AUTO_ARGS=" + +set "HAS_SERVER_FLAG=0" +for %%A in (%*) do ( + echo %%A | findstr /b /c:"--apxs" /c:"--httpd" >nul 2>&1 && set "HAS_SERVER_FLAG=1" +) + +if "%HAS_SERVER_FLAG%"=="0" ( + if defined APXS ( + set "AUTO_ARGS=--apxs=!APXS!" + ) else if defined HTTPD ( + set "AUTO_ARGS=--httpd=!HTTPD!" + ) else ( + echo runtests.bat: note: no apxs/httpd given or found; using the build of >&2 + echo pyhttpd\config.ini ^(or PYHTTPD_CONFIG^) if there is one. >&2 + ) +) + +set "HAS_PHP_FLAG=0" +for %%A in (%*) do ( + echo %%A | findstr /b /c:"--php-fpm" >nul 2>&1 && set "HAS_PHP_FLAG=1" +) + +if "%HAS_PHP_FLAG%"=="0" ( + if defined PHP_FPM ( + set "AUTO_ARGS=!AUTO_ARGS! --php-fpm=!PHP_FPM!" + ) else ( + echo runtests.bat: note: no php-fpm found; PHP tests will skip. >&2 + echo Set PHP_FPM=C:\path\to\php-fpm to run them. >&2 + ) +) + +set "HAS_PERL_FLAG=0" +for %%A in (%*) do ( + echo %%A | findstr /b /c:"--with-perl" >nul 2>&1 && set "HAS_PERL_FLAG=1" +) + +if "%HAS_PERL_FLAG%"=="0" ( + if defined APACHE_TEST_PERL ( + set "AUTO_ARGS=!AUTO_ARGS! --with-perl=!APACHE_TEST_PERL!" + ) else ( + echo runtests.bat: note: no perl found; rewrite prg-map tests may fail. >&2 + echo Pass --with-perl=C:\path\to\perl.exe or set APACHE_TEST_PERL=... >&2 + ) +) + +rem A stale mod_cgid socket in the classic suite's t\logs can break a fresh run. +del /q "%HERE%\pytest_suite\t\logs\cgisock*" 2>nul + +rem --- Windows-specific exclusions -------------------------------------------- +set "WIN_IGNORE=--ignore=modules\md" +set "WIN_SKIP=not test_800_websockets and not test_02_unix" + +rem --- run, from test\ ---------------------------------------------------------- +cd /d "%HERE%" +echo runtests.bat: "%PYTEST%" %AUTO_ARGS% %WIN_IGNORE% -k "%WIN_SKIP%" %* >&2 +"%PYTEST%" %AUTO_ARGS% %WIN_IGNORE% -k "%WIN_SKIP%" %* +exit /b %errorlevel% diff -Nru apache2-2.4.69/test/runtests.sh apache2-2.4.71/test/runtests.sh --- apache2-2.4.69/test/runtests.sh 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/runtests.sh 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,149 @@ +#!/bin/sh +# +# runtests.sh -- run the Python test suites: modules/ (HTTP/2, mod_md, HTTP/1, +# proxy, core, ...) and pytest_suite/ (the classic Apache::Test port), in one +# pytest session against one built httpd. +# +# It makes sure the venv exists and is current (via uv), puts the venv's bin/ +# first on PATH (CGI scripts forked by httpd must use the same Python and +# packages as pytest), fills in --apxs and --php-fpm when they can be found, +# and runs pytest from test/. Everything else goes straight to pytest. +# +# Usage: +# ./runtests.sh # both suites, build found via APXS/config.ini/PATH +# ./runtests.sh --apxs /path/to/apxs # a specific build +# ./runtests.sh modules # only the modules/ suites +# ./runtests.sh pytest_suite/tests # only the classic suite +# ./runtests.sh modules/http2 -k 003 -v +# ./runtests.sh -m "not slow" # markers and -k select across both +# +# A path relative to pytest_suite/ (tests/t/php, how the classic suite always +# printed them) is accepted as well. +# +# Environment (each used when the matching --flag is not given): +# APXS path to apxs (default: the build of this tree, named by the +# configure-generated pyhttpd/config.ini or by +# PYHTTPD_CONFIG; without one, the first 'apxs' +# on PATH) +# HTTPD path to httpd (when there is no apxs) +# PHP_FPM path to php-fpm (default: first php-fpm on PATH; PHP tests skip if none) +# +set -eu + +here="$(cd "$(dirname "$0")" && pwd)" +cd "$here" + +# --- ensure the venv exists and is current ---------------------------------- +# uv (https://docs.astral.sh/uv/) manages the dependencies: it reads +# pyproject.toml + uv.lock, the single source for both suites. We call +# .venv/bin/pytest directly rather than `uv run`, so this also works where +# `uv run` is shimmed or unavailable. +# +# Create .venv on first run, and rebuild it when pyproject.toml or uv.lock is +# newer than the venv (i.e. the dependencies changed). +PYTEST="$here/.venv/bin/pytest" +if [ ! -x "$PYTEST" ] || [ "$here/pyproject.toml" -nt "$here/.venv" ] \ + || [ "$here/uv.lock" -nt "$here/.venv" ]; then + if ! command -v uv >/dev/null 2>&1; then + echo "runtests.sh: ERROR: 'uv' is required but not installed." >&2 + echo " Install it from https://docs.astral.sh/uv/ and re-run." >&2 + exit 1 + fi + echo "runtests.sh: (re)creating $here/.venv via 'uv sync'..." >&2 + uv sync --project "$here" + # Mark the venv as freshly built so the staleness check above won't + # retrigger until pyproject.toml or uv.lock changes again. + touch "$here/.venv" +fi + +# CGI scripts forked by httpd resolve python3 to the venv's interpreter, and +# any shim wrappers earlier on PATH are shadowed. +export PATH="$here/.venv/bin:$PATH" + +# --- discover apxs / php-fpm --------------------------------------------------- +discover() { + # discover cmd1 cmd2 ... -> echo the first one found on PATH + for c in "$@"; do + if command -v "$c" >/dev/null 2>&1; then command -v "$c"; return 0; fi + done + return 1 +} + +# A configured tree tests its own build: an apxs on PATH may well be another +# httpd (a distribution's), so it is only looked for when there is none. +config_ini="${PYHTTPD_CONFIG:-$here/pyhttpd/config.ini}" +if [ -z "${APXS:-}" ] && [ -z "${HTTPD:-}" ] && [ ! -f "$config_ini" ]; then + APXS="$(discover apxs || true)" +fi +PHP_FPM="${PHP_FPM:-$(discover php-fpm php-fpm8.3 php-fpm83 php-fpm8.2 php-fpm82 || true)}" + +# Arguments: a path that does not exist relative to test/ but does relative to +# pytest_suite/ is a classic-suite path. Everything else is passed on as is, +# the values of options included (`-k tests` must stay `-k tests`). +# (A ::nodeid suffix is not part of the path.) +have_server=0 +have_php=0 +for arg in "$@"; do + case "$arg" in + --apxs|--apxs=*|--httpd|--httpd=*) have_server=1 ;; + --php-fpm|--php-fpm=*) have_php=1 ;; + esac +done + +# Build the argument list in "$@" form, so that spaces survive. +n=$# +while [ "$n" -gt 0 ]; do + arg="$1"; shift; n=$((n - 1)) + case "$arg" in + # `--apxs PATH` would make pytest take PATH for a test path and look + # for conftest.py there instead of here, so join option and value. + --apxs|--httpd|--conf|--php-fpm|--with-perl|--prefix) + if [ "$n" -gt 0 ]; then arg="$arg=$1"; shift; n=$((n - 1)); fi ;; + # options of pytest and of the suites that take a separate value: + # pass the value on untouched + -k|-m|-p|-o|-c|-W|-n|--tb|--deselect|--ignore|--ignore-glob|--rootdir|\ + --basetemp|--junitxml|--junit-xml|--maxfail|--durations|--log-level|\ + --log-file|--confcutdir|--import-mode|--color|--capture|--defines|\ + --php-fpm-port|--base-port|--classic-elog-report|--archive|--repeat) + if [ "$n" -gt 0 ]; then + set -- "$@" "$arg"; arg="$1"; shift; n=$((n - 1)) + fi ;; + -*) ;; + *) p="${arg%%::*}" + if [ ! -e "$p" ] && [ -e "pytest_suite/$p" ]; then + arg="pytest_suite/$arg" + fi + ;; + esac + set -- "$@" "$arg" +done + +if [ "$have_server" = 0 ]; then + if [ -n "${APXS:-}" ]; then + set -- "--apxs=$APXS" "$@" + elif [ -n "${HTTPD:-}" ]; then + set -- "--httpd=$HTTPD" "$@" + elif [ -f "$config_ini" ]; then + echo "runtests.sh: testing the build of $config_ini" >&2 + else + # the framework self-tests need none; the suites fail and say why + echo "runtests.sh: note: no httpd to test: no apxs on PATH and no" >&2 + echo " $config_ini (configure the tree)." >&2 + echo " Pass --apxs=/path/to/apxs, or set APXS=... or HTTPD=..." >&2 + fi +fi + +if [ "$have_php" = 0 ]; then + if [ -n "${PHP_FPM:-}" ]; then + set -- "--php-fpm=$PHP_FPM" "$@" + else + echo "runtests.sh: note: no php-fpm found; PHP tests will skip." >&2 + echo " Set PHP_FPM=/path/to/php-fpm (any version) to run them." >&2 + fi +fi + +# A stale mod_cgid socket in the classic suite's t/logs can break a fresh run. +rm -f "$here"/pytest_suite/t/logs/cgisock* 2>/dev/null || true + +echo "runtests.sh: $PYTEST $*" >&2 +exec "$PYTEST" "$@" diff -Nru apache2-2.4.69/test/test_backport_marks.py apache2-2.4.71/test/test_backport_marks.py --- apache2-2.4.69/test/test_backport_marks.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/test_backport_marks.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,122 @@ +"""Self-test for pyhttpd/backport.py and its use by pyhttpd.gating: which +servers a not_backported mark expects a failure on, and what it turns into an +expected failure there. Needs no built httpd.""" +from pathlib import Path + +import pytest + +from pyhttpd import backport, gating +from pyhttpd.build import HttpdInfo + + +@pytest.mark.parametrize("server,since,expected", [ + ("2.4.70", None, False), + ("2.4.70-dev", None, False), + ("2.4.70", "2.4.71", False), + ("2.4.71-dev", "2.4.71", True), + ("2.4.71", "2.4.71", True), + ("2.4.72", "2.4.71", True), + # numeric, not textual, comparison + ("2.4.9", "2.4.10", False), + ("2.4.10", "2.4.9", True), + ("2.5.0", None, True), + ("2.5.1-dev", None, True), + ("2.5.1-dev", "2.4.71", True), +]) +def test_has_change(server, since, expected): + assert backport.has_change(server, since) is expected + + +def test_unparsable_version(): + with pytest.raises(ValueError): + backport.has_change("trunk") + + +def test_marks_record_their_arguments(): + xfail = backport.not_backported("r1", "a change", backported_in="2.4.71", + platform="win32").mark + skip = backport.skip_not_backported("r1", "a change").mark + assert xfail.name == "not_backported" + assert xfail.args == ("r1", "a change") + assert xfail.kwargs == {"backported_in": "2.4.71", "platform": "win32"} + assert skip.name == "skip_not_backported" + assert skip.kwargs == {} + + +@pytest.mark.parametrize("server,since,lacks", [ + ("2.4.70", None, True), + ("2.4.70", "2.4.71", True), + ("2.4.71-dev", "2.4.71", False), + ("2.5.1-dev", None, False), + # the classic suite hands over a tuple + ((2, 4, 70), None, True), + ((2, 5, 1), None, False), +]) +def test_lacks_change(server, since, lacks): + assert backport.lacks_change(server, since) is lacks + + +@pytest.mark.parametrize("server,platform,expected", [ + ("2.4.70", "win32", True), + ("2.4.70", "linux", False), + ("2.4.70", None, True), + ("2.5.1-dev", "win32", False), +]) +def test_platform(monkeypatch, server, platform, expected): + monkeypatch.setattr(backport.sys, "platform", "win32") + assert backport.lacks_change(server, platform=platform) is expected + + +_GATED = ''' +import pytest +from pyhttpd.backport import not_backported + +@pytest.fixture +def broken(): + raise RuntimeError("the server did not start") + +@pytest.fixture +def broken_teardown(): + yield + raise RuntimeError("the error log check failed") + +@not_backported("r1", "a change") +def test_setup_error(broken): + pass + +@not_backported("r1", "a change") +def test_teardown_error(broken_teardown): + pass + +@not_backported("r1", "a change") +def test_fails(): + assert False + +@not_backported("r1", "a change") +def test_passes(): + pass +''' + + +@pytest.fixture +def gated_run(pytester, monkeypatch): + """Run _GATED, with pyhttpd.gating, as if against an httpd of *version*.""" + def run(version): + info = HttpdInfo(httpd=Path("httpd"), version=version, mpm="event") + monkeypatch.setattr(gating, "resolve_build", lambda config: info) + pytester.makepyfile(_GATED) + pytester.makeini("[pytest]\nmarkers =\n not_backported\n") + return pytester.runpytest_inprocess("-p", "pyhttpd.gating", "-p", "no:cacheprovider") + return run + + +def test_not_backported_is_an_xfail_of_the_test_only(gated_run): + result = gated_run((2, 4, 70)) + # a broken fixture is an error, not an expected failure (the body of + # test_teardown_error passes: XPASS, and an error) + result.assert_outcomes(errors=2, xfailed=1, xpassed=2) + + +def test_not_backported_does_nothing_with_the_change(gated_run): + result = gated_run((2, 5, 1)) + result.assert_outcomes(errors=2, failed=1, passed=2) diff -Nru apache2-2.4.69/test/test_build.py apache2-2.4.71/test/test_build.py --- apache2-2.4.69/test/test_build.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/test_build.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,72 @@ +"""Self-test for pyhttpd/build.py: one httpd under test for the whole session, +whichever way it was named, and what is learned about it.""" +import os + +import pytest + +from pyhttpd.build import current, parse_loadmodules, resolve_build +from pyhttpd.env import HttpdTestEnv + + +@pytest.fixture +def subprocess_run(pytester, monkeypatch): + monkeypatch.setenv("PYTHONPATH", os.path.dirname(os.path.abspath(__file__))) + pytester.makepyfile("def test_nothing():\n pass\n") + + def run(*args): + return pytester.runpytest_subprocess("-p", "pyhttpd.plugin", + "-p", "no:cacheprovider", *args) + return run + + +@pytest.mark.parametrize("option", ["--apxs", "--httpd"]) +def test_unusable_target_is_a_usage_error(subprocess_run, tmp_path, option): + # and not a run against the build of config.ini instead + result = subprocess_run(f"{option}={tmp_path / 'nonexistent'}") + assert result.ret == pytest.ExitCode.USAGE_ERROR + result.stderr.fnmatch_lines(["*httpd under test:*nonexistent*no such file*"]) + + +def test_the_session_has_one_target(pytestconfig): + info = resolve_build(pytestconfig) + if info is None: + pytest.skip("no httpd build to test against") + assert current() is info + # an environment created at import time, without the pytest config, + # tests that httpd too, not the one config.ini names + env = HttpdTestEnv() + assert os.path.realpath(os.path.join(env.bin_dir, "httpd")) == \ + os.path.realpath(info.httpd) + assert env.get_httpd_version().startswith(info.version_str) + if info.source != "config.ini": + assert os.path.realpath(env.libexec_dir) == os.path.realpath(info.libexecdir) + + +def test_static_modules_count_as_available(pytestconfig): + info = resolve_build(pytestconfig) + if info is None: + pytest.skip("no httpd build to test against") + # mod_so is always compiled in, never a DSO + assert "mod_so.c" in info.static_modules + assert HttpdTestEnv.has_shared_module("so") + + +def test_loadmodule_lines(tmp_path): + (tmp_path / "conf.d").mkdir() + (tmp_path / "httpd.conf").write_text( + 'LoadModule a_module modules/mod_a.so\n' + '#LoadModule b_module modules/mod_b.so\n' + 'LoadModule c_module "/opt/my modules/mod_c.so"\n' + 'IncludeOptional conf.d/*.conf\n' + 'Include "conf.d/missing.conf"\n') + (tmp_path / "conf.d" / "d.conf").write_text( + 'loadmodule d_module modules/mod_d.so\n' + 'Include httpd.conf\n') # a loop is followed once + found = {(d.symbol, d.so, d.active) + for d in parse_loadmodules(tmp_path / "httpd.conf", tmp_path)} + assert found == { + ("a_module", str(tmp_path / "modules/mod_a.so"), True), + ("b_module", str(tmp_path / "modules/mod_b.so"), False), + ("c_module", "/opt/my modules/mod_c.so", True), + ("d_module", str(tmp_path / "modules/mod_d.so"), True), + } diff -Nru apache2-2.4.69/test/test_classic_errorlog.py apache2-2.4.71/test/test_classic_errorlog.py --- apache2-2.4.69/test/test_classic_errorlog.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/test_classic_errorlog.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,100 @@ +"""Self-test for pyhttpd/classic_errorlog.py: what the shared server of the +classic suite logs while it starts and stops counts, not only what it logs +during the tests. Needs no built httpd: a fake 'framework' fixture writes +the error log. Run in a subprocess, as the plugin keeps session state.""" +import os + +import pytest + +_SUITE = ''' +import pytest +from pyhttpd.classic_errorlog import check_shutdown, check_startup + +LINE = "[Wed Oct 28 12:00:00.000000 2026] [{0}] [pid 1] {1}\\n" + +@pytest.fixture(scope="session") +def config(tmp_path_factory): + class Config: + vars = {"t_logs": str(tmp_path_factory.mktemp("logs"))} + open(Config.vars["t_logs"] + "/error_log", "w").close() + return Config + +def log(config, level, msg): + with open(config.vars["t_logs"] + "/error_log", "a") as fd: + fd.write(LINE.format(level, msg)) + +@pytest.fixture(scope="session") +def framework(request, config): + log(config, "mpm_event:notice", "AH00489: configured -- resuming normal operations") + if "{start}": + log(config, "ssl:warn", "AH01909: server certificate does not match") + if "{start_error}": + log(config, "md:error", "AH10046: setup store failed") + check_startup(request.config, config.vars["t_logs"] + "/error_log", 0) + yield config, None + if "{stop}": + log(config, "core:notice", "AH00052: child pid 2 exit signal Segmentation fault (11)") + check_shutdown(request.config) + +def test_one(framework): + pass +''' + + +@pytest.fixture +def run(pytester, monkeypatch): + monkeypatch.setenv("PYTHONPATH", os.path.dirname(os.path.abspath(__file__))) + + def run(start="", start_error="", stop=""): + pytester.makepyfile(_SUITE.replace("{start}", start) + .replace("{start_error}", start_error) + .replace("{stop}", stop)) + return pytester.runpytest_subprocess("-p", "pyhttpd.classic_errorlog", + "-p", "no:cacheprovider") + return run + + +def test_clean_start_and_stop_pass(run): + run().assert_outcomes(passed=1) + + +def test_crash_while_stopping_fails(run): + result = run(stop="yes") + result.assert_outcomes(passed=1, errors=1) + result.stdout.fnmatch_lines(["*during the shutdown of the server*", + "*exit signal Segmentation fault*"]) + + +def test_error_while_starting_fails(run): + result = run(start_error="yes") + result.assert_outcomes(errors=1) + result.stdout.fnmatch_lines(["*during the start of the server*", "*AH10046*"]) + + +def test_warning_while_starting_passes(run): + # the shared t/conf makes httpd warn at every start + run(start="yes").assert_outcomes(passed=1) + + +@pytest.mark.parametrize("filename,logno,errors", [ + ("test_ldap", "AH01617", 0), + ("test_ldap", "AH01631", 0), + ("test_ldap", "AH99999", 1), + ("test_unrelated", "AH01617", 1), + ("test_unrelated", "AH01631", 1), +]) +def test_ldap_expected_errors_are_file_scoped(pytester, monkeypatch, + filename, logno, errors): + monkeypatch.setenv("PYTHONPATH", os.path.dirname(os.path.abspath(__file__))) + pytester.makeconftest(_SUITE.split("def test_one")[0] + .replace("{start}", "") + .replace("{start_error}", "") + .replace("{stop}", "")) + pytester.makepyfile(**{filename: f''' +def test_auth_failure(framework, config): + with open(config.vars["t_logs"] + "/error_log", "a") as fd: + fd.write("[auth_basic:error] [pid 1] {logno}: authentication failure\\n") +'''}) + result = pytester.runpytest_subprocess("-p", "pyhttpd.classic_errorlog", + "-p", "no:cacheprovider") + result.assert_outcomes(passed=1, errors=errors) diff -Nru apache2-2.4.69/test/test_cmodule_header.py apache2-2.4.71/test/test_cmodule_header.py --- apache2-2.4.69/test/test_cmodule_header.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/test_cmodule_header.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,17 @@ +"""CMake's header generator must work without pytest or test dependencies.""" +import subprocess +import sys +from pathlib import Path + + +def test_header_generator_standalone(tmp_path): + script = Path(__file__).parent / "pytest_suite" / "gen_test_header.py" + dest = tmp_path / "apache_httpd_test.h" + # -I ignores PYTHONPATH; -S excludes site-packages. This is the direct + # invocation used by CMake, from outside the test project. + result = subprocess.run([sys.executable, "-I", "-S", str(script), str(dest)], + cwd=tmp_path, capture_output=True, text=True, check=False) + assert result.returncode == 0, result.stderr + header = dest.read_text() + assert "#define APACHE_HTTPD_TEST_MODULE(name)" in header + assert "#define APACHE_HTTPD_TEST_HANDLER NULL" in header diff -Nru apache2-2.4.69/test/test_config_off_t_marks.py apache2-2.4.71/test/test_config_off_t_marks.py --- apache2-2.4.69/test/test_config_off_t_marks.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/test_config_off_t_marks.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,20 @@ +"""Parameter-level expectations must cover the no-digits cache parsing.""" +import pytest +from pyhttpd import backport + +from modules.core import test_012_config_off_t as off_t + + +@pytest.mark.parametrize("directive", [d for d in off_t.DIRECTIVES + if d.startswith("Cache")]) +@pytest.mark.parametrize("value", ["abc", "abc123", "k", "+", "-"]) +def test_cache_no_digits_expectations(directive, value): + marks = next(m.args[1] for m in + off_t.TestConfigOffT.test_core_012_002.pytestmark + if m.name == "parametrize") + case = next(c for c in marks if c.values == (directive, value)) + assert len(case.marks) == 1 + mark = case.marks[0].mark + assert mark.name == "not_backported" + assert backport.lacks_change("2.4.70", **mark.kwargs) + assert not backport.lacks_change("2.5.1", **mark.kwargs) diff -Nru apache2-2.4.69/test/test_instance.py apache2-2.4.71/test/test_instance.py --- apache2-2.4.69/test/test_instance.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/test_instance.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,180 @@ +"""Self-test for pyhttpd/instance.py: starting, stopping and restarting a bare +httpd. Needs a built httpd (--apxs, --httpd or the build's config.ini), but no +module beyond the MPM, and uses a free port and a temporary directory.""" +import os +import subprocess +import time +from pathlib import Path + +import pytest + +from pyhttpd.build import resolve_build +from pyhttpd.instance import (HttpdInstance, HttpdStartError, free_port, + pid_alive, port_open, read_pid) + + +@pytest.fixture(scope="module") +def build(pytestconfig): + info = resolve_build(pytestconfig) + if info is None or info.prefix is None: + pytest.skip("no httpd build to test against") + return info + + +def _module_lines(build): + """LoadModule lines for what a bare server needs, for the modules that + are not compiled in.""" + static = subprocess.run([str(build.httpd), "-l"], capture_output=True, + text=True).stdout + mpm = os.environ.get("MPM", "event") + lines = [] + for name in (f"mpm_{mpm}", "unixd", "authz_core"): + so = Path(build.prefix) / "modules" / f"mod_{name}.so" + if f"mod_{name}.c" in static: + continue + if so.is_file(): + lines.append(f"LoadModule {name}_module {so}") + return lines + + +@pytest.fixture +def server(build, tmp_path): + port = free_port() + conf = tmp_path / "httpd.conf" + conf.write_text("\n".join([ + f'ServerRoot "{tmp_path}"', + "ServerName localhost", + f"Listen 127.0.0.1:{port}", + f'PidFile "{tmp_path}/httpd.pid"', + f'ErrorLog "{tmp_path}/error_log"', + "LogLevel warn", + *_module_lines(build), + "StartServers 1", + "ThreadsPerChild 5", + ""])) + inst = HttpdInstance(build.httpd, ["-f", str(conf)], ports=[port], + error_log=tmp_path / "error_log", + pid_files=[tmp_path / "httpd.pid"], + stderr_file=tmp_path / "stderr") + yield inst, tmp_path, conf + inst.stop() + + +def _children(parent): + out = subprocess.run(["ps", "-A", "-o", "pid=,ppid="], capture_output=True, + text=True).stdout + return {int(a) for a, b in (ln.split() for ln in out.splitlines() if ln.strip()) + if int(b) == parent} + + +def test_start_stop(server): + inst, _, _ = server + assert not inst.is_running() and inst.pid == 0 + inst.start() + assert inst.is_running() and inst.pid > 0 + assert inst.all_ports_open() + pid = inst.pid + assert inst.stop() is True + assert not pid_alive(pid) + assert not port_open(inst.ports[0]) + + +def test_stop_is_idempotent(server): + inst, _, _ = server + assert inst.stop() is True # never started + inst.start() + assert inst.stop() is True + assert inst.stop() is True # already stopped + + +def test_stop_reaps_children(server): + inst, _, _ = server + inst.start() + children = _children(inst.pid) + assert children, "the MPM has started no children" + inst.stop() + assert not any(pid_alive(c) for c in children) + + +def test_bad_config_reports_the_error(server): + inst, _, conf = server + conf.write_text(conf.read_text() + "\nNoSuchDirective on\n") + with pytest.raises(HttpdStartError) as e: + inst.start() + assert "NoSuchDirective" in e.value.stderr + assert e.value.returncode != 0 + assert not inst.is_running() + assert not port_open(inst.ports[0]) + + +def test_bad_config_found_by_configtest_first(server): + inst, _, conf = server + inst.configtest_first = True + conf.write_text(conf.read_text() + "\nNoSuchDirective on\n") + with pytest.raises(HttpdStartError) as e: + inst.start() + assert "NoSuchDirective" in e.value.stderr + + +def test_start_replaces_a_stale_server(server): + inst, tmp_path, _ = server + inst.start() + stale = inst.pid + # a server from an earlier run: known only by its pid file + inst.proc.poll() + other = HttpdInstance(inst.httpd, inst.args, ports=inst.ports, + error_log=tmp_path / "error_log", + pid_files=[tmp_path / "httpd.pid"], + stderr_file=tmp_path / "stderr2") + assert read_pid(tmp_path / "httpd.pid") == stale + other.start() + try: + # a real stale server is somebody else's child, reaped by init; + # this one is ours and is a zombie until we wait for it + assert inst.proc.wait(timeout=10) is not None + assert not pid_alive(stale) + assert other.is_running() + finally: + other.stop() + inst.proc = None + + +def test_graceful_restart_keeps_the_parent(server): + inst, _, _ = server + inst.start() + parent, children = inst.pid, _children(inst.pid) + assert inst.restart(graceful=True) + assert inst.pid == parent and inst.all_ports_open() + deadline = time.monotonic() + 10 + while time.monotonic() < deadline and children & _children(parent): + time.sleep(0.1) # the old generation drains + assert not (children & _children(parent)) + + +def test_hard_restart_serves_again(server): + inst, tmp_path, _ = server + inst.start() + before = (tmp_path / "error_log").read_text().count("resuming normal operations") + assert inst.restart(graceful=False) + after = (tmp_path / "error_log").read_text().count("resuming normal operations") + assert after == before + 1 + assert inst.all_ports_open() + + +def test_restart_without_server(server): + inst, _, _ = server + assert inst.restart() is False + + +def test_graceful_stop(server): + inst, _, _ = server + inst.start() + pid = inst.pid + assert inst.graceful_stop() is True + assert not pid_alive(pid) + + +def test_stderr_of_a_good_start_is_available(server): + inst, _, _ = server + inst.start() + assert isinstance(inst.stderr_text(), str) diff -Nru apache2-2.4.69/test/test_md_renewal.py apache2-2.4.71/test/test_md_renewal.py --- apache2-2.4.69/test/test_md_renewal.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/test_md_renewal.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,115 @@ +"""Renewal failures keep their diagnostics without hiding test failures.""" +import json +from pathlib import Path +from types import SimpleNamespace + +import pytest +from modules.md.md_env import MDTestEnv +from pyhttpd.log import HttpdErrorLog + +from modules.md import test_702_auto as auto + + +def test_renewal_failure_preserves_diagnostics(tmp_path): + env = MDTestEnv.__new__(MDTestEnv) + env._gen_dir = str(tmp_path) + env._server_dir = str(tmp_path / "apache") + env._store_dir = str(tmp_path / "store") + env.set_current_test_name("test_renewal[http/01]") + logs = Path(env.server_dir) / "logs" + logs.mkdir(parents=True) + (logs / "error_log").write_text("renewal error\n") + env._error_log = HttpdErrorLog(str(logs / "error_log")) + conf = Path(env.server_dir) / "conf" + conf.mkdir() + (conf / "httpd.conf").write_text("MDPortMap 80:12345\n") + (tmp_path / "pebble.log").write_text("challenge failed\n") + staging = Path(env.store_stagings()) / "example.org" + staging.mkdir(parents=True) + (staging / "job.json").write_text('{"errors": 1}') + (staging / "privkey.pem").write_text("private key -- must not be copied") + statuses = {"example.org": {"renewal": {"errors": 1}}} + + env.save_renewal_failure(["example.org"], statuses) + env.httpd_error_log.clear_log() + env.clear_store() + + dest = tmp_path / "failed-tests" / "test_renewal_http_01_" + assert (dest / "error_log").read_text() == "renewal error\n" + assert (dest / "pebble.log").read_text() == "challenge failed\n" + assert json.loads((dest / "md-status.json").read_text()) == statuses + assert json.loads((dest / "example.org-job.json").read_text()) == {"errors": 1} + assert not list(dest.rglob("*.pem")) + + +def test_await_completion_keeps_failure_status(monkeypatch): + calls = [] + status = {"renewal": {"errors": 1}} + env = SimpleNamespace( + get_md_status=lambda *args, **kwargs: status, + save_renewal_failure=lambda names, statuses: calls.append((names, statuses)), + ) + times = iter([0, 0, 60]) + monkeypatch.setattr("modules.md.md_env.time.time", lambda: next(times)) + monkeypatch.setattr("modules.md.md_env.time.sleep", lambda duration: None) + assert not MDTestEnv.await_completion(env, ["example.org"]) + assert calls == [(["example.org"], {"example.org": status})] + + +def test_await_completion_preserves_client_failure(monkeypatch): + calls = [] + + def get_md_status(*args, **kwargs): + raise AssertionError("curl failed") + + env = SimpleNamespace( + get_md_status=get_md_status, + save_renewal_failure=lambda names, statuses: calls.append((names, statuses)), + ) + with pytest.raises(AssertionError, match="curl failed"): + MDTestEnv.await_completion(env, ["example.org"]) + assert calls == [(["example.org"], {})] + + +def test_store_reset_stops_the_previous_server(): + calls = [] + env = SimpleNamespace( + apache_stop=lambda: calls.append("stop") or 0, + clear_store=lambda: calls.append("clear"), + get_request_domain=lambda request: "example.org", + ) + test = auto.TestAutov2() + auto.TestAutov2._method_scope.__wrapped__(test, env, None) + assert calls == ["stop", "clear"] + + +@pytest.mark.parametrize("method", ["test_md_702_010", "test_md_702_011"]) +def test_expected_challenge_errors_handled_before_completion_failure(monkeypatch, method): + calls = [] + + class Conf: + def __init__(self, *args, **kwargs): + pass + + def __getattr__(self, name): + return lambda *args: None + + def await_completion(names): + assert calls == ["ignored"] + return False + + monkeypatch.setattr(auto, "MDConf", Conf) + env = SimpleNamespace( + http_port=5002, https_port=5001, apachectl_stderr="", + apache_restart=lambda: 0, + await_error=lambda domain: {"renewal": {"errors": 1}}, + check_md=lambda domains: None, + await_completion=await_completion, + httpd_error_log=SimpleNamespace( + ignore_recent=lambda **kwargs: calls.append("ignored")), + ) + test = auto.TestAutov2() + test.test_domain = "example.org" + with pytest.raises(AssertionError): + getattr(test, method)(env) + assert calls == ["ignored"] diff -Nru apache2-2.4.69/test/test_strict_optional.py apache2-2.4.71/test/test_strict_optional.py --- apache2-2.4.69/test/test_strict_optional.py 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/test_strict_optional.py 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,308 @@ +"""Self-test for the optional-dependency gating in pyhttpd/depends.py. + +Runs a synthetic test file in a pytester subprocess, with and without +--strict-optional, and asserts skipped-vs-failed outcomes. The subprocess +loads the real plugin with '-p pyhttpd.depends' and nothing else, so this +needs no built httpd, no config.ini and no network. +""" +import os + +import pytest + +from pyhttpd.env import HttpdTestEnv + +TEST_DIR = os.path.dirname(os.path.abspath(__file__)) + +pytest_plugins = ["pytester"] + + +class _StubErrorLog: + """Enough of HttpdErrorLog for the autouse _package_scope fixture.""" + + def clear_ignored_matches(self): + pass + + def clear_ignored_lognos(self): + pass + + def add_ignored_lognos(self, lognos): + pass + + +class _StubEnv: + """Satisfies the autouse fixtures in test/conftest.py. + + Those are written for a test that drives a real httpd; this module + drives a pytester subprocess instead, so every one of them is a no-op + here. apache_stop() must report success, _package_scope asserts on it. + """ + + httpd_error_log = _StubErrorLog() + + def set_current_test_name(self, name): + pass + + def check_error_log(self): + pass + + def apache_stop(self): + return 0 + + +@pytest.fixture(scope="package") +def env(): + return _StubEnv() + + +SYNTHETIC = """ +import pytest +from pyhttpd.depends import needs_dependency + +# dependency missing: skip by default, fail when strict +@needs_dependency("frobnicator", False, + reason="frobnicator >= 2.0 not installed", + detected="frobnicator 1.0 at /usr/bin/frobnicator") +def test_dep_missing(): + assert False, "must never run" + +# dependency present: always runs +@needs_dependency("widget", True, reason="widget not installed") +def test_dep_present(): + assert True + +# not applicable: skips in both modes +@pytest.mark.skipif(True, reason="httpd too old") +def test_not_applicable(): + assert False, "must never run" + +# both kinds of gate on one test: "not applicable" wins, even when strict +@pytest.mark.skipif(True, reason="STRESS_TEST not set in env") +@needs_dependency("frobnicator", False, reason="frobnicator not installed") +def test_not_applicable_beats_missing_dep(): + assert False, "must never run" + +# an inactive skipif must not shield a missing dependency +@pytest.mark.skipif(False, reason="httpd too old") +@needs_dependency("frobnicator", False, reason="frobnicator not installed") +def test_inactive_skipif_does_not_shield(): + assert False, "must never run" + +# two dependencies missing at once, both should be reported +@needs_dependency("OCSP responder", False, reason="no OCSP responder") +@needs_dependency("mod_ssl", False, reason="no mod_ssl available") +class TestStacked: + def test_stacked(self): + assert False, "must never run" + +# a class-level marker must reach the method +@needs_dependency("a2md", False, reason="no a2md available") +class TestClassLevel: + def test_class_level(self): + assert False, "must never run" +""" + +MODULE_LEVEL = """ +import pytest +from pyhttpd.depends import needs_dependency + +# module-level pytestmark, which a per-decorator scan would not see at all +pytestmark = [needs_dependency("nghttp", False, reason="nghttp not available")] + +def test_module_gated(): + assert False, "must never run" +""" + +LAZY = """ +from pyhttpd.depends import needs_dependency + +CALLS = [] + +def probe(): + CALLS.append(1) + return False + +@needs_dependency("lazy", probe, reason="lazy dep missing") +def test_lazy(): + assert False, "must never run" + +def test_probe_ran_once(): + assert CALLS == [1] +""" + +# test_dep_present is the only one that runs; of the rest, test_dep_missing, +# TestStacked and TestClassLevel are dependency gates and the other three +# are "not applicable" or shielded. +ALL_SKIPPED = dict(passed=1, skipped=6) +STRICT_SPLIT = dict(passed=1, skipped=2, errors=4) + + +@pytest.fixture +def synth(pytester, monkeypatch): + # never inherit strict mode from the outer run + monkeypatch.delenv("PYHTTPD_STRICT_OPTIONAL", raising=False) + # let the subprocess import pyhttpd.depends + monkeypatch.setenv("PYTHONPATH", os.pathsep.join( + [TEST_DIR, os.environ.get("PYTHONPATH", "")]).rstrip(os.pathsep)) + pytester.makepyfile(test_synthetic=SYNTHETIC, test_modlevel=MODULE_LEVEL, + test_lazy=LAZY) + return pytester + + +def _run(pytester, *args): + return pytester.runpytest_subprocess("-p", "pyhttpd.depends", + "test_synthetic.py", *args) + + +def test_flag_is_advertised(synth): + r = synth.runpytest_subprocess("-p", "pyhttpd.depends", "--help") + r.stdout.fnmatch_lines(["*--strict-optional*"]) + + +def test_default_is_off_and_missing_dep_skips(synth): + r = _run(synth) + r.assert_outcomes(**ALL_SKIPPED) + assert r.ret == 0 + + +def test_default_skip_keeps_the_original_reason(synth): + r = _run(synth, "-rs") + r.stdout.fnmatch_lines(["*frobnicator >= 2.0 not installed*"]) + r.stdout.fnmatch_lines(["*no a2md available*"]) + + +def test_strict_flag_fails_on_missing_dep(synth): + r = _run(synth, "--strict-optional") + r.assert_outcomes(**STRICT_SPLIT) + assert r.ret != 0 + r.stdout.fnmatch_lines([ + "*[[]strict-optional[]] missing optional dependency: frobnicator*", + "*reason:*frobnicator >= 2.0 not installed*", + "*detected: frobnicator 1.0 at /usr/bin/frobnicator*", + "*drop*--strict-optional*", + ]) + + +def test_strict_reports_every_missing_dep(synth): + r = _run(synth, "--strict-optional") + r.stdout.fnmatch_lines(["*missing optional dependency: OCSP responder*"]) + r.stdout.fnmatch_lines(["*missing optional dependency: mod_ssl*"]) + + +def test_not_applicable_still_skips_when_strict(synth): + r = _run(synth, "--strict-optional", "-rs") + # a plain skipif carrying no dependency marker at all + r.stdout.fnmatch_lines(["*httpd too old*"]) + # and one stacked on a missing dependency: the skipif wins, and the + # reason reported is its own, not the dependency's + r.stdout.fnmatch_lines(["*STRESS_TEST not set in env*"]) + + +def test_env_var_enables_strict(synth, monkeypatch): + monkeypatch.setenv("PYHTTPD_STRICT_OPTIONAL", "1") + r = _run(synth) + r.assert_outcomes(**STRICT_SPLIT) + assert r.ret != 0 + + +@pytest.mark.parametrize("val", ["", "0", "false", "no", "off"]) +def test_env_var_falsey_values_keep_strict_off(synth, monkeypatch, val): + monkeypatch.setenv("PYHTTPD_STRICT_OPTIONAL", val) + r = _run(synth) + r.assert_outcomes(**ALL_SKIPPED) + assert r.ret == 0 + + +def test_module_level_pytestmark_is_honoured(synth): + r = synth.runpytest_subprocess("-p", "pyhttpd.depends", "test_modlevel.py") + r.assert_outcomes(skipped=1) + r = synth.runpytest_subprocess("-p", "pyhttpd.depends", "test_modlevel.py", + "--strict-optional") + r.assert_outcomes(errors=1) + + +def test_callable_available_is_evaluated_once_at_collection(synth): + r = synth.runpytest_subprocess("-p", "pyhttpd.depends", "test_lazy.py") + r.assert_outcomes(passed=1, skipped=1) + + +# --- env.require(), the in-test counterpart of the decorator --------------- +# +# Exercised against a stand-in rather than a real HttpdTestEnv, which would +# need a built httpd and a running server just to reach the one branch that +# matters here. + +class _RequireEnv: + require = HttpdTestEnv.require + + def __init__(self, strict): + self._strict_optional = strict + self.current_test_name = "unit::require" + + +def test_require_is_a_noop_when_the_dependency_is_present(): + _RequireEnv(strict=False).require(True, "need curl") + _RequireEnv(strict=True).require(True, "need curl") + + +def test_require_skips_when_not_strict(): + with pytest.raises(BaseException) as exc: + _RequireEnv(strict=False).require( + False, "need at least curl v8.0.0 for this", + dep="curl", detected="curl 7.88.1") + assert exc.value.__class__.__name__ == "Skipped" + assert "need at least curl v8.0.0 for this" in str(exc.value.args[0]) + + +def test_require_reports_the_dependency_name_not_the_reason(): + """dep is what is missing; reason is why the test wanted it.""" + with pytest.raises(BaseException) as exc: + _RequireEnv(strict=True).require(False, "no mod_ssl available") + # no dep= given, so the reason stands in for the name + assert "missing optional dependency: no mod_ssl available" in str(exc.value.args[0]) + + +# --- the tool probes ------------------------------------------------------- +# +# config.ini always names curl/nghttp/h2load, so the name alone says nothing +# about whether the tool is installed. Probing used to raise FileNotFoundError +# out of collection; it must report "absent" instead. + +class _ProbeEnv: + """Drives the curl probe without a config.ini or a built httpd.""" + + _run_version = staticmethod(HttpdTestEnv._run_version) + _versiontuple = HttpdTestEnv._versiontuple + _probe_curl = HttpdTestEnv._probe_curl + has_curl = HttpdTestEnv.has_curl + curl_version_str = HttpdTestEnv.curl_version_str + curl_is_at_least = HttpdTestEnv.curl_is_at_least + curl_is_less_than = HttpdTestEnv.curl_is_less_than + curl_is_8_1_x = HttpdTestEnv.curl_is_8_1_x + + def __init__(self, curl): + self._curl = curl + self._curl_version = None + + +@pytest.mark.parametrize("curl", ["/nonexistent/curl", "curl-does-not-exist", ""]) +def test_absent_curl_probes_as_missing_rather_than_raising(curl): + env = _ProbeEnv(curl) + assert env.has_curl() is False + assert env.curl_version_str() is None + # every comparison answers False rather than blowing up collection + assert env.curl_is_at_least('8.0.0') is False + assert env.curl_is_less_than('8.0.0') is False + assert env.curl_is_8_1_x() is False + + +def test_require_fails_when_strict_and_names_the_dependency(): + with pytest.raises(BaseException) as exc: + _RequireEnv(strict=True).require( + False, "need at least curl v8.0.0 for this", + dep="curl", detected="curl 7.88.1") + assert exc.value.__class__.__name__ == "Failed" + msg = str(exc.value.args[0]) + assert "[strict-optional] missing optional dependency: curl" in msg + assert "reason: need at least curl v8.0.0 for this" in msg + assert "detected: curl 7.88.1" in msg + assert "unit::require" in msg diff -Nru apache2-2.4.69/test/test_travis_conditions.sh apache2-2.4.71/test/test_travis_conditions.sh --- apache2-2.4.69/test/test_travis_conditions.sh 2022-03-07 11:07:32.000000000 +0000 +++ apache2-2.4.71/test/test_travis_conditions.sh 2026-11-09 03:07:05.000000000 +0000 @@ -33,7 +33,6 @@ testit '{"tag": "2.4.49"}' 2.4.x testit '{"tag": "2.5.59"}' trunk testit '{"branch": "2.4.x"}' 2.4.x -testit '{"branch": "candidate-2.4.49"}' 2.4.x testit '{"branch": "2.4.55-candidate"}' 2.4.x testit '{"branch": "2.4-backport-branch"}' 2.4.x testit '{"branch": "2.4.x-something"}' 2.4.x diff -Nru apache2-2.4.69/test/travis_Dockerfile_slapd.centos apache2-2.4.71/test/travis_Dockerfile_slapd.centos --- apache2-2.4.69/test/travis_Dockerfile_slapd.centos 2025-01-31 14:38:37.000000000 +0000 +++ apache2-2.4.71/test/travis_Dockerfile_slapd.centos 2026-11-09 03:07:05.000000000 +0000 @@ -1,5 +1,5 @@ FROM quay.io/centos/centos:stream9 RUN rpm -q openldap-servers || (dnf install --refresh -y epel-release && \ - dnf install --nobest -y openldap-clients openldap-servers openldap-devel && \ + dnf install --refresh --nobest -y openldap-clients openldap-servers openldap-devel && \ dnf -y clean all --enablerepo='*') CMD /usr/sbin/slapd -u ldap -d1 '-h ldap:// ldapi:///' diff -Nru apache2-2.4.69/test/travis_before_linux.sh apache2-2.4.71/test/travis_before_linux.sh --- apache2-2.4.69/test/travis_before_linux.sh 2026-06-15 10:04:24.000000000 +0000 +++ apache2-2.4.71/test/travis_before_linux.sh 2026-11-09 03:07:05.000000000 +0000 @@ -1,11 +1,13 @@ #!/bin/bash -xe +# The 2.4.x workflow caches ~/root under a key which does not name what is +# in it, and sets CLEAR_CACHE for the jobs which must start afresh. if test -v CLEAR_CACHE; then rm -rf $HOME/root fi -: Travis tag = ${TRAVIS_TAG} -: Travis branch = ${TRAVIS_BRANCH} +# The 2.4.x workflow does not set MFLAGS. +: ${MFLAGS=-j2} : /etc/hosts -- cat /etc/hosts @@ -21,61 +23,86 @@ cat /etc/hosts fi +# Build and install $1 at version $2 into the cached +# $HOME/root/$1-$2, configured with the arguments in $3 and bootstrapped +# with ./buildconf $4, unless it was restored from the cache. The +# source goes in $HOME/build, which is not cached; it is a git checkout +# or, with TEST_APR_TARBALL, the release tarball. +# +# The cache key covers APR and APR-util together, along with the +# configuration, $CC and, for a branch, the commit which +# gha-resolve-deps.sh resolved it to, so an install restored here is +# usable as-is. The build uses that same commit, since resolving the +# branch again would race with it moving. function install_apx() { local name=$1 local version=$2 + local config=$3 + local buildconf=$4 local prefix=${HOME}/root/${name}-${version} local build=${HOME}/build/${name}-${version} local giturl=https://github.com/apache/${name}.git - local config=$3 - local buildconf=$4 - local commit=tarball - - mkdir -p ${HOME}/build + local ref commit=tarball if ! test -v TEST_APR_TARBALL; then - case $version in - trunk|*.x) ref=refs/heads/${version} ;; - *) ref=refs/tags/${version} ;; - esac - - # Fetch the object ID (hash) of latest commit - commit=`git ls-remote ${giturl} ${ref} | cut -f1` - if test -z "$commit"; then - : Could not determine latest commit hash for ${ref} in ${giturl} - check branch is valid? - exit 1 - fi - fi - - # Blow away the cached install root if the cached install is stale - # or doesn't match the expected configuration. - grep -q "${version} ${commit} ${config} CC=$CC" ${HOME}/root/.key-${name} || rm -rf ${prefix} + case ${name}:${version} in + apr:trunk|apr:*.x) ref=${APR_COMMIT:-refs/heads/${version}} ;; + apr-util:trunk|apr-util:*.x) ref=${APU_COMMIT:-refs/heads/${version}} ;; + *) ref=refs/tags/${version} ;; + esac + fi + + # Without $ROOTID (see gha-resolve-deps.sh) the cache key does not say + # what was built, so an install restored here may be stale or built + # otherwise: pin the ref to a commit, and rebuild unless that commit, + # the configuration and $CC are those the install was made from. + if ! test -v ROOTID; then + if ! test -v TEST_APR_TARBALL; then + commit=${ref} + if ! [[ ${commit} =~ ^[0-9a-f]{40}$ ]]; then + commit=`git ls-remote ${giturl} ${ref} | cut -f1` + if test -z "$commit"; then + : Could not determine latest commit hash for ${ref} in ${giturl} - check branch is valid? + exit 1 + fi + ref=${commit} + fi + fi + grep -q "${version} ${commit} ${config} CC=$CC" ${HOME}/root/.key-${name} || rm -rf ${prefix} + fi if test -d ${prefix}; then return 0 fi if test -v TEST_APR_TARBALL; then - curl https://archive.apache.org/dist/apr/${name}-${version}.tar.gz > apx.tar.gz - tar -C ${HOME}/build -xzf apx.tar.gz - rm apx.tar.gz - pushd ${build} + mkdir -p ${HOME}/build + curl https://archive.apache.org/dist/apr/${name}-${version}.tar.gz | + tar -C ${HOME}/build -xzf - + pushd ${build} else - git init -q ${build} - pushd $build - # Clone and checkout the commit identified above. - git remote add origin ${giturl} - git fetch -q --depth=1 origin ${commit} - git checkout ${commit} - ./buildconf ${buildconf} + mkdir -p ${build} + pushd ${build} + git init -q + git fetch -q --depth=1 ${giturl} ${ref} + git checkout -q FETCH_HEAD + ./buildconf ${buildconf} fi - ./configure --prefix=${prefix} ${config} - make -j2 + # glibc's C23 const-preserving strrchr() et al trip + # -Wdiscarded-qualifiers in APR 1.7.x. + case " ${NOTEST_CFLAGS-} " in + *" -Werror "*) + make $MFLAGS NOTEST_CFLAGS="${NOTEST_CFLAGS} -Wno-error=discarded-qualifiers" ;; + *) + make $MFLAGS ;; + esac make install popd - echo ${version} ${commit} "${config}" "CC=${CC}" > ${HOME}/root/.key-${name} + if ! test -v ROOTID; then + echo ${version} ${commit} "${config}" "CC=${CC}" > ${HOME}/root/.key-${name} + fi } # Allow to load $HOME/build/apache/httpd/.gdbinit @@ -101,10 +128,10 @@ : Purging cache since "perl -V" output has changed rm -rf ~/perl5 fi - + cpanm --local-lib=~/perl5 local::lib && eval $(perl -I ~/perl5/lib/perl5/ -Mlocal::lib) - pkgs="Net::SSL LWP::Protocol::https \ + pkgs="LWP::Protocol::https \ LWP::Protocol::AnyEvent::http ExtUtils::Embed Test::More \ AnyEvent DateTime HTTP::DAV FCGI \ AnyEvent::WebSocket::Client Apache::Test" @@ -138,26 +165,35 @@ # Build the requested version of OpenSSL if it's not already installed # in the cached ~/root if test -v TEST_OPENSSL3; then - # For a branch, rebuild if the remote branch has updated. - if test -v TEST_OPENSSL3_BRANCH -a -f $HOME/root/openssl-is-${TEST_OPENSSL3}; then - latest=`git ls-remote https://github.com/openssl/openssl refs/heads/${TEST_OPENSSL3_BRANCH} | cut -f1` - : Got branch latest commit ${latest} - if grep -q ^${latest} $HOME/root/openssl-is-${TEST_OPENSSL3}; then - : Cached repos already at ${latest} - else - : Forcing rebuild - rm -f $HOME/root/openssl-is-${TEST_OPENSSL3} + # The cache key covers $TEST_OPENSSL3, $OPENSSL_CONFIG and, for a + # branch build, the resolved commit, so an install found here is + # current. Without $ROOTID it does not, so the commit built is + # recorded, and a build of a branch is redone if the branch has moved. + if ! test -v ROOTID; then + if test -v TEST_OPENSSL3_BRANCH -a -f $HOME/root/openssl-is-${TEST_OPENSSL3}; then + latest=`git ls-remote https://github.com/openssl/openssl refs/heads/${TEST_OPENSSL3_BRANCH} | cut -f1` + : Got branch latest commit ${latest} + if grep -q ^${latest} $HOME/root/openssl-is-${TEST_OPENSSL3}; then + : Cached repos already at ${latest} + else + : Forcing rebuild + rm -f $HOME/root/openssl-is-${TEST_OPENSSL3} + fi fi + test -f $HOME/root/openssl-is-${TEST_OPENSSL3} || rm -rf $HOME/root/openssl3 fi - if ! test -f $HOME/root/openssl-is-${TEST_OPENSSL3}; then - # Remove any previous install. - rm -rf $HOME/root/openssl3 - + if ! test -d $HOME/root/openssl3; then mkdir -p build/openssl pushd build/openssl if test -v TEST_OPENSSL3_BRANCH; then git clone --depth=1 -b $TEST_OPENSSL3_BRANCH -q https://github.com/openssl/openssl openssl-${TEST_OPENSSL3} + # Build the commit named in the cache key, not whatever + # the branch tip has become since it was resolved. + if test -n "${OPENSSL_COMMIT-}"; then + git -C openssl-${TEST_OPENSSL3} fetch -q --depth=1 origin ${OPENSSL_COMMIT} + git -C openssl-${TEST_OPENSSL3} checkout -q ${OPENSSL_COMMIT} + fi else curl -L "https://github.com/openssl/openssl/releases/download/openssl-${TEST_OPENSSL3}/openssl-${TEST_OPENSSL3}.tar.gz" | tar -xzf - @@ -169,11 +205,13 @@ '-Wl,-rpath=$(LIBRPATH)' make $MFLAGS make install_sw - if test -d .git; then - : Caching git commit hash: - git rev-parse HEAD | tee $HOME/root/openssl-is-${TEST_OPENSSL3} - else - touch $HOME/root/openssl-is-${TEST_OPENSSL3} + if ! test -v ROOTID; then + if test -d .git; then + : Caching git commit hash: + git rev-parse HEAD | tee $HOME/root/openssl-is-${TEST_OPENSSL3} + else + touch $HOME/root/openssl-is-${TEST_OPENSSL3} + fi fi popd fi @@ -189,6 +227,25 @@ fi fi +# Build the requested version of nghttp2 if it's not already installed +# in the cached ~/root; the nghttp/h2load tools come from the package. +if test -v TEST_NGHTTP2; then + if ! test -d $HOME/root/nghttp2; then + mkdir -p build/nghttp2 + pushd build/nghttp2 + curl -L "https://github.com/nghttp2/nghttp2/releases/download/v${TEST_NGHTTP2}/nghttp2-${TEST_NGHTTP2}.tar.xz" | + tar -xJf - + cd nghttp2-${TEST_NGHTTP2} + ./configure --prefix=$HOME/root/nghttp2 --enable-lib-only + make $MFLAGS + make install + popd + fi + + : -- Using nghttp2 from $HOME/root/nghttp2 -- + grep -H '^Version' $HOME/root/nghttp2/lib/pkgconfig/libnghttp2.pc +fi + if test -v APR_VERSION; then install_apx apr ${APR_VERSION} "${APR_CONFIG}" ldd $HOME/root/apr-${APR_VERSION}/lib/libapr-?.so || true diff -Nru apache2-2.4.69/test/travis_run_linux.sh apache2-2.4.71/test/travis_run_linux.sh --- apache2-2.4.69/test/travis_run_linux.sh 2026-06-15 10:04:24.000000000 +0000 +++ apache2-2.4.71/test/travis_run_linux.sh 2026-11-09 03:07:05.000000000 +0000 @@ -1,5 +1,17 @@ #!/bin/bash -ex +# Not every branch has every MPM: MotorZ is on trunk only, for now. A job +# which configures a branch without it has nothing to build or test, so it +# says so and succeeds; the same workflow can then be used on both branches. +case "${CONFIG:-}" in + *--with-mpm=motorz*) + if ! test -d server/mpm/motorz; then + echo "::notice::This branch has no MotorZ MPM, nothing to do." + exit 0 + fi + ;; +esac + # Test for APLOGNO() macro errors (duplicates, empty args) etc. For # trunk, run the updater script to see if it fails. If it succeeds # and changes any files (because there was a missing argument), the @@ -31,8 +43,8 @@ PREFIX=${PREFIX:-$HOME/build/httpd-root} -# If perl-framework testing is required it is checked out here by -# _before_linux.sh: +# If perl-framework testing is required, the workflow checks it out +# here: if test -d test/perl-framework; then CONFIG="$CONFIG --enable-load-all-modules" if grep -q ^check: Makefile.in; then @@ -55,8 +67,8 @@ fi # Pick up the rustls install built previously. -if test -v TEST_MOD_TLS -a RUSTLS_VERSION; then - CONFIG="$CONFIG --with-tls --with-rustls=$HOME/root/rustls" +if test -v TEST_MOD_TLS -a -v RUSTLS_VERSION; then + CONFIG="$CONFIG --with-tls --with-rustls=$HOME/root/rustls" fi if test -v TEST_OPENSSL3; then @@ -67,6 +79,13 @@ openssl version fi +if test -v TEST_NGHTTP2; then + # APACHE_CHECK_NGHTTP2 only yields -L, so an RPATH is needed here or + # mod_http2.so resolves to the system libnghttp2 at run-time. + CONFIG="$CONFIG --with-nghttp2=$HOME/root/nghttp2" + export LDFLAGS="${LDFLAGS:-} -Wl,-rpath,$HOME/root/nghttp2/lib" +fi + srcdir=$PWD if test -v TEST_VPATH; then @@ -74,9 +93,19 @@ cd ../vpath fi +builddir=$PWD + $srcdir/configure --prefix=$PREFIX $CONFIG make $MFLAGS +if test -v TEST_NGHTTP2; then + ldd modules/http2/.libs/mod_http2.so | grep --color=always nghttp2 + if ! ldd modules/http2/.libs/mod_http2.so | grep -q "$HOME/root/nghttp2/lib"; then + : mod_http2 is not linked against the nghttp2 built from source + exit 1 + fi +fi + if test -v TEST_OPENSSL3; then # Clear the library/run paths so that anything else run during # testing is not forced to use the custom OpenSSL build; e.g. perl, @@ -133,37 +162,62 @@ sudo sysctl -w kernel.core_pattern=core || true ulimit -c unlimited 2>/dev/null || true -if test -v WITH_TEST_SUITE; then - make check TESTS="${TESTS}" TEST_CONFIG="${TEST_ARGS}" - RV=$? -else - test -v TEST_INSTALL || make install - pushd test/perl-framework - perl Makefile.PL -apxs $PREFIX/bin/apxs - make test APACHE_TEST_EXTRA_ARGS="${TEST_ARGS} ${TESTS}" | tee test.log +if ! test -v NO_TEST_FRAMEWORK; then + # 2.4.x has no libcheck-based unit tests. + if grep -q httpdunit Makefile.in; then + : Running libcheck-based unit tests. + if ! prove -v ./test/httpdunit; then + RV=1 + fi + fi + + if test -v WITH_TEST_SUITE -a $RV -eq 0; then + make check-test-framework TESTS="${TESTS}" TEST_CONFIG="${TEST_ARGS}" | tee test.log RV=${PIPESTATUS[0]} # re-run failing tests with -v, avoiding set -e if [ $RV -ne 0 ]; then - #mv t/logs/error_log t/logs/error_log_save + # mv test/perl-framework/t/logs/error_log test/perl-framework/t/logs/error_log_save FAILERS="" while read FAILER; do FAILERS="$FAILERS $FAILER" done < <(awk '/Failed:/{print $1}' test.log) if [ -n "$FAILERS" ]; then - t/TEST -v $FAILERS || true + make check-test-framework TESTS="-v $FAILERS" || true fi # set -e would have killed us after the original t/TEST rm -f test.log - #mv t/logs/error_log_save t/logs/error_log + # mv test/perl-framework/t/logs/error_log_save test/perl-framework/t/logs/error_log false fi - popd -fi + else + test -v TEST_INSTALL || make install + pushd test/perl-framework + perl Makefile.PL -apxs $PREFIX/bin/apxs + make test APACHE_TEST_EXTRA_ARGS="${TEST_ARGS} ${TESTS}" | tee test.log + RV=${PIPESTATUS[0]} + # re-run failing tests with -v, avoiding set -e + if [ $RV -ne 0 ]; then + # mv t/logs/error_log t/logs/error_log_save + FAILERS="" + while read FAILER; do + FAILERS="$FAILERS $FAILER" + done < <(awk '/Failed:/{print $1}' test.log) + if [ -n "$FAILERS" ]; then + t/TEST -v $FAILERS || true + fi + # set -e would have killed us after the original t/TEST + rm -f test.log + # mv t/logs/error_log_save t/logs/error_log + false + fi + popd + fi -# Skip further testing if a core dump was created during the test -# suite run above. -if test $RV -eq 0 && test -n "`ls test/perl-framework/t/core{,.*} 2>/dev/null`"; then - RV=4 + # Skip further testing if a core dump was created during the test + # suite run above. + if test $RV -eq 0 && test -n "`ls test/perl-framework/t/core{,.*} 2>/dev/null`"; then + RV=4 + fi fi if test \( -v TEST_SSL -o -v TEST_OPENSSL3 \) \ @@ -179,9 +233,6 @@ ./t/TEST -defines "TEST_SSL_DES3_KEY TEST_SSL_PASSPHRASE_EXEC" t/ssl RV=$? - # Log the OpenSSL version. - grep 'mod_ssl.*compiled against' t/logs/error_log | tail -n 1 - # Test various session cache backends for cache in shmcb redis:localhost:6379 memcache:localhost:11211; do test $RV -eq 0 || break @@ -189,6 +240,13 @@ SSL_SESSCACHE=$cache ./t/TEST -sslproto TLSv1.2 -defines TEST_SSL_SESSCACHE -start ./t/TEST t/ssl RV=$? + if test $RV -eq 0; then + # TODO: only really useful in e.g. triggering + # server segfaults which are caught later, doesn't + # directly catch non-200 responses etc. + $builddir/support/ab -qd -n 4000 -c 20 -f TLS1.2 https://localhost:8532/ + RV=$? + fi ./t/TEST -stop SRV=$? if test $RV -eq 0 -a $SRV -ne 0; then @@ -214,35 +272,33 @@ grep -v ':\(debug\|trace[12345678]\)\]' test/perl-framework/t/logs/error_log fi -if test -v TEST_CORE -a $RV -eq 0; then - # Run HTTP/2 tests. - MPM=event py.test-3 test/modules/core - RV=$? -fi - -if test -v TEST_H2 -a $RV -eq 0; then - # Build the test clients - (cd test/clients && make) - # Run HTTP/2 tests. - MPM=event py.test-3 test/modules/http2 - RV=$? - if test $RV -eq 0; then - MPM=worker py.test-3 test/modules/http2 - RV=$? +if test -v TEST_PYTEST -a $RV -eq 0; then + # Run all available pytest-based test suites against this build, via + # the unified `make check-all-pytest` target: test/pytest_suite/ (the + # self-contained port of the classic Apache::Test suite, incl. PHP + # tests if PHP_FPM is set) and every test/modules/*/ suite (core, + # http1, http2, proxy, ssl, aaa, ...), in one pytest session. This + # replaces the old per-suite TEST_CORE / TEST_PROXY / TEST_H2 / + # TEST_MD flags, which each invoked py.test-3 directly against a + # `make install`ed tree; check-all-pytest instead builds and tests + # entirely from the in-tree check/ build, needing no install. This is + # the same on trunk and 2.4.x. + # + # Suites whose dependencies are missing skip themselves: modules/md + # without `pebble` on PATH (a job wanting md coverage must install + # pebble first, see README.ci), modules/http2 when mod_http2 wasn't + # built (e.g. the UBSan job's --disable-http2). + # + # A job can narrow this down with PYTEST_ARGS: test paths relative to + # test/ and any pytest options; see README.ci. + if test -v TEST_LDAP && ! test -v PYTEST_ARGS; then + # Only pytest_suite has LDAP coverage, and it needs the LDAP + # define to configure the mod_authnz_ldap vhosts. The other + # suites are covered by the pytest-specific jobs. + PYTEST_ARGS="--defines=LDAP pytest_suite/tests/t/modules/test_ldap.py" fi -fi - -if test -v TEST_MD -a $RV -eq 0; then - # Run ACME tests. - # need the go based pebble as ACME test server - # which is a package on debian sid, but not on focal - export GOPATH=${PREFIX}/gocode - mkdir -p "${GOPATH}" - export PATH="${GOROOT}/bin:${GOPATH}/bin:${PATH}" - go get -u github.com/letsencrypt/pebble/... - (cd $GOPATH/src/github.com/letsencrypt/pebble && go install ./...) - - py.test-3 test/modules/md + (cd test/clients && make) + make check-all-pytest PYTEST_ARGS="${PYTEST_ARGS:-}" RV=$? fi diff -Nru apache2-2.4.69/test/unit/authn.c apache2-2.4.71/test/unit/authn.c --- apache2-2.4.69/test/unit/authn.c 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/unit/authn.c 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,155 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +#include "../httpdunit.h" + +#include "httpd.h" +#include "http_config.h" +#include "http_protocol.h" + +/* + * Test Fixture -- runs once per test + */ + +static apr_pool_t *g_pool; +static request_rec *g_request; + +static void authn_setup(void) +{ + if (apr_pool_create(&g_pool, NULL) != APR_SUCCESS) { + exit(1); + } + + /* Stub out just enough of a request_req to get the tests working. + * Unfortunately this couples us to implementation details in the code being + * tested, but the logic to get a "real" request_rec requires spinning up + * half of the world. */ + g_request = apr_pcalloc(g_pool, sizeof(*g_request)); + if (!g_request) { + exit(1); + } + + g_request->pool = g_pool; + g_request->headers_in = apr_table_make(g_pool, 1); + + if (!g_request->headers_in) { + exit(1); + } +} + +static void authn_teardown(void) +{ + apr_pool_destroy(g_pool); +} + +/* + * ap_get_basic_auth_components() + */ + +static const char * const basic_auth_cases[][3] = { + /* + * case[0] - Authorization header value + * case[1] - expected username + * case[2] - expected password + */ + { "Basic Ym9iOm15cGFzcw==", "bob", "mypass" }, + { "Basic Ym9iOm15cGFzcw==", "bob", "mypass" }, + { "Basic Ym9iOg==", "bob", "" }, + { "Basic Om15cGFzcw==", "", "mypass" }, + { "Basic Og==", "", "" }, + + /* + * Dubious compatibility cases + */ + + /* HT is disallowed per 7235, but ap_get_basic_auth_pw() allowed it */ + { "Basic \tYm9iOm15cGFzcw==", "bob", "mypass" }, + /* username without colon separator, technically disallowed per 2617 */ + { "Basic Ym9i", "bob", "" }, + /* no data at all, technically disallowed per 2617/7235 */ + { "Basic ", "", "" }, + { "Basic", "", "" }, + /* completely invalid junk, disallowed per 2617/7235 */ + { "Basic ?*J#kd92%$@", "", "" }, +}; +static const size_t basic_auth_cases_len = sizeof(basic_auth_cases) / + sizeof(basic_auth_cases[0]); + +HTTPD_START_LOOP_TEST(test_get_basic_auth_components_correctly_decodes_credentials, basic_auth_cases_len) +{ + const char *header_val = basic_auth_cases[_i][0]; + const char *expected_user = basic_auth_cases[_i][1]; + const char *expected_pass = basic_auth_cases[_i][2]; + + apr_status_t status; + const char *username; + const char *password; + + apr_table_setn(g_request->headers_in, "Authorization", header_val); + + status = ap_get_basic_auth_components(g_request, &username, &password); + + ck_assert_int_eq(status, APR_SUCCESS); + ck_assert_str_eq(username, expected_user); + ck_assert_str_eq(password, expected_pass); +} +END_TEST + +START_TEST(test_get_basic_auth_components_fails_without_Authorization_header) +{ + apr_status_t status; + + status = ap_get_basic_auth_components(g_request, NULL, NULL); + ck_assert_int_eq(status, APR_EINVAL); +} +END_TEST + +START_TEST(test_get_basic_auth_components_fails_with_non_Basic_credentials) +{ + apr_status_t status; + + apr_table_setn(g_request->headers_in, "Authorization", + "Digest Ym9iOm15cGFzcw=="); + + status = ap_get_basic_auth_components(g_request, NULL, NULL); + ck_assert_int_eq(status, APR_EINVAL); +} +END_TEST + +START_TEST(test_get_basic_auth_components_uses_Proxy_Authorization_for_proxied_requests) +{ + apr_status_t status; + const char *username; + const char *password; + + g_request->proxyreq = PROXYREQ_PROXY; + apr_table_setn(g_request->headers_in, "Proxy-Authorization", + "Basic Ym9iOm15cGFzcw=="); + + status = ap_get_basic_auth_components(g_request, &username, &password); + + ck_assert_int_eq(status, APR_SUCCESS); + ck_assert_str_eq(username, "bob"); + ck_assert_str_eq(password, "mypass"); +} +END_TEST + +/* + * Test Case Boilerplate + */ +HTTPD_BEGIN_TEST_CASE_WITH_FIXTURE(authn, authn_setup, authn_teardown) +#include "test/unit/authn.tests" +HTTPD_END_TEST_CASE diff -Nru apache2-2.4.69/test/unit/base64.c apache2-2.4.71/test/unit/base64.c --- apache2-2.4.69/test/unit/base64.c 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/unit/base64.c 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,175 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +#include "../httpdunit.h" + +#include "httpd.h" + +/* + * Test Fixture -- runs once per test + */ + +static apr_pool_t *g_pool; + +static void base64_setup(void) +{ + if (apr_pool_create(&g_pool, NULL) != APR_SUCCESS) { + exit(1); + } +} + +static void base64_teardown(void) +{ + apr_pool_destroy(g_pool); +} + +/* + * Tests + */ + +/* + * case[0]: encoded value + * case[1]: expected decoded value + */ +static const char * const base64_cases[][2] = { + /* Test case 1 derived from RFC 3548 sec. 7. */ + { "FPucA9l+", "\x14\xfb\x9c\x03\xd9\x7e" }, + { "aGVsbG8=", "hello" }, + { "dGVzdA==", "test" }, + { "", "" }, +}; +static const size_t base64_cases_len = sizeof(base64_cases) / + sizeof(base64_cases[0]); + +HTTPD_START_LOOP_TEST(strict_decoding_works, base64_cases_len) +{ + const char *encoded = base64_cases[_i][0]; + const char *expected = base64_cases[_i][1]; + char *decoded; + apr_size_t len; + apr_status_t status; + + status = ap_pbase64decode_strict(g_pool, encoded, &decoded, &len); + + ck_assert_int_eq(status, APR_SUCCESS); + ck_assert_uint_eq(len, strlen(expected)); + ck_assert(!memcmp(decoded, expected, len)); +} +END_TEST + +START_TEST(strict_decoding_works_with_embedded_nulls) +{ + static unsigned char expected[] = { 'n', '\0', 'u', '\0', 'l', '\0', 'l' }; + char *decoded; + apr_size_t len; + apr_status_t status; + + status = ap_pbase64decode_strict(g_pool, "bgB1AGwAbA==", &decoded, &len); + + ck_assert_int_eq(status, APR_SUCCESS); + ck_assert_uint_eq(len, sizeof(expected)); + ck_assert(!memcmp(decoded, expected, len)); +} +END_TEST + +START_TEST(strict_decoding_produces_null_terminated_buffer) +{ + char *decoded; + apr_size_t len; + + ap_pbase64decode_strict(g_pool, "aaaabbbbccccdddd", &decoded, &len); + + ck_assert(decoded[len] == '\0'); +} +END_TEST + +START_TEST(strict_decoding_allows_all_base64_characters) +{ + char *decoded; + apr_size_t len; + apr_status_t status; + + status = ap_pbase64decode_strict(g_pool, + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/", + &decoded, &len);; + + ck_assert_int_eq(status, APR_SUCCESS); +} +END_TEST + +static const char * const invalid_chars[] = { + "bad?", + "not-good", + "also_bad", + "a\x01sd", +}; +static const size_t invalid_chars_len = sizeof(invalid_chars) / + sizeof(invalid_chars[0]); + +HTTPD_START_LOOP_TEST(strict_decoding_rejects_non_base64_characters, invalid_chars_len) +{ + char *decoded = NULL; + apr_size_t len = (apr_size_t) -1; + apr_status_t status; + + status = ap_pbase64decode_strict(g_pool, invalid_chars[_i], &decoded, &len); + + ck_assert_int_eq(status, APR_EINVAL); + ck_assert(decoded == NULL); + ck_assert_uint_eq(len, (apr_size_t) -1); +} +END_TEST + +static const char * const invalid_padding[] = { + "AAAA=", /* no padding needed here */ + "AA=", /* not enough padding */ + "AA===", /* too much padding */ + "A===", /* only one or two padding characters allowed */ + "A==", + "==", + "AAA=AAA=", /* mid-string padding prohibited */ + "AAA", /* missing padding entirely */ + "AA", /* missing padding entirely */ + "A", /* just completely wrong */ + "AAb=", /* one-padded strings must end in one of AEIMQUYcgkosw048 */ + "Ab==", /* two-padded strings must end in one of AQgw */ +}; +static const size_t invalid_padding_len = sizeof(invalid_padding) / + sizeof(invalid_padding[0]); + +HTTPD_START_LOOP_TEST(strict_decoding_rejects_incorrect_padding, invalid_padding_len) +{ + char *decoded = NULL; + apr_size_t len = (apr_size_t) -1; + apr_status_t status; + + status = ap_pbase64decode_strict(g_pool, invalid_padding[_i], &decoded, + &len); + + ck_assert_int_eq(status, APR_EINVAL); + ck_assert(decoded == NULL); + ck_assert_uint_eq(len, (apr_size_t) -1); +} +END_TEST + +/* + * Test Case Boilerplate + */ +HTTPD_BEGIN_TEST_CASE_WITH_FIXTURE(base64, base64_setup, base64_teardown) +#include "test/unit/base64.tests" +HTTPD_END_TEST_CASE diff -Nru apache2-2.4.69/test/unit/util.c apache2-2.4.71/test/unit/util.c --- apache2-2.4.69/test/unit/util.c 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/unit/util.c 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,226 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +#include "../httpdunit.h" + +#include "httpd.h" +#include "apr_strings.h" + +/* + * Test Fixture -- runs once per test + */ + +static apr_pool_t *g_pool; + +static void util_setup(void) +{ + if (apr_pool_create(&g_pool, NULL) != APR_SUCCESS) { + exit(1); + } +} + +static void util_teardown(void) +{ + apr_pool_destroy(g_pool); +} + +/* + * ap_test_token() + */ + +struct ap_test_token_case { + const char *list; + const char *token; + int expected; +}; + +const struct ap_test_token_case ap_test_token_cases[] = { + { "one, two, three", "one", 1 }, + { "one, two, three", "two", 1 }, + { "one, two, three", "three", 1 }, + { "one,two,three", "two", 1 }, + { NULL, "token", 0 }, + + /* Regression test for CVE-2017-7668 */ + { "one, two, \0three", "three", 0 }, + + /* + * Dubious compatibility cases + */ + { ",\x01,one,,two,/,,three,,", "one", 1 }, + { ",\x01,one,,two,/,,three,,", "two", 1 }, + { ",\x01,one,,two,/,,three,,", "three", 1 }, + { ",\x01,one,,two,/,,three,,", "\x01", 0 }, + { ",\x01,one,,two,/,,three,,", "/", 0 }, +}; + +const size_t ap_test_token_cases_len = sizeof(ap_test_token_cases) / + sizeof(ap_test_token_cases[0]); + +HTTPD_START_LOOP_TEST(find_token_correctly_parses_token_list, ap_test_token_cases_len) +{ + const struct ap_test_token_case *c = &ap_test_token_cases[_i]; + int result; + + result = ap_find_token(g_pool, c->list, c->token); + ck_assert_int_eq(result, c->expected); +} +END_TEST + + +/* + * ap_cstr_stripws() + */ + +struct ap_cstr_stripws_case { + const char *input; + const char *expected; +}; + +const struct ap_cstr_stripws_case ap_cstr_stripws_cases[] = { + { "", "" }, + { " ", "" }, + { "\t\r\n", "" }, + { "value", "value" }, + { "value ", "value" }, + { "value\t ", "value" }, + { " value ", " value" }, +}; + +const size_t ap_cstr_stripws_cases_len = sizeof(ap_cstr_stripws_cases) / + sizeof(ap_cstr_stripws_cases[0]); + +HTTPD_START_LOOP_TEST(cstr_stripws_strips_trailing_whitespace, ap_cstr_stripws_cases_len) +{ + const struct ap_cstr_stripws_case *c = &ap_cstr_stripws_cases[_i]; + char *str = apr_pstrdup(g_pool, c->input); + char *end = ap_cstr_stripws(str); + + ck_assert_str_eq(str, c->expected); + ck_assert_ptr_eq(end, str + strlen(c->expected)); +} +END_TEST + + +/* + * ap_escape_quotes() + */ + +struct ap_escape_quotes_case { + const char *input; + const char *expected; +}; + +#define ESCQ "\\\"" + +const struct ap_escape_quotes_case ap_test_escape_quotes_cases[] = { + { "one", "one" }, + { "o\"ne", "o"ESCQ"ne" }, + { "o"ESCQ"ne", "o"ESCQ"ne" }, + { "one\\", "one\\" }, + { "o\\ne", "o\\ne" }, + { "o\\"ESCQ"ne", "o\\\\"ESCQ"ne" }, /* 1st \ escapes 2nd, following '"' needs \ */ +}; + +const size_t ap_test_escape_quotes_cases_len = sizeof(ap_test_escape_quotes_cases) / + sizeof(ap_test_escape_quotes_cases[0]); + +HTTPD_START_LOOP_TEST(check_escape_quotes, ap_test_escape_quotes_cases_len) +{ + const struct ap_escape_quotes_case *c = &ap_test_escape_quotes_cases[_i]; + const char *result; + + result = ap_escape_quotes(g_pool, c->input); + ck_assert_str_eq(result, c->expected); +} +END_TEST + + +/* + * ap_size_parameter_parse() + */ + +struct ap_size_parameter_parse_case { + const char *input; + apr_status_t expected_rv; + apr_int64_t expected; /* for a 64-bit apr_off_t */ +}; + +const struct ap_size_parameter_parse_case ap_size_parameter_parse_cases[] = { + { "0", APR_SUCCESS, 0 }, + { "1", APR_SUCCESS, 1 }, + { "-1", APR_SUCCESS, -1 }, + { "1024", APR_SUCCESS, 1024 }, + { "0b", APR_SUCCESS, 0 }, + { "1b", APR_SUCCESS, 1 }, + { "512B", APR_SUCCESS, 512 }, + { "1k", APR_SUCCESS, 1024 }, + { "1K", APR_SUCCESS, 1024 }, + { "10k", APR_SUCCESS, 10240 }, + { "1m", APR_SUCCESS, 1024 * 1024 }, + { "2M", APR_SUCCESS, 2 * 1024 * 1024 }, + { "1g", APR_SUCCESS, 1024 * 1024 * 1024 }, + { "4G", APR_SUCCESS, APR_INT64_C(4) * 1024 * 1024 * 1024 }, + { "-2k", APR_SUCCESS, -2048 }, + { "", APR_EINVAL, 0 }, + { "k", APR_EINVAL, 0 }, + { "-", APR_EINVAL, 0 }, + { "1t", APR_EINVAL, 0 }, + { "1kb", APR_EINVAL, 0 }, + { "1 k", APR_EINVAL, 0 }, + { "1k ", APR_EINVAL, 0 }, + { "2 ", APR_EINVAL, 0 }, + { "0x10", APR_EINVAL, 0 }, + { "1.5k", APR_EINVAL, 0 }, + /* overflows whether apr_off_t is 32 or 64 bits wide */ + { "99999999999999999999", APR_ERANGE, 0 }, + { "9223372036854775807k", APR_ERANGE, 0 }, + { "9007199254740992m", APR_ERANGE, 0 }, + { "-9007199254740992m", APR_ERANGE, 0 }, + { "8589934592g", APR_ERANGE, 0 }, +}; + +const size_t ap_size_parameter_parse_cases_len = + sizeof(ap_size_parameter_parse_cases) / + sizeof(ap_size_parameter_parse_cases[0]); + +HTTPD_START_LOOP_TEST(size_parameter_parse_handles_suffixes, ap_size_parameter_parse_cases_len) +{ + const struct ap_size_parameter_parse_case *c = &ap_size_parameter_parse_cases[_i]; + apr_status_t expected_rv = c->expected_rv; + apr_off_t value = 0; + apr_status_t rv; + + /* A value which does not fit a narrower apr_off_t is a range error. */ + if (expected_rv == APR_SUCCESS && (apr_off_t)c->expected != c->expected) { + expected_rv = APR_ERANGE; + } + + rv = ap_size_parameter_parse(c->input, &value); + ck_assert_int_eq(rv, expected_rv); + if (expected_rv == APR_SUCCESS) { + ck_assert_int_eq(value, (apr_off_t)c->expected); + } +} +END_TEST + + +/* + * Test Case Boilerplate + */ +HTTPD_BEGIN_TEST_CASE_WITH_FIXTURE(util, util_setup, util_teardown) +#include "test/unit/util.tests" +HTTPD_END_TEST_CASE diff -Nru apache2-2.4.69/test/uv.lock apache2-2.4.71/test/uv.lock --- apache2-2.4.69/test/uv.lock 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.71/test/uv.lock 2026-11-09 03:07:05.000000000 +0000 @@ -0,0 +1,536 @@ +version = 1 +revision = 5 +requires-python = ">=3.11" + +[[package]] +name = "anyio" +version = "4.15.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.15'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/d2/f4d173e22df740bc37b1db102b386ba719b66e95b0f0d751f556b387e6d2/anyio-4.15.1.tar.gz", hash = "sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94", size = 276966, upload-time = "2026-09-05T10:42:39.44Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl", hash = "sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101", size = 132079, upload-time = "2026-09-05T10:42:37.923Z" }, +] + +[[package]] +name = "certifi" +version = "2026.10.23" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/11/b524cbd9bbdaca58608e4cf4053fd463417549e1959fa8d3aa18319f9555/certifi-2026.10.23.tar.gz", hash = "sha256:a9895e6e2758b371e0545e321a3a8fd1fcfd9829ada8824a936dfb16aaaf36cd", size = 136225, upload-time = "2026-10-23T11:36:16.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d1/a3652fe2c19134d0d15922177821d863cbd7e842d53a21bbe7e8e3a1b5ac/certifi-2026.10.23-py3-none-any.whl", hash = "sha256:e9b54de76e530bb6cf0ccd036787103acb58fd42ba930ea8bf948ddbaa2f61d1", size = 135082, upload-time = "2026-10-23T11:36:14.763Z" }, +] + +[[package]] +name = "cffi" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/70/d2/16d99a0c4948febc0ebd133a13b2f688ff7f8cb04da971e1128872ce0c03/cffi-2.1.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:c8d2c9fd1f2d16f780d15127abb050d13d1a76c03a4bd87d7e4980e45e511e12", size = 183838, upload-time = "2026-08-03T21:19:29.637Z" }, + { url = "https://files.pythonhosted.org/packages/cd/95/31b535a9f0220ae9f357de4a08d57ce89cb417653c2fd9f075f50822a388/cffi-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:398aff33cee2767e3e781d2554c54bd0dff386bb437581e0d8011fde1a942ec1", size = 184168, upload-time = "2026-08-03T21:19:30.764Z" }, + { url = "https://files.pythonhosted.org/packages/ad/5a/4707a0dc1f203f5dde5a907b0d4e3c25d71120241048bd5bc6f1bb9d4e71/cffi-2.1.1-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:154852545011f779917b11c78db2358d095da62a9a172b78ad0a583ee5adc0d0", size = 211805, upload-time = "2026-08-03T21:19:31.867Z" }, + { url = "https://files.pythonhosted.org/packages/ad/66/c19feabb28485b6e0bbaaafa90837a1ef5d302e90f2178bd33f17a49879b/cffi-2.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3311ed60d36f83378794e1009ac6258bafbf81f7888b4caa7b35a521e3f95813", size = 218716, upload-time = "2026-08-03T21:19:32.896Z" }, + { url = "https://files.pythonhosted.org/packages/a7/92/500760486c8baab49a7a8a58ba7fc3355ec3974b454b8a09e528efde9e1d/cffi-2.1.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6e192623c49c94421616a5778fba35cf0d5a8d000650c1967ef4448ee5cdd990", size = 205569, upload-time = "2026-08-03T21:19:34.142Z" }, + { url = "https://files.pythonhosted.org/packages/a5/a7/a67c733254d6e7373f7822f8082d8d6beade791e0cf12a7611f376fa61c7/cffi-2.1.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a6e721d4b0e45d5b65e87534470e67b18dcd092c83f68fba09f152b9cbc061af", size = 204907, upload-time = "2026-08-03T21:19:35.174Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a4/4399daaf8f7dfee9d7c3327fdb0426ee041cc63edc358b93911ceb2bfc7a/cffi-2.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34e261f78cb6ceaaa36f42f2613f4380d94d9c759a9c73c769ee6e0247364632", size = 217807, upload-time = "2026-08-03T21:19:36.286Z" }, + { url = "https://files.pythonhosted.org/packages/28/f7/dabe6da2466ecbd82dc62e7342dc6b1065dad990c06f00f0ede9ebf2a0ed/cffi-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7225e4514edb64eb6740324353e0da0711954fd8d7da4576755b1c6e09b697cd", size = 221252, upload-time = "2026-08-03T21:19:37.416Z" }, + { url = "https://files.pythonhosted.org/packages/ce/87/616202d8e51342c07d2534c510111c4cc37201775ce8f60802c9335d1edd/cffi-2.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df913725b79db7bcf03448f36b7bf8815363417d5b58deecf9305e3e30f0f21a", size = 214214, upload-time = "2026-08-03T21:19:38.507Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c6/ab025d75d2c26c19b087c0124e75ee31cb65032f4fe345d356d8c507ab97/cffi-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f5cfbc5fe74540d335175b656c725d74d90e3730c626d92575eea35029d9afaa", size = 219408, upload-time = "2026-08-03T21:19:39.809Z" }, + { url = "https://files.pythonhosted.org/packages/db/e2/7e8109f65445bdc673a7b54f02c677de462db75674220fd1335efc8eb598/cffi-2.1.1-cp311-cp311-win32.whl", hash = "sha256:f8ec5e643a9a937f64e1999eb9f75d072263751912dc5cd06d3c85f8f44be7c3", size = 174470, upload-time = "2026-08-03T21:19:41.246Z" }, + { url = "https://files.pythonhosted.org/packages/73/c0/77ba02423c2f7d7091143c45cd49e0e6575c4c1967394bb542bd923a9b74/cffi-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:42f6930c31dc7f50732c9ae793c2786c7b6b044195967bbdde40bb9be81c4cc0", size = 185096, upload-time = "2026-08-03T21:19:42.615Z" }, + { url = "https://files.pythonhosted.org/packages/7c/47/9f1f85f9672ceda4984dc6c4f8824e8558992a2972c3d3c81fb8eb28d4ba/cffi-2.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:c7659f22557c5a0bc4855cd635f55edec690cc008a40768527762cb9fb263455", size = 179941, upload-time = "2026-08-03T21:19:43.747Z" }, + { url = "https://files.pythonhosted.org/packages/10/69/43965eccfdead3b9220015fd1320e117be8c6ed01a62ffab76eeb752f5d5/cffi-2.1.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0", size = 184821, upload-time = "2026-08-03T21:19:44.887Z" }, + { url = "https://files.pythonhosted.org/packages/54/7d/16e5a096677b5e313ca80cd5e5170efa3ea44624a82bb111925522da64b1/cffi-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf", size = 184719, upload-time = "2026-08-03T21:19:46.129Z" }, + { url = "https://files.pythonhosted.org/packages/56/e6/8941622732edec876dd17d0453dce07317ae96db34f2ec1436c9d3785986/cffi-2.1.1-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a", size = 214799, upload-time = "2026-08-03T21:19:47.218Z" }, + { url = "https://files.pythonhosted.org/packages/44/de/f98430906df1545ffde0d543dd124a7a439bc2cd32b36b9c53f805df7333/cffi-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890", size = 222389, upload-time = "2026-08-03T21:19:48.331Z" }, + { url = "https://files.pythonhosted.org/packages/6a/5b/717f1526b9957b34456313c31645c5b82b8fb5c3fe9e4752999be7128bfc/cffi-2.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50", size = 210249, upload-time = "2026-08-03T21:19:49.543Z" }, + { url = "https://files.pythonhosted.org/packages/64/b3/f8aa4f3e34986c7e4ec45072d1b1b9dd295b6b18007b45518d79726dd725/cffi-2.1.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e", size = 208775, upload-time = "2026-08-03T21:19:50.918Z" }, + { url = "https://files.pythonhosted.org/packages/b1/db/dceb9dd5b231e1da801793f8acc9f3c52a7e1afe40bb1aae37e02b0faad5/cffi-2.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf", size = 221822, upload-time = "2026-08-03T21:19:52.054Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d2/6cd24ae3be000a634109c247d1475d62e5616d0dc78c82770942ec384248/cffi-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517", size = 225232, upload-time = "2026-08-03T21:19:53.109Z" }, + { url = "https://files.pythonhosted.org/packages/cb/52/3fa190537004dd7f0ab860a6dc7c0175b8667f68d1e618a46f5498d30250/cffi-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735", size = 223597, upload-time = "2026-08-03T21:19:54.515Z" }, + { url = "https://files.pythonhosted.org/packages/80/fb/0bb75b7039588c074b37ae99f40d9bfddf990ecb2fbc346ebccd2e56b9be/cffi-2.1.1-cp312-cp312-win32.whl", hash = "sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e", size = 175292, upload-time = "2026-08-03T21:19:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/d9/79/615cc094e2fb508cade7de88d3b4f6c4ec2bab695c97bce9153dc65aadf5/cffi-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a", size = 185919, upload-time = "2026-08-03T21:19:56.89Z" }, + { url = "https://files.pythonhosted.org/packages/70/c6/d0ea84713fe46b243a436a18fcd47d639732747e21635c8a27191b06dc30/cffi-2.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80", size = 180093, upload-time = "2026-08-03T21:19:58.155Z" }, + { url = "https://files.pythonhosted.org/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e", size = 194248, upload-time = "2026-08-03T21:19:59.399Z" }, + { url = "https://files.pythonhosted.org/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c", size = 196908, upload-time = "2026-08-03T21:20:00.746Z" }, + { url = "https://files.pythonhosted.org/packages/a7/46/2e5fdde8555706dd98139a910ca11be02809f3f605ce956f655d0214e100/cffi-2.1.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6", size = 184805, upload-time = "2026-08-03T21:20:02.02Z" }, + { url = "https://files.pythonhosted.org/packages/55/41/4c7042f317b9217502988f0873af87e16ad606dc20f84e546e3e6ce9764c/cffi-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971", size = 184764, upload-time = "2026-08-03T21:20:03.141Z" }, + { url = "https://files.pythonhosted.org/packages/43/1f/1c3d90d91811c8f86ced9ed637956c54bfe5b79ca98fe976d7f8c8979f6b/cffi-2.1.1-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c", size = 214722, upload-time = "2026-08-03T21:20:04.377Z" }, + { url = "https://files.pythonhosted.org/packages/37/6f/3b5ce4c3b2192d250f04908f2bfd91ef34552ec8f7716a5d4abdb8d67bb2/cffi-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125", size = 222369, upload-time = "2026-08-03T21:20:05.544Z" }, + { url = "https://files.pythonhosted.org/packages/02/10/4b3c75dde3d9663c9e02ba05c2668b954f671d4bbe346413ca8c696b295a/cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264", size = 210175, upload-time = "2026-08-03T21:20:06.75Z" }, + { url = "https://files.pythonhosted.org/packages/df/62/14f74b9543e605d17701dc797b815958b8bb70b7624ce1b832ddad48ed6c/cffi-2.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3", size = 208670, upload-time = "2026-08-03T21:20:08.04Z" }, + { url = "https://files.pythonhosted.org/packages/95/95/86342356ff5953b3fb06f7ef7c5bee212d45e770abc7218d451b9148313c/cffi-2.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2", size = 221824, upload-time = "2026-08-03T21:20:09.274Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ff/7b3429ff53aafe931ed8a5fc69f481bbef7ba6de87ddcbb63d08f483f613/cffi-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b", size = 225148, upload-time = "2026-08-03T21:20:10.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/34/a95870b9221e09cf4f2ce3178b1a210abdfe63a1bd357da940418d7b8d15/cffi-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7", size = 223564, upload-time = "2026-08-03T21:20:12.165Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/839b50531021a647fb5e929f72cf97bc1ff702b5472166164b5b6e76b851/cffi-2.1.1-cp313-cp313-win32.whl", hash = "sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac", size = 175263, upload-time = "2026-08-03T21:20:13.559Z" }, + { url = "https://files.pythonhosted.org/packages/60/a6/8b149b2c3f2e11aaa1618ef64500b45f50f22c57a977a4dff1aff1f91042/cffi-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d", size = 185688, upload-time = "2026-08-03T21:20:14.69Z" }, + { url = "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", size = 180078, upload-time = "2026-08-03T21:20:15.917Z" }, + { url = "https://files.pythonhosted.org/packages/d3/7b/d6bbf82b8b96e7391438898c42f5bd96dd02030fd5b64937d248220003e2/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c", size = 194064, upload-time = "2026-08-03T21:20:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/94/e6/bcc91b283be94735e268487a054004f0aa19947b6348fa367db53230abc8/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb", size = 196720, upload-time = "2026-08-03T21:20:18.268Z" }, + { url = "https://files.pythonhosted.org/packages/d9/99/c4b0c17cacdc9c3b8f280026286a9826d6a208c0f047591a3c3ce99b91fd/cffi-2.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54", size = 184964, upload-time = "2026-08-03T21:20:19.708Z" }, + { url = "https://files.pythonhosted.org/packages/b3/a9/9db617d05d7367c1ad0ab00b3aa6e6f9281edd689b4ee9ea0e5a84e89c97/cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72", size = 184962, upload-time = "2026-08-03T21:20:20.833Z" }, + { url = "https://files.pythonhosted.org/packages/67/b8/b42132ca113dc567d37684437b46ca1dafc885902b02a110a02d5b511857/cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1", size = 222328, upload-time = "2026-08-03T21:20:22.118Z" }, + { url = "https://files.pythonhosted.org/packages/80/10/c5c0cbf0a657aecf59ef511409734230bf556f05a0d6c9eed7aa5c0a0166/cffi-2.1.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062", size = 209985, upload-time = "2026-08-03T21:20:23.401Z" }, + { url = "https://files.pythonhosted.org/packages/d5/6c/bfa0b87b03b9238148beca990292843c9396ba069b54496596594173de7b/cffi-2.1.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03", size = 208530, upload-time = "2026-08-03T21:20:24.628Z" }, + { url = "https://files.pythonhosted.org/packages/e9/02/4e7d553a7ac4b4238b38b3c1b80d486e9d4436f8d2acbf87a0997fe3f402/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96", size = 221525, upload-time = "2026-08-03T21:20:25.758Z" }, + { url = "https://files.pythonhosted.org/packages/82/1d/a4aaf9babd75acb4d5f223bff71533bee748dd770a382619a798960ee9ba/cffi-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527", size = 225053, upload-time = "2026-08-03T21:20:26.985Z" }, + { url = "https://files.pythonhosted.org/packages/81/10/5dc0e7bdd18e22107054288283380fc97a06ae3f1656a106908d666a3c88/cffi-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13", size = 223213, upload-time = "2026-08-03T21:20:28.277Z" }, + { url = "https://files.pythonhosted.org/packages/0b/e9/d0061c364cde06ee43168a0d076ac1da512cbc380d44767b844ba34fe2b6/cffi-2.1.1-cp314-cp314-win32.whl", hash = "sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c", size = 177682, upload-time = "2026-08-03T21:20:44.288Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1c3e01e3ba14c39f6d10bfbac52753b7e22259e38088e5cfe1d704918690/cffi-2.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48", size = 187949, upload-time = "2026-08-03T21:20:45.623Z" }, + { url = "https://files.pythonhosted.org/packages/87/5b/da4e39efe18eeb89cf580ea9cfc66b6a7c3eadb808fc0cc1d3a295cb5a5d/cffi-2.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836", size = 182947, upload-time = "2026-08-03T21:20:46.955Z" }, + { url = "https://files.pythonhosted.org/packages/23/59/40338bf421c5accea1d45158170c87006ef1cd371b05c077e76476949728/cffi-2.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3", size = 188504, upload-time = "2026-08-03T21:20:29.495Z" }, + { url = "https://files.pythonhosted.org/packages/7d/47/5ecf1023850036e674c77ec4de86182d309ae344e39e7cba984b7df5d647/cffi-2.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2", size = 188259, upload-time = "2026-08-03T21:20:31.291Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9c/92934c3bea9f785b23eba304538c0b4d37a2a96d2431eb3a1bc87a11aa19/cffi-2.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94", size = 223864, upload-time = "2026-08-03T21:20:32.571Z" }, + { url = "https://files.pythonhosted.org/packages/4d/45/ba4c93527bc38616a8bd36488acb69a2212d60486794f0c1f318949bbb76/cffi-2.1.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc", size = 211538, upload-time = "2026-08-03T21:20:33.808Z" }, + { url = "https://files.pythonhosted.org/packages/80/e9/b6ef565e452acb932fb0cb5443f44a78efbd1233e566f02b5a83855e9115/cffi-2.1.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29", size = 210688, upload-time = "2026-08-03T21:20:34.974Z" }, + { url = "https://files.pythonhosted.org/packages/9a/95/eff5f0cee78d2eabc7eebffec40d3fc1876b5f3c95582e018bb4b99601f2/cffi-2.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676", size = 223803, upload-time = "2026-08-03T21:20:36.564Z" }, + { url = "https://files.pythonhosted.org/packages/fa/01/579d39fb8bef00a335a23d83757b44feb24cd6345a2c451b64cb67b9c362/cffi-2.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e", size = 226763, upload-time = "2026-08-03T21:20:37.816Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b0/0b44f47c60b01b57b6e2bbd92343f13a85a1d93bc46ccf6e47e244acd99c/cffi-2.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f", size = 225688, upload-time = "2026-08-03T21:20:38.959Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d2/3b7176cb570a1d3e27faf67b72f591af508036e0d8b2be2ef9af9e8c84bb/cffi-2.1.1-cp314-cp314t-win32.whl", hash = "sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4", size = 182868, upload-time = "2026-08-03T21:20:40.388Z" }, + { url = "https://files.pythonhosted.org/packages/56/78/31f00c1bcd97c9bbf55f1bfdf5bc809a5de8887473e90bb9960dca825e80/cffi-2.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e", size = 194104, upload-time = "2026-08-03T21:20:41.725Z" }, + { url = "https://files.pythonhosted.org/packages/7b/1b/58496f2ed0a35de575250c02a43ab3cc2c04d494a88fed31c1cabc0fd176/cffi-2.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5", size = 186402, upload-time = "2026-08-03T21:20:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/c1/8f/9ebe220eab48a093d1a5a5e339ab0dc7316eef3bb04d63c42f0251b61f50/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:dddad92b554513a31f272570678ba307fb9f618f05e3d4a5eacafff9eae03e1d", size = 194043, upload-time = "2026-08-03T21:20:48.179Z" }, + { url = "https://files.pythonhosted.org/packages/ff/69/844bad3ece306c4782c2ecb93597035b6690d48704b803914c199da1e8b3/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:da0e573f9f97159390c89d9f1a9e41908b66d408cc5b58d08cf3847d844c531b", size = 196737, upload-time = "2026-08-03T21:20:49.457Z" }, + { url = "https://files.pythonhosted.org/packages/1b/8a/af668013284634733f02d683458a0728739c7d6ddb5e14cb0c20832266fe/cffi-2.1.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:fb92203a88b3d3053034db775110081c49d28be6551923805e039924093761e4", size = 184933, upload-time = "2026-08-03T21:20:50.639Z" }, + { url = "https://files.pythonhosted.org/packages/0c/75/2f5207ff6d1a613133b23a5203cc0c2a628313b5eb3974d7956ae3c57950/cffi-2.1.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2ae64be792b8966f2c69538199728b290e34726562896df1e5dc8ffd8d8188e8", size = 185002, upload-time = "2026-08-03T21:20:52.173Z" }, + { url = "https://files.pythonhosted.org/packages/e2/31/9e1313b0a6e30e91b3b3d3fff51ae99c857c07738e3afcce1f7334e1b7ab/cffi-2.1.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:507a24c282e0f42f8ed737cf048572cbf580468da5555764a8331735e9c736b6", size = 222271, upload-time = "2026-08-03T21:20:53.462Z" }, + { url = "https://files.pythonhosted.org/packages/50/e3/f6234a833e6e08c7007003074723c406559eecf9b48dfc97471e5a8eb7a0/cffi-2.1.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:246fa40ce8645a614ff682e0b70f37134e460eaf93a775e0cbe3cca585a67a80", size = 209919, upload-time = "2026-08-03T21:20:54.783Z" }, + { url = "https://files.pythonhosted.org/packages/0d/fc/5f74e293fced6edb51af3a46c4ccf6c23c9943774ecb375ddbd522c76add/cffi-2.1.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:471cee653ae88de62096552e6d24ccb4a5adb8c8c9f10b5054d0122c15bf2779", size = 208529, upload-time = "2026-08-03T21:20:56.066Z" }, + { url = "https://files.pythonhosted.org/packages/44/16/29e6d01b388bef055ecd6ca8244b3f4d336bd09e92d5d892187b9601084e/cffi-2.1.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aeae0e330c9f6acd681f647d46cefd30c29f93e3392882e792e82080c9691399", size = 221630, upload-time = "2026-08-03T21:20:57.336Z" }, + { url = "https://files.pythonhosted.org/packages/a4/18/fa7f1f6857d5eb88a4ca99ffcbfb7c387a287ccc154c64a73e86314745d7/cffi-2.1.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:42a494cee34437f05546455144f2b5d9ac09b1face62bcfce597d2e521066688", size = 225134, upload-time = "2026-08-03T21:20:58.675Z" }, + { url = "https://files.pythonhosted.org/packages/e0/9f/e8e3dfa04a1b4c241f8c91faacad872b4d4efd051d49764ad4e2fd4b9fea/cffi-2.1.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:cc572dace3f60ef98d7b12ff411d20f5362feb31a0439eab0085bbfd349982d7", size = 223197, upload-time = "2026-08-03T21:20:59.968Z" }, + { url = "https://files.pythonhosted.org/packages/f8/7e/8debeb04f1ab9fe2a6963964cd6f1aaf7192627b83926586a6a4e089c9fa/cffi-2.1.1-cp315-cp315-win32.whl", hash = "sha256:4f42141fc14250de6dde5ee7ea4432be017252d91f19c5ad043c084cea629cac", size = 177683, upload-time = "2026-08-03T21:21:14.901Z" }, + { url = "https://files.pythonhosted.org/packages/e0/31/5158704cc474ab65c1647932e88be78dc0873f47130e253be38bcaf13d01/cffi-2.1.1-cp315-cp315-win_amd64.whl", hash = "sha256:e6e8cff14d6fb0be70a09c0bdc58096f501952d04624ebf867e0e56da2df8960", size = 187897, upload-time = "2026-08-03T21:21:16.108Z" }, + { url = "https://files.pythonhosted.org/packages/cc/4b/b3a2da8570c704ffc0f9762cdc3ec0f02c8573798e0b5cf7f11c82bbb70f/cffi-2.1.1-cp315-cp315-win_arm64.whl", hash = "sha256:27350daa11d4f10c540e6e89dada4c54feb7256ad03e9a4dc075ebad7ba360d1", size = 182935, upload-time = "2026-08-03T21:21:17.271Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ef/5443574510a1207e6f6bc38ba6e1f1de36cb48fef07b2728bb896a21f430/cffi-2.1.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c26608d2222fb1e94487e4a387d85f13eb55d5ed725cb25a0c589ac4ee60e7bc", size = 188464, upload-time = "2026-08-03T21:21:01.163Z" }, + { url = "https://files.pythonhosted.org/packages/7e/ae/a56fa8c4686ad50e148fcbc8d3ae0d03915ff5c30d795058988c24118cef/cffi-2.1.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:4be96343e422f2dfcd12ab5c9f5aebe03f82f737c6bffeca6830b3875cb44aab", size = 188262, upload-time = "2026-08-03T21:21:02.382Z" }, + { url = "https://files.pythonhosted.org/packages/53/b2/6187f46f2912276a3ae284076109cc5c8680482f11f766ccf26db4a86427/cffi-2.1.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:937c0052c05a31ca1daf18de3158eed4dbfcb9cc107adbea227728d647be701e", size = 223779, upload-time = "2026-08-03T21:21:03.553Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f6/c3ad28bd19f77047a03084424fbd4cbe997303267c14423737324be0385d/cffi-2.1.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:df423d40ee8654634421812bc3b196da3f9bd7d32929da813f8394c4348a5358", size = 211520, upload-time = "2026-08-03T21:21:04.863Z" }, + { url = "https://files.pythonhosted.org/packages/a0/cd/ccac9013a5bd9fd764de118674ab9c805b5ca10c19270d90ee273f8b2240/cffi-2.1.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a730a083190634c65cca36ba5f489531576ebd79bcd5c8e172130f6453127231", size = 210673, upload-time = "2026-08-03T21:21:06.223Z" }, + { url = "https://files.pythonhosted.org/packages/52/86/2976131c639aead931c5bee5aba67e4b09fbeb8018b6f282f70803f923a7/cffi-2.1.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:363e05fa78e15116c3c32c210ee36884fd6b9afa6d440e47112c3bd511d64cb6", size = 223835, upload-time = "2026-08-03T21:21:07.539Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0c/33a7aeab2f9c76918c52e084beb39c570db3588133412929e8ec06fab90b/cffi-2.1.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:770de9db11e84213beec501cfcaa013b019820ca881e03344dea5844f7876d94", size = 226705, upload-time = "2026-08-03T21:21:08.774Z" }, + { url = "https://files.pythonhosted.org/packages/e3/26/2cde30fdde421130bfc18f70395731a6e6b2053c6a1978a5258ff04e72fa/cffi-2.1.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5", size = 225539, upload-time = "2026-08-03T21:21:09.911Z" }, + { url = "https://files.pythonhosted.org/packages/6d/cd/a361394c94b2129d604bb846f624a8e88255a3ee33129c434a00d715e64f/cffi-2.1.1-cp315-cp315t-win32.whl", hash = "sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66", size = 182707, upload-time = "2026-08-03T21:21:11.226Z" }, + { url = "https://files.pythonhosted.org/packages/9b/b5/ba2b299993c26577d529b6ae29841f9e15b9fcf004d65f423f4fcf94ade9/cffi-2.1.1-cp315-cp315t-win_amd64.whl", hash = "sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3", size = 193772, upload-time = "2026-08-03T21:21:12.39Z" }, + { url = "https://files.pythonhosted.org/packages/aa/29/35e016098c814cd93de9cd320c66b5bfba14dc6ecedd3cb518fa7c408c69/cffi-2.1.1-cp315-cp315t-win_arm64.whl", hash = "sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692", size = 186360, upload-time = "2026-08-03T21:21:13.636Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "cryptography" +version = "50.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/af/182eb91b0df3fe75c4d9f26fe70684569566745f6ba7e5c9c73a862c5252/cryptography-50.0.2.tar.gz", hash = "sha256:7b46165bb56eb4704e2eaaf86f3c940d19154535d9b0ca7d6d590b04060e00d5", size = 880623, upload-time = "2026-09-30T15:30:04.884Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/56/d194340cc4a57535e82e1bee9e89667ac4b7c13b5d3f59686deae3094dd5/cryptography-50.0.2-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:fa8f5efb344d6908a1ce62f4a24e2e5780f825d6f53f5f50ec5ffacac72936cb", size = 3914904, upload-time = "2026-09-30T14:43:44.339Z" }, + { url = "https://files.pythonhosted.org/packages/d9/69/c9bd862c3bf43d6399c433caf002df16e2dffd4be49bdf515cda38038711/cryptography-50.0.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:79def8d059362e7831389ed3be0ecdf58a89386e1271e35dd9f5af84e81bffd0", size = 4731146, upload-time = "2026-09-30T14:43:47.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/69/64cef1f702bf6657e0cc186ed1a2891d50d29fb41586b254e1c07adea261/cryptography-50.0.2-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:630ebfea3bf689d075f82316324ff7433dc447fe6bc1bfc76524b74b4a9567d2", size = 4719841, upload-time = "2026-09-30T14:43:49.01Z" }, + { url = "https://files.pythonhosted.org/packages/38/6b/61a3f8d8c5e1e49a6cddccafc4015cc1c0021360ab0acb4080e7a423644a/cryptography-50.0.2-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f9f6143a8c75945eb960d9eb98905a441394abfa24afaae239d514ffb2586480", size = 4738340, upload-time = "2026-09-30T14:43:50.932Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2e/7212ca32fd43dc91f2f41db20160b268098874b4c9a0e7be94d6835f5b2e/cryptography-50.0.2-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a582ab2ae1d34f67112cadc86702774c9ea4374df6bca6afe672817203c99134", size = 5367029, upload-time = "2026-09-30T14:43:52.911Z" }, + { url = "https://files.pythonhosted.org/packages/1a/f1/b474e930c4d910328780e3940da76f5aa5cbc48ce1fc14e44d239d9ea9db/cryptography-50.0.2-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4061c0079120205fb760c58acab6443e217307dcf05e3702cf970e0689972856", size = 4753050, upload-time = "2026-09-30T14:43:55.272Z" }, + { url = "https://files.pythonhosted.org/packages/7c/52/9af10e80ac16b0fcc2123f9cbd5e7afbd0fd5075bb7a607c592258a39cda/cryptography-50.0.2-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:ac9ed99d81760c62fe89d5f0815cdfa1ba9a35141cf30f1c2d044f04b4803d2e", size = 4376724, upload-time = "2026-09-30T14:43:57.24Z" }, + { url = "https://files.pythonhosted.org/packages/71/37/6202e488cc1eb625ea110c292c6bda92823176e023f427d8d5660ce8d632/cryptography-50.0.2-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:87e9ce85beb6b328ba370cc6e6aea483c92617b4c95b1d33a49297eb662bfb04", size = 4737859, upload-time = "2026-09-30T14:43:59.541Z" }, + { url = "https://files.pythonhosted.org/packages/8f/30/e86d7d518489b0ae2497091a35287abcb1a2ce4037837a34afbe9b1d6964/cryptography-50.0.2-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f265528741e048bce55c3463ed721fb0aa45a5888d8add8cfeccb3035451bbdc", size = 5324103, upload-time = "2026-09-30T14:44:01.901Z" }, + { url = "https://files.pythonhosted.org/packages/d3/69/2c833a049475e0a3444e94c7d0aca0aa51d166374a449b09e92ac98138de/cryptography-50.0.2-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9dab55f57c74c3cad24c323bacbbd04be4705ba6eb0d92e920b1fc4837ed5079", size = 4752576, upload-time = "2026-09-30T14:44:04.545Z" }, + { url = "https://files.pythonhosted.org/packages/6c/5d/906970b83bbfc1f5bbfb677a143c181f2801f23b6a7204a3b47c42c97e65/cryptography-50.0.2-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25784ce8b9621c90c643efb9e1e2162ab3b0224cae446ad5e70e7fcb1ce18b51", size = 4870819, upload-time = "2026-09-30T14:44:06.884Z" }, + { url = "https://files.pythonhosted.org/packages/68/e3/f2298d3bb55e0c4a91841ec4d01b3f020ba8c5fbf15ccdcc6dcf03f97025/cryptography-50.0.2-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:85d0d9a31b9098e98534226d5686b47264b95e62ce459dc2e62fdfc809f9fe93", size = 5030152, upload-time = "2026-09-30T14:44:09.443Z" }, + { url = "https://files.pythonhosted.org/packages/9a/4f/adfc442765721292fff86d314ce385d3249d22db42295c0dd057727b60f3/cryptography-50.0.2-cp311-abi3-win_amd64.whl", hash = "sha256:7afa5a6602a9f29af1f3a2965f831bae7c9d5d597b7cbb716d41ab3b7d89879c", size = 3824692, upload-time = "2026-09-30T14:44:11.671Z" }, + { url = "https://files.pythonhosted.org/packages/ce/cb/52eb3770c0d0be2702a98c6e96065ddc0a2877cf0845aa9c23397c142cd4/cryptography-50.0.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f785f6161f202ab04d8ca194158968798e480ca058943907972da5f12e2881e8", size = 3892731, upload-time = "2026-09-30T14:44:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/19/8e/aa1fc533d4546b127b45de8aa024eb5933d23eff9debfe25931e56861095/cryptography-50.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0ecbc5652bdb6fc9eaf89a7d196e20941adfe812f43bc4ca05d9150496821047", size = 4710431, upload-time = "2026-09-30T14:44:15.427Z" }, + { url = "https://files.pythonhosted.org/packages/6a/64/72bc3f75176e7e406b748a3e3830432b8c51297b38368713df04dc04898a/cryptography-50.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ab50ee449bf968271e820086f10a33d101dd060370abc10bcd22279be2656539", size = 4694824, upload-time = "2026-09-30T14:44:17.69Z" }, + { url = "https://files.pythonhosted.org/packages/4e/c6/62c77550edfa5ca3f14bf44a1e6739b9fa09d6e998a11d97ed8213bccc98/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a9f7355e6fab51f6c369b86fb7571cffa05edee2c2121e0380a37fb9ac1cd5c1", size = 4716967, upload-time = "2026-09-30T14:44:19.661Z" }, + { url = "https://files.pythonhosted.org/packages/f4/37/cce70f150c432914460157a6ecc161752e053aa5ec0ef3b3f7dc6e31039a/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:94e5e9f108ee10471288214d3d233fbfbb492840a8457eb85178d643ddeb32c7", size = 5328676, upload-time = "2026-09-30T14:44:21.744Z" }, + { url = "https://files.pythonhosted.org/packages/aa/9a/6f2f0304d634ceafdeaf23e84537336664ac419b5d07611675c2ad3f6b7a/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:241449bf940a5d27309bd317e6f9a2af6932113818bb2b8f5c59ddc7ef16da18", size = 4727698, upload-time = "2026-09-30T14:44:24.178Z" }, + { url = "https://files.pythonhosted.org/packages/1d/de/66bcf9244d118663b2e1aaded8990f4640e3d7b7411870a5765f252074d2/cryptography-50.0.2-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d8947001be83df1394050758ce0e745dd74fb134eef0a4b5124208dfc3a68c37", size = 4354821, upload-time = "2026-09-30T14:44:26.263Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e6/db28a28c7b6c676addce89136de3d8db49ea825a8c863472e36e42ead4ad/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4a20ce1e5cb4284a86692fdcba7cb8754185c6b2e5c56fcef3751cf451d3cdc2", size = 4716748, upload-time = "2026-09-30T14:44:28.447Z" }, + { url = "https://files.pythonhosted.org/packages/30/96/01546c7f69ea0e2ab790a2e4f0934a4052fb9b388147fbf83c2fd72f1e57/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:84f964e537f916e2cc85199e5a88742e964939b575ac8598b3f9d6cc416cdaf1", size = 5285085, upload-time = "2026-09-30T14:44:30.704Z" }, + { url = "https://files.pythonhosted.org/packages/6c/01/03263395f74d50b071e9e66daace3f8bef80493e5d410726f2ba8554736b/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:828d49b0ff5a0e3975865571c5d91dbbdd0d38d8289b249a163e9425413a5e05", size = 4727268, upload-time = "2026-09-30T14:44:32.92Z" }, + { url = "https://files.pythonhosted.org/packages/eb/94/2bfe8f29ec0cc9c0d99359c4161adf32858e4934b72c6d100d2ac0bbe962/cryptography-50.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:deb9fde5c60e437ee4821bc9bc39ff31b42135c27e1dc61ef0a629389c1de62e", size = 4849503, upload-time = "2026-09-30T14:44:34.969Z" }, + { url = "https://files.pythonhosted.org/packages/54/44/e80651ecbf0e42b62e2bb5f5768916e07eea72e1297338956a61df361f88/cryptography-50.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8c71ba2cd31fc93748c38e1b613200ff1c2665cbfd5341fe3a61cfde35a1430e", size = 5004057, upload-time = "2026-09-30T14:44:37.064Z" }, + { url = "https://files.pythonhosted.org/packages/f8/cc/1d33befb3cd7ea7e77d2d73f43f2066471da1b21f24a6156efcaabf6d2e8/cryptography-50.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:78198641e5be9521beea5aa782bb551a58068d10e6eb04c9c680c1b69f2e7d45", size = 3795868, upload-time = "2026-09-30T14:44:39.71Z" }, + { url = "https://files.pythonhosted.org/packages/2d/49/93f6a6e7a87c9aa68d44d3e1cdb5fe8f60c90d5d2f46acae9a56892816b8/cryptography-50.0.2-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:edc3342adf8f697fc5f59c887a304356f147b397809440ed64e2fa6af2f50f37", size = 4133708, upload-time = "2026-09-30T14:44:41.807Z" }, + { url = "https://files.pythonhosted.org/packages/8c/75/32ac2a56243d778805c16ca6a32b8f74fb757df7e28d7ecb560afafb59cf/cryptography-50.0.2-cp315-abi3.abi3t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d370b8d1dfcdf7130178137f6fbee6140774a1acc6cacefc4b42643ec11d0a3a", size = 4956267, upload-time = "2026-09-30T14:44:43.693Z" }, + { url = "https://files.pythonhosted.org/packages/aa/a4/2c8d734e43d97f0842ee9f1b7b4bfb3d0cf5e19edebf43c2afe6675c2320/cryptography-50.0.2-cp315-abi3.abi3t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f2f9bd7f90c64fe89253f0a2c05e3c4856072660429ce8831b4235bf29403a67", size = 4966465, upload-time = "2026-09-30T14:44:45.769Z" }, + { url = "https://files.pythonhosted.org/packages/c2/58/ee288c829a6f41f6235ae9dd33d82fd19b45442b65b4c8a3da36963d9f7a/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_aarch64.whl", hash = "sha256:e275096ea1e60cc595cda2836fd4a6c725d1125108b868be17f53684d164e2cc", size = 4959356, upload-time = "2026-09-30T14:44:48.211Z" }, + { url = "https://files.pythonhosted.org/packages/92/20/9ded6d51ddd9897f6b6e81fb9ebea7951d7cc5d6c890b0ed8abf77a51a80/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_ppc64le.whl", hash = "sha256:b13478603dcd0a2479ff8e87e2c19a7d525734686fe3c49542472293a204212d", size = 5548822, upload-time = "2026-09-30T14:44:50.86Z" }, + { url = "https://files.pythonhosted.org/packages/02/a8/8df951850d6b31d2a00218f19e2b3f999523437ed7a819df7fa427942fca/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_x86_64.whl", hash = "sha256:58a0c478eeca76fe5e07993c5a0703def34a6dc6a0cda4f5564639b33112ffe7", size = 5001199, upload-time = "2026-09-30T14:44:53.379Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f9/36b3022218ce75b7cdf068fb95f809f9bd0d820e4955ef43b90c255cc7ac/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_31_armv7l.whl", hash = "sha256:d38cdff612d06fa6a32840d5e1b1f7a27cee4a349aa9085d94a67789d6bfd408", size = 4629333, upload-time = "2026-09-30T14:44:55.635Z" }, + { url = "https://files.pythonhosted.org/packages/8c/72/20f99a219f6af47cdd1cbd978c243b92d71496e168a746138af44ded4f29/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_aarch64.whl", hash = "sha256:fdd28f912fccfec1846a94e2e1e8f9b0012f557f0c46fe4f3eb0d7a87afcf90b", size = 4958822, upload-time = "2026-09-30T14:44:59.639Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/196f112617fb08eb4d608a2a6c422373d46f9cc2857f38fc0667033c0899/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_ppc64le.whl", hash = "sha256:cbc8738fd8526d80f35cb3a40d41f41a2e7030bb3b18b09a6778ef63d291c2fd", size = 5506351, upload-time = "2026-09-30T14:45:02.267Z" }, + { url = "https://files.pythonhosted.org/packages/24/95/83378121ef3eaaaf71d4b781577ff794acb39b9e1b87a3f156898c8497ed/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_x86_64.whl", hash = "sha256:e105ab60406787da31fccc883fc0f733af1efd78f0136a4599692c4083a73d0c", size = 5000859, upload-time = "2026-09-30T14:45:05.009Z" }, + { url = "https://files.pythonhosted.org/packages/22/f7/70fd7ae4d1dbfa7ba29b02e1b9068771519a86027756510b700ce81086a8/cryptography-50.0.2-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:6f8700550aa1474a91e5dc07049c46f98b423b5b1ddd0483e0b51362eeeaf5be", size = 5092151, upload-time = "2026-09-30T15:29:15.932Z" }, + { url = "https://files.pythonhosted.org/packages/d4/be/688367b74de86984bd58d8efacfc7c9e68b89a6a22ced0fb4f38db50254a/cryptography-50.0.2-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:c71be1cbfa5cd9a41ee452acf1eccd82b2c05950358b106ec8ceb83411d1a020", size = 5286120, upload-time = "2026-09-30T15:29:18.309Z" }, + { url = "https://files.pythonhosted.org/packages/39/d1/55f8a3f2ef5d1529e16835ef10cf0fe3d559ce237b46dddc440c0bba3649/cryptography-50.0.2-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:c423ab384a46c4dff7217b2ea5ba2e11cffdeab6441acd04cf65a369caf0366c", size = 4111557, upload-time = "2026-09-30T15:29:20.155Z" }, + { url = "https://files.pythonhosted.org/packages/23/ad/ac987755d00e1e64273760228d2635ae38dae2be83e3c6e0d3289d91dec3/cryptography-50.0.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:0ec5f09541743261e66e291b4a0cbf0fb2997aeaab6d9e9c740b9dba1b58d1c2", size = 3943588, upload-time = "2026-09-30T15:29:22.265Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8d/6d585339bedf85d45044c85d8412dac53f2bb6f918e8b7777efba1787844/cryptography-50.0.2-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c5e67125c7dca78d199ec4e116aa93dbb83494808ecbb8211a2cb09b1bf41dbd", size = 4756166, upload-time = "2026-09-30T15:29:24.58Z" }, + { url = "https://files.pythonhosted.org/packages/bf/f1/1c1f6874e8550cfddd4b688ceb38cefb6ed15ceed224d56f133f3d88c214/cryptography-50.0.2-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee247f5c245c9a2fe7c8e2214e295918838e44e00a45a6718451e4004219e767", size = 4749145, upload-time = "2026-09-30T15:29:26.807Z" }, + { url = "https://files.pythonhosted.org/packages/c1/63/61b15dc1a8de03fe0adbe3fd7608b3ad5c73bf50993bbcb1faaa930afe33/cryptography-50.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dfe9763530994147d9af1def057a5b9658b00e8f8fe8743d144d1e0911c2e454", size = 4763638, upload-time = "2026-09-30T15:29:28.588Z" }, + { url = "https://files.pythonhosted.org/packages/fc/35/b345bdfa40c9126df1a9d33236aa98418367931b8725f84fc3ae2b98dc59/cryptography-50.0.2-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:58ddb5a8e3179d12f19e4ea34d2d32e9d63a4baa142c875c1eb59f41b7243acd", size = 5382217, upload-time = "2026-09-30T15:29:30.589Z" }, + { url = "https://files.pythonhosted.org/packages/4f/87/ef344a9e616871f2519c22d6afcda79ddd5d35e9592d95eb6e677608d055/cryptography-50.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f21e8a22c8605750c7af886bab299a363721264061b4ac0a30efb73cfd58efc5", size = 4781387, upload-time = "2026-09-30T15:29:32.605Z" }, + { url = "https://files.pythonhosted.org/packages/90/5b/f2fdb13cd0b96f6f932c8627bb292a45f11c64d21620a8e120aee9a3b848/cryptography-50.0.2-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:9c8402a82ea0dc4ceeab793db05f0fafa8ca139ca34fcde5df0f596103c74107", size = 4403790, upload-time = "2026-09-30T15:29:34.374Z" }, + { url = "https://files.pythonhosted.org/packages/bc/ce/7e4f662b1e3c393513569e402cfc85ac7da0bd3d5435e122a3140219eb2d/cryptography-50.0.2-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:0ddc924c04591c2811ca024d62ecad4f7f6f08af8939c211438f48a16bd23602", size = 4764319, upload-time = "2026-09-30T15:29:36.149Z" }, + { url = "https://files.pythonhosted.org/packages/3c/3f/86ff33ce34cc0de6847fb96e035a1a760d81652e38643f617c02ad32ef7a/cryptography-50.0.2-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a6557e5f38e065ca9fbdaf7cfc7435ecb1d113aa81a022d1b51921ee7432e227", size = 5338560, upload-time = "2026-09-30T15:29:39.053Z" }, + { url = "https://files.pythonhosted.org/packages/40/cf/6b5c8e2fd9202d98988ab7cb5cc5c991704c4ad55f492ff408e4969f83f1/cryptography-50.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:1981f1db4630889b9ef7803fadef12b056f428cb6b85c27ba57b774793b6093c", size = 4780973, upload-time = "2026-09-30T15:29:41.251Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/8d6ebc7dded797bd0f0160d52188021211f011a2b164ef0ae1dac4587465/cryptography-50.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7a8701d6b584d76e909e3d305b7d126b41439876a5aaf76cddc67fc230eafa2e", size = 4897738, upload-time = "2026-09-30T15:29:43.106Z" }, + { url = "https://files.pythonhosted.org/packages/d4/aa/f3f6e0de7e6253b8baa8b2d8fb9d50924fa75cee3d4624bd4bc1208ee923/cryptography-50.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ce47f66801c20ec6c6632453bb5960fe38939e9306970b48b3a5a26de7745d94", size = 5058280, upload-time = "2026-09-30T15:29:44.827Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b6/a1faf3a27ae9405fb34b1713cc73b2d8a26b04d5c561578fa2e6ef3e5bb9/cryptography-50.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:4e81d95e5bafc2d6e34e4bed780e53e4d5b9a2f928573428aa4d35fbec1eb0de", size = 3854095, upload-time = "2026-09-30T15:29:46.782Z" }, + { url = "https://files.pythonhosted.org/packages/1d/7a/f08d34ce09d60f89ebd391e2ebc6ba2b995e6dd7552f41820f8085f94e53/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:92e665960f25fcdc73725b9cec7a3824f279ba97a98653afe9ffac2e43668f67", size = 4716035, upload-time = "2026-09-30T15:29:48.681Z" }, + { url = "https://files.pythonhosted.org/packages/45/67/e18fb65592451a2acb76e9f2fbe14e0f47a8318b4c5430f1633851d03daa/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:eef4c2f3423810b3070ab391f85436d2f8bbfcb286ac15cbc73190b3563b1f1a", size = 4726917, upload-time = "2026-09-30T15:29:50.608Z" }, + { url = "https://files.pythonhosted.org/packages/83/28/38fdce17e60f6b825e69fc3b7f75e70a6612759980704697e1de4cbfaf6e/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:7c6d0330c472d96f6a6afe24d80dfdf15176c33096f0a4397ae4c60f3dd3be48", size = 4715341, upload-time = "2026-09-30T15:29:52.522Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b1/d9121a717e0f893c64bd6ca7702614778d7df2a5c309128a002421788516/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:1ba34f04897fcdaa73f74145c25f3ec146fbd56593853e88adc2e811303c5f42", size = 4726322, upload-time = "2026-09-30T15:29:54.263Z" }, + { url = "https://files.pythonhosted.org/packages/36/8b/e6d153808bf353e152abd2fd4d8f09670d956ac78379ac46e60d7efbf04c/cryptography-50.0.2-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:3dc4fd8058cea1644971207d530e1a03a184a805ffc8ebdddf0599d78a331b81", size = 3873097, upload-time = "2026-09-30T15:29:56.097Z" }, + { url = "https://files.pythonhosted.org/packages/ca/1d/1271f287ff7170ddafc2aad36260c4eec20ccd2fea70f38455e9d56d427b/cryptography-50.0.2-pp311-pypy311_pp80-win_amd64.whl", hash = "sha256:7b75de3c8b3be1cdb1052747c929440c3eea46c1bc2cb8a6e3a48388e9b7b452", size = 3805376, upload-time = "2026-09-30T15:29:58.729Z" }, +] + +[[package]] +name = "filelock" +version = "4.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/28/3893324bcadf8f89b1616637468d766af3f625008c0735bf704fdfa86617/filelock-4.2.4.tar.gz", hash = "sha256:c6d8663535e1650ee430507b4ffbac7b464dd64b7647ea0112a9f406499d163d", size = 652444, upload-time = "2026-10-19T01:57:51.001Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/1b/5e74289a1878e666bb4b65fe5805a04cafcac2aa5f519ef1ead67ef91c56/filelock-4.2.4-py3-none-any.whl", hash = "sha256:301241438c57936ef93f4243a94f2559022268bfcf9b375a9a72eb0f8db8358e", size = 162475, upload-time = "2026-10-19T01:57:49.419Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpd-tests" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "cryptography" }, + { name = "filelock" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "pyopenssl" }, + { name = "pytest" }, + { name = "python-multipart" }, + { name = "websockets" }, +] + +[package.metadata] +requires-dist = [ + { name = "cryptography" }, + { name = "filelock" }, + { name = "httpx", specifier = ">=0.27" }, + { name = "packaging" }, + { name = "pyopenssl" }, + { name = "pytest", specifier = ">=8.0" }, + { name = "python-multipart" }, + { name = "websockets" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "idna" +version = "3.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/08/8eea9d4b8302028f3abb2c0813953f7aec26d33b7a8960ed760e65ff29fa/idna-3.20.tar.gz", hash = "sha256:a7db850025b95ded1eae8a46181a1a6c56c92c96f0e2b005d9ff8dc0210cab44", size = 216463, upload-time = "2026-09-17T14:11:04.752Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl", hash = "sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c", size = 69583, upload-time = "2026-09-17T14:11:03.168Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/e1/2069291243c926a2ff1cd706c7f3eeb9b62144bf60f77c9fb9ff2fb26bd3/iniconfig-2.3.1.tar.gz", hash = "sha256:67f4b9c50da0dedf52af349e7749a80a9057a5031199791b906c3bb3ae878960", size = 21209, upload-time = "2026-10-06T22:48:38.076Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl", hash = "sha256:9121e2c1fdb355232495be3194c8dfe87ccc2d5dee45947b78e68f499790d7a7", size = 7552, upload-time = "2026-10-06T22:48:36.959Z" }, +] + +[[package]] +name = "packaging" +version = "26.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, +] + +[[package]] +name = "pluggy" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/db/7fc19e6f2dc92a966727031389fc2e08b558f0f25eb7403c1119ad4713cd/pluggy-1.7.0.tar.gz", hash = "sha256:d1eaa46ebb595891b860ab086b4d09c8588af65ebd4361b8e8f4bb8920b90ba8", size = 123304, upload-time = "2026-10-15T09:50:58.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/9e/2b38731e0fc536806f16490e1a12d7f0dc2a1235aa8cc07bcc75416a7daa/pluggy-1.7.0-py3-none-any.whl", hash = "sha256:7dd7b0d8832ba3cb632c306926ded123429211b83641b35dc5c41ad2d34f9bec", size = 27082, upload-time = "2026-10-15T09:50:56.808Z" }, +] + +[[package]] +name = "pycparser" +version = "3.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/27/eeb9f307bf07060f3d9f9aa0fb8c15b01bdfded0a941e4b2719c7f94cacd/pycparser-3.12.tar.gz", hash = "sha256:f03387877eab82ce01bbdfd0ba7252ee55a90b1c708d5129e2ce915475cf1db6", size = 158785, upload-time = "2026-10-22T13:40:03.56Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/ae/315bbeee126a769cbde43cf4908acb4b40408d8bf8bbf334dd7368d8dadf/pycparser-3.12-py3-none-any.whl", hash = "sha256:a076f3f489b39b43ed13563a1dafe42447445bec2e294154ec5c57fcdda4c85e", size = 62600, upload-time = "2026-10-22T13:40:02.021Z" }, +] + +[[package]] +name = "pygments" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, +] + +[[package]] +name = "pyopenssl" +version = "26.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3f/e8/7325d258199b159eb2c03fe32107533e2832e70e63f4fb88a6aa00023201/pyopenssl-26.4.0.tar.gz", hash = "sha256:28dfcce0162b9211413e26dfbfdf1d24317fbeba18fc93c12400a1856b2a0bc7", size = 182046, upload-time = "2026-08-01T19:50:50.512Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/ad/2cf6d3fa2fae5c79e1ed9960c0d42badd0f94d81dd12b50604cdc839e648/pyopenssl-26.4.0-py3-none-any.whl", hash = "sha256:f0eb0cb2d581d3ad2b9c489468485e7f2ab6727d08401bcf9d824c3caddf3c1c", size = 56026, upload-time = "2026-08-01T19:50:48.94Z" }, +] + +[[package]] +name = "pytest" +version = "9.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/42/bb4641652e58dedeac2ec51abc1f8cae4fee4ec63899a0999016229a0f2a/pytest-9.2.0.tar.gz", hash = "sha256:97c4f3b1f3c7414aa36465e09b8220f1d03627993bd51b1282e193753c80557b", size = 1797555, upload-time = "2026-10-27T21:30:20.728Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/39/1f5d8c8877a0fa327db959c19341d4be61660de1ab6b7391f9eb805bcd67/pytest-9.2.0-py3-none-any.whl", hash = "sha256:07acdcf88ec174f907dc01af70bf52d49b014bbab03faeeade31c96b18c2e588", size = 411776, upload-time = "2026-10-27T21:30:19.131Z" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.32" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/42/55c32bb9b12693c092ad250a0e82edb5b31ddeda6eb772de5f308b3804ad/python_multipart-0.0.32.tar.gz", hash = "sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e", size = 46881, upload-time = "2026-06-04T16:18:58.647Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/04/e8135ebd1ad02c56ec633277529b2602ff99ff634be76cdba5744cf554fd/python_multipart-0.0.32-py3-none-any.whl", hash = "sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23", size = 30042, upload-time = "2026-06-04T16:18:57.319Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + +[[package]] +name = "websockets" +version = "17.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/4b/4692be6d5bf739ed049b3813901c42091f8c054df89d5c089343823dea0d/websockets-17.3.tar.gz", hash = "sha256:1c7dd93a1c9f89297471493674b1fc1d9467cafd45374f2172213046bdb3a329", size = 188878, upload-time = "2026-10-19T16:58:05.813Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/b8/282f114a67886fccde02a77ff296501ad7fea25f4d696b2b1b337cbd27da/websockets-17.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:45cc1ccb12ced6d242335159e8221733c8696da5ad0b6ceef6debb0f5ce89110", size = 218223, upload-time = "2026-10-19T16:54:25.998Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ba/8043c303aa60af053e092c1d667165f46d6729217ce2af795a23a3cd700a/websockets-17.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46ae05aa1aecd47d4d7de0f05ce2d1d2eb418f8de614964d754cc3b9a409c400", size = 215908, upload-time = "2026-10-19T16:54:27.785Z" }, + { url = "https://files.pythonhosted.org/packages/21/3d/5d0eaa9b29db6c999a2cf749605141796cff21a5e235b2488bd39dcdcac9/websockets-17.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:43f270a5f21e12db08c73d83849a877c02ab7b7d0fb6e2718b5101d9c810212f", size = 216181, upload-time = "2026-10-19T16:54:29.267Z" }, + { url = "https://files.pythonhosted.org/packages/43/fb/33080a7b2d8427b343056eab32ba78235a4bfbbdc79b5593688c7b7c62ef/websockets-17.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2d875224bac1a05a20acade268a7a0e3498e7f8cf890acb7c2b23bacd2bd3659", size = 225246, upload-time = "2026-10-19T16:54:30.807Z" }, + { url = "https://files.pythonhosted.org/packages/61/8c/d4220455cee00adb553c602ba4f1f6a47cd882d86dd9e5124679c0c95c75/websockets-17.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5ad409b7ea102339041281117cf2dadfe71edbb653dc48037cc55d516fb7f448", size = 225517, upload-time = "2026-10-19T16:54:32.182Z" }, + { url = "https://files.pythonhosted.org/packages/75/a3/0ff8e4f0195884ae446844185e81bf7619673d816424b8a58bb5812dd4b8/websockets-17.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d43c311f3519fb1057b6114dc24e9060bc3fb6aedf4aa11160484154b66b63", size = 226751, upload-time = "2026-10-19T16:54:33.52Z" }, + { url = "https://files.pythonhosted.org/packages/06/cf/82c88e3fbb6a75a62b2997932664bccfe68256661c69144bd48020160cf1/websockets-17.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a9eacb382f400bb5865e7e6d24bbe709a84510291e707f169dd4a24ee4a5ac6a", size = 230063, upload-time = "2026-10-19T16:54:34.855Z" }, + { url = "https://files.pythonhosted.org/packages/4a/e4/9131d3b73504902a83d10b1e1244319377ca72755351998ac9c408ba6ddb/websockets-17.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f09f145b89ea292cf6c25fd7c8c0d90207fd6a9e54c7828499669996a1e5bdcc", size = 227310, upload-time = "2026-10-19T16:54:36.248Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b2/2208506afb45d181b0d6ccf377d9d79985fbcbf871af02ef4f7f3d405f5b/websockets-17.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:49fb5b4960ebe1bc557b435da8687deef1302a08f030a33c5606fa6fb11b773b", size = 226087, upload-time = "2026-10-19T16:54:37.583Z" }, + { url = "https://files.pythonhosted.org/packages/39/53/da73e3809f1d4e8c3346ca89a4bed7995de5708f2cb14c9359172a85f5a5/websockets-17.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:13cffaedde6e06183860fc92d88fe1d190abda73837fe4c14f5d4803c472f5dd", size = 223360, upload-time = "2026-10-19T16:54:38.91Z" }, + { url = "https://files.pythonhosted.org/packages/9a/7b/eaf828fd414401672e615570b9f2bfd84342b49c86ffb2ae735e4aada32f/websockets-17.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ef2950896673a9809e8e5ab857525924fbcacfc98b858b0509ccf50d341f80f4", size = 226240, upload-time = "2026-10-19T16:54:40.294Z" }, + { url = "https://files.pythonhosted.org/packages/79/bd/4b6aa6e761c8d89aa6defdd83e40a6b2d9aa21bd01520c8e8470fe8d8681/websockets-17.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2d08ca4bf5a9cdb6fd81b86b51bd5cfb10527d3a1199888f2b02cc6c6a3d5f80", size = 224833, upload-time = "2026-10-19T16:54:41.939Z" }, + { url = "https://files.pythonhosted.org/packages/37/a8/0a6aea08cad7db2c1aa53dbd3ece3ebc09e420e8301cb02f2b812a728a5b/websockets-17.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5e0c0ed37472c4729f3001066510a656486f08f149dda1ebfd3dfe4450c9756e", size = 225599, upload-time = "2026-10-19T16:54:43.54Z" }, + { url = "https://files.pythonhosted.org/packages/1d/82/3998ea56beb079f43fc39dd5935ef8dc150f915bf2de8bd6ddcd50c0fae6/websockets-17.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:62059d739af7fc035ed6e63c5390118a60a5e9aa5a5541cd0486a83e741842c7", size = 226643, upload-time = "2026-10-19T16:54:44.914Z" }, + { url = "https://files.pythonhosted.org/packages/c1/a5/6568add8d0a2f37c5780c6de62b5b8ee191ca9d2d0b8ff2123b8277c41b7/websockets-17.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:bc6f87d03e3691154673aaa8f08991723d6e3394c83ef2875c7a9049eac9595c", size = 224223, upload-time = "2026-10-19T16:54:46.429Z" }, + { url = "https://files.pythonhosted.org/packages/72/79/b11bc05b0f64d8b14901443a4b01ea80d039cc68b260250b4cb488070aca/websockets-17.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:3d78d93846eddc990551c1c31914d90d71957ca3af468969826f7ed0d7d1d51c", size = 225255, upload-time = "2026-10-19T16:54:47.82Z" }, + { url = "https://files.pythonhosted.org/packages/71/f9/9a8902074db7044b64a05feff349e4f44144a961310d3419bab612f00dd1/websockets-17.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2a377c7132b1e69be7a50d7434d7f26f69e93c70166ebb6d8f8cabf802ac4506", size = 225494, upload-time = "2026-10-19T16:54:49.171Z" }, + { url = "https://files.pythonhosted.org/packages/99/ba/7b873060ce6764207a24c10e81294739abffe2a90dc67c7f7db5042c596c/websockets-17.3-cp311-cp311-win32.whl", hash = "sha256:29b01520c39ef2f23c88b002f11e6ef4165675f6c92f5f3578fd587d69b5a5fc", size = 218720, upload-time = "2026-10-19T16:54:50.501Z" }, + { url = "https://files.pythonhosted.org/packages/28/11/7469f3f28513ec0d43f10b1c88bf970b0b6f0aa8190c42731954297e824c/websockets-17.3-cp311-cp311-win_amd64.whl", hash = "sha256:d338813a440bf6d2f0df55140d3f3080284410b1ebfe9df50e3847bcdb1a17bb", size = 219017, upload-time = "2026-10-19T16:54:51.843Z" }, + { url = "https://files.pythonhosted.org/packages/bc/fd/4724610baabbba94a44c5d1cf1b1a33ff77031e94a51addb0387c7f56b10/websockets-17.3-cp311-cp311-win_arm64.whl", hash = "sha256:6a8248b1ba8861e48e4c7ced3895a1264d2a3b617de72ec94172ce6e64bf16c7", size = 218950, upload-time = "2026-10-19T16:54:53.37Z" }, + { url = "https://files.pythonhosted.org/packages/0d/29/902ffe19c0d35603102f31490dea342f083bc77d8f8bcdc7d8b16f5810f9/websockets-17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:41665c7ea01d3ae497a0c61fd22d3424230d18782a5fceb79f04db9ccb2042c9", size = 218250, upload-time = "2026-10-19T16:54:54.718Z" }, + { url = "https://files.pythonhosted.org/packages/f0/66/f56a2637b1aac263205b0e3cd7b2ff2e2f8b404b8118c991f4d122fd381f/websockets-17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8f7469408740cf06a79b01605f2ae0b2acc8839c03fcbbf05da49baee12d3302", size = 215927, upload-time = "2026-10-19T16:54:56.124Z" }, + { url = "https://files.pythonhosted.org/packages/22/7d/a27f25451c4cc62141992b51005688481210f3d3e63de3dc66f9e882ba59/websockets-17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d68da281ac208b6236ffa8beef23fe7c7c956d8bf09b421d447232fd94441b35", size = 216196, upload-time = "2026-10-19T16:54:57.538Z" }, + { url = "https://files.pythonhosted.org/packages/9f/77/40bbc1a5c016cf9f598dea21d1250a93245c995eafaec6401d0e4eb4e647/websockets-17.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:078d1d41f45ee34eed5a4f3bbe7c588caecbdd6e12dfca73e8c1b0e803773793", size = 225516, upload-time = "2026-10-19T16:54:59.104Z" }, + { url = "https://files.pythonhosted.org/packages/3d/9c/91889c8c62fb7d16cd58e3074473a259303d6229bf0ce496aec87e66f3dc/websockets-17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7f391838e8adcf1f2fe71561d9e5a9f74ea7ffa5052eb2db329aac677383136", size = 225795, upload-time = "2026-10-19T16:55:00.438Z" }, + { url = "https://files.pythonhosted.org/packages/b8/29/2e5e2158e9920f806cd45f3cbc81cf5f3eef2b9e5a74b9ef049e2a3578e3/websockets-17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c15a165673bb3fb57facbf040edf5c0556d20ccd2e0ccc5f23ebb69fb715fdc1", size = 227034, upload-time = "2026-10-19T16:55:02.065Z" }, + { url = "https://files.pythonhosted.org/packages/b6/af/fbab5c4098b26026b877bfc139b1ea0d7c6eaf7a86b84544933b4c445f82/websockets-17.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28d1935c16782a1e3650c9baa79069b016fff739e292e4cb14bbb830d6930491", size = 229863, upload-time = "2026-10-19T16:55:03.415Z" }, + { url = "https://files.pythonhosted.org/packages/ac/72/d6e460a991607794072fe3fdfcb3221a0342af61586f5bd7ea40bddcfd21/websockets-17.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c534c9524a2acd0b97b7022074bec3784ac273c683cf9535087e437672ea8531", size = 227669, upload-time = "2026-10-19T16:55:04.763Z" }, + { url = "https://files.pythonhosted.org/packages/ec/8f/1c7da1cfb632e141e019cf2e0bbbd4e6dc8c198c8cc29544e4acf1a89f56/websockets-17.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b459d29c8b82a420a98fa0ce8e9cf787ce0062d98cc90604ef9574fb3cc26ff8", size = 226429, upload-time = "2026-10-19T16:55:06.112Z" }, + { url = "https://files.pythonhosted.org/packages/3e/63/c48ef8bbfbd982f2cfae1cfcf9e63f5bfbb8468fd37ca700507efc0b0007/websockets-17.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:53209dffd91b0aefed9ea630fa6ad5c2944d6210ba3a8a3a8664dc1575289b14", size = 223653, upload-time = "2026-10-19T16:55:07.473Z" }, + { url = "https://files.pythonhosted.org/packages/cb/37/7bedaad48ae8e89eaa4f3942c11be0c41de77ce2118a475f3ba05e9b5a0f/websockets-17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9662a9fe457820c60365b3209e03cbd8e4a817195d55de65a80fa680b4bea136", size = 226465, upload-time = "2026-10-19T16:55:08.791Z" }, + { url = "https://files.pythonhosted.org/packages/33/cf/ab9a8a0de4769ca1dce392021b6a922e21b4d3687fe9e1ab4db585a088f8/websockets-17.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5670a762c7ae388bf8aa04199fd436180a4d28318dca9c1eae7b38604676dbfd", size = 225071, upload-time = "2026-10-19T16:55:10.127Z" }, + { url = "https://files.pythonhosted.org/packages/89/96/64d8460cec95f873c61c6d13a09b435915150640525ae56d5871400aec09/websockets-17.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ed59cb4ee258674a6ed9b8515e42079db02cb3be94f133fdd3f200183a40c49d", size = 225841, upload-time = "2026-10-19T16:55:11.519Z" }, + { url = "https://files.pythonhosted.org/packages/a6/98/2b83211ee3087db4ae66533782664c0e283cf4a08cbb14036018d0803e09/websockets-17.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0f78bb2e82fe88912c9d7b25c98e57a9b08655579af89f164fd4c0657a075d62", size = 226945, upload-time = "2026-10-19T16:55:12.85Z" }, + { url = "https://files.pythonhosted.org/packages/9f/fe/6b2a37d422d7f4dfc0c92c95a14649d9bd662243ed8b0774523ed2703604/websockets-17.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:71250e010eede33c1d638e2c7a98288520d67f782fd5eceecaeca3129233a09a", size = 224502, upload-time = "2026-10-19T16:55:14.48Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c5/364015c9311c89ea6085d54c70d654c5d27ba05014d44ef9fd9daa09462b/websockets-17.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:729989759feb81ff6493879a0d60a0bd7d2eee41001bbec0381a080318439382", size = 225483, upload-time = "2026-10-19T16:55:15.899Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b3/75d0cae8398816ed34a16a78252bfc8651f2f883cc5a568e1198395f15df/websockets-17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:90d8c4fa5ba293cf92de351fbc292cdaadb1a1d58d031a1a97ff9b8fb6250ec7", size = 225761, upload-time = "2026-10-19T16:55:17.28Z" }, + { url = "https://files.pythonhosted.org/packages/f0/af/9a7c4b2e87f47a2a6a066acdc2157e9b7cb6222a224b95a432f54ae786ce/websockets-17.3-cp312-cp312-win32.whl", hash = "sha256:0824e6ecee838cc28e6481062574c7fde49f9a2cedfacd36f0e91cb4b37fd6fe", size = 218722, upload-time = "2026-10-19T16:55:18.658Z" }, + { url = "https://files.pythonhosted.org/packages/f7/e8/e52953a8280a1e51cb9307218443f68c8f4dc4b9952db6021d5c12d03c0f/websockets-17.3-cp312-cp312-win_amd64.whl", hash = "sha256:6644eecd99c4e8cdf72e646e17b391bb70b054f44becde37dcdc48cbec7e1527", size = 219024, upload-time = "2026-10-19T16:55:19.987Z" }, + { url = "https://files.pythonhosted.org/packages/85/a4/005eb7c1f5c1a249c1231a6ed83a54ada0b6b8ffab33a608ca20e3034ea0/websockets-17.3-cp312-cp312-win_arm64.whl", hash = "sha256:4a9133dbaedc6f1af358127275676171d9a1bb3e61f47ee62b2ff9acb78af3d0", size = 218950, upload-time = "2026-10-19T16:55:22.161Z" }, + { url = "https://files.pythonhosted.org/packages/ee/1d/c1f4a1c45c5ca2c35ae9fd7cac0e491f8a014c9a86933a5da7b04ff825cf/websockets-17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:52a2ca6f401fb49a8d5d9e0e48ade5f58f1bdda33132907b0e4e3b702628fa4e", size = 218243, upload-time = "2026-10-19T16:55:23.851Z" }, + { url = "https://files.pythonhosted.org/packages/4e/e8/f1b47d0ced4964ae748931a54347fd27b06b62e8ea7837a9de3aee0c5999/websockets-17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:80578e91764bfca297ddca322c00a3136d993b829e9839e7fa378a19c1fcc25d", size = 215927, upload-time = "2026-10-19T16:55:25.317Z" }, + { url = "https://files.pythonhosted.org/packages/16/df/ab0ed0445d8d81a133c8fed760f3b4f743a2f563804fdbecb2c32fe05d94/websockets-17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fe7a168d805c8a7bf1c2de55b7ab2bc958586bfd88d0e87235a92cac66b590a7", size = 216185, upload-time = "2026-10-19T16:55:26.678Z" }, + { url = "https://files.pythonhosted.org/packages/9f/51/b30fd6c5a33dac76a78083be125e3f9804f3d735da494ff03a46834f31f4/websockets-17.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:54fcd24944e6d127d198c28dc363967b7131ff0c97833962c2ef2048018de02d", size = 225574, upload-time = "2026-10-19T16:55:28.097Z" }, + { url = "https://files.pythonhosted.org/packages/1c/8a/8ede659919c0805d1fcc79c2ed16588d3419f1687c5117ba46f0217750ec/websockets-17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:de2c12555bf05546f3ee75733f2c81de70aab7f530e86d0d2162b0ddcb3c39ae", size = 225854, upload-time = "2026-10-19T16:55:29.491Z" }, + { url = "https://files.pythonhosted.org/packages/a6/6a/e75d10e3896edc61320062890f71cdc0767dc4a54e2a26a943b703042d13/websockets-17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c95d25b5382ee0e6191ecafdeabefdc1f7bff8dfee29239b9b79e760ef3b04bd", size = 227091, upload-time = "2026-10-19T16:55:31.146Z" }, + { url = "https://files.pythonhosted.org/packages/dc/d3/658f1e153566e5edae02812122d9f020c30785d8b53c4bda61b8e976f77f/websockets-17.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8f244dfee05ea29b8064701880865eb1684f3a704a5665bd10b64694444dbbd4", size = 228526, upload-time = "2026-10-19T16:55:32.524Z" }, + { url = "https://files.pythonhosted.org/packages/28/00/002f95a02d721d8c6ec2676c5aee904a823838a9c205fc0b31eedd882a7b/websockets-17.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e99440ef446c7833690c1754e23b0188d440f3cb89812b0764c697a7880cb33", size = 227718, upload-time = "2026-10-19T16:55:34.085Z" }, + { url = "https://files.pythonhosted.org/packages/fa/9c/d36ec9499a62ec4e2a4eb570f93c6fcedd80ad5685e0f9d6866e8cfbbb84/websockets-17.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:26838e2d95d7004c52e8c7b6b51676c872093a3b55a497824f899116a15a8a55", size = 226472, upload-time = "2026-10-19T16:55:35.484Z" }, + { url = "https://files.pythonhosted.org/packages/36/5d/3a6d74e409f24ae4e8661a9d74d6645f71b3e91425a527c0e3f2e78bb15b/websockets-17.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:508775efa11d26aec186d10154359f52276e2baf9ca201b52356330883276dfd", size = 223710, upload-time = "2026-10-19T16:55:36.853Z" }, + { url = "https://files.pythonhosted.org/packages/e1/90/e08eb5d69b73cf5babf80922a8c964019b48339ce5c694a5d0c0f083c5a8/websockets-17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2789d203babb6ceb2770aadc2394c1f99263ec37fba57a93064f9a8ad899e8ff", size = 226532, upload-time = "2026-10-19T16:55:38.191Z" }, + { url = "https://files.pythonhosted.org/packages/c2/45/b1916efa1dad5af33beafe5c536810e1c80856b7e913f671c35031c57745/websockets-17.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cfef7f7aa434a1e632e0ebda14b39f4f1d7975e0fce5c77ea7b43156fabf9bd8", size = 225129, upload-time = "2026-10-19T16:55:39.635Z" }, + { url = "https://files.pythonhosted.org/packages/04/c1/d427abb3f11e92f102f70f8e73addb767fc62c614565c0ee3f5657fa3551/websockets-17.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:39f532fa0d9026de4b9315a175998646112776baad54d55348435c11b05d034e", size = 225900, upload-time = "2026-10-19T16:55:41.033Z" }, + { url = "https://files.pythonhosted.org/packages/07/2b/462fab8e4b09eaa4eac2f40fcff8ee30f851aa40fb518efb0177262f6590/websockets-17.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:85d6daa3a7ca404570ae130babbeb347ef9d3faca5300fb13cfa4bb48c3d30d1", size = 227007, upload-time = "2026-10-19T16:55:42.314Z" }, + { url = "https://files.pythonhosted.org/packages/71/36/7404a4376021a0cc7ae9fcbdb1196c0c8aa48df4fd370c185f4f5b677bff/websockets-17.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:ce39283ef08b0f28478e920d4d89a1fc169176e568e8a6a821332673145522eb", size = 224565, upload-time = "2026-10-19T16:55:43.93Z" }, + { url = "https://files.pythonhosted.org/packages/c9/5f/b8ada5787d11e6edbf58e2d680235fb3b2fb3298991cc310566f4d6c2fbf/websockets-17.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:23046098ebf844753cfd211033624a8247520e5bf2194c24cb1a8c222917acff", size = 225516, upload-time = "2026-10-19T16:55:45.398Z" }, + { url = "https://files.pythonhosted.org/packages/23/1c/0533f760237ac6604efd0bf98c18ea4c1381f2cadd8c8d0b05faecb07a80/websockets-17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:724224d4c457207271239114bf43afafc3eca4a1b4661ccdd428d11869a03bbd", size = 225797, upload-time = "2026-10-19T16:55:46.989Z" }, + { url = "https://files.pythonhosted.org/packages/97/b4/0e580e8f96197130ebdb32b727e9c891594c930be30d8a9de9aa0bbb7e2c/websockets-17.3-cp313-cp313-win32.whl", hash = "sha256:5af7070d80d278832cbca9c3c3dd44b30a26dcfa3ae1e0f6f19ff34edc27b689", size = 218712, upload-time = "2026-10-19T16:55:48.379Z" }, + { url = "https://files.pythonhosted.org/packages/7d/6c/db5c907d1cbea84444bc759ff63db97fc586988d11a34d5565e8b50080fa/websockets-17.3-cp313-cp313-win_amd64.whl", hash = "sha256:66027e401c70d0cf55021b91b8a7ec2cd5810e20602f945b4fe6ae8b37ed232b", size = 219026, upload-time = "2026-10-19T16:55:50.688Z" }, + { url = "https://files.pythonhosted.org/packages/f1/d3/30f04bec5b9cf0c00c98c038494b210ece923a0b05b6df38a162b759e5ed/websockets-17.3-cp313-cp313-win_arm64.whl", hash = "sha256:6b14914e67044724c31a9360c7b89e9d070117c630746eefdc1620d967545acd", size = 218958, upload-time = "2026-10-19T16:55:52.085Z" }, + { url = "https://files.pythonhosted.org/packages/27/ad/f65e365bbf38d61164b0c2f64776fc7714bfa59ac51e492b038a35f4ab1b/websockets-17.3-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:bc8fd230ef013951900c1fda100eebe4a4b1b407dde2a44186995042dda2c628", size = 218288, upload-time = "2026-10-19T16:55:53.387Z" }, + { url = "https://files.pythonhosted.org/packages/48/1c/33271eba53b4927a1b91d99d838a6d7b888e6f708425262c911450d73696/websockets-17.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:cdb8e4b0f6a41b857c74c03116e8ffbef59ef8e533e934b532843178990d01d5", size = 215977, upload-time = "2026-10-19T16:55:55.063Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f3/6d982c60f6c7c95e5faed53d39263df273ecfda904c7d6920c37eee58dd1/websockets-17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:018fab26140ce221c3853454a608e4e7004f4f10641cb97819dcd5b917bb2e99", size = 216193, upload-time = "2026-10-19T16:55:56.424Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c6/4ff6b8d2a4b32719d4658bbd791ee9d19939a47a69b961c961fd9dc3a64c/websockets-17.3-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:dd0f3037d501178a761fd2814370aca547f79e99a200a61a1f6e7b79ec3d78b9", size = 225573, upload-time = "2026-10-19T16:55:58.252Z" }, + { url = "https://files.pythonhosted.org/packages/eb/8f/46e4030947f5590cb61832901f44a3edb07ef8d1e27f6c06b42c5f1c4d33/websockets-17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8dbb3862aa649de95660b6337d97101977f160b5b354d07b82fe309874d2c8bc", size = 225927, upload-time = "2026-10-19T16:55:59.561Z" }, + { url = "https://files.pythonhosted.org/packages/f0/1b/3db30fdf4fbefa4ac463060d3f87e2d444f960aa1234028468c09b88bffc/websockets-17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af37a1e6b6bd506b597d5fb4cf111fb527d1b2223608a5d441d4738827cfd166", size = 227169, upload-time = "2026-10-19T16:56:01.029Z" }, + { url = "https://files.pythonhosted.org/packages/02/ed/849799cfc0e3e359b75ca1d50605376da52dc9ef0b2a53931e3f5991f96e/websockets-17.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:740bba4223fa5216d4d6c5dcd29e697f37229b8a61b69e1a50527b0ea993ab2c", size = 228542, upload-time = "2026-10-19T16:56:02.377Z" }, + { url = "https://files.pythonhosted.org/packages/19/53/dcc397e20ce792fffd9a8f79b6333275748ee7a23f807e576166a0b16edd/websockets-17.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4170556762c6fd2913d99dfdd8dbb4c927584899df7facb1034627e355263c45", size = 227772, upload-time = "2026-10-19T16:56:03.768Z" }, + { url = "https://files.pythonhosted.org/packages/c9/36/e5935782f71e9e22247b2a2e562da05e18d8945c49f2692ca1bdaceec178/websockets-17.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d355b22270be8062360b9df2ee0b10277590789a1059257916fb9e256a0cff80", size = 226518, upload-time = "2026-10-19T16:56:05.332Z" }, + { url = "https://files.pythonhosted.org/packages/27/76/bd5978a1e403fc646230cf35cf7c06b40b3a232d440881b9e89ede229b18/websockets-17.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4aa85b34c21cf97b3470793d0bf0763d32bfd1e67b83067a0a3ba8dc3474dacf", size = 223766, upload-time = "2026-10-19T16:56:06.703Z" }, + { url = "https://files.pythonhosted.org/packages/da/87/c66eb39bd851d5c14d9eb75aa64d03cfcf2ded77927ca067b5917291d19d/websockets-17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a0e2e7c2c1a4f25db7b6596738d810c10732e77f852247300a1bc49e364d6dc9", size = 226644, upload-time = "2026-10-19T16:56:08.198Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ae/b9a31d5497fa0b73629a097b173a26123da2ed656b562114644bc623414b/websockets-17.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7fa69212ea60ea24e2d8aff23fe102b1f87f1b27a320f92d2e7b3468997a496a", size = 225107, upload-time = "2026-10-19T16:56:09.627Z" }, + { url = "https://files.pythonhosted.org/packages/61/0c/b773a102f84b60b2052b2cfde773d2f3cd3e8eaa93a145f54450460549cf/websockets-17.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:80a6e37e235780f189558ab804f3cbc01c18f5c578995739b49613d544d34ebf", size = 225891, upload-time = "2026-10-19T16:56:10.973Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fa/e7e538575dd4912dea9ea4c9ae86519aa1420969d9cb5cf02fd10fd20ba7/websockets-17.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:628efe13630ac3397e7d1278b6be6f22f37a86d638a324334b4cd43c1daf9928", size = 227064, upload-time = "2026-10-19T16:56:12.295Z" }, + { url = "https://files.pythonhosted.org/packages/28/87/bf2b928c156deefd0c50285a48fc985e2ca85d284e7d9981cc5b79b0e696/websockets-17.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:443fa28652682d2a3ec3eca49af1b87cb2523561257683e62e03a98967b22138", size = 224618, upload-time = "2026-10-19T16:56:13.73Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9a/d59817ba3535f787c1cf8990a01b3d2896a7d9d8f3b5d55bed22fce595a6/websockets-17.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6705af9d1005127302c04d9f1533ba56e289336c465923ef878430e99295a0b9", size = 225571, upload-time = "2026-10-19T16:56:15.199Z" }, + { url = "https://files.pythonhosted.org/packages/a6/96/8210379dac889e16c75099aefffa200dc55dc8cb030010f309a7eb9c75c1/websockets-17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9bde5d82d7ac4859e0cb3a35e148f537a8d4e56aa43f42582b5f263982f095ae", size = 225869, upload-time = "2026-10-19T16:56:16.569Z" }, + { url = "https://files.pythonhosted.org/packages/97/0b/d4741c977fec9e6482beffece572d544d42d230f56fc3ef504f96521063a/websockets-17.3-cp314-cp314-win32.whl", hash = "sha256:be7be57097a65a8fab3d0f0a10d12931deb626a7d8082eb732b1ae41f3a202e3", size = 218232, upload-time = "2026-10-19T16:56:18.028Z" }, + { url = "https://files.pythonhosted.org/packages/33/69/2f9ce899776d0c522f7f44080cad4228a1c14ec13b0125a2efc12ebe41c9/websockets-17.3-cp314-cp314-win_amd64.whl", hash = "sha256:2b06213c8e1ce93b18d0476d7a5e647436c8f23d099888c9065e930e80f4bed5", size = 218582, upload-time = "2026-10-19T16:56:19.399Z" }, + { url = "https://files.pythonhosted.org/packages/4e/e9/11b885666d72add674440a3435c059bea325616959cec8be42605948714c/websockets-17.3-cp314-cp314-win_arm64.whl", hash = "sha256:6b7b540bb321f7f0a7b8f5d107f941bb361e3a154c0b594dae0394997b91b743", size = 218465, upload-time = "2026-10-19T16:56:21.005Z" }, + { url = "https://files.pythonhosted.org/packages/c4/0e/0c0f40ab8eefe9d5a6c78b4e267d8e37d70d3db8dd195574e0ab867e4f81/websockets-17.3-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:c9d06edfd974e918b40e0662a7e76dac13125529ee5ec9fbe42dc0d6faa209c3", size = 218614, upload-time = "2026-10-19T16:56:22.457Z" }, + { url = "https://files.pythonhosted.org/packages/1f/93/d22b3d63aa439bc01a9477880070d05e0b257b0548294786a33be3e842ec/websockets-17.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0a0bcf66289b9d017c175562c35c5304b5cc3ed56aded994ae30b0a9654ba405", size = 216151, upload-time = "2026-10-19T16:56:23.95Z" }, + { url = "https://files.pythonhosted.org/packages/b9/92/f60a9672cdeb8cb4f7156e6e83c76d59fdc586737a97ac77129e9fc188a2/websockets-17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f3ed747b7651668131029c720fbc32c7ee2d129bdad200e6c8f4037b20f7913a", size = 216354, upload-time = "2026-10-19T16:56:25.251Z" }, + { url = "https://files.pythonhosted.org/packages/9d/da/2be8c5f3c6612d4523f95b074e2352bc11bd090cf121d32bc11af1d52e70/websockets-17.3-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5868a02c4f6d698ddd19b4c9742748c03dec045289915806845189d96bc3d314", size = 225937, upload-time = "2026-10-19T16:56:26.726Z" }, + { url = "https://files.pythonhosted.org/packages/85/1c/cb84aec871840acd66dfa84520bf7b8e48ce70bdb3f4baa4efd097fbe6b1/websockets-17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa87ea2ad64e59fe5622c1dc5716106b6d6e7e4b26b848cb5f5710a75c292ef5", size = 226215, upload-time = "2026-10-19T16:56:28.1Z" }, + { url = "https://files.pythonhosted.org/packages/9b/7b/cfb65557accec2eda2c80288e13eca6f1c797c579375906eb73f4053cfd1/websockets-17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65e937576141ac50fac32fa9736dbb3e0e4a7769f91be004011a09216c6ab05f", size = 227389, upload-time = "2026-10-19T16:56:29.518Z" }, + { url = "https://files.pythonhosted.org/packages/70/a3/7e08ec4b23e376ed228831aa44b2f2a3f2e7025cc66dda4b2e53dcc2619e/websockets-17.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:35381b575f70a08d4241e73c0cde4672bb9b2a9aae8767ca6b7fb61305a052c8", size = 229534, upload-time = "2026-10-19T16:56:31.072Z" }, + { url = "https://files.pythonhosted.org/packages/4b/7d/8a813e3e53956f876cfaad4f6358625bbd94168aa8046c44e2f3561f12af/websockets-17.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5142798871a248b422f353e0684d51dfa119de3403a6fa0904b7cb806d9790ce", size = 227959, upload-time = "2026-10-19T16:56:32.656Z" }, + { url = "https://files.pythonhosted.org/packages/41/23/5f3dab6ab44f2f30042db83a658a57129d5e3c0425c798b3c3e727bc060a/websockets-17.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e164c4a6868d11988c7f2386e3278ccff21c665cd7d08279b62709dd37a3167c", size = 226691, upload-time = "2026-10-19T16:56:34.101Z" }, + { url = "https://files.pythonhosted.org/packages/5b/49/99a3fb5a2e94d677fbd0d014a6f9a56a3d2b8e8007b7175727bc4fc4ae94/websockets-17.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:10b24e836ee0233ca078e1244f6ba40343a22127a9a736f46a7e7b18de2cc711", size = 224225, upload-time = "2026-10-19T16:56:35.452Z" }, + { url = "https://files.pythonhosted.org/packages/6f/e5/f503a4d757858d083c90ea85dd1fd636ae9eb63c9552c75a4c38514ee7ed/websockets-17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:56a76556ab04ab54a675acce2cac8b4e74ea2cec558a7c9ed37e0367b04b5d50", size = 226984, upload-time = "2026-10-19T16:56:36.887Z" }, + { url = "https://files.pythonhosted.org/packages/4a/a6/8586844ad4b21988837624e1f4b331bc71919c85a6eb1ee3c8bd29893f27/websockets-17.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:33221e49be79d7bfe6d12bc52ef9458b256ab74fa8ac5660acfa3065de1c7870", size = 225436, upload-time = "2026-10-19T16:56:38.213Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b9/f419506494d6ffd205a9c3258f56ff5aeae6976de26361057de74f4ab659/websockets-17.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:2fa79b9a48ae59744316c71a34cb9f76f30c93e117338522c5135cb7eac10351", size = 226325, upload-time = "2026-10-19T16:56:39.606Z" }, + { url = "https://files.pythonhosted.org/packages/28/c5/55a58faa67149e84cb98b33f9d8fda95242677ea07b78f3ecaf2e77877d6/websockets-17.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f87f02afe05c1c246012e3de100a77cfbabd17db709fe93cf074271ab239b312", size = 227342, upload-time = "2026-10-19T16:56:41.041Z" }, + { url = "https://files.pythonhosted.org/packages/35/e0/fbe561dff24b6ef1b2034877f37b56eb9b81b3c103e2a8b03819bf2b3b6d/websockets-17.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8135a549e27e9941a70e2d2156676ef2ffeff4b06e9d564dbaaae963482afdef", size = 225036, upload-time = "2026-10-19T16:56:42.433Z" }, + { url = "https://files.pythonhosted.org/packages/0d/37/d80753064f8370a24d3bec74807c3928d7b0ebef5381956e2a0108b365c2/websockets-17.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ace8dd1efa5f44c482d091aa38322e20e3ffe5adcdba6b67ad9f4e7da58f3f68", size = 225807, upload-time = "2026-10-19T16:56:43.774Z" }, + { url = "https://files.pythonhosted.org/packages/f1/9d/a11d3168522a2959c4a5661728744d62b9eee344abdba5f9fbc1d359742f/websockets-17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2129c9a5fc05a230ce088745a790af073e582b584badc7b05db067ee326f56d7", size = 226241, upload-time = "2026-10-19T16:56:45.088Z" }, + { url = "https://files.pythonhosted.org/packages/06/28/16107b3b83fd2b5024bfecf8d000140f555204a0191202f9c4c981137810/websockets-17.3-cp314-cp314t-win32.whl", hash = "sha256:af7c474f0d8f4d2a196370e85033873c7b063a0b5da9a4f143aa023e3bd25407", size = 218365, upload-time = "2026-10-19T16:56:46.41Z" }, + { url = "https://files.pythonhosted.org/packages/77/dd/8b00e1dd552788d8c7762d7ef61de8f2f238d7357b1a02d4c4650b112196/websockets-17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:0c63ad657695e8f1a48624754cb6b464118ac50d4c1d477c26461c98390f0a62", size = 218706, upload-time = "2026-10-19T16:56:47.754Z" }, + { url = "https://files.pythonhosted.org/packages/39/eb/5d90d57a5cbe614b2f13433aaf1c0b068c804589379061ba06812a185a89/websockets-17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:3c4f631dc8d00e470cde3ec0a35d4b58a817bea592411956f1cf4099dbb306b3", size = 218622, upload-time = "2026-10-19T16:56:49.07Z" }, + { url = "https://files.pythonhosted.org/packages/6e/41/1efe5ecae1f32495dc75641704f959bd2d8e56cdc825f3acc5e010a51960/websockets-17.3-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:f300f28dff74ff4148c21e6971e4de6ca0c34e0086a3d95c017c5174ebf85ca5", size = 218211, upload-time = "2026-10-19T16:56:50.423Z" }, + { url = "https://files.pythonhosted.org/packages/01/ec/4edb653533241203db3f11522e122338d4fda50a41ce2a542029086f8eda/websockets-17.3-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:32029441af953e980b9940f042b337fe4ad9a97fa8b17a4ed878ec67b5b89ef4", size = 215944, upload-time = "2026-10-19T16:56:51.766Z" }, + { url = "https://files.pythonhosted.org/packages/d5/b9/ec450b9650925dd5cf27c1f36d0464786fe3ac702ed656879c4c0dd5c4df/websockets-17.3-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:09f007b2150eff015b2109f006e2ac2012d376f41486defa88ae78a776b00cff", size = 216164, upload-time = "2026-10-19T16:56:53.125Z" }, + { url = "https://files.pythonhosted.org/packages/15/1e/63f7fbff9f0540a0331f1a62424c9ff24faa6ba8aec264f55e9e31017eea/websockets-17.3-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bdc658c9474864893e0b6b94f1390bf6a912952df1870dd93bb7d1a902b5628", size = 225608, upload-time = "2026-10-19T16:56:54.442Z" }, + { url = "https://files.pythonhosted.org/packages/0c/1b/f6679ad61cb8c110033a1b30d561d5c1052983e202157251ff1778f27932/websockets-17.3-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:15decc0fe293d9e3f924bd483f3d76f3d0ff26845112a470e3f8842fec2c82a1", size = 226042, upload-time = "2026-10-19T16:56:55.727Z" }, + { url = "https://files.pythonhosted.org/packages/28/c6/ed993aaef8046cbbb939ae367ee3dd5383dbd055f0500a5b8aed101168b6/websockets-17.3-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:997617dd7856c5dad742bf6893b872319156dd43f47587541a59e4a852d52bbc", size = 227203, upload-time = "2026-10-19T16:56:57.076Z" }, + { url = "https://files.pythonhosted.org/packages/aa/96/7057dc71affe6c8252df8648f5a225642228a6fdd8d046a6b9aa1fb0e9eb/websockets-17.3-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a004c1fe5a72c496f28c6c1d8c2dc5cfe3278d99478e18f8b838c70a912b366f", size = 228900, upload-time = "2026-10-19T16:56:58.633Z" }, + { url = "https://files.pythonhosted.org/packages/43/e3/8b3268bb7e72b465e55172b86d55f58aa277fb6d9bac92aee615345874df/websockets-17.3-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:35fe35d20baa661cef4a12ee7bc42eaf2376d43ef9816ab29dd47ca957f7a73a", size = 227684, upload-time = "2026-10-19T16:56:59.975Z" }, + { url = "https://files.pythonhosted.org/packages/59/15/fa31c0a12240b1225c3712ff080d2f7ede4f695ca90e59fdb58e8affabba/websockets-17.3-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8b3bede7d583bc9f47d8693a29984882e59077db1a5238ad977fc70ae8169741", size = 226580, upload-time = "2026-10-19T16:57:01.371Z" }, + { url = "https://files.pythonhosted.org/packages/22/b5/75d911c206a3035e2ecfad107878e62c06e574b2437fa2cfb4ecef436fa3/websockets-17.3-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dafbb58a149d1ebd5767b3f064f911c5ee0897608005e51c3904b0ada2a8d99c", size = 223967, upload-time = "2026-10-19T16:57:02.839Z" }, + { url = "https://files.pythonhosted.org/packages/0e/f8/f98aa36f611acd5770819f6690125c20d55f221fd6db8d00d284b5f78e89/websockets-17.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:80326bc571164bbbb6bead2e35d0c943b23b24fde629032197c58151529d24d8", size = 226689, upload-time = "2026-10-19T16:57:04.458Z" }, + { url = "https://files.pythonhosted.org/packages/1a/36/d52f5dff7597de6fbfc26c3d126abd668137915719aebf97abcc3be77eca/websockets-17.3-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:7d53fc4b90ef6e11de639dc18744d9be8cbd70ccb5ce80027aaffe7b1ab0215a", size = 225353, upload-time = "2026-10-19T16:57:05.857Z" }, + { url = "https://files.pythonhosted.org/packages/d4/10/dcd70ad9f9cd20e8af43284ac250af610e8bf9022da25f5b409153d033b4/websockets-17.3-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:fdd00937f15f5f93c8e53d8060a8c4c010431ab595a9b5b50ddd569a04603944", size = 225879, upload-time = "2026-10-19T16:57:07.249Z" }, + { url = "https://files.pythonhosted.org/packages/70/ab/f26c6bfa99d38ca3d1bf23145306d97c5d0c75e133a8d837373e4b3d4629/websockets-17.3-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:1dae5cd6c92d4440b15f20aa2987baa73c47ea14baa0fec8536a542e82e0ab33", size = 227031, upload-time = "2026-10-19T16:57:08.817Z" }, + { url = "https://files.pythonhosted.org/packages/d7/3f/c8fd74cdb526cc65295b7468f2f84ba16678646d8174288822f99f510274/websockets-17.3-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:b225c656efa2dedecc81651d2290170af25887b7de4f38c5efc7c5ae2b9442ab", size = 224842, upload-time = "2026-10-19T16:57:10.298Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c5/b3a07ce1c54fc0f5441642f5352662b90d7e658c966730eaf6d78a5f20bc/websockets-17.3-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:bd645b5d2f810a181c44f4a71135e7c365b44f4be9ed3d1892635be13a857553", size = 225744, upload-time = "2026-10-19T16:57:11.615Z" }, + { url = "https://files.pythonhosted.org/packages/66/37/8321a8a6ab0215d4df0238e49ace5f539364cf337bb122b85c788e8c93ec/websockets-17.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:d2605a3b2af8d66f8b75312d223642c065ec773446c04566d9030ad5e2db13c9", size = 226006, upload-time = "2026-10-19T16:57:13.049Z" }, + { url = "https://files.pythonhosted.org/packages/f8/04/8c457a7e6a260b673bf4e6f2f422eb4af5f11c48505748359706f24f1bef/websockets-17.3-cp315-cp315-win32.whl", hash = "sha256:82e60c00cfb8493a5ba27cdf9b3695fdd153f666f73e11f18968f78ead1d8705", size = 218196, upload-time = "2026-10-19T16:57:14.41Z" }, + { url = "https://files.pythonhosted.org/packages/a0/7c/c2df129d9e344b99bb388bcac54fbbdfeec29e6c716c12dc9a3398757940/websockets-17.3-cp315-cp315-win_amd64.whl", hash = "sha256:4d5f2492cb2192aab2d42e57f2caa2159b4fd20fde06f859ec13354f524b176c", size = 218490, upload-time = "2026-10-19T16:57:16.234Z" }, + { url = "https://files.pythonhosted.org/packages/e6/4f/721b778c85559b2949e46246838e31a346dd815b3cdd8460fe517235675a/websockets-17.3-cp315-cp315-win_arm64.whl", hash = "sha256:55494475206113dbaf731dc8c5ffd5e1094175b753b7bbaad09fe3d7df61165c", size = 218403, upload-time = "2026-10-19T16:57:17.631Z" }, + { url = "https://files.pythonhosted.org/packages/b3/28/241d9ebb4dad79346b5a6abf9f14d7685b12ca358d250eea4920cdb1caf3/websockets-17.3-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:23cb6f6d64e3a389bb8ba150f947f9a6c8c0aca383e814ca3c5cffab738d5646", size = 218523, upload-time = "2026-10-19T16:57:18.978Z" }, + { url = "https://files.pythonhosted.org/packages/b0/bb/559fe539992102c66cbca38b488af6c7b184ab7736d819afc0f72268a2b4/websockets-17.3-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:f27e7e8a85512b6a4b490d5756edde8e6772883a5800e1476172e44eda3f7d7c", size = 216103, upload-time = "2026-10-19T16:57:20.412Z" }, + { url = "https://files.pythonhosted.org/packages/34/ee/e8ade4f13a3d0da68636fe44bb8ac1a3e36fc57070512788320dead42067/websockets-17.3-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:6342f79d6f50e680c8aa59e0615d745e867e51ffd59c77093be60b7c433067af", size = 216316, upload-time = "2026-10-19T16:57:21.761Z" }, + { url = "https://files.pythonhosted.org/packages/4d/06/4812b2a7a5e6571d9be425e76cf54ee69aaa15720caa02307785aa5b19a7/websockets-17.3-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fc23e72bd7f8a8e540fed2244524d0a9aeb6ae3325f3d56f04a7a3debe5b04c2", size = 226467, upload-time = "2026-10-19T16:57:23.077Z" }, + { url = "https://files.pythonhosted.org/packages/d3/e8/adfc05d7f9b6b0cce6cc554426c50b7058609119b6b84a39171fe5ed4e50/websockets-17.3-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:722ab747ff5dc28070b1a8c020dba6466f13f2ab56bc251ad301fefa2454aa1e", size = 226736, upload-time = "2026-10-19T16:57:24.419Z" }, + { url = "https://files.pythonhosted.org/packages/96/b2/d5b93dd933552554507edae9e3a4890269a8ea8b46019450ea9c8a30e7cc/websockets-17.3-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0d20d6ea5aabbeba2e054d33e405192ef75dc12947bbd00ec97d23b14969d325", size = 228065, upload-time = "2026-10-19T16:57:25.908Z" }, + { url = "https://files.pythonhosted.org/packages/10/37/642f0f1cf1c4814147a4f0586f796a0b38e42b69e5a296bfc270fb660051/websockets-17.3-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:90cc9973c51811918247a78d9ab2f54871296e4e37e69a98aba4d3c9acedbf62", size = 229692, upload-time = "2026-10-19T16:57:27.63Z" }, + { url = "https://files.pythonhosted.org/packages/5b/33/eac81cf62ac394b61db6a27ccbe8fb9ae382016ef73ede1edf9242a6a43e/websockets-17.3-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:63b87c93e28957e6dc2bb92d31727a863c9bf4a4daa080f0995fdcb0242c9166", size = 228503, upload-time = "2026-10-19T16:57:29.031Z" }, + { url = "https://files.pythonhosted.org/packages/0d/84/e057788a9bd056f4f7d3a06b59efaaf724bbc6f2d1e1d316f8a93c951abc/websockets-17.3-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4f7431e71072962d3ed1768038200aa4f1e76d9c2d7bedd554d57a55faaf6644", size = 227379, upload-time = "2026-10-19T16:57:30.571Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a7/cca1f9f45b893bb5b7545f2b234f910e07fcd2f8065724ac1a62a0961be2/websockets-17.3-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:534ed583fd4323de45d196535a444cebd1af2105e6e4e0cdcbf8cea266a8747a", size = 224719, upload-time = "2026-10-19T16:57:31.895Z" }, + { url = "https://files.pythonhosted.org/packages/1f/a0/5d07451027150c802ce4c9999e86994a3f0de5f4dbe3d1cce46995999269/websockets-17.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:8879b6a4040ee925c638548238ab7fe612cf5fa8752fe847346da0da8be81c3f", size = 227564, upload-time = "2026-10-19T16:57:33.236Z" }, + { url = "https://files.pythonhosted.org/packages/62/90/835f37e13fac0c615a700caae0d39deff1de2d2b9b341836a786f9b359e0/websockets-17.3-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:58e5d1e935a90878a935c7d2737a25c71cf4f1c13b6e6bef454ab722c4f338bb", size = 226218, upload-time = "2026-10-19T16:57:34.876Z" }, + { url = "https://files.pythonhosted.org/packages/16/8a/718639939b947fe715501e8073350fd3f8a42f8b5eba3c0c2389c5836513/websockets-17.3-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:0a657b51e4a5f4ac577c11e706ff9a884ea5aaf3e2666bae283a251c63e66acb", size = 226712, upload-time = "2026-10-19T16:57:36.248Z" }, + { url = "https://files.pythonhosted.org/packages/e8/45/6bef8572b9b0dee51369b69776af1e4c7cc3a807f199dc3c165bbcf1199a/websockets-17.3-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:411aa0fb234f42a1444dea1e177890ddfc577b36ffdfafec25cb0d2c818e1af8", size = 227819, upload-time = "2026-10-19T16:57:37.586Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3f/3b726d0b46a132e641d2d8c0243ea8a7d39464dbaf28c7a206e51fbae681/websockets-17.3-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:623d4b44f64efdf235b1e8dce266057073d93cde6197e7d7684f20c297f67f30", size = 225573, upload-time = "2026-10-19T16:57:39.285Z" }, + { url = "https://files.pythonhosted.org/packages/90/96/cd972a12e1412d9b42c42ef42efbd81aa396ca9408d982d524012f011a8d/websockets-17.3-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:5d0c871efc696ef5eb8514df64b86ab28736db753ebb42a2101f3b562f4849c0", size = 226430, upload-time = "2026-10-19T16:57:40.915Z" }, + { url = "https://files.pythonhosted.org/packages/7d/2d/e11524771588188a39f6da234779d4942496b96c5a10b92f3df30db48608/websockets-17.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:1db2604e6aedb7a2cb2f6e0575d6c9a852ccac8239703a930ac83b63309177a5", size = 226728, upload-time = "2026-10-19T16:57:42.245Z" }, + { url = "https://files.pythonhosted.org/packages/17/9b/63722aed973f53ea579e1ce0c4563a2b8b536629e556feff26e4c6a2a60d/websockets-17.3-cp315-cp315t-win32.whl", hash = "sha256:07246d4f0293ef0b87eff3d7dd88c1f03dd9f678fa39f6de87b2af8504e7e0de", size = 218325, upload-time = "2026-10-19T16:57:43.585Z" }, + { url = "https://files.pythonhosted.org/packages/34/07/5e0c92e225546f1789c957c0383852797be7c899b07207e9b6eb4a93473f/websockets-17.3-cp315-cp315t-win_amd64.whl", hash = "sha256:856bbb24f2ee6436af7609267e73309e4bb0a0f9982f79f1c2e01019279c6671", size = 218641, upload-time = "2026-10-19T16:57:44.918Z" }, + { url = "https://files.pythonhosted.org/packages/d4/e4/339663de65c44ab3eb5209890e86eed26e1a131d6afab3df6f1c3e5b4d8c/websockets-17.3-cp315-cp315t-win_arm64.whl", hash = "sha256:a22c4956a4d858200dba83f0691bb692d77b0eeddfca80157820d20b85dea4f1", size = 218564, upload-time = "2026-10-19T16:57:46.332Z" }, + { url = "https://files.pythonhosted.org/packages/e1/f1/b462b1518779adf0b6fce305beee07357a79cf0724bbe61113ed6521882e/websockets-17.3-pp311-pypy311_pp80-macosx_10_15_x86_64.whl", hash = "sha256:34c78052277053bcb09810ac8e9ba4029f9e3e277833fc18ec1c928a6246ac91", size = 215839, upload-time = "2026-10-19T16:57:47.742Z" }, + { url = "https://files.pythonhosted.org/packages/a9/59/7dcea408892967516cc00d491ee7bba48577059c6eb0811f436b720bcd98/websockets-17.3-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:ef07e0b6b879f699a16fc7a513b974246fdeaef44908a021130cf4afe6a1c89c", size = 216114, upload-time = "2026-10-19T16:57:49.111Z" }, + { url = "https://files.pythonhosted.org/packages/c6/b7/2d447e461f1160dc8dcdd8b143e2adba54d402e5f828eeb1f8469970c937/websockets-17.3-pp311-pypy311_pp80-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:99c5637b74e2805ee4de9e2a43c8b45f678e50abb1c2da9880f0372f6498ba2f", size = 217065, upload-time = "2026-10-19T16:57:50.424Z" }, + { url = "https://files.pythonhosted.org/packages/01/51/35aa5edd12b23e0001b85517ad447342c07a29fcee62009b0859825b77a7/websockets-17.3-pp311-pypy311_pp80-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c460aeb12c2bd1c204944d9e9199595bf9e505eb542018d304bf9e63cb60cad", size = 216952, upload-time = "2026-10-19T16:57:51.72Z" }, + { url = "https://files.pythonhosted.org/packages/6c/db/537306b92236d761b99dc56a7316c0ad46f2854cd0756585b3a9d3fe5639/websockets-17.3-pp311-pypy311_pp80-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e1c174b366565e101f903f9d1cc96d5508f805759dd37d012743f1c73ec0d7b", size = 217844, upload-time = "2026-10-19T16:57:53.144Z" }, + { url = "https://files.pythonhosted.org/packages/e0/c2/494bed87536f3822872e57b07ac9afa744af88ff64dda664b9caa32b1a72/websockets-17.3-pp311-pypy311_pp80-win_amd64.whl", hash = "sha256:c4b3f2c7d84f303da873b634d0beef83b5e1137294c3de4e23102841dac0cf4a", size = 306274, upload-time = "2026-10-19T16:57:54.525Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1a/5582103e0362f86c4725051ded89e585ceaf750f26beace530b7e42c81c0/websockets-17.3-pp312-pypy312_pp80-macosx_10_15_x86_64.whl", hash = "sha256:0e65260a1b1d7b81651f47343d836de70b88b02358ae3658efa6382104ec37b1", size = 215872, upload-time = "2026-10-19T16:57:55.95Z" }, + { url = "https://files.pythonhosted.org/packages/ef/a7/f69d65ed3ff9ae73d428a918300c23a992e4d50683090b8dc33cc3341d5a/websockets-17.3-pp312-pypy312_pp80-macosx_11_0_arm64.whl", hash = "sha256:94c02b0702656f083c098bdbd93bce22a7aa7a2b34ba56d2472efb67dab7cf58", size = 216136, upload-time = "2026-10-19T16:57:57.251Z" }, + { url = "https://files.pythonhosted.org/packages/69/76/c64fda4ac18a3aeb32e66acec481011a766add480b6362fad80e2c3c1963/websockets-17.3-pp312-pypy312_pp80-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ade76f8c84ade325bbed96806357fffd353fcc29a5166d6dc6d39381f6d9f76c", size = 217080, upload-time = "2026-10-19T16:57:58.593Z" }, + { url = "https://files.pythonhosted.org/packages/6b/6d/085a3f0e6152bbe79917973f3d33cd008437de6c04675e2f15253b921f21/websockets-17.3-pp312-pypy312_pp80-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9a215749c9a88ef2842e880215193e56ae60b856773c99702b078236bc6d4adb", size = 216966, upload-time = "2026-10-19T16:57:59.924Z" }, + { url = "https://files.pythonhosted.org/packages/ed/b8/7eb904583ea2090b329f50487e6f8dc58eb558712b9f3c16f289f0be668e/websockets-17.3-pp312-pypy312_pp80-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e7b5d23ef144b32645d97bd99f382c4394c443395f45fe1576786838d26e308", size = 217828, upload-time = "2026-10-19T16:58:01.822Z" }, + { url = "https://files.pythonhosted.org/packages/79/36/f54b4a1cd9456ca0a65a76d77d181621433793aca633dfeed214748ff4f0/websockets-17.3-pp312-pypy312_pp80-win_amd64.whl", hash = "sha256:13eace1b315345ebac5b27821f5dcce24e5cf9976ab18f79e00a59bdd67e5d19", size = 306301, upload-time = "2026-10-19T16:58:03.161Z" }, + { url = "https://files.pythonhosted.org/packages/d7/b0/3f01fcb3406662f6deb1e3e72b34dd0979e993991c0b7435a9e12bc14f02/websockets-17.3-py3-none-any.whl", hash = "sha256:dc2b495a92b3971966bf102e73589f950db3ec1ca6713265355ce7771d97b199", size = 212375, upload-time = "2026-10-19T16:58:04.483Z" }, +]
    NetWarempm_netware
    OS/2mpmt_os2
    Unixprefork, worker, or event, depending on platform capabilities
    /new