From d5325781b38052fbdf4cc28a6c6d3052b9424b51 Mon Sep 17 00:00:00 2001 From: Stefan Fritsch Date: Sat, 19 Dec 2015 09:17:42 +0100 Subject: Imported Upstream version 2.4.18 --- CHANGES | 97 ++ CMakeLists.txt | 15 +- INSTALL | 17 - build/ltmain.sh | 4 +- configure | 70 +- docs/conf/httpd.conf.in | 11 - docs/manual/convenience.map | 7 + docs/manual/mod/core.html.de | 17 + docs/manual/mod/core.html.en | 25 + docs/manual/mod/core.html.es | 17 + docs/manual/mod/core.html.fr | 28 + docs/manual/mod/core.html.ja.utf8 | 17 + docs/manual/mod/core.html.tr.utf8 | 17 + docs/manual/mod/directives.html.de | 9 +- docs/manual/mod/directives.html.en | 9 +- docs/manual/mod/directives.html.es | 9 +- docs/manual/mod/directives.html.fr | 9 +- docs/manual/mod/directives.html.ja.utf8 | 9 +- docs/manual/mod/directives.html.ko.euc-kr | 9 +- docs/manual/mod/directives.html.tr.utf8 | 9 +- docs/manual/mod/directives.html.zh-cn.utf8 | 9 +- docs/manual/mod/mod_allowmethods.html.en | 12 +- docs/manual/mod/mod_http2.html.en | 402 ++++++- docs/manual/mod/mod_proxy.html.en | 5 + docs/manual/mod/mod_proxy.html.fr | 2 + docs/manual/mod/mod_proxy_fdpass.html.en | 11 +- docs/manual/mod/mod_proxy_fdpass.html.fr | 2 + docs/manual/mod/mod_rewrite.html.fr | 7 +- docs/manual/mod/mod_ssl.html.en | 32 +- docs/manual/mod/mod_ssl.html.fr | 39 +- docs/manual/mod/mod_substitute.html.fr | 27 +- docs/manual/mod/mpm_common.html.fr | 56 +- docs/manual/mod/quickreference.html.de | 468 ++++---- docs/manual/mod/quickreference.html.en | 466 ++++---- docs/manual/mod/quickreference.html.es | 466 ++++---- docs/manual/mod/quickreference.html.fr | 474 ++++---- docs/manual/mod/quickreference.html.ja.utf8 | 462 ++++---- docs/manual/mod/quickreference.html.ko.euc-kr | 462 ++++---- docs/manual/mod/quickreference.html.tr.utf8 | 470 ++++---- docs/manual/mod/quickreference.html.zh-cn.utf8 | 466 ++++---- docs/manual/style/version.ent | 2 +- httpd.spec | 2 +- include/ap_config_auto.h.in | 7 + include/ap_mmn.h | 3 +- include/ap_release.h | 2 +- include/http_core.h | 13 + include/http_protocol.h | 71 +- include/httpd.h | 7 + modules/aaa/mod_auth_basic.c | 6 +- modules/aaa/mod_authn_anon.c | 4 +- modules/aaa/mod_authnz_ldap.c | 4 +- modules/arch/win32/mod_isapi.c | 3 +- modules/cache/cache_util.h | 2 +- modules/http/http_request.c | 88 +- modules/http2/config.m4 | 14 +- modules/http2/h2_alt_svc.c | 2 +- modules/http2/h2_bucket_eoc.c | 109 ++ modules/http2/h2_bucket_eoc.h | 31 + modules/http2/h2_bucket_eos.c | 109 ++ modules/http2/h2_bucket_eos.h | 30 + modules/http2/h2_config.c | 268 ++++- modules/http2/h2_config.h | 28 +- modules/http2/h2_conn.c | 321 +----- modules/http2/h2_conn.h | 23 +- modules/http2/h2_conn_io.c | 225 ++-- modules/http2/h2_conn_io.h | 26 +- modules/http2/h2_ctx.c | 10 +- modules/http2/h2_ctx.h | 18 +- modules/http2/h2_from_h1.c | 71 +- modules/http2/h2_from_h1.h | 16 +- modules/http2/h2_h2.c | 563 +++++++++- modules/http2/h2_h2.h | 74 +- modules/http2/h2_io.c | 180 ++- modules/http2/h2_io.h | 53 +- modules/http2/h2_io_set.c | 63 +- modules/http2/h2_io_set.h | 19 +- modules/http2/h2_mplx.c | 554 ++++++---- modules/http2/h2_mplx.h | 107 +- modules/http2/h2_push.c | 395 +++++++ modules/http2/h2_push.h | 31 + modules/http2/h2_request.c | 417 +++++-- modules/http2/h2_request.h | 61 +- modules/http2/h2_response.c | 242 ++--- modules/http2/h2_response.h | 65 +- modules/http2/h2_session.c | 1388 ++++++++++++++++-------- modules/http2/h2_session.h | 88 +- modules/http2/h2_stream.c | 641 +++++++++-- modules/http2/h2_stream.h | 252 ++++- modules/http2/h2_stream_set.c | 177 ++- modules/http2/h2_stream_set.h | 17 +- modules/http2/h2_switch.c | 31 +- modules/http2/h2_task.c | 358 ++---- modules/http2/h2_task.h | 123 +-- modules/http2/h2_task_input.c | 75 +- modules/http2/h2_task_input.h | 6 +- modules/http2/h2_task_output.c | 50 +- modules/http2/h2_task_output.h | 8 +- modules/http2/h2_task_queue.c | 153 ++- modules/http2/h2_task_queue.h | 141 +-- modules/http2/h2_to_h1.c | 305 ------ modules/http2/h2_to_h1.h | 87 -- modules/http2/h2_util.c | 382 ++++++- modules/http2/h2_util.h | 75 +- modules/http2/h2_version.h | 4 +- modules/http2/h2_worker.c | 68 +- modules/http2/h2_worker.h | 14 +- modules/http2/h2_workers.c | 78 +- modules/http2/h2_workers.h | 1 + modules/http2/mod_http2.dsp | 16 +- modules/proxy/mod_proxy.c | 2 +- modules/proxy/mod_proxy_fdpass.c | 6 +- modules/ssl/mod_ssl.c | 30 +- modules/ssl/ssl_engine_config.c | 6 +- modules/ssl/ssl_engine_io.c | 174 ++- modules/ssl/ssl_engine_kernel.c | 216 +++- modules/ssl/ssl_engine_vars.c | 62 +- modules/ssl/ssl_private.h | 3 + modules/ssl/ssl_util_stapling.c | 71 +- server/core.c | 46 +- server/protocol.c | 83 +- server/scoreboard.c | 25 +- server/util_script.c | 31 +- 122 files changed, 9335 insertions(+), 5040 deletions(-) create mode 100644 modules/http2/h2_bucket_eoc.c create mode 100644 modules/http2/h2_bucket_eoc.h create mode 100644 modules/http2/h2_bucket_eos.c create mode 100644 modules/http2/h2_bucket_eos.h create mode 100644 modules/http2/h2_push.c create mode 100644 modules/http2/h2_push.h delete mode 100644 modules/http2/h2_to_h1.c delete mode 100644 modules/http2/h2_to_h1.h diff --git a/CHANGES b/CHANGES index 020aea1a..ef9a38bc 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,99 @@ -*- coding: utf-8 -*- +Changes with Apache 2.4.18 + + *) mod_ssl: for all ssl_engine_vars.c lookups, fall back to master connection + if conn_rec itself holds no valid SSLConnRec*. Fixes PR58666. + [Stefan Eissing] + + *) mod_http2: connection level window for flow control is set to protocol + maximum of 2GB-1, preventing window exhaustion when sending data on many + streams with higher cumulative window size. + Reducing write frequency unless push promises need to be flushed. + [Stefan Eissing] + + *) mod_http2: required minimum version of libnghttp2 is 1.2.1 + [Stefan Eissing] + + *) mod_proxy_fdpass: Fix AH01153 error when using the default configuration. + In earlier version of httpd, you can explicitelly set the 'flusher' parameter + to 'flush' as a workaround. (i.e. flusher=flush) + Add documentation for the 'flusher' parameter when defining a proxy worker. + [Christophe Jaillet] + + *) mod_ssl: For the "SSLStaplingReturnResponderErrors off" case, make sure + to only staple responses with certificate status "good". [Kaspar Brand] + + *) mod_http2: new directive 'H2PushPriority' to allow priority specifications + on server pushed streams according to their content-type. + [Stefan Eissing] + + *) mod_http2: fixes crash on connection abort for a busy connection. + fixes crash on a request that did not produce any response. + [Stefan Eissing] + + *) mod_http2: trailers are sent after reponse body if set in request_rec + trailers_out before the end-of-request bucket is sent through the + output filters. [Stefan Eissing] + + *) mod_http2: incoming trailers (headers after request body) are properly + forwarded to the processing engine. [Stefan Eissing] + + *) mod_http2: new directive 'H2Push' to en-/disable HTTP/2 server + pushes a server/virtual host. Pushes are initiated by the presence + of 'Link:' headers with relation 'preload' on a response. [Stefan Eissing] + + *) mod_http2: write performance of http2 improved for larger resources, + especially static files. [Stefan Eissing] + + *) core: if the first HTTP/1.1 request on a connection goes to a server that + prefers different protocols, these protocols are announced in a Upgrade: + header on the response, mentioning the preferred protocols. + [Stefan Eissing] + + *) mod_http2: new directives 'H2TLSWarmUpSize' and 'H2TLSCoolDownSecs' + to control TLS record sizes during connection lifetime. + [Stefan Eissing] + + *) mod_http2: new directive 'H2ModernTLSOnly' to enforce security + requirements of RFC 7540 on TLS connections. [Stefan Eissing] + + *) core: add ap_get_protocol_upgrades() to retrieve the list of protocols + that a client could possibly upgrade to. Use in first request on a + connection to announce protocol choices. [Stefan Eissing] + + *) mod_http2: reworked deallocation on connection shutdown and worker + abort. Separate parent pool for all workers. worker threads are joined + on planned worker shutdown. [Yann Ylavic, Stefan Eissing] + + *) mod_ssl: when receiving requests for other virtual hosts than the handshake + server, the SSL parameters are checked for equality. With equal + configuration, requests are passed for processing. Any change will trigger + the old behaviour of "421 Misdirected Request". + SSL now remembers the cipher suite that was used for the last handshake. + This is compared against for any vhost/directory cipher specification. + Detailed examination of renegotiation is only done when these do not + match. + Renegotiation is 403ed when a master connection is present. Exact reason + is given additionally in a request note. [Stefan Eissing] + + *) core: Fix scoreboard crash (SIGBUS) on hardware requiring strict 64bit + alignment (SPARC64, PPC64). [Yann Ylavic] + + *) mod_cache: Accept HT (Horizontal Tab) when parsing cache related header + fields as described in RFC7230. [Christophe Jaillet] + + *) core/util_script: making REDIRECT_URL a full URL is now opt-in + via new 'QualifyRedirectURL' directive. + + *) core: Limit to ten the number of tolerated empty lines between request, + and consume them before the pipelining check to avoid possible response + delay when reading the next request without flushing. [Yann Ylavic] + + *) mod_ssl: Extend expression parser registration to support ssl variables + in any expression using mod_rewrite syntax "%{SSL:VARNAME}" or function + syntax "ssl(VARNAME)". [Rainer Jung] + Changes with Apache 2.4.17 *) mod_http2: added donated HTTP/2 implementation via core module. Similar @@ -9,6 +103,9 @@ Changes with Apache 2.4.17 to avoid reusing it should the close be effective after some new request is ready to be sent. [Yann Ylavic] + *) mod_ssl: Make the output filter more friendly with deferred write and + response pipelining. [Yann Ylavic, Joe Orton] + *) mod_substitute: Allow to configure the patterns merge order with the new SubstituteInheritBefore on|off directive. PR 57641 [Marc.Stern , Yann Ylavic, William Rowe] diff --git a/CMakeLists.txt b/CMakeLists.txt index b13636f2..7de4c5a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -379,17 +379,18 @@ SET(mod_http2_requires NGHTTP2_FOUND) SET(mod_http2_extra_defines ssize_t=long) SET(mod_http2_extra_libs ${NGHTTP2_LIBRARIES}) SET(mod_http2_extra_sources - modules/http2/h2_alt_svc.c modules/http2/h2_config.c + modules/http2/h2_alt_svc.c modules/http2/h2_bucket_eoc.c + modules/http2/h2_bucket_eos.c modules/http2/h2_config.c modules/http2/h2_conn.c modules/http2/h2_conn_io.c modules/http2/h2_ctx.c modules/http2/h2_from_h1.c modules/http2/h2_h2.c modules/http2/h2_io.c modules/http2/h2_io_set.c modules/http2/h2_mplx.c - modules/http2/h2_request.c modules/http2/h2_response.c - modules/http2/h2_session.c modules/http2/h2_stream.c - modules/http2/h2_stream_set.c modules/http2/h2_switch.c - modules/http2/h2_task.c modules/http2/h2_task_input.c - modules/http2/h2_task_output.c modules/http2/h2_task_queue.c - modules/http2/h2_to_h1.c modules/http2/h2_util.c + modules/http2/h2_push.c modules/http2/h2_request.c + modules/http2/h2_response.c modules/http2/h2_session.c + modules/http2/h2_stream.c modules/http2/h2_stream_set.c + modules/http2/h2_switch.c modules/http2/h2_task.c + modules/http2/h2_task_input.c modules/http2/h2_task_output.c + modules/http2/h2_task_queue.c modules/http2/h2_util.c modules/http2/h2_worker.c modules/http2/h2_workers.c ) SET(mod_ldap_extra_defines LDAP_DECLARE_EXPORT) diff --git a/INSTALL b/INSTALL index 155df5fc..2fde242c 100644 --- a/INSTALL +++ b/INSTALL @@ -74,23 +74,6 @@ For complete documentation, see manual/platform/windows.html.en or http://httpd.apache.org/docs/2.4/platform/windows.html. - The Apache/Win32 binaries are distributed as Windows Installer packages - (.msi) named httpd-2.4.xx-win32-x86-no_ssl.msi for a version without mod_ssl - and httpd-2.4.xx-win32-x86-openssl-1.0.1x.msi for a version including the - mod_ssl plus the openssl library and command line utility. Additional 64 bit - binaries have similarly named -win64-x64 package names. These packages - may be unpacked without "installing" them by using the msiexec /a option. - - If you have unpacked a source distribution (named httpd-2.4.x-win32-src.zip, - without any -x86 or -x64 notation) you must compile the package yourself, - see the links mentioned above. Unless you intended to do this, please look - again for an .msi package in http://www.apache.org/dist/httpd/binaries/win32/ - and install that desired .msi package. - - The .msi package configures the httpd.conf file, and installs and starts - the Apache2.4 service for you. It also installs plenty of useful shortcuts - and the taskbar ApacheMonitor. We strongly encourage you to use it. - Postscript ---------- diff --git a/build/ltmain.sh b/build/ltmain.sh index 0f0a2da3..b6f3fcbb 100644 --- a/build/ltmain.sh +++ b/build/ltmain.sh @@ -7273,9 +7273,11 @@ func_mode_link () # --sysroot=* for sysroot support # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization # -stdlib=* select c++ std lib with clang + # -fsanitize=* Clang memory and address sanitizer -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ - -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*) + -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \ + -fsanitize=*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result func_append compile_command " $arg" diff --git a/configure b/configure index a3d0f9b3..9f97e0d7 100755 --- a/configure +++ b/configure @@ -18162,7 +18162,7 @@ EOF > $modpath_current/modules.mk -http2_objs="mod_http2.lo h2_alt_svc.lo h2_config.lo h2_conn.lo h2_conn_io.lo h2_ctx.lo h2_from_h1.lo h2_h2.lo h2_io.lo h2_io_set.lo h2_mplx.lo h2_request.lo h2_response.lo h2_session.lo h2_stream.lo h2_stream_set.lo h2_switch.lo h2_task.lo h2_task_input.lo h2_task_output.lo h2_task_queue.lo h2_to_h1.lo h2_util.lo h2_worker.lo h2_workers.lo " +http2_objs="mod_http2.lo h2_alt_svc.lo h2_bucket_eoc.lo h2_bucket_eos.lo h2_config.lo h2_conn.lo h2_conn_io.lo h2_ctx.lo h2_from_h1.lo h2_h2.lo h2_io.lo h2_io_set.lo h2_mplx.lo h2_push.lo h2_request.lo h2_response.lo h2_session.lo h2_stream.lo h2_stream_set.lo h2_switch.lo h2_task.lo h2_task_input.lo h2_task_output.lo h2_task_queue.lo h2_util.lo h2_worker.lo h2_workers.lo " @@ -18586,8 +18586,8 @@ fi fi fi - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for nghttp2 version >= 1.0.0" >&5 -$as_echo_n "checking for nghttp2 version >= 1.0.0... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for nghttp2 version >= 1.2.1" >&5 +$as_echo_n "checking for nghttp2 version >= 1.2.1... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include @@ -18598,7 +18598,7 @@ main () #if !defined(NGHTTP2_VERSION_NUM) #error "Missing nghttp2 version" #endif -#if NGHTTP2_VERSION_NUM < 0x010000 +#if NGHTTP2_VERSION_NUM < 0x010201 #error "Unsupported nghttp2 version " NGHTTP2_VERSION_TEXT #endif ; @@ -18702,6 +18702,68 @@ done { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: nghttp2 library is unusable" >&5 $as_echo "$as_me: WARNING: nghttp2 library is unusable" >&2;} fi + for ac_func in nghttp2_stream_get_weight +do : + ac_fn_c_check_func "$LINENO" "nghttp2_stream_get_weight" "ac_cv_func_nghttp2_stream_get_weight" +if test "x$ac_cv_func_nghttp2_stream_get_weight" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_NGHTTP2_STREAM_GET_WEIGHT 1 +_ACEOF + + if test "x$MOD_CPPFLAGS" = "x"; then + test "x$silent" != "xyes" && echo " setting MOD_CPPFLAGS to \""-DH2_NG2_STREAM_API"\"" + MOD_CPPFLAGS=""-DH2_NG2_STREAM_API"" + else + apr_addto_bugger=""-DH2_NG2_STREAM_API"" + for i in $apr_addto_bugger; do + apr_addto_duplicate="0" + for j in $MOD_CPPFLAGS; do + if test "x$i" = "x$j"; then + apr_addto_duplicate="1" + break + fi + done + if test $apr_addto_duplicate = "0"; then + test "x$silent" != "xyes" && echo " adding \"$i\" to MOD_CPPFLAGS" + MOD_CPPFLAGS="$MOD_CPPFLAGS $i" + fi + done + fi + +fi +done + + for ac_func in nghttp2_session_change_stream_priority +do : + ac_fn_c_check_func "$LINENO" "nghttp2_session_change_stream_priority" "ac_cv_func_nghttp2_session_change_stream_priority" +if test "x$ac_cv_func_nghttp2_session_change_stream_priority" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_NGHTTP2_SESSION_CHANGE_STREAM_PRIORITY 1 +_ACEOF + + if test "x$MOD_CPPFLAGS" = "x"; then + test "x$silent" != "xyes" && echo " setting MOD_CPPFLAGS to \""-DH2_NG2_CHANGE_PRIO"\"" + MOD_CPPFLAGS=""-DH2_NG2_CHANGE_PRIO"" + else + apr_addto_bugger=""-DH2_NG2_CHANGE_PRIO"" + for i in $apr_addto_bugger; do + apr_addto_duplicate="0" + for j in $MOD_CPPFLAGS; do + if test "x$i" = "x$j"; then + apr_addto_duplicate="1" + break + fi + done + if test $apr_addto_duplicate = "0"; then + test "x$silent" != "xyes" && echo " adding \"$i\" to MOD_CPPFLAGS" + MOD_CPPFLAGS="$MOD_CPPFLAGS $i" + fi + done + fi + +fi +done + else { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: nghttp2 version is too old" >&5 $as_echo "$as_me: WARNING: nghttp2 version is too old" >&2;} diff --git a/docs/conf/httpd.conf.in b/docs/conf/httpd.conf.in index 6e418913..966d2c3a 100644 --- a/docs/conf/httpd.conf.in +++ b/docs/conf/httpd.conf.in @@ -406,15 +406,4 @@ Include @rel_sysconfdir@/extra/proxy-html.conf SSLRandomSeed startup builtin SSLRandomSeed connect builtin -# -# uncomment out the below to deal with user agents that deliberately -# violate open standards by misusing DNT (DNT *must* be a specific -# end-user choice) -# -# -#BrowserMatch "MSIE 10.0;" bad_DNT -# -# -#RequestHeader unset DNT env=bad_DNT -# diff --git a/docs/manual/convenience.map b/docs/manual/convenience.map index fd639959..1d6a4866 100644 --- a/docs/manual/convenience.map +++ b/docs/manual/convenience.map @@ -245,9 +245,15 @@ h2maxsessionstreams mod/mod_http2.html#h2maxsessionstreams h2maxworkeridleseconds mod/mod_http2.html#h2maxworkeridleseconds h2maxworkers mod/mod_http2.html#h2maxworkers h2minworkers mod/mod_http2.html#h2minworkers +h2moderntlsonly mod/mod_http2.html#h2moderntlsonly +h2push mod/mod_http2.html#h2push +h2pushpriority mod/mod_http2.html#h2pushpriority h2serializeheaders mod/mod_http2.html#h2serializeheaders h2sessionextrafiles mod/mod_http2.html#h2sessionextrafiles h2streammaxmemsize mod/mod_http2.html#h2streammaxmemsize +h2tlscooldownsecs mod/mod_http2.html#h2tlscooldownsecs +h2tlswarmupsize mod/mod_http2.html#h2tlswarmupsize +h2upgrade mod/mod_http2.html#h2upgrade h2windowsize mod/mod_http2.html#h2windowsize header mod/mod_headers.html#header headername mod/mod_autoindex.html#headername @@ -426,6 +432,7 @@ proxysourceaddress mod/mod_proxy.html#proxysourceaddress proxystatus mod/mod_proxy.html#proxystatus proxytimeout mod/mod_proxy.html#proxytimeout proxyvia mod/mod_proxy.html#proxyvia +qualifyredirecturl mod/core.html#qualifyredirecturl readmename mod/mod_autoindex.html#readmename receivebuffersize mod/mpm_common.html#receivebuffersize redirect mod/mod_alias.html#redirect diff --git a/docs/manual/mod/core.html.de b/docs/manual/mod/core.html.de index 4345682f..c3e52968 100644 --- a/docs/manual/mod/core.html.de +++ b/docs/manual/mod/core.html.de @@ -102,6 +102,7 @@ Servers
  • Protocol
  • Protocols
  • ProtocolsHonorOrder
  • +
  • QualifyRedirectURL
  • RLimitCPU
  • RLimitMEM
  • RLimitNPROC
  • @@ -2762,6 +2763,22 @@ On Windows, from Apache 2.3.3 and later.
    top
    +

    QualifyRedirectURL-Direktive

    + + + + + + + + + +
    Beschreibung:Controls whether the REDIRECT_URL environent variable is + fully qualified
    Syntax:QualifyRedirectURL ON|OFF
    Voreinstellung:QualifyRedirectURL OFF
    Kontext:Serverkonfiguration, Virtual Host, Verzeichnis
    AllowOverride:FileInfo
    Status:Core
    Modul:core
    Kompatibilität:Directive supported in 2.4.18 and later. 2.4.17 acted +as if 'QualifyRedirectURL ON' was configured.

    Die Dokumentation zu dieser Direktive wurde + noch nicht übersetzt. Bitte schauen Sie in die englische + Version.

    +
    top

    RLimitCPU-Direktive

  • Protocol
  • Protocols
  • ProtocolsHonorOrder
  • +
  • QualifyRedirectURL
  • RLimitCPU
  • RLimitMEM
  • RLimitNPROC
  • @@ -3663,6 +3664,30 @@ On Windows, from Apache 2.3.3 and later. + +
    top
    +
    Beschreibung:Begrenzt den CPU-Verbrauch von Prozessen, die von diff --git a/docs/manual/mod/core.html.en b/docs/manual/mod/core.html.en index 22d0a080..549cbafc 100644 --- a/docs/manual/mod/core.html.en +++ b/docs/manual/mod/core.html.en @@ -99,6 +99,7 @@ available
    + + + + + + + + +
    Description:Controls whether the REDIRECT_URL environent variable is + fully qualified
    Syntax:QualifyRedirectURL ON|OFF
    Default:QualifyRedirectURL OFF
    Context:server config, virtual host, directory
    Override:FileInfo
    Status:Core
    Module:core
    Compatibility:Directive supported in 2.4.18 and later. 2.4.17 acted +as if 'QualifyRedirectURL ON' was configured.
    +

    This directive controls whether the server will ensure that the + REDIRECT_URL environment variable is fully qualified. By default, + the variable contains the verbatim URL requested by the client, + such as "/index.html". With QualifyRedirectURL ON, the same request would result in a + value such as "http://www.example.com/index.html".

    +

    Even without this directive set, when a request is issued against a + fully qualified URL, REDIRECT_URL will remain fully qualified. +

    +
    top

    RLimitCPU Directive

    diff --git a/docs/manual/mod/core.html.es b/docs/manual/mod/core.html.es index 546664d6..9a5eeb6f 100644 --- a/docs/manual/mod/core.html.es +++ b/docs/manual/mod/core.html.es @@ -102,6 +102,7 @@
  • Protocol
  • Protocols
  • ProtocolsHonorOrder
  • +
  • QualifyRedirectURL
  • RLimitCPU
  • RLimitMEM
  • RLimitNPROC
  • @@ -3407,6 +3408,22 @@ On Windows from Apache 2.3.3 and later.
    top
    +

    QualifyRedirectURL Directiva

    + + + + + + + + + +
    Descripción:Controls whether the REDIRECT_URL environent variable is + fully qualified
    Sintaxis:QualifyRedirectURL ON|OFF
    Valor por defecto:QualifyRedirectURL OFF
    Contexto:server config, virtual host, directory
    Prevalece sobre:FileInfo
    Estado:Core
    Módulo:core
    Compatibilidad:Directive supported in 2.4.18 and later. 2.4.17 acted +as if 'QualifyRedirectURL ON' was configured.

    The documentation for this directive has + not been translated yet. Please have a look at the English + version.

    +
    top

    RLimitCPU Directiva

  • Protocol
  • Protocols
  • ProtocolsHonorOrder
  • +
  • QualifyRedirectURL
  • RLimitCPU
  • RLimitMEM
  • RLimitNPROC
  • @@ -3940,6 +3941,33 @@ seulement depuis la version 2.3.3 sous Windows. + +
    top
    +
    Descripción:Limits the CPU consumption of processes launched diff --git a/docs/manual/mod/core.html.fr b/docs/manual/mod/core.html.fr index ed5cb1c6..fb354463 100644 --- a/docs/manual/mod/core.html.fr +++ b/docs/manual/mod/core.html.fr @@ -99,6 +99,7 @@ disponibles
    + + + + + + + + +
    Description:Vérifie si la variable d'environnement REDIRECT_URL est +pleinement qualifiée
    Syntaxe:QualifyRedirectURL ON|OFF
    Défaut:QualifyRedirectURL OFF
    Contexte:configuration du serveur, serveur virtuel, répertoire
    AllowOverride:FileInfo
    Statut:Core
    Module:core
    Compatibilité:Directive supportée à partir de la version 2.4.18 du +serveur HTTP Apache. Jusqu'à la version 2.4.17, le serveur se comportait +comme si la directive QualifyRedirectURL était définie à ON.
    +

    Cette directive permet de s'assurer que le serveur vérifiera que + la variable d'environnement REDIRECT_URL est bien pleinement + qualifiée. Par défaut, cette variable contient l'URL textuellement + demandée par le client, par exemple "/index.html". Avec QualifyRedirectURL ON, la même requête + affectera à la variable REDIRECT_URL une valeur du style + "http://www.example.com/index.html".

    +

    Même si cette directive n'est pas définie, lorsqu'une requête est + soumise avec une URL pleinement qualifiée, la variable REDIRECT_URL + contiendra quand-même une URL pleinement qualifiée. +

    +
    top

    Directive RLimitCPU

    diff --git a/docs/manual/mod/core.html.ja.utf8 b/docs/manual/mod/core.html.ja.utf8 index 2333386c..fbe38336 100644 --- a/docs/manual/mod/core.html.ja.utf8 +++ b/docs/manual/mod/core.html.ja.utf8 @@ -102,6 +102,7 @@
  • Protocol
  • Protocols
  • ProtocolsHonorOrder
  • +
  • QualifyRedirectURL
  • RLimitCPU
  • RLimitMEM
  • RLimitNPROC
  • @@ -2704,6 +2705,22 @@ On Windows, from Apache 2.3.3 and later.
    top
    +

    QualifyRedirectURL ディレクティブ

    + + + + + + + + + +
    説明:Controls whether the REDIRECT_URL environent variable is + fully qualified
    æ§‹æ–‡:QualifyRedirectURL ON|OFF
    デフォルト:QualifyRedirectURL OFF
    コンテキスト:サーãƒè¨­å®šãƒ•ァイル, ãƒãƒ¼ãƒãƒ£ãƒ«ãƒ›ã‚¹ãƒˆ, ディレクトリ
    上書ã:FileInfo
    ステータス:Core
    モジュール:core
    äº’æ›æ€§:Directive supported in 2.4.18 and later. 2.4.17 acted +as if 'QualifyRedirectURL ON' was configured.

    ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒ†ã‚£ãƒ–ã®è§£èª¬æ–‡æ›¸ã¯ + ã¾ã ç¿»è¨³ã•れã¦ã„ã¾ã›ã‚“。英語版をã”覧ãã ã•ã„。 +

    +
    top

    RLimitCPU ディレクティブ

    説明:Apache ã®å­ãƒ—ロセスã‹ã‚‰èµ·å‹•ã•れãŸãƒ—ロセス㮠CPU 消費é‡ã‚’ diff --git a/docs/manual/mod/core.html.tr.utf8 b/docs/manual/mod/core.html.tr.utf8 index 8fc1bb5a..1e37d4db 100644 --- a/docs/manual/mod/core.html.tr.utf8 +++ b/docs/manual/mod/core.html.tr.utf8 @@ -33,6 +33,7 @@  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
    @@ -99,6 +100,7 @@
  • Protocol
  • Protocols
  • ProtocolsHonorOrder
  • +
  • QualifyRedirectURL
  • RLimitCPU
  • RLimitMEM
  • RLimitNPROC
  • @@ -3650,6 +3652,21 @@ Mutex fcntl:/var/httpd/locks mpm-accept
    top
    +

    QualifyRedirectURL Yönergesi

    + + + + + + + + + +
    Açıklama:Controls whether the REDIRECT_URL environent variable is + fully qualified
    Sözdizimi:QualifyRedirectURL ON|OFF
    Öntanımlı:QualifyRedirectURL OFF
    BaÄŸlam:sunucu geneli, sanal konak, dizin
    Geçersizleştirme:FileInfo
    Durum:Çekirdek
    Modül:core
    Uyumluluk:Directive supported in 2.4.18 and later. 2.4.17 acted +as if 'QualifyRedirectURL ON' was configured.

    Bu yönergenin belgesi henüz Türkçeye çevrilmedi. + Lütfen İngilizce sürümüne bakınız.

    +
    top

    RLimitCPU Yönergesi

    Açıklama:Apache httpd alt süreçleri tarafından çalıştırılan süreçlerin diff --git a/docs/manual/mod/directives.html.de b/docs/manual/mod/directives.html.de index 83de76d5..8b9f60e0 100644 --- a/docs/manual/mod/directives.html.de +++ b/docs/manual/mod/directives.html.de @@ -45,7 +45,7 @@ zu jeder Direktive eine Zusammenfassung der Details enthält.

    -

     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  R  |  S  |  T  |  U  |  V  |  W  |  X 

    +

     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  Q  |  R  |  S  |  T  |  U  |  V  |  W  |  X 

    Source File:mod_allowmethods.c

    Summary

    -

    This module makes it easy to restrict what HTTP methods can -used on an server. The most common configuration would be:

    +

    This module makes it easy to restrict what HTTP methods can be +used on a server. The most common configuration would be:

    <Location "/">
        AllowMethods GET POST OPTIONS
    @@ -62,9 +62,9 @@ used on an server. The most common configuration would be:

    Module:mod_allowmethods
    -

    The HTTP-methods are case sensitive, and are generally as per -RFC given in upper case. The GET and HEAD methods are treated as -equivalent. The reset keyword can be used +

    The HTTP-methods are case sensitive and are generally, as per +RFC, given in upper case. The GET and HEAD methods are treated as +equivalent. The reset keyword can be used to turn off mod_allowmethods in a deeper nested context:

    <Location "/svn">
    @@ -73,7 +73,7 @@ turn off mod_allowme
     
     
     
     
    diff --git a/docs/manual/mod/mod_http2.html.en b/docs/manual/mod/mod_http2.html.en
    index e62abec2..c3c6d18d 100644
    --- a/docs/manual/mod/mod_http2.html.en
    +++ b/docs/manual/mod/mod_http2.html.en
    @@ -30,8 +30,9 @@
     
    - -
    Description:Support for the HTTP/2 transport layer
    Status:Extension
    Module Identifier:h2_module
    Source File:mod_http2.c
    +Module Identifier:http2_module +Source File:mod_http2.c +Compatibility:Available in version 2.4.17 and later

    Summary

    This module provides HTTP/2 (RFC 7540) support for the Apache @@ -46,6 +47,13 @@ release relative to other standard modules. Users are encouraged to consult the "CHANGES" file for potential updates.

    + +

    You must enable HTTP/2 via Protocols in order to use the + functionality described in this document:

    + +
    Protocols h2 http/1.1
    + + @@ -66,7 +80,7 @@ - + @@ -76,12 +90,31 @@ should be used inside a <VirtualHost> section to enable direct HTTP/2 communication for that virtual host. +

    +

    Direct communication means that if the first bytes received by the server on a connection match the HTTP/2 preamble, the HTTP/2 protocol is switched to immediately without further negotiation. - This mode falls outside the RFC 7540 but has become widely implemented - as it is very convenient for development and testing. - By default the direct HTTP/2 mode is enabled. + This mode is defined in RFC 7540 for the cleartext (h2c) case. Its + use on TLS connections not mandated by the standard. +

    +

    + When a server/vhost does not have h2 or h2c enabled via + <Protocols>, + the connection is never inspected for a HTTP/2 preamble. H2Direct + does not matter then. This is important for connections that + use protocols where an initial read might hang indefinitely, such + as NNTP. +

    +

    + For clients that have out-of-band knowledge about a server + supporting h2c, direct HTTP/2 saves the client from having to + perform an HTTP/1.1 upgrade, resulting in better performance + and avoiding the Upgrade restrictions on request bodies. +

    +

    + This makes direct h2c attractive for server to server communication + as well, when the connection can be trusted or is secured by other means.

    Example

    H2Direct on
    @@ -162,11 +195,233 @@

    Example

    H2MinWorkers 10
    + +
    top
    +
    Description:H2 Direct Protocol Switch
    Syntax:H2Direct on|off
    Default:H2Direct on (for non TLS)
    Default:H2Direct on for h2c, off for h2 protocol
    Context:server config, virtual host
    Status:Extension
    Module:mod_http2
    + + + + + + + +
    Description:Require HTTP/2 connections to be "modern TLS" only
    Syntax:H2ModernTLSOnly on|off
    Default:H2ModernTLSOnly on
    Context:server config, virtual host
    Status:Extension
    Module:mod_http2
    Compatibility:Available in version 2.4.18 and later.
    +

    + This directive toggles the security checks on HTTP/2 connections + in TLS mode (https:). This can be used server wide or for specific + <VirtualHost>s. +

    +

    + The security checks require that the TSL 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. +

    +

    + The name stems from the + Security/Server Side TLS + definitions at mozilla where "modern compatibility" is defined. Mozilla Firefox and + other browsers require modern compatibility for HTTP/2 connections. As everything + in OpSec, this is a moving target and can be expected to evolve in the future. +

    +

    + One purpose of having these checks in mod_http2 is to enforce this + security level for all connections, not only those from browsers. The other + purpose is to prevent the negotiation of HTTP/2 as a protocol should + the requirements not be met. +

    +

    + Ultimately, the security of the TLS connection is determined by the + server configuration directives for mod_ssl. +

    +

    Example

    H2ModernTLSOnly off
    +
    + + +
    top
    +

    H2Push Directive

    + + + + + + + + +
    Description:H2 Server Push Switch
    Syntax:H2Push on|off
    Default:H2Push on
    Context:server config, virtual host
    Status:Extension
    Module:mod_http2
    Compatibility:Available in version 2.4.18 and later.
    +

    + This directive toggles the usage of the HTTP/2 server push + protocol feature. This should be used inside a + <VirtualHost> + section to enable direct HTTP/2 communication for that virtual host. +

    +

    + The HTTP/2 protocol allows the server to push other resources to + a client when it asked for a particular one. This is helpful + if those resources are connected in some way and the client can + be expected to ask for it anyway. The pushing then saves the + time it takes the client to ask for the resources itself. On the + other hand, pushing resources the client never needs or already + has is a waste of bandwidth. +

    +

    + Server pushes are detected by inspecting the Link headers of + responses (see https://tools.ietf.org/html/rfc5988 for the + specification). When a link thus specified has the rel=preload + attribute, it is treated as a resource to be pushed. +

    +

    + Link headers in responses are either set by the application or + can be configured via mod_headers as: +

    +

    mod_headers example

    <Location /index.html>
    +    Header add Link "</css/site.css>;rel=preload"
    +    Header add Link "</images/logo.jpg>;rel=preload"
    +</Location>
    +
    +

    + As the example shows, there can be several link headers added + to a response, resulting in several pushes being triggered. There + are no checks in the module to avoid pushing the same resource + twice or more to one client. Use with care. +

    +

    + HTTP/2 server pushes are enabled by default. This directive + allows it to be switch off on all resources of this server/virtual + host. +

    +

    Example

    H2Push off
    +
    +

    + Last but not least, pushes happen only when the client signals + its willingness to accept those. Most browsers do, some, like Safari 9, + do not. Also, pushes also only happen for resources from the same + authority as the original response is for. +

    + +
    +
    top
    +

    H2PushPriority Directive

    + + + + + + + + +
    Description:H2 Server Push Priority
    Syntax:H2PushPriority mime-type [after|before|interleaved] [weight]
    Default:H2PushPriority * After 16
    Context:server config, virtual host
    Status:Extension
    Module:mod_http2
    Compatibility:Available in version 2.4.18 and later. For having an + effect, a nghttp2 library version 1.5.0 or newer is necessary.
    +

    + This directive defines the priority handling of pushed responses + based on the content-type of the response. This is usually defined + per server config, but may also appear in a virtual host. +

    +

    + HTTP/2 server pushes are always related to a client request. Each + such request/response pairs, or streams have a dependency + and a weight, together defining the priority of a stream. +

    +

    + When a stream depends on another, say X depends on Y, + then Y gets all bandwidth before X gets any. Note that this + does not men that Y will block X. If Y has no data to send, + all bandwidth allocated to Y can be used by X. +

    +

    + When a stream has more than one dependant, say X1 and X2 both + depend on Y, the weight determines the bandwidth + allocation. If X1 and X2 have the same weight, they both get + half of the available bandwdith. If the weight of X1 is twice + as large as that for X2, X1 gets twice the bandwidth of X2. +

    +

    + Ultimately, every stream depends on the root stream which + gets all the bandwidht available, but never sends anything. So all + its bandwidth is distributed by weight among its children. Which + either have data to send or distribute the bandwidth to their + own children. And so on. If none of the children have data + to send, that bandwidth get distributed somewhere else according + to the same rules. +

    +

    + The purpose of this priority system is to always make use of + available bandwidth while allowing precedence and weight + to be given to specific streams. Since, normally, all streams + are initiated by the client, it is also the one that sets + these priorities. +

    +

    + Only when such a stream results in a PUSH, gets the server to + decide what the initial priority of such a pushed + stream is. In the examples below, X is the client stream. It + depends on Y and the server decides to PUSH streams P1 and P2 + onto X. +

    +

    + The default priority rule is: +

    +

    Default Priority Rule

    H2PushPriority * After 16
    +
    +

    + which reads as 'Send a pushed stream of any content-type + depending on the client stream with weight 16'. And so P1 + and P2 will be send after X and, as they have equal weight, + share bandwidth equally among themselves. +

    +

    Interleaved Priority Rule

    H2PushPriority text/css Interleaved 256
    +
    +

    + which reads as 'Send any CSS resource on the same dependency and + weight as the client stream'. If P1 has content-type 'text/css', + it will depend on Y (as does X) and its effective weight will be + calculated as P1ew = Xw * (P1w / 256). With P1w being + 256, this will make the effective weight the same as the weight + of X. If both X and P1 have data to send, bandwidth will be allocated + to both equally. +

    +

    + With Pw specified as 512, a pushed, interleaved stream would + get double the weight of X. With 128 only half as much. Note that + effective weights are always capped at 256. +

    +

    Before Priority Rule

    H2PushPriority application/json Before
    +
    +

    + This says that any pushed stream of content type 'application/json' + should be send out before X. This makes P1 dependent + on Y and X dependent on P1. So, X will be stalled as long as + P1 has data to send. The effective weight is inherited from the + client stream. Specifying a weight is not allowed. +

    +

    + Be aware that the effect of priority specifications is limited + by the available server resources. If a server does not have + workers available for pushed streams, the data for the stream + may only ever arrive when other streams have been finished. +

    +

    + Last, but not least, there are some specifics of the syntax + to be used in this directive: +

    +
      +
    1. '*' is the only special content-type that matches all others. + 'image/*' will not work.
    2. +
    3. The default dependency is 'After'.
    4. +
    5. There are also default weights: for 'After' it is 16, 'interleaved' is 256. +
    6. +
    +

    Shorter Priority Rules

    H2PushPriority application/json 32         # an After rule
    +H2PushPriority image/jpeg before           # weight inherited
    +H2PushPriority text/css   interleaved      # weight 256 default
    +
    +
    top

    H2SerializeHeaders Directive

    - + @@ -200,7 +455,7 @@

    This directive sets maximum number of extra file handles a HTTP/2 session is allowed to use. A file handle is counted as - extra when it is transfered from a h2 worker thread to + extra when it is transferred from a h2 worker thread to the main HTTP/2 connection handling. This commonly happens when serving static files.

    @@ -238,6 +493,137 @@

    Example

    H2StreamMaxMemSize 128000
    + +
    top
    +
    Description:Serialize Request/Resoonse Processing Switch
    Description:Serialize Request/Response Processing Switch
    Syntax:H2SerializeHeaders on|off
    Default:H2SerializeHeaders off
    Context:server config, virtual host
    + + + + + + + +
    Description:
    Syntax:H2TLSCoolDownSecs seconds
    Default:H2TLSCoolDownSecs 1
    Context:server config, virtual host
    Status:Extension
    Module:mod_http2
    Compatibility:Available in version 2.4.18 and later.
    +

    + This directive sets the number of seconds of idle time on a TLS + connection before the TLS write size falls back to small (~1300 bytes) + length. + This can be used server wide or for specific + <VirtualHost>s. +

    +

    + See <H2TLSWarmUpSize> for a + description of TLS warmup. H2TLSCoolDownSecs reflects the fact + that connections may deteriorate over time (and TCP flow adjusts) + for idle connections as well. It is beneficial to overall performance + to fall back to the pre-warmup phase after a number of seconds that + no data has been sent. +

    +

    + In deployments where connections can be considered reliable, this + timer can be disabled by setting it to 0. +

    +

    + The following example sets the seconds to zero, effectively disabling + any cool down. Warmed up TLS connections stay on maximum record + size. +

    +

    Example

    H2TLSCoolDownSecs 0
    +
    + +
    +
    top
    +

    H2TLSWarmUpSize Directive

    + + + + + + + + +
    Description:
    Syntax:H2TLSWarmUpSize amount
    Default:H2TLSWarmUpSize 1048576
    Context:server config, virtual host
    Status:Extension
    Module:mod_http2
    Compatibility:Available in version 2.4.18 and later.
    +

    + This directive sets the number of bytes to be sent in small + TLS records (~1300 bytes) until doing maximum sized writes (16k) + on https: HTTP/2 connections. + This can be used server wide or for specific + <VirtualHost>s. +

    +

    + Measurements by google performance + labs show that best performance on TLS connections is reached, + if initial record sizes stay below the MTU level, to allow a + complete record to fit into an IP packet. +

    +

    + While TCP adjust its flow-control and window sizes, longer TLS + records can get stuck in queues or get lost and need retransmission. + This is of course true for all packets. TLS however needs the + whole record in order to decrypt it. Any missing bytes at the end + will stall usage of the received ones. +

    +

    + After a sufficient number of bytes have been send successfully, + the TCP state of the connection is stable and maximum TLS record + sizes (16 KB) can be used for optimal performance. +

    +

    + In deployments where servers are reached locally or over reliable + connections only, the value might be decreased with 0 disabling + any warmup phase altogether. +

    +

    + The following example sets the size to zero, effectively disabling + any warmup phase. +

    +

    Example

    H2TLSWarmUpSize 0
    +
    + +
    +
    top
    +

    H2Upgrade Directive

    + + + + + + + +
    Description:H2 Upgrade Protocol Switch
    Syntax:H2Upgrade on|off
    Default:H2Upgrade on for h2c, off for h2 protocol
    Context:server config, virtual host
    Status:Extension
    Module:mod_http2
    +

    + This directive toggles the usage of the HTTP/1.1 Upgrade method + for switching to HTTP/2. This + should be used inside a + <VirtualHost> + section to enable Upgrades to HTTP/2 for that virtual host. +

    +

    + This method of switching protocols is defined in HTTP/1.1 and + uses the "Upgrade" header (thus the name) to announce willingness + to use another protocol. This may happen on any request of a + HTTP/1.1 connection. +

    +

    + This method of protocol switching is enabled by default on cleartext + (potential h2c) connections and disabled on TLS (potential h2), + as mandated by RFC 7540. +

    +

    + Please be aware that Upgrades are only accepted for requests + that carry no body. POSTs and PUTs with content will never + trigger an upgrade to HTTP/2. + See <H2Direct> for an + alternative to Upgrade. +

    +

    + This mode only has an effect when h2 or h2c is enabled via + the <Protocols>. +

    +

    Example

    H2Upgrade on
    +
    +
    top

    H2WindowSize Directive

    diff --git a/docs/manual/mod/mod_proxy.html.en b/docs/manual/mod/mod_proxy.html.en index 5ce4af60..b83fdf65 100644 --- a/docs/manual/mod/mod_proxy.html.en +++ b/docs/manual/mod/mod_proxy.html.en @@ -1270,6 +1270,11 @@ ProxyPass "/mirror/foo" "http://backend.example.com" connection will not be used again; it will be closed at some later time. + flusher + flush +

    Name of the provider used by mod_proxy_fdpass. + See the documentation of this module for more details.

    + diff --git a/docs/manual/mod/mod_proxy.html.fr b/docs/manual/mod/mod_proxy.html.fr index 91f022c5..25ee09c5 100644 --- a/docs/manual/mod/mod_proxy.html.fr +++ b/docs/manual/mod/mod_proxy.html.fr @@ -30,6 +30,8 @@  fr  |  ja 

    +
    Cette traduction peut être périmée. Vérifiez la version + anglaise pour les changements récents.
    diff --git a/docs/manual/mod/mod_proxy_fdpass.html.en b/docs/manual/mod/mod_proxy_fdpass.html.en index 6d7610c6..bbc5f4f6 100644 --- a/docs/manual/mod/mod_proxy_fdpass.html.en +++ b/docs/manual/mod/mod_proxy_fdpass.html.en @@ -47,9 +47,14 @@

    The module has a proxy_fdpass_flusher provider interface, which allows another module to optionally send the response headers, or even - the start of the response body. The default flush provider disables keep-alive, - and sends the response headers, letting the external process just send a - response body.

    + the start of the response body. The default flush provider + disables keep-alive, and sends the response headers, letting the external + process just send a response body. +

    + +

    In order to use another provider, you have to set the flusher + parameter in the ProxyPass directive. +

    At this time the only data passed to the external process is the client socket. To receive a client socket, call recvfrom with an allocated diff --git a/docs/manual/mod/mod_proxy_fdpass.html.fr b/docs/manual/mod/mod_proxy_fdpass.html.fr index e79c3a32..754c4535 100644 --- a/docs/manual/mod/mod_proxy_fdpass.html.fr +++ b/docs/manual/mod/mod_proxy_fdpass.html.fr @@ -29,6 +29,8 @@

    Langues Disponibles:  en  |  fr 

    +
    Cette traduction peut être périmée. Vérifiez la version + anglaise pour les changements récents.
    Description:Serveur mandataire/passerelle multi-protocole
    Statut:Extension
    Identificateur de Module:proxy_module
    diff --git a/docs/manual/mod/mod_rewrite.html.fr b/docs/manual/mod/mod_rewrite.html.fr index c7401c43..ca9de10e 100644 --- a/docs/manual/mod/mod_rewrite.html.fr +++ b/docs/manual/mod/mod_rewrite.html.fr @@ -29,8 +29,6 @@

    Langues Disponibles:  en  |  fr 

    -
    Cette traduction peut être périmée. Vérifiez la version - anglaise pour les changements récents.
    Description:Module fournissant le support des processus externes fdpass à mod_proxy
    Statut:Extension
    @@ -451,7 +449,10 @@ la r d'environnement SSL . Si mod_ssl n'est pas chargé, cette variable contiendra toujours une chaîne vide. Exemple : %{SSL:SSL_CIPHER_USEKEYSIZE} pourra - contenir la valeur 128. + contenir la valeur 128. Ces variables sont + disponibles même si l'option StdEnvVars de la + directive SSLOptions n'a + pas été définie.
  • On peut utiliser %{HTTP:en-tête}, où diff --git a/docs/manual/mod/mod_ssl.html.en b/docs/manual/mod/mod_ssl.html.en index 6a0c4614..3e7f45a6 100644 --- a/docs/manual/mod/mod_ssl.html.en +++ b/docs/manual/mod/mod_ssl.html.en @@ -50,6 +50,7 @@ to provide the cryptography engine.

  • Environment Variables
  • Custom Log Formats
  • Request Notes
  • +
  • Expression Parser Extension
  • Authorization providers for use with Require
  • Directives

    Description:Ce module fournit un moteur de réécriture à base de règles permettant de réécrire les URLs des requêtes à la volée
    Compatibility:Available if using OpenSSL 0.9.8h or later

    When enabled, mod_ssl will pass responses from unsuccessful -stapling related OCSP queries (such as status errors, expired responses etc.) -on to the client. If set to off, no stapled responses -for failed queries will be included in the TLS handshake.

    +stapling related OCSP queries (such as responses with an overall status +other than "successful", responses with a certificate status other than +"good", expired responses etc.) on to the client. +If set to off, only responses indicating a certificate status +of "good" will be included in the TLS handshake.

    top
    diff --git a/docs/manual/mod/mod_ssl.html.fr b/docs/manual/mod/mod_ssl.html.fr index 75fd7dbd..cef69e96 100644 --- a/docs/manual/mod/mod_ssl.html.fr +++ b/docs/manual/mod/mod_ssl.html.fr @@ -29,8 +29,6 @@

    Langues Disponibles:  en  |  fr 

    -
    Cette traduction peut être périmée. Vérifiez la version - anglaise pour les changements récents.
    @@ -52,6 +50,8 @@ pour fournir le moteur de chiffrement.

  • Formats de journaux personnalisés
  • Information à propos de la requête
  • +
  • Extension pour l'interprétation +des expressions
  • Fournisseurs d'autorisation disponibles avec Require
  • Directives

    @@ -333,6 +333,9 @@ cryptographique suppl les informations à propos de cette fonction dans le chapitre Compatibilité.

    Exemple

    CustomLog "logs/ssl_request_log" "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
    +

    Ces formats sont disponibles même si l'option StdEnvVars de la +directive SSLOptions n'a pas été +définie.

    top

    Information à propos de la requête

    @@ -363,6 +366,25 @@ format %{nom}n via le module sera pas défini. +
    top
    +
    +

    Extension pour l'interprétation +des expressions

    + +

    Lorsque mod_ssl est compilé statiquement avec +Apache, ou même chargé dynamiquement (en tant que module DSO), toute variable en provenance de mod_ssl peut +être utilisée pour l'interprétation des +expression ap_expr. Les variables peuvent être référencées en +utilisant la syntaxe ``%{varname}''. +A partir de la version 2.4.18, on peut aussi utiliser la syntaxe de +style mod_rewrite +``%{SSL:varname}'', ou la syntaxe de +style fonction ``ssl(varname)''.

    +

    Exemple (en utilisant mod_headers)

    Header set X-SSL-PROTOCOL "expr=%{SSL_PROTOCOL}"
    +Header set X-SSL-CIPHER "expr=%{SSL:SSL_CIPHER}"
    +
    +

    Cette fonctionnalité est disponible même si l'option +StdEnvVars de la directive SSLOptions n'a pas été définie.

    top
    @@ -2686,10 +2708,13 @@ OCSP
    Description:Chiffrement de haut niveau basé sur les protocoles Secure Sockets Layer (SSL) et Transport Layer Security (TLS)
    Statut:Extension
    Compatibilité:Disponible si on utilise OpenSSL version 0.9.8h ou supérieure

    Lorsque cette directive est activée, mod_ssl va transmettre au client les -réponses concernant les requêtes OCSP échouées (erreurs d'état, réponses -périmées, etc...). Lorsqu'elle est à off, aucune réponse -concernant les requêtes OCSP échouées ne sera incluse dans les -négociation TLS avec les clients.

    +réponses concernant les requêtes OCSP +échouées (comme les réponses avec un statut général autre que +"successful", les réponses avec un statut de certificat autre que +"good", les réponses arrivées à expiration, etc...). +Lorsqu'elle est à off, seules les réponses avec un +statut de certificat égal à "good" seront incluses dans la négociation +TLS.

    top
    diff --git a/docs/manual/mod/mod_substitute.html.fr b/docs/manual/mod/mod_substitute.html.fr index f631c46a..8ab6874a 100644 --- a/docs/manual/mod/mod_substitute.html.fr +++ b/docs/manual/mod/mod_substitute.html.fr @@ -29,8 +29,6 @@

    Langues Disponibles:  en  |  fr 

    -
    Cette traduction peut être périmée. Vérifiez la version - anglaise pour les changements récents.
    @@ -149,17 +147,30 @@ Substitute "s|http://internal.blog.example.com/|http://www.example.com/blog/|i"<
    top
    Description:Effectue des opérations de recherche/remplacement sur les corps de réponses
    Statut:Extension
    - + - + - -
    Description:Change the merge order of inherited patterns
    Description:Modifie l'ordre de fusion des modèles hérités
    Syntaxe:SubstituteInheritBefore on|off
    Défaut:SubstituteInheritBefore off
    Défaut:SubstituteInheritBefore on
    Contexte:répertoire, .htaccess
    AllowOverride:FileInfo
    Statut:Extension
    Module:mod_substitute
    Compatibilité:Available in httpd 2.4.17 and later

    La documentation de cette directive - n'a pas encore t traduite. Veuillez vous reporter la version - en langue anglaise.

    +Compatibilité:Disponible à partir de la version 2.5 du serveur HTTP +Apache + +

    Cette directive permet de définir si l'on applique les modèles +Substitute hérités en premier (valeur +on), ou après ceux du +contexte courant (valeur off). Sa valeur est maintenant +définie par défaut à on ; il est cependant possible de +restaurer le comportement des versions 2.4 et antérieures du serveur qui +était équivalent à une définition à off de cette directive. +La valeur de la directive SubstituteInheritBefore est +elle-même héritée, et les contextes qui en héritent (ceux qui ne +définissent pas explicitement leur propre directive +SubstituteInheritBefore) appliqueront donc +l'ordre de fusion défini le plus proche.

    + +
    top

    Directive SubstituteMaxLineLength

    diff --git a/docs/manual/mod/mpm_common.html.fr b/docs/manual/mod/mpm_common.html.fr index 921b440a..c33a6518 100644 --- a/docs/manual/mod/mpm_common.html.fr +++ b/docs/manual/mod/mpm_common.html.fr @@ -32,8 +32,6 @@  ja  |  tr 

    -
    Cette traduction peut être périmée. Vérifiez la version - anglaise pour les changements récents.
    Description:Une série de directives implémentées par plusieurs modules multi-processus (MPM)
    Statut:MPM
    @@ -73,7 +71,7 @@ positionner avant d'effectuer un vidage m Défaut:Voir ci-dessous pour le répertoire par défaut Contexte:configuration du serveur Statut:MPM -Module:event, prefork, worker +Module:event, worker, prefork

    Cette directive permet de définir le répertoire dans lequel Apache httpd va tenter de se positionner avant d'effectuer un vidage @@ -134,7 +132,7 @@ gestionnaires d'exception apr Défaut:EnableExceptionHook Off Contexte:configuration du serveur Statut:MPM -Module:event, prefork, worker +Module:event, worker, prefork

    Pour des raisons de sécurité, cette directive n'est disponible que si la compilation du serveur a été configurée avec l'option @@ -159,7 +157,7 @@ s'arr Défaut:GracefulShutdownTimeout 0 Contexte:configuration du serveur Statut:MPM -Module:prefork, worker, event +Module:event, worker, prefork Compatibilité:Disponible dans les versions 2.2 et supérieures

    La directive GracefulShutdownTimeout @@ -180,7 +178,7 @@ s'arr [protocole] Contexte:configuration du serveur Statut:MPM -Module:mpm_netware, mpm_winnt, mpmt_os2, prefork, worker, event +Module:event, worker, prefork, mpm_winnt, mpm_netware, mpmt_os2 Compatibilité:L'argument protocole est supporté depuis la version 2.1.5 @@ -265,7 +263,7 @@ connexions Défaut:ListenBacklog 511 Contexte:configuration du serveur Statut:MPM -Module:event, mpm_netware, mpm_winnt, mpmt_os2, prefork, worker +Module:event, worker, prefork, mpm_winnt, mpm_netware, mpmt_os2

    La longueur maximale de la liste d'attente des connexions. En général, aucune modification n'est nécessaire, ni même souhaitable ; @@ -290,7 +288,7 @@ le nombre de segments d' Défaut:ListenCoresBucketsRatio 0 (disabled) Contexte:configuration du serveur Statut:MPM -Module:event, prefork, worker +Module:event, worker, prefork Compatibilité:Disponible à partir de la version 2.4.13 du serveur HTTP Apache, avec un noyau supportant l'option de socket SO_REUSEPORT, et distribuant uniformément les nouvelles @@ -333,7 +331,7 @@ traiter au cours de son fonctionnement Défaut:MaxConnectionsPerChild 0 Contexte:configuration du serveur Statut:MPM -Module:event, mpm_netware, mpm_winnt, mpmt_os2, prefork, worker +Module:event, worker, prefork, mpm_winnt, mpm_netware, mpmt_os2 Compatibilité:Disponible depuis la version 2.3.9 du serveur HTTP Apache. L'ancien nom MaxRequestsPerChild est encore supporté. @@ -361,7 +359,7 @@ autoris Défaut:MaxMemFree 2048 Contexte:configuration du serveur Statut:MPM -Module:event, mpm_netware, prefork, worker, mpm_winnt +Module:event, worker, prefork, mpm_winnt, mpm_netware

    La directive MaxMemFree permet de définir le nombre maximum de KOctets libres que tout allocateur est @@ -380,7 +378,7 @@ simultan Défaut:Voir ci-dessous pour plus de détails Contexte:configuration du serveur Statut:MPM -Module:event, prefork, worker +Module:event, worker, prefork

    La directive MaxRequestWorkers permet de fixer le nombre maximum de requêtes pouvant être traitées simultanément. @@ -424,12 +422,12 @@ simultan Défaut:Voir ci-dessous pour plus de détails Contexte:configuration du serveur Statut:MPM -Module:event, mpm_netware, mpmt_os2, worker +Module:event, worker, mpm_netware, mpmt_os2

    C'est le nombre maximum de threads inactifs. Les MPMs utilisent cette directive de différentes manières.

    -

    Pour worker, la définition par défaut est +

    Pour worker et event, la définition par défaut est MaxSpareThreads 250. Ce MPM gère les threads inactifs au niveau du serveur. Si le serveur possède trop de threads inactifs, des processus enfants seront arrêtés jusqu'à ce que le @@ -452,7 +450,7 @@ simultan

  • Avec mpm_netware, MaxSpareThreads doit être supérieure à MinSpareThreads.
  • Avec - worker, MaxSpareThreads + worker et event, MaxSpareThreads doit être supérieure ou égale à la somme de MinSpareThreads et ThreadsPerChild.
  • @@ -473,13 +471,13 @@ pour pouvoir traiter les pics de requ Défaut:Voir ci-dessous pour plus de détails Contexte:configuration du serveur Statut:MPM -Module:event, mpm_netware, mpmt_os2, worker +Module:event, worker, mpm_netware, mpmt_os2

    C'est le nombre minimum de threads inactifs pour être en mesure de traiter les pics de requêtes. Les MPMs utilisent cette directive de différentes manières.

    -

    Avec worker, la définition par défaut est +

    Avec worker et event, la définition par défaut est MinSpareThreads 75, et le nombre de threads inactifs est surveillé au niveau du serveur. Si le serveur ne possède pas assez de threads inactifs, des processus enfants sont créés jusqu'à @@ -512,7 +510,7 @@ de processus du d Défaut:PidFile logs/httpd.pid Contexte:configuration du serveur Statut:MPM -Module:event, mpm_winnt, mpmt_os2, prefork, worker +Module:event, worker, prefork, mpm_winnt, mpmt_os2

    La directive PidFile permet de définir le ficher dans lequel le serveur @@ -550,7 +548,7 @@ de processus du d Défaut:ReceiveBufferSize 0 Contexte:configuration du serveur Statut:MPM -Module:event, mpm_netware, mpm_winnt, mpmt_os2, prefork, worker +Module:event, worker, prefork, mpm_winnt, mpm_netware, mpmt_os2

    Le serveur va fixer la taille du tampon TCP en entrée au nombre d'octets spécifié.

    @@ -569,7 +567,7 @@ la coordination des processus enfants Défaut:ScoreBoardFile logs/apache_runtime_status Contexte:configuration du serveur Statut:MPM -Module:event, mpm_winnt, prefork, worker +Module:event, worker, prefork, mpm_winnt

    Le serveur HTTP Apache utilise un tableau de bord pour la communication entre le processus parent et les processus enfants. @@ -609,7 +607,7 @@ le serveur HTTP Apache Défaut:SendBufferSize 0 Contexte:configuration du serveur Statut:MPM -Module:event, mpm_netware, mpm_winnt, mpmt_os2, prefork, worker +Module:event, worker, prefork, mpm_winnt, mpm_netware, mpmt_os2

    Définit la taille du tampon TCP en sortie avec le nombre d'octets spécifié. Ceci s'avère souvent très utile pour augmenter les @@ -643,12 +641,12 @@ processus Défaut:Voir ci-dessous pour plus de détails Contexte:configuration du serveur Statut:MPM -Module:event, prefork, worker +Module:event, worker, prefork

    Avec le MPM prefork, cette directive définit le nombre maximum que l'on peut affecter à la directive MaxRequestWorkers, et ceci pour la - durée de vie du processus Apache httpd. Avec le - MPM worker, cette directive, en combinaison avec + durée de vie du processus Apache httpd. Avec les + MPMs worker et event, cette directive, en combinaison avec ThreadLimit, définit le nombre maximum que l'on peut affecter à MaxRequestWorkers, et ceci pour la durée de vie du processus Apache httpd. Au cours d'un redémarrage, vous pouvez @@ -663,7 +661,7 @@ processus supérieures à ce que le système peut supporter, ce dernier peut devenir instable ou Apache httpd peut tout simplement refuser de démarrer.

    -

    Avec le MPM prefork, n'utilisez cette directive +

    Avec les MPMs prefork et event, n'utilisez cette directive que si vous devez définir MaxRequestWorkers à une valeur supérieure à 256 (valeur par défaut). N'affectez pas à la directive ServerLimit une valeur supérieure à celle que vous avez prévu d'affecter à la directive MaxRequestWorkers.

    @@ -700,7 +698,7 @@ d Défaut:Voir ci-dessous pour plus de détails Contexte:configuration du serveur Statut:MPM -Module:event, mpmt_os2, prefork, worker +Module:event, worker, prefork, mpmt_os2

    La directive StartServers permet de définir le nombre de processus enfants du serveur créés au @@ -709,7 +707,7 @@ d pas nécessaire d'ajuster ce paramètre.

    La valeur par défaut diffère d'un MPM à l'autre. Pour - worker, la définition par défaut est + worker et event, la définition par défaut est StartServers 3 ; la valeur par défaut est 5 pour prefork et 2 pour mpmt_os2.

    @@ -745,7 +743,7 @@ processus enfant Défaut:Voir ci-dessous pour plus de détails Contexte:configuration du serveur Statut:MPM -Module:event, mpm_winnt, worker +Module:event, worker, mpm_winnt

    Cette directive permet de définir le nombre maximum que l'on peut affecter à la directive ThreadsPerChild pour la durée de vie @@ -791,7 +789,7 @@ enfant Défaut:Voir ci-dessous pour plus de détails Contexte:configuration du serveur Statut:MPM -Module:event, mpm_winnt, worker +Module:event, worker, mpm_winnt

    Cette directive permet de définir le nombre de threads que va créer chaque processus enfant. Un processus enfant crée ces threads @@ -819,7 +817,7 @@ traitent les connexions clients d'exploitation Contexte:configuration du serveur Statut:MPM -Module:event, mpm_netware, mpmt_os2, mpm_winnt, worker, event +Module:event, worker, mpm_winnt, mpm_netware, mpmt_os2 Compatibilité:Disponible dans les versions 2.1 et supérieures du serveur HTTP Apache diff --git a/docs/manual/mod/quickreference.html.de b/docs/manual/mod/quickreference.html.de index bf4fed3d..55792fee 100644 --- a/docs/manual/mod/quickreference.html.de +++ b/docs/manual/mod/quickreference.html.de @@ -51,7 +51,7 @@ der Legende.

    - +
     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  R  |  S  |  T  |  U  |  V  |  W  |  X 
     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  Q  |  R  |  S  |  T  |  U  |  V  |  W  |  X  @@ -472,14 +472,20 @@ angegebenen MIME-Content-Type will exit. - + - - - + + + + + + + + + - + - - + - - - - - - - - - - - + + + + + + + - - - - - - - - + - - - - - - + - - - - - + + + + - - - - - - - - - - + - - + - - + - - + - - - - - - + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - + + + + + - - + - - - - - + + - - + - - - + + - - + - - - - - + - - - - - + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + - - - + - - - - - - - - - - - - + + + + + + - - - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - + + + - - - - - - - - + + + + + + - - - - - - - - - - - + + + + + + + - - - - + - - - - + + - - + - - - - - - + - - + - +
    sServerkonfiguration
    vVirtual Host
    dVerzeichnis
    Group unix-group #-1 sB
    Group under which the server will answer requests
    H2Direct on|off on (for non TLS) svE
    H2 Direct Protocol Switch
    H2Direct on|off on for h2c, off for +svE
    H2 Direct Protocol Switch
    H2MaxSessionStreams n 100 svE
    Maximum number of active streams per HTTP/2 session.
    H2MaxWorkerIdleSeconds n 600 sE
    Maximum number of seconds h2 workers remain idle until shut down.
    H2MaxWorkers nsE
    Maximum number of worker threads to use per child process.
    H2MinWorkers nsE
    Minimal number of worker threads to use per child process.
    H2SerializeHeaders on|off off svE
    Serialize Request/Resoonse Processing Switch
    H2SessionExtraFiles n 5 svE
    Number of Extra File Handles
    H2StreamMaxMemSize bytes 65536 svE
    Maximum amount of output data buffered per stream.
    H2ModernTLSOnly on|off on svE
    Require HTTP/2 connections to be "modern TLS" only
    H2Push on|off on svE
    H2 Server Push Switch
    H2PushPriority mime-type [after|before|interleaved] [weight] * After 16 svE
    H2 Server Push Priority
    H2SerializeHeaders on|off off svE
    Serialize Request/Response Processing Switch
    H2SessionExtraFiles n 5 svE
    Number of Extra File Handles
    H2StreamMaxMemSize bytes 65536 svE
    Maximum amount of output data buffered per stream.
    H2TLSCoolDownSecs seconds 1 svE
    -
    H2TLSWarmUpSize amount 1048576 svE
    -
    H2Upgrade on|off on for h2c, off for +svE
    H2 Upgrade Protocol Switch
    H2WindowSize bytes 65536 svE
    Size of Stream Window for upstream data.
    Header [condition] add|append|echo|edit|edit*|merge|set|setifempty|unset|note header [[expr=]value [replacement] @@ -772,331 +778,333 @@ header
    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
    ReadmeName filenamesvdhB
    Name of the file that will be inserted at the end +
    QualifyRedirectURL ON|OFF OFF svdC
    Controls whether the REDIRECT_URL environent variable is + fully qualified
    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
    RedirectTemp URL-path URLsvdhB
    Sends an external temporary redirect asking the client to fetch +
    RedirectTemp URL-path URLsvdhB
    Sends an external temporary redirect asking the client to fetch a different URL
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    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
    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 +
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    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
    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 [header=timeout[-maxtimeout][,MinRate=rate] [body=timeout[-maxtimeout][,MinRate=rate] -svE
    Set timeout values for receiving request headers and body from client. +svE
    Set timeout values for receiving request headers and 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 CondPatternsvdhE
    Defines a condition under which rewriting will take place +
    RewriteBase URL-pathdhE
    Sets the base URL for per-directory rewrites
    RewriteCond + TestString CondPatternsvdhE
    Defines a condition under which rewriting will take place
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource -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 +
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource +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 dhE
    Interaction between host-level access control and +
    Satisfy Any|All All dhE
    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-pathsvB
    Maps a URL to a filesystem location and designates the +
    ScriptAlias URL-path +file-path|directory-pathsvB
    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 namesvdhX
    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 namesvdhX
    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
    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 +
    SessionExclude pathsvdhE
    Define URL prefixes for which a session is ignored
    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 none svE
    Enable CRL-based revocation checking
    SSLCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for +
    SSLCARevocationCheck chain|leaf|none 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-pathsvE
    Server PEM-encoded X.509 certificate data file
    SSLCertificateKeyFile file-pathsvE
    Server PEM-encoded private key file
    SSLCipherSuite 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-pathsvE
    Server PEM-encoded X.509 certificate data file
    SSLCertificateKeyFile file-pathsvE
    Server PEM-encoded private key file
    SSLCipherSuite 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|optional 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
    SSLOCSDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|off off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    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|optional 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
    SSLOCSDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|off off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    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 cipher-spec ALL:!ADH:RC4+RSA:+H +svdhE
    Cipher Suite available for negotiation in SSL +
    SSLProxyCipherSuite cipher-spec ALL:!ADH:RC4+RSA:+H +svdhE
    Cipher Suite available for negotiation in SSL proxy handshake
    SSLProxyEngine on|off off svE
    SSL Proxy Engine Operation Switch
    SSLProxyMachineCertificateChainFile filenamesE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysE
    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 filenamesE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysE
    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
    StartServers AnzahlsM
    Anzahl der Kindprozesse des Servers, die beim Start erstellt +
    StartServers AnzahlsM
    Anzahl der Kindprozesse des Servers, die beim Start erstellt werden
    StartThreads AnzahlsM
    Anzahl der Threads, die beim Start erstellt werden
    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 +
    StartThreads AnzahlsM
    Anzahl der Threads, die beim Start erstellt werden
    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 sC
    Zeitspanne, die der Server auf verschiedene Ereignisse wartet, +
    TimeOut Sekunden 60 sC
    Zeitspanne, die der Server auf verschiedene Ereignisse wartet, bevor er die Anfrage abbricht
    TraceEnable [on|off|extended] on sC
    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
    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 +
    TraceEnable [on|off|extended] on sC
    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
    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 vX
    Determines whether the virtualhost can run +
    UserDir directory-filename [directory-filename] ... +svB
    Location of the user-specific directories
    VHostCGIMode On|Off|Secure On vX
    Determines whether the virtualhost can run subprocesses, and the privileges available to subprocesses.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to subprocesses created +
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to subprocesses created by a virtual host.
    VHostGroup unix-groupidvX
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vX
    Determines whether the server runs with enhanced security +
    VHostGroup unix-groupidvX
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vX
    Determines whether the server runs with enhanced security for the virtualhost.
    VHostUser unix-useridvX
    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-useridvX
    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 number-of-seconds 1 sB
    Watchdog interval in seconds
    XBitHack on|off|full off svdhB
    Parse SSI directives in files with the execute bit +
    WatchdogInterval number-of-seconds 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
    Recognise Aliases for encoding values
    xml2EncDefault namesvdhB
    Sets a default encoding to assume when absolutely no information +
    xml2EncAlias charset alias [alias ...]sB
    Recognise 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 --git a/docs/manual/mod/quickreference.html.en b/docs/manual/mod/quickreference.html.en index 11a38299..7f2b46fd 100644 --- a/docs/manual/mod/quickreference.html.en +++ b/docs/manual/mod/quickreference.html.en @@ -49,7 +49,7 @@ tables below.

    - +
     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  R  |  S  |  T  |  U  |  V  |  W  |  X 
     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  Q  |  R  |  S  |  T  |  U  |  V  |  W  |  X  @@ -467,14 +467,20 @@ media type in the HTTP Content-Type header field will exit. - + - - - + + + + + + + + + - + - - + - - - - - - - - - - - + + + + + + + - - - - - - - - + - - - - - - + - - - - - + + + + - - - - - - - - - - + - - + - - + - - + - - - + - - - - + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - + + + + + - - + - - - - - + - - + - - - + + - - + - - - - - + - - - - - + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + - - - + - - - - - - - - - - - - + + + + + + - - - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - + + + - - - - - - - - + + + + + + + - - + - - - - - - - - - + + + + + + + - - + - - + - - - - + + - - + - - - - - - + - - + - +
    sserver config
    vvirtual host
    ddirectory
    Group unix-group #-1 sB
    Group under which the server will answer requests
    H2Direct on|off on (for non TLS) svE
    H2 Direct Protocol Switch
    H2Direct on|off on for h2c, off for +svE
    H2 Direct Protocol Switch
    H2MaxSessionStreams n 100 svE
    Maximum number of active streams per HTTP/2 session.
    H2MaxWorkerIdleSeconds n 600 sE
    Maximum number of seconds h2 workers remain idle until shut down.
    H2MaxWorkers nsE
    Maximum number of worker threads to use per child process.
    H2MinWorkers nsE
    Minimal number of worker threads to use per child process.
    H2SerializeHeaders on|off off svE
    Serialize Request/Resoonse Processing Switch
    H2SessionExtraFiles n 5 svE
    Number of Extra File Handles
    H2StreamMaxMemSize bytes 65536 svE
    Maximum amount of output data buffered per stream.
    H2ModernTLSOnly on|off on svE
    Require HTTP/2 connections to be "modern TLS" only
    H2Push on|off on svE
    H2 Server Push Switch
    H2PushPriority mime-type [after|before|interleaved] [weight] * After 16 svE
    H2 Server Push Priority
    H2SerializeHeaders on|off off svE
    Serialize Request/Response Processing Switch
    H2SessionExtraFiles n 5 svE
    Number of Extra File Handles
    H2StreamMaxMemSize bytes 65536 svE
    Maximum amount of output data buffered per stream.
    H2TLSCoolDownSecs seconds 1 svE
    -
    H2TLSWarmUpSize amount 1048576 svE
    -
    H2Upgrade on|off on for h2c, off for +svE
    H2 Upgrade Protocol Switch
    H2WindowSize bytes 65536 svE
    Size of Stream Window for upstream data.
    Header [condition] add|append|echo|edit|edit*|merge|set|setifempty|unset|note header [[expr=]value [replacement] @@ -764,324 +770,326 @@ header
    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
    ReadmeName filenamesvdhB
    Name of the file that will be inserted at the end +
    QualifyRedirectURL ON|OFF OFF svdC
    Controls whether the REDIRECT_URL environent variable is + fully qualified
    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
    RedirectTemp URL-path URLsvdhB
    Sends an external temporary redirect asking the client to fetch +
    RedirectTemp URL-path URLsvdhB
    Sends an external temporary redirect asking the client to fetch a different URL
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    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
    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 +
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    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
    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 [header=timeout[-maxtimeout][,MinRate=rate] [body=timeout[-maxtimeout][,MinRate=rate] -svE
    Set timeout values for receiving request headers and body from client. +svE
    Set timeout values for receiving request headers and 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 CondPatternsvdhE
    Defines a condition under which rewriting will take place +
    RewriteBase URL-pathdhE
    Sets the base URL for per-directory rewrites
    RewriteCond + TestString CondPatternsvdhE
    Defines a condition under which rewriting will take place
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource -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 +
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource +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 dhE
    Interaction between host-level access control and +
    Satisfy Any|All All dhE
    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-pathsvB
    Maps a URL to a filesystem location and designates the +
    ScriptAlias URL-path +file-path|directory-pathsvB
    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 namesvdhX
    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 namesvdhX
    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
    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 +
    SessionExclude pathsvdhE
    Define URL prefixes for which a session is ignored
    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 none svE
    Enable CRL-based revocation checking
    SSLCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for +
    SSLCARevocationCheck chain|leaf|none 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-pathsvE
    Server PEM-encoded X.509 certificate data file
    SSLCertificateKeyFile file-pathsvE
    Server PEM-encoded private key file
    SSLCipherSuite 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-pathsvE
    Server PEM-encoded X.509 certificate data file
    SSLCertificateKeyFile file-pathsvE
    Server PEM-encoded private key file
    SSLCipherSuite 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|optional 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
    SSLOCSDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|off off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    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|optional 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
    SSLOCSDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|off off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    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 cipher-spec ALL:!ADH:RC4+RSA:+H +svdhE
    Cipher Suite available for negotiation in SSL +
    SSLProxyCipherSuite cipher-spec ALL:!ADH:RC4+RSA:+H +svdhE
    Cipher Suite available for negotiation in SSL proxy handshake
    SSLProxyEngine on|off off svE
    SSL Proxy Engine Operation Switch
    SSLProxyMachineCertificateChainFile filenamesE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysE
    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 filenamesE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysE
    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
    StartServers numbersM
    Number of child server processes created at startup
    StartThreads numbersM
    Number of threads created on startup
    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 +
    StartServers numbersM
    Number of child server processes created at startup
    StartThreads numbersM
    Number of threads created on startup
    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
    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 +
    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
    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 vX
    Determines whether the virtualhost can run +
    UserDir directory-filename [directory-filename] ... +svB
    Location of the user-specific directories
    VHostCGIMode On|Off|Secure On vX
    Determines whether the virtualhost can run subprocesses, and the privileges available to subprocesses.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to subprocesses created +
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to subprocesses created by a virtual host.
    VHostGroup unix-groupidvX
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vX
    Determines whether the server runs with enhanced security +
    VHostGroup unix-groupidvX
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vX
    Determines whether the server runs with enhanced security for the virtualhost.
    VHostUser unix-useridvX
    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-useridvX
    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 number-of-seconds 1 sB
    Watchdog interval in seconds
    XBitHack on|off|full off svdhB
    Parse SSI directives in files with the execute bit +
    WatchdogInterval number-of-seconds 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
    Recognise Aliases for encoding values
    xml2EncDefault namesvdhB
    Sets a default encoding to assume when absolutely no information +
    xml2EncAlias charset alias [alias ...]sB
    Recognise 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 --git a/docs/manual/mod/quickreference.html.es b/docs/manual/mod/quickreference.html.es index e2b41e56..31b0f109 100644 --- a/docs/manual/mod/quickreference.html.es +++ b/docs/manual/mod/quickreference.html.es @@ -56,7 +56,7 @@ acuerdo con las notas que detallan más abajo.

    - +
     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  R  |  S  |  T  |  U  |  V  |  W  |  X 
     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  Q  |  R  |  S  |  T  |  U  |  V  |  W  |  X  @@ -474,14 +474,20 @@ media type in the HTTP Content-Type header field will exit. - + - - - + + + + + + + + + - + - - + - - - - - - - - - - - + + + + + + + - - - - - - - - + - - - - - - + - - - - - + + + + - - - - - - - - - - + - - + - - + - - + - - - + - - - - + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - + + + + + - - + - - - - - + - - + - - - + + - - + - - - - - + - - - - - + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + - - - + - - - - - - - - - - - - + + + + + + - - - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - + + + - - - - - - - - + + + + + + + - - + - - - - - - - - - + + + + + + + - - - - + - - - - + + - - + - - - - - - + - - + - +
    sserver config
    vvirtual host
    ddirectory
    Group unix-group #-1 sB
    Group under which the server will answer requests
    H2Direct on|off on (for non TLS) svE
    H2 Direct Protocol Switch
    H2Direct on|off on for h2c, off for +svE
    H2 Direct Protocol Switch
    H2MaxSessionStreams n 100 svE
    Maximum number of active streams per HTTP/2 session.
    H2MaxWorkerIdleSeconds n 600 sE
    Maximum number of seconds h2 workers remain idle until shut down.
    H2MaxWorkers nsE
    Maximum number of worker threads to use per child process.
    H2MinWorkers nsE
    Minimal number of worker threads to use per child process.
    H2SerializeHeaders on|off off svE
    Serialize Request/Resoonse Processing Switch
    H2SessionExtraFiles n 5 svE
    Number of Extra File Handles
    H2StreamMaxMemSize bytes 65536 svE
    Maximum amount of output data buffered per stream.
    H2ModernTLSOnly on|off on svE
    Require HTTP/2 connections to be "modern TLS" only
    H2Push on|off on svE
    H2 Server Push Switch
    H2PushPriority mime-type [after|before|interleaved] [weight] * After 16 svE
    H2 Server Push Priority
    H2SerializeHeaders on|off off svE
    Serialize Request/Response Processing Switch
    H2SessionExtraFiles n 5 svE
    Number of Extra File Handles
    H2StreamMaxMemSize bytes 65536 svE
    Maximum amount of output data buffered per stream.
    H2TLSCoolDownSecs seconds 1 svE
    -
    H2TLSWarmUpSize amount 1048576 svE
    -
    H2Upgrade on|off on for h2c, off for +svE
    H2 Upgrade Protocol Switch
    H2WindowSize bytes 65536 svE
    Size of Stream Window for upstream data.
    Header [condition] add|append|echo|edit|edit*|merge|set|setifempty|unset|note header [[expr=]value [replacement] @@ -771,325 +777,327 @@ header
    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
    ReadmeName filenamesvdhB
    Name of the file that will be inserted at the end +
    QualifyRedirectURL ON|OFF OFF svdC
    Controls whether the REDIRECT_URL environent variable is + fully qualified
    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
    RedirectTemp URL-path URLsvdhB
    Sends an external temporary redirect asking the client to fetch +
    RedirectTemp URL-path URLsvdhB
    Sends an external temporary redirect asking the client to fetch a different URL
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    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
    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 +
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    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
    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 [header=timeout[-maxtimeout][,MinRate=rate] [body=timeout[-maxtimeout][,MinRate=rate] -svE
    Set timeout values for receiving request headers and body from client. +svE
    Set timeout values for receiving request headers and 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 CondPatternsvdhE
    Defines a condition under which rewriting will take place +
    RewriteBase URL-pathdhE
    Sets the base URL for per-directory rewrites
    RewriteCond + TestString CondPatternsvdhE
    Defines a condition under which rewriting will take place
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource -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 +
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource +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 dhE
    Interaction between host-level access control and +
    Satisfy Any|All All dhE
    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-pathsvB
    Maps a URL to a filesystem location and designates the +
    ScriptAlias URL-path +file-path|directory-pathsvB
    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 namesvdhX
    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 namesvdhX
    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
    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 +
    SessionExclude pathsvdhE
    Define URL prefixes for which a session is ignored
    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 none svE
    Enable CRL-based revocation checking
    SSLCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for +
    SSLCARevocationCheck chain|leaf|none 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-pathsvE
    Server PEM-encoded X.509 certificate data file
    SSLCertificateKeyFile file-pathsvE
    Server PEM-encoded private key file
    SSLCipherSuite 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-pathsvE
    Server PEM-encoded X.509 certificate data file
    SSLCertificateKeyFile file-pathsvE
    Server PEM-encoded private key file
    SSLCipherSuite 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|optional 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
    SSLOCSDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|off off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    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|optional 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
    SSLOCSDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|off off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    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 cipher-spec ALL:!ADH:RC4+RSA:+H +svdhE
    Cipher Suite available for negotiation in SSL +
    SSLProxyCipherSuite cipher-spec ALL:!ADH:RC4+RSA:+H +svdhE
    Cipher Suite available for negotiation in SSL proxy handshake
    SSLProxyEngine on|off off svE
    SSL Proxy Engine Operation Switch
    SSLProxyMachineCertificateChainFile filenamesE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysE
    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 filenamesE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysE
    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
    StartServers numbersM
    Number of child server processes created at startup
    StartThreads numbersM
    Number of threads created on startup
    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 +
    StartServers numbersM
    Number of child server processes created at startup
    StartThreads numbersM
    Number of threads created on startup
    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 sC
    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
    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 +
    TraceEnable [on|off|extended] on sC
    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
    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 vX
    Determines whether the virtualhost can run +
    UserDir directory-filename [directory-filename] ... +svB
    Location of the user-specific directories
    VHostCGIMode On|Off|Secure On vX
    Determines whether the virtualhost can run subprocesses, and the privileges available to subprocesses.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to subprocesses created +
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to subprocesses created by a virtual host.
    VHostGroup unix-groupidvX
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vX
    Determines whether the server runs with enhanced security +
    VHostGroup unix-groupidvX
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vX
    Determines whether the server runs with enhanced security for the virtualhost.
    VHostUser unix-useridvX
    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-useridvX
    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 number-of-seconds 1 sB
    Watchdog interval in seconds
    XBitHack on|off|full off svdhB
    Parse SSI directives in files with the execute bit +
    WatchdogInterval number-of-seconds 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
    Recognise Aliases for encoding values
    xml2EncDefault namesvdhB
    Sets a default encoding to assume when absolutely no information +
    xml2EncAlias charset alias [alias ...]sB
    Recognise 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 --git a/docs/manual/mod/quickreference.html.fr b/docs/manual/mod/quickreference.html.fr index cc823ef9..4bb55531 100644 --- a/docs/manual/mod/quickreference.html.fr +++ b/docs/manual/mod/quickreference.html.fr @@ -51,7 +51,7 @@ tableau des légendes ci-dessous.

    - +
     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  R  |  S  |  T  |  U  |  V  |  W  |  X 
     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  Q  |  R  |  S  |  T  |  U  |  V  |  W  |  X  @@ -577,14 +577,20 @@ gmon.out. s'arrêter dans le cas d'un arrêt "en douceur" - + - - - + + + + + + + + + mandatées - + - - + - - - - - + - - - - - - - - - - - - - - + - - - - - - - - - - - + - - - - - - - + - - - - - - - - - + - - - - - - - - - + - - - - + - - + - - - - - - - + - - - - - - - - + - - + - - - - - + - - + - - - - + - - - - - - + - - - - - - - + - - - + + - - - - - - - + + - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - + - - - - - - + - - - - + + - - - + - - - - + + + - - - - - - - - - - - - + + + + + + - - - - - - - - + - - - - - - - - + - - + -
    sconfiguration du serveur
    vserveur virtuel
    drépertoire
    Group groupe unix #-1 sB
    Groupe sous lequel le serveur va traiter les requêtes
    H2Direct on|off on (for non TLS) svE
    H2 Direct Protocol Switch
    H2Direct on|off on for h2c, off for +svE
    H2 Direct Protocol Switch
    H2MaxSessionStreams n 100 svE
    Maximum number of active streams per HTTP/2 session.
    H2MaxWorkerIdleSeconds n 600 sE
    Maximum number of seconds h2 workers remain idle until shut down.
    H2MaxWorkers nsE
    Maximum number of worker threads to use per child process.
    H2MinWorkers nsE
    Minimal number of worker threads to use per child process.
    H2SerializeHeaders on|off off svE
    Serialize Request/Resoonse Processing Switch
    H2SessionExtraFiles n 5 svE
    Number of Extra File Handles
    H2StreamMaxMemSize bytes 65536 svE
    Maximum amount of output data buffered per stream.
    H2ModernTLSOnly on|off on svE
    Require HTTP/2 connections to be "modern TLS" only
    H2Push on|off on svE
    H2 Server Push Switch
    H2PushPriority mime-type [after|before|interleaved] [weight] * After 16 svE
    H2 Server Push Priority
    H2SerializeHeaders on|off off svE
    Serialize Request/Response Processing Switch
    H2SessionExtraFiles n 5 svE
    Number of Extra File Handles
    H2StreamMaxMemSize bytes 65536 svE
    Maximum amount of output data buffered per stream.
    H2TLSCoolDownSecs seconds 1 svE
    -
    H2TLSWarmUpSize amount 1048576 svE
    -
    H2Upgrade on|off on for h2c, off for +svE
    H2 Upgrade Protocol Switch
    H2WindowSize bytes 65536 svE
    Size of Stream Window for upstream data.
    Header [condition] add|append|echo|edit|edit*|merge|set|setifempty|unset|note en-tête [[expr=]valeur @@ -965,414 +971,416 @@ mod_status
    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
    ReadmeName nom-fichiersvdhB
    Nom du fichier dont le contenu sera inséré à la fin de +
    QualifyRedirectURL ON|OFF OFF svdC
    Vérifie si la variable d'environnement REDIRECT_URL est +pleinement qualifiée
    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] chemin URL -URLsvdhB
    Envoie une redirection externe demandant au client +
    ReceiveBufferSize octets 0 sM
    Taille du tampon TCP en entrée
    Redirect [état] chemin URL +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
    RedirectTemp chemin URL URLsvdhB
    Envoie une redirection externe temporaire demandant au +
    RedirectTemp chemin URL URLsvdhB
    Envoie une redirection externe temporaire demandant au client d'effectuer une nouvelle requête avec une URL différente
    ReflectorHeader en-tête-entrée [en-tête-sortie]svdhB
    Réfléchit un en-tête d'entrée dans les en-têtes de sortie
    RemoteIPHeader en-têtesvB
    Définit le champ d'en-tête qui contiendra les adresses IP +
    ReflectorHeader en-tête-entrée [en-tête-sortie]svdhB
    Réfléchit un en-tête d'entrée dans les en-têtes de sortie
    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
    RemoteIPTrustedProxy -ip-mandataire|ip-mandataire/sous-réseau|nom-hôte ...svB
    Déclare les adresses IP intranet clients comme dignes de +
    RemoteIPTrustedProxy +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
    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 langue à un ensemble +
    RemoveLanguage extension [extension] +...vdhB
    Supprime toute association de langue à 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 [header=délai[-délai-maxi][,MinRate=taux-mini] [body=délai[-délai-maxi][,MinRate=taux-mini] -svE
    Définit des délais maximums pour la réception des en-têtes +svE
    Définit des délais maximums pour la réception des en-têtes et 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 - chaîne_de_test expression_de_comparaisonsvdhE
    Définit une condition qui devra être satisfaite pour que +
    RewriteCond + chaîne_de_test expression_de_comparaisonsvdhE
    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 nom_de_la_correspondance type_de_correspondance:source_de_la_correspondance -svE
    Définit une fonction de mise en correspondance pour la +
    RewriteMap nom_de_la_correspondance type_de_correspondance:source_de_la_correspondance +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 - Modèle Substitution [drapeaux]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 + Modèle Substitution [drapeaux]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 dhE
    Interaction entre le contrôle d'accès en fonction de l'hôte +
    Satisfy Any|All All dhE
    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épertoiresvB
    Fait correspondre une URL à une zone du système de fichiers -et désigne la cible comme script CGI
    ScriptAliasMatch regex +
    ScriptAlias chemin URL chemin fichier|chemin répertoiresvB
    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 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 -entièrement qualifié[:port]svC
    Nom d'hôte et port que le serveur utilise pour +
    ServerName [protocole://]nom de domaine +entièrement qualifié[: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 algorithmesvdhX
    L'algorithme à utiliser pour le chiffrement de la session
    SessionCryptoDriver nom [param[=valeur]]sX
    Le pilote de chiffrement à utiliser pour chiffrer les +
    SessionCryptoCipher algorithmesvdhX
    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
    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 nom gestionnaire|NonesvdhC
    Force le traitement des fichiers spécifiés par un +
    SetHandler nom gestionnaire|NonesvdhC
    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 chemin-fichiersvE
    Fichier contenant une concaténation des certificats de CA +
    SSLCACertificateFile chemin-fichiersvE
    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 chemin-fichiersvE
    Fichier contenant la concaténation des certificats de CA +
    SSLCADNRequestFile chemin-fichiersvE
    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 none svE
    Active la vérification des révocations basée sur les CRL
    SSLCARevocationFile chemin-fichiersvE
    Fichier contenant la concaténation des CRLs des CA codés en +
    SSLCARevocationCheck chain|leaf|none none svE
    Active la vérification des révocations basée sur les CRL
    SSLCARevocationFile chemin-fichiersvE
    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 chemin-fichiersvE
    Fichier contenant les certificats de CA du serveur codés en +
    SSLCertificateChainFile chemin-fichiersvE
    Fichier contenant les certificats de CA du serveur codés en PEM
    SSLCertificateFile chemin-fichiersvE
    Fichier de données contenant le certificat X.509 du serveur codé en +
    SSLCertificateFile chemin-fichiersvE
    Fichier de données contenant le certificat X.509 du serveur codé en PEM
    SSLCertificateKeyFile chemin-fichiersvE
    Fichier contenant la clé privée du serveur codée en +
    SSLCertificateKeyFile chemin-fichiersvE
    Fichier contenant la clé privée du serveur codée en PEM
    SSLCipherSuite algorithmes DEFAULT (dépend de +svdhE
    Algorithmes de chiffrement disponibles pour la négociation +
    SSLCipherSuite algorithmes 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|optional 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|optional 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
    SSLOCSDefaultResponder urisvE
    Définit l'URI du répondeur par défaut pour la validation +
    SSLOCSDefaultResponder urisvE
    Définit l'URI du répondeur par défaut pour la validation OCSP
    SSLOCSPEnable on|off off svE
    Active la validation OCSP de la chaîne de certificats du +
    SSLOCSPEnable on|off off svE
    Active la validation OCSP de la chaîne de certificats du client
    SSLOCSPOverrideResponder on|off off svE
    Force l'utilisation de l'URI du répondeur par défaut pour +
    SSLOCSPOverrideResponder on|off off svE
    Force l'utilisation de l'URI du répondeur par défaut pour la validation OCSP
    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-pathsvE
    Fichier contenant la concaténation des certificats de CA +
    SSLProxyCACertificateFile file-pathsvE
    Fichier contenant la concaténation des certificats de CA codés en PEM pour l'authentification des serveurs distants
    SSLProxyCACertificatePath chemin-répertoiresvE
    Répertoire des certificats de CA codés en PEM pour +
    SSLProxyCACertificatePath chemin-répertoiresvE
    Répertoire des certificats de CA codés en PEM pour l'authentification des serveurs distants
    SSLProxyCARevocationCheck chain|leaf|none none svE
    Active la vérification des révocations basée sur les CRLs +
    SSLProxyCARevocationCheck chain|leaf|none none svE
    Active la vérification des révocations basée sur les CRLs pour l'authentification du serveur distant
    SSLProxyCARevocationFile chemin-fichiersvE
    Fichier contenant la concaténation des CRLs de CA codés en +
    SSLProxyCARevocationFile chemin-fichiersvE
    Fichier contenant la concaténation des CRLs de CA codés en PEM pour l'authentification des serveurs distants
    SSLProxyCARevocationPath chemin-répertoiresvE
    Répertoire des CRLs de CA codés en PEM pour +
    SSLProxyCARevocationPath chemin-répertoiresvE
    Répertoire des CRLs de CA codés en PEM pour l'authentification des serveurs distants
    SSLProxyCheckPeerCN on|off on svE
    Configuration de la vérification du champ CN du certificat +
    SSLProxyCheckPeerCN on|off on svE
    Configuration de la vérification du champ CN du certificat du serveur distant
    SSLProxyCheckPeerExpire on|off on svE
    Configuration de la vérification de l'expiration du +
    SSLProxyCheckPeerExpire on|off on svE
    Configuration de la vérification de l'expiration du certificat du serveur distant
    SSLProxyCheckPeerName on|off on svE
    Configure la vérification du nom d'hôte dans les +
    SSLProxyCheckPeerName on|off on svE
    Configure la vérification du nom d'hôte dans les certificats serveur distants
    SSLProxyCipherSuite algorithmes ALL:!ADH:RC4+RSA:+H +svdhE
    Algorithmes de chiffrement disponibles pour la négociation +
    SSLProxyCipherSuite algorithmes ALL:!ADH:RC4+RSA:+H +svdhE
    Algorithmes de chiffrement disponibles pour la négociation lors de l'initialisation d'une connexion SSL de mandataire
    SSLProxyEngine on|off off svE
    Interrupteur marche/arrêt du moteur de mandataire +
    SSLProxyEngine on|off off svE
    Interrupteur marche/arrêt du moteur de mandataire SSL
    SSLProxyMachineCertificateChainFile nom-fichiersE
    Fichier de certificats de CA encodés PEM concaténés permettant au +
    SSLProxyMachineCertificateChainFile nom-fichiersE
    Fichier de certificats de CA encodés PEM concaténés permettant au mandataire de choisir un certificat
    SSLProxyMachineCertificateFile chemin-fichiersE
    Fichier contenant la concaténation des clés et certificats +
    SSLProxyMachineCertificateFile chemin-fichiersE
    Fichier contenant la concaténation des clés et certificats clients codés en PEM que le mandataire doit utiliser
    SSLProxyMachineCertificatePath chemin-répertoiresE
    Répertoire des clés et certificats clients codés en PEM que +
    SSLProxyMachineCertificatePath chemin-répertoiresE
    Répertoire des clés et certificats clients codés en PEM que le mandataire doit utiliser
    SSLProxyProtocol [+|-]protocole ... all -SSLv3 (jusqu'à +svE
    Définit les protocoles SSL disponibles pour la fonction de +
    SSLProxyProtocol [+|-]protocole ... all -SSLv3 (jusqu'à +svE
    Définit les protocoles SSL disponibles pour la fonction de mandataire
    SSLProxyVerify niveau none svE
    Niveau de vérification du certificat du serveur +
    SSLProxyVerify niveau none svE
    Niveau de vérification du certificat du serveur distant
    SSLProxyVerifyDepth niveau 1 svE
    Niveau de profondeur maximum dans les certificats de CA +
    SSLProxyVerifyDepth niveau 1 svE
    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 chemin-fichiersvE
    Clé de chiffrement/déchiffrement permanente pour les +
    SSLSessionTicketKeyFile chemin-fichiersvE
    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
    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
    Substitute s/modèle/substitution/[infq]dhE
    Modèle de substition dans le contenu de la +
    StartThreads nombresM
    Nombre de threads créés au démarrage
    Substitute s/modèle/substitution/[infq]dhE
    Modèle de substition dans le contenu de la réponse
    SubstituteInheritBefore on|off off dhE
    Change the merge order of inherited patterns
    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
    UnDefine nom-variablesC
    Invalide la définition d'une variable
    UndefMacro namesvdB
    Undefine a 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 +
    TransferLog fichier|pipesvB
    Spécifie l'emplacement d'un fichier journal
    TypesConfig chemin-fichier conf/mime.types sB
    Le chemin du fichier mime.types
    UnDefine nom-variablesC
    Invalide la définition d'une variable
    UndefMacro namesvdB
    Undefine a 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 vX
    Détermine si le serveur virtuel peut exécuter des +
    VHostCGIMode On|Off|Secure On vX
    Détermine si le serveur virtuel peut exécuter des sous-processus, et définit les privilèges disponibles pour ces dernier.
    VHostPrivs [+-]?nom-privilège [[+-]?nom-privilège] ...vX
    Assigne des privilèges au choix aux sous-processus créés +
    VHostPrivs [+-]?nom-privilège [[+-]?nom-privilège] ...vX
    Assigne des privilèges au choix aux sous-processus créés par un serveur virtuel.
    VHostGroup identifiant-groupe-unixvX
    Définit l'identifiant du groupe sous lequel s'exécute un +
    VHostGroup identifiant-groupe-unixvX
    Définit l'identifiant du groupe sous lequel s'exécute un serveur virtuel.
    VHostPrivs [+-]?nom-privilège [[+-]?nom-privilège] ...vX
    Assigne des privilèges à un serveur virtuel.
    VHostSecure On|Off On vX
    Détermine si le serveur s'exécute avec une sécurité avancée +
    VHostPrivs [+-]?nom-privilège [[+-]?nom-privilège] ...vX
    Assigne des privilèges à un serveur virtuel.
    VHostSecure On|Off On vX
    Détermine si le serveur s'exécute avec une sécurité avancée pour les serveurs virtuels.
    VHostUser identifiant-utilisateur-unixvX
    Définit l'identifiant utilisateur sous lequel s'exécute un +
    VHostUser identifiant-utilisateur-unixvX
    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 number-of-seconds 1 sB
    Watchdog interval in seconds
    XBitHack on|off|full off svdhB
    Interprète les directives SSI dans les fichiers dont le bit +
    WatchdogInterval number-of-seconds 1 sB
    Watchdog interval in seconds
    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 --git a/docs/manual/mod/quickreference.html.ja.utf8 b/docs/manual/mod/quickreference.html.ja.utf8 index 0deece93..fa1d38fd 100644 --- a/docs/manual/mod/quickreference.html.ja.utf8 +++ b/docs/manual/mod/quickreference.html.ja.utf8 @@ -51,7 +51,7 @@ ディレクティブã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãŒç¤ºã•れã¦ã„ã¾ã™ã€‚

    - +
     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  R  |  S  |  T  |  U  |  V  |  W  |  X 
     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  Q  |  R  |  S  |  T  |  U  |  V  |  W  |  X  @@ -443,14 +443,20 @@ request - + - - - + + + + + + + + + @@ -714,304 +720,306 @@ header - - - + + + - - - - - - - - - - - + + + + + + + - - - - - - + + + - - + - - - - - - + - - - - - + + + + - - - - + - - - - - - - - - - + + + + + + - - + - - - - + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + - - + - - - - - - - + + + + + + + - - - - - + + + + - - - - - + - - - - - + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + - - - + - - - - - - - - - - - - + + + + + + - - - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - + + + - - - - - - - - + + + + + + + - - + - - - - - - - - - - + + + + + + + - - - + - - - - + + - - + - - - - - - + - - + - +
    sサーãƒè¨­å®šãƒ•ァイル
    vãƒãƒ¼ãƒãƒ£ãƒ«ãƒ›ã‚¹ãƒˆ
    dディレクトリ
    GracefulShutDownTimeout secondssM
    ç©ã‚„ã‹ãªåœæ­¢ã‚’ã‹ã‘ãŸå¾Œã€çµ‚了ã™ã‚‹ã¾ã§å¾…ã¤æ™‚é–“
    Group unix-group #-1 sB
    Group under which the server will answer requests
    H2Direct on|off on (for non TLS) svE
    H2 Direct Protocol Switch
    H2Direct on|off on for h2c, off for +svE
    H2 Direct Protocol Switch
    H2MaxSessionStreams n 100 svE
    Maximum number of active streams per HTTP/2 session.
    H2MaxWorkerIdleSeconds n 600 sE
    Maximum number of seconds h2 workers remain idle until shut down.
    H2MaxWorkers nsE
    Maximum number of worker threads to use per child process.
    H2MinWorkers nsE
    Minimal number of worker threads to use per child process.
    H2SerializeHeaders on|off off svE
    Serialize Request/Resoonse Processing Switch
    H2SessionExtraFiles n 5 svE
    Number of Extra File Handles
    H2StreamMaxMemSize bytes 65536 svE
    Maximum amount of output data buffered per stream.
    H2ModernTLSOnly on|off on svE
    Require HTTP/2 connections to be "modern TLS" only
    H2Push on|off on svE
    H2 Server Push Switch
    H2PushPriority mime-type [after|before|interleaved] [weight] * After 16 svE
    H2 Server Push Priority
    H2SerializeHeaders on|off off svE
    Serialize Request/Response Processing Switch
    H2SessionExtraFiles n 5 svE
    Number of Extra File Handles
    H2StreamMaxMemSize bytes 65536 svE
    Maximum amount of output data buffered per stream.
    H2TLSCoolDownSecs seconds 1 svE
    -
    H2TLSWarmUpSize amount 1048576 svE
    -
    H2Upgrade on|off on for h2c, off for +svE
    H2 Upgrade Protocol Switch
    H2WindowSize bytes 65536 svE
    Size of Stream Window for upstream data.
    Header [condition] set|append|add|unset|echo header [value] [early|env=[!]variable]svdhE
    HTTP 応答ヘッダã®è¨­å®š
    ProxyTimeout seconds 300 svE
    プロキシã•れãŸãƒªã‚¯ã‚¨ã‚¹ãƒˆã®ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆ
    ProxyVia On|Off|Full|Block Off svE
    プロキシã•れãŸãƒªã‚¯ã‚¨ã‚¹ãƒˆã® Via HTTP 応答ヘッダ ã«ã‚ˆã‚Šæä¾›ã•れる情報
    ReadmeName filenamesvdhB
    ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ä¸€è¦§ã®æœ€å¾Œã«æŒ¿å…¥ã•れるファイルã®åå‰
    ReceiveBufferSize bytes 0 sM
    TCP å—ä¿¡ãƒãƒƒãƒ•ァサイズ
    Redirect [status] URL-path -URLsvdhB
    クライアントãŒé•ㆠURL ã‚’å–å¾—ã™ã‚‹ã‚ˆã†ã«å¤–部ã¸ã®ãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆã‚’ +
    QualifyRedirectURL ON|OFF OFF svdC
    Controls whether the REDIRECT_URL environent variable is + fully qualified
    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 ã‚’å–å¾—ã™ã‚‹ã‚ˆã†ã«å¤–部ã¸ã®æ°¸ä¹…的㪠リダイレクトをé€ã‚‹
    RedirectTemp URL-path URLsvdhB
    クライアントãŒé•ㆠURL ã‚’å–å¾—ã™ã‚‹ã‚ˆã†ã«å¤–部ã¸ã®ä¸€æ™‚的㪠+
    RedirectTemp URL-path URLsvdhB
    クライアントãŒé•ㆠURL ã‚’å–å¾—ã™ã‚‹ã‚ˆã†ã«å¤–部ã¸ã®ä¸€æ™‚的㪠リダイレクトをé€ã‚‹
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    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
    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
    ãƒ•ã‚¡ã‚¤ãƒ«ã®æ‹¡å¼µå­ã«é–¢é€£ä»˜ã‘られãŸã™ã¹ã¦ã®æ–‡å­—セット +
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    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
    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 [header=timeout[-maxtimeout][,MinRate=rate] [body=timeout[-maxtimeout][,MinRate=rate] -svE
    Set timeout values for receiving request headers and body from client. +svE
    Set timeout values for receiving request headers and 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 CondPatternsvdhE
    Defines a condition under which rewriting will take place +
    RewriteBase URL-pathdhE
    Sets the base URL for per-directory rewrites
    RewriteCond + TestString CondPatternsvdhE
    Defines a condition under which rewriting will take place
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource -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 消費é‡ã‚’ +
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource +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-pathsvB
    URL をファイルシステムã®ä½ç½®ã¸ãƒžãƒƒãƒ—ã—ã€ãƒžãƒƒãƒ—先を +
    ScriptAlias URL-path +file-path|directory-pathsvB
    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 namesvdhX
    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 namesvdhX
    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
    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 +
    SessionExclude pathsvdhE
    Define URL prefixes for which a session is ignored
    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 none svE
    Enable CRL-based revocation checking
    SSLCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for +
    SSLCARevocationCheck chain|leaf|none 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-pathsvE
    Server PEM-encoded X.509 certificate data file
    SSLCertificateKeyFile file-pathsvE
    Server PEM-encoded private key file
    SSLCipherSuite 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-pathsvE
    Server PEM-encoded X.509 certificate data file
    SSLCertificateKeyFile file-pathsvE
    Server PEM-encoded private key file
    SSLCipherSuite 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|optional 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
    SSLOCSDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|off off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    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|optional 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
    SSLOCSDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|off off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    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 cipher-spec ALL:!ADH:RC4+RSA:+H +svdhE
    Cipher Suite available for negotiation in SSL +
    SSLProxyCipherSuite cipher-spec ALL:!ADH:RC4+RSA:+H +svdhE
    Cipher Suite available for negotiation in SSL proxy handshake
    SSLProxyEngine on|off off svE
    SSL Proxy Engine Operation Switch
    SSLProxyMachineCertificateChainFile filenamesE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysE
    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 filenamesE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysE
    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
    StartServers numbersM
    起動時ã«ç”Ÿæˆã•れるå­ã‚µãƒ¼ãƒãƒ—ãƒ­ã‚»ã‚¹ã®æ•°
    StartThreads 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
    設定å¯èƒ½ãªå­ãƒ—ロセス毎ã®ã‚¹ãƒ¬ãƒƒãƒ‰æ•°ã®ä¸Šé™ã‚’ +
    StartServers numbersM
    起動時ã«ç”Ÿæˆã•れるå­ã‚µãƒ¼ãƒãƒ—ãƒ­ã‚»ã‚¹ã®æ•°
    StartThreads 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 sC
    TRACE メソッドã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆã«å¯¾ã™ã‚‹å¿œç­”方法を決ã‚ã‚‹ +
    TraceEnable [on|off|extended] on sC
    TRACE メソッドã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆã«å¯¾ã™ã‚‹å¿œç­”方法を決ã‚ã‚‹
    TransferLog file|pipesvB
    ログファイルã®ä½ç½®ã‚’指定
    TypesConfig file-path conf/mime.types s
    mime.types ファイルã®ä½ç½®
    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 ファイルã®ä½ç½®
    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 vX
    Determines whether the virtualhost can run +
    UserDir directory-filename [directory-filename] ...svB
    ユーザ専用ディレクトリã®ä½ç½®
    VHostCGIMode On|Off|Secure On vX
    Determines whether the virtualhost can run subprocesses, and the privileges available to subprocesses.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to subprocesses created +
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to subprocesses created by a virtual host.
    VHostGroup unix-groupidvX
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vX
    Determines whether the server runs with enhanced security +
    VHostGroup unix-groupidvX
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vX
    Determines whether the server runs with enhanced security for the virtualhost.
    VHostUser unix-useridvX
    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-useridvX
    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 number-of-seconds 1 sB
    Watchdog interval in seconds
    XBitHack on|off|full off svdhB
    実行ビットãŒè¨­å®šã•れãŸãƒ•ァイル㮠SSI ディレクティブを +
    WatchdogInterval number-of-seconds 1 sB
    Watchdog interval in seconds
    XBitHack on|off|full off svdhB
    実行ビットãŒè¨­å®šã•れãŸãƒ•ァイル㮠SSI ディレクティブを è§£æžã™ã‚‹
    xml2EncAlias charset alias [alias ...]sB
    Recognise Aliases for encoding values
    xml2EncDefault namesvdhB
    Sets a default encoding to assume when absolutely no information +
    xml2EncAlias charset alias [alias ...]sB
    Recognise 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 --git a/docs/manual/mod/quickreference.html.ko.euc-kr b/docs/manual/mod/quickreference.html.ko.euc-kr index 7ee2dc23..e1a577fc 100644 --- a/docs/manual/mod/quickreference.html.ko.euc-kr +++ b/docs/manual/mod/quickreference.html.ko.euc-kr @@ -48,7 +48,7 @@ ¼ö ÀÖ´Â Àå¼Ò¿Í Áö½Ã¾îÀÇ »óŸ¦ ³ªÅ¸³½´Ù.

    - +
     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  R  |  S  |  T  |  U  |  V  |  W  |  X 
     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  Q  |  R  |  S  |  T  |  U  |  V  |  W  |  X  @@ -443,14 +443,20 @@ media type in the HTTP Content-Type header field will exit. - + - - - + + + + + + + + + @@ -731,315 +737,317 @@ header - - - + + + - - - - - - - - - - - + + + + + + + - - - - - - - - + - - - - - - + - - - - - + + + + - - - - - - - - - - - - - - + + + + + - - + - - - + - - - - + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - + + + + + - - + + - - - - - + - - + - - - + + - - + - - - - - + - - - - - + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + - - - + - - - - - - - - - - - - + + + + + + - - - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - + + + - - - - - - - - + + + + + + + - - + - - - - - - - - - + + + + + + + - - + - - + - - - - + + - - + - - - - - - + - - + - +
    sÁÖ¼­¹ö¼³Á¤
    v°¡»óÈ£½ºÆ®
    ddirectory
    Group unix-group #-1 sB
    Group under which the server will answer requests
    H2Direct on|off on (for non TLS) svE
    H2 Direct Protocol Switch
    H2Direct on|off on for h2c, off for +svE
    H2 Direct Protocol Switch
    H2MaxSessionStreams n 100 svE
    Maximum number of active streams per HTTP/2 session.
    H2MaxWorkerIdleSeconds n 600 sE
    Maximum number of seconds h2 workers remain idle until shut down.
    H2MaxWorkers nsE
    Maximum number of worker threads to use per child process.
    H2MinWorkers nsE
    Minimal number of worker threads to use per child process.
    H2SerializeHeaders on|off off svE
    Serialize Request/Resoonse Processing Switch
    H2SessionExtraFiles n 5 svE
    Number of Extra File Handles
    H2StreamMaxMemSize bytes 65536 svE
    Maximum amount of output data buffered per stream.
    H2ModernTLSOnly on|off on svE
    Require HTTP/2 connections to be "modern TLS" only
    H2Push on|off on svE
    H2 Server Push Switch
    H2PushPriority mime-type [after|before|interleaved] [weight] * After 16 svE
    H2 Server Push Priority
    H2SerializeHeaders on|off off svE
    Serialize Request/Response Processing Switch
    H2SessionExtraFiles n 5 svE
    Number of Extra File Handles
    H2StreamMaxMemSize bytes 65536 svE
    Maximum amount of output data buffered per stream.
    H2TLSCoolDownSecs seconds 1 svE
    -
    H2TLSWarmUpSize amount 1048576 svE
    -
    H2Upgrade on|off on for h2c, off for +svE
    H2 Upgrade Protocol Switch
    H2WindowSize bytes 65536 svE
    Size of Stream Window for upstream data.
    Header [condition] set|append|add|unset|echo header [value] [early|env=[!]variable]svdhE
    HTTP ÀÀ´ä Çì´õ¸¦ ±¸¼ºÇÑ´Ù
    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
    ReadmeName filenamesvdhB
    ÆÄÀϸñ·Ï ¸¶Áö¸·¿¡ »ðÀÔÇÒ ÆÄÀÏÀÇ À̸§
    ReceiveBufferSize bytes 0 sM
    TCP receive buffer size
    Redirect [status] URL-path -URLsvdhB
    Ŭ¶óÀÌ¾ðÆ®°¡ ´Ù¸¥ URL¿¡ Á¢¼ÓÇϵµ·Ï ¿äûÇÏ´Â ¿ÜºÎ +
    QualifyRedirectURL ON|OFF OFF svdC
    Controls whether the REDIRECT_URL environent variable is + fully qualified
    ReadmeName filenamesvdhB
    ÆÄÀϸñ·Ï ¸¶Áö¸·¿¡ »ðÀÔÇÒ ÆÄÀÏÀÇ À̸§
    ReceiveBufferSize bytes 0 sM
    TCP 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¿¡ Á¢¼ÓÇϵµ·Ï ¿äûÇÏ´Â ¿ÜºÎ ¿µ±¸ ¸®´ÙÀÌ·º¼ÇÀ» º¸³½´Ù
    RedirectTemp URL-path URLsvdhB
    Ŭ¶óÀÌ¾ðÆ®°¡ ´Ù¸¥ URL¿¡ Á¢¼ÓÇϵµ·Ï ¿äûÇÏ´Â ¿ÜºÎ +
    RedirectTemp URL-path URLsvdhB
    Ŭ¶óÀÌ¾ðÆ®°¡ ´Ù¸¥ URL¿¡ Á¢¼ÓÇϵµ·Ï ¿äûÇÏ´Â ¿ÜºÎ Àӽà ¸®´ÙÀÌ·º¼ÇÀ» º¸³½´Ù
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    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
    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 +
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    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
    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 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 [header=timeout[-maxtimeout][,MinRate=rate] [body=timeout[-maxtimeout][,MinRate=rate] -svE
    Set timeout values for receiving request headers and body from client. +svE
    Set timeout values for receiving request headers and 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 CondPatternsvdhE
    Defines a condition under which rewriting will take place +
    RewriteBase URL-pathdhE
    Sets the base URL for per-directory rewrites
    RewriteCond + TestString CondPatternsvdhE
    Defines a condition under which rewriting will take place
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource -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 +
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource +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 dhE
    Interaction between host-level access control and +
    Satisfy Any|All All dhE
    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
    ƯÁ¤ ¿äû¸Þ¼­µå¿¡ ´ëÇØ CGI ½ºÅ©¸³Æ®¸¦ +
    Script method cgi-scriptsvdB
    ƯÁ¤ ¿äû¸Þ¼­µå¿¡ ´ëÇØ CGI ½ºÅ©¸³Æ®¸¦ »ç¿ëÇÑ´Ù.
    ScriptAlias URL-path -file-path|directory-pathsvB
    URLÀ» ƯÁ¤ ÆÄÀϽýºÅÛ Àå¼Ò·Î ´ëÀÀÇÏ°í ´ë»óÀÌ CGI +
    ScriptAlias URL-path +file-path|directory-pathsvB
    URLÀ» ƯÁ¤ ÆÄÀϽýºÅÛ Àå¼Ò·Î ´ëÀÀÇÏ°í ´ë»óÀÌ CGI ½ºÅ©¸³Æ®¶ó°í ¾Ë¸°´Ù
    ScriptAliasMatch regex -file-path|directory-pathsvB
    Á¤±ÔÇ¥Çö½ÄÀ» »ç¿ëÇÏ¿© URLÀ» ƯÁ¤ ÆÄÀϽýºÅÛ Àå¼Ò·Î +
    ScriptAliasMatch regex +file-path|directory-pathsvB
    Á¤±ÔÇ¥Çö½ÄÀ» »ç¿ëÇÏ¿© URLÀ» ƯÁ¤ ÆÄÀϽýºÅÛ Àå¼Ò·Î ´ëÀÀÇÏ°í ´ë»óÀÌ CGI ½ºÅ©¸³Æ®¶ó°í ¾Ë¸°´Ù
    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
    CGI ½ºÅ©¸³Æ® ¿À·ù·Î±×ÆÄÀÏÀÇ À§Ä¡
    ScriptLogBuffer bytes 1024 svB
    ½ºÅ©¸³Æ® ·Î±×¿¡ ±â·ÏÇÒ PUT ȤÀº POST ¿äûÀÇ ÃÖ´ë·®
    ScriptLogLength bytes 10385760 svB
    CGI ½ºÅ©¸³Æ® ·Î±×ÆÄÀÏÀÇ Å©±â Á¦ÇÑ
    ScriptSock file-path logs/cgisock svB
    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 +
    ScriptLog file-pathsvB
    CGI ½ºÅ©¸³Æ® ¿À·ù·Î±×ÆÄÀÏÀÇ À§Ä¡
    ScriptLogBuffer bytes 1024 svB
    ½ºÅ©¸³Æ® ·Î±×¿¡ ±â·ÏÇÒ PUT ȤÀº POST ¿äûÀÇ ÃÖ´ë·®
    ScriptLogLength bytes 10385760 svB
    CGI ½ºÅ©¸³Æ® ·Î±×ÆÄÀÏÀÇ Å©±â Á¦ÇÑ
    ScriptSock file-path logs/cgisock svB
    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 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 namesvdhX
    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 namesvdhX
    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
    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 +
    SessionExclude pathsvdhE
    Define URL prefixes for which a session is ignored
    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
    ¿äûÀÇ ¼ºÁú¿¡ µû¶ó ȯ°æº¯¼ö¸¦ ¼³Á¤ÇÑ´Ù
    svdhB
    Sets environment variables based on an ap_expr expression
    SetEnvIfNoCase attribute regex + [[!]env-variable[=value]] ...svdhB
    ¿äûÀÇ ¼ºÁú¿¡ µû¶ó ȯ°æº¯¼ö¸¦ ¼³Á¤ÇÑ´Ù
    svdhB
    Sets 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|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 none svE
    Enable CRL-based revocation checking
    SSLCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for +
    SSLCARevocationCheck chain|leaf|none 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-pathsvE
    Server PEM-encoded X.509 certificate data file
    SSLCertificateKeyFile file-pathsvE
    Server PEM-encoded private key file
    SSLCipherSuite 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-pathsvE
    Server PEM-encoded X.509 certificate data file
    SSLCertificateKeyFile file-pathsvE
    Server PEM-encoded private key file
    SSLCipherSuite 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|optional 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
    SSLOCSDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|off off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    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|optional 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
    SSLOCSDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|off off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    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 cipher-spec ALL:!ADH:RC4+RSA:+H +svdhE
    Cipher Suite available for negotiation in SSL +
    SSLProxyCipherSuite cipher-spec ALL:!ADH:RC4+RSA:+H +svdhE
    Cipher Suite available for negotiation in SSL proxy handshake
    SSLProxyEngine on|off off svE
    SSL Proxy Engine Operation Switch
    SSLProxyMachineCertificateChainFile filenamesE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysE
    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 filenamesE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysE
    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
    StartServers numbersM
    Number of child server processes created at startup
    StartThreads numbersM
    Number of threads created on startup
    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
    Sets the upper limit on the configurable number of threads +
    StartServers numbersM
    Number of child server processes created at startup
    StartThreads numbersM
    Number of threads created on startup
    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
    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
    ·Î±×ÆÄÀÏ À§Ä¡¸¦ ¼³Á¤ÇÑ´Ù
    TypesConfig file-path conf/mime.types sB
    The location of the mime.types file
    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
    Configures how the server determines its own name and +
    TraceEnable [on|off|extended] on svC
    Determines the behavior on TRACE requests
    TransferLog file|pipesvB
    ·Î±×ÆÄÀÏ À§Ä¡¸¦ ¼³Á¤ÇÑ´Ù
    TypesConfig file-path conf/mime.types sB
    The location of the mime.types file
    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
    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 public_html svB
    »ç¿ëÀÚº° µð·ºÅ丮 À§Ä¡
    VHostCGIMode On|Off|Secure On vX
    Determines whether the virtualhost can run +
    UserDir directory-filename public_html svB
    »ç¿ëÀÚº° µð·ºÅ丮 À§Ä¡
    VHostCGIMode On|Off|Secure On vX
    Determines whether the virtualhost can run subprocesses, and the privileges available to subprocesses.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to subprocesses created +
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to subprocesses created by a virtual host.
    VHostGroup unix-groupidvX
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vX
    Determines whether the server runs with enhanced security +
    VHostGroup unix-groupidvX
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vX
    Determines whether the server runs with enhanced security for the virtualhost.
    VHostUser unix-useridvX
    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-useridvX
    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 number-of-seconds 1 sB
    Watchdog interval in seconds
    XBitHack on|off|full off svdhB
    Parse SSI directives in files with the execute bit +
    WatchdogInterval number-of-seconds 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
    Recognise Aliases for encoding values
    xml2EncDefault namesvdhB
    Sets a default encoding to assume when absolutely no information +
    xml2EncAlias charset alias [alias ...]sB
    Recognise 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 --git a/docs/manual/mod/quickreference.html.tr.utf8 b/docs/manual/mod/quickreference.html.tr.utf8 index 9855dea6..425921ba 100644 --- a/docs/manual/mod/quickreference.html.tr.utf8 +++ b/docs/manual/mod/quickreference.html.tr.utf8 @@ -49,7 +49,7 @@ yönergenin durumu gösterilmiÅŸtir.

    - +
     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  R  |  S  |  T  |  U  |  V  |  W  |  X 
     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  Q  |  R  |  S  |  T  |  U  |  V  |  W  |  X  @@ -465,14 +465,20 @@ found - + - - - + + + + + + + + + - + - - + - - - - - - - - - - - + + + + + + + - - - - - - - - + - - - - - - + - - - - - + + + + - - - - - - - + - - - + + - - + - - + - - + - - - - - - + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - + + + + + - - + - - - - - + + - - + - - - + + - - + - - - - - + - - - - - + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + - - - + - - - - - - - - - - - - + + + + + + - - - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - + + + - - - - - - - + + + + - - - - - - - - - - - - + + + + + + - - - - + - - - - + + - - + - - - + - - - + - - + - +
    ssunucu geneli
    ksanal konak
    ddizin
    GracefulShutdownTimeout saniye 0 sM
    Sunucunun nazikçe kapatılmasının ardından ana süreç çıkana kadar geçecek süre için bir zaman aşımı belirler.
    Group unix-grubu #-1 sT
    İsteklere yanıt verecek sunucunun ait olacağı grubu belirler.
    H2Direct on|off on (for non TLS) skE
    H2 Direct Protocol Switch
    H2Direct on|off on for h2c, off for +skE
    H2 Direct Protocol Switch
    H2MaxSessionStreams n 100 skE
    Maximum number of active streams per HTTP/2 session.
    H2MaxWorkerIdleSeconds n 600 sE
    Maximum number of seconds h2 workers remain idle until shut down.
    H2MaxWorkers nsE
    Maximum number of worker threads to use per child process.
    H2MinWorkers nsE
    Minimal number of worker threads to use per child process.
    H2SerializeHeaders on|off off skE
    Serialize Request/Resoonse Processing Switch
    H2SessionExtraFiles n 5 skE
    Number of Extra File Handles
    H2StreamMaxMemSize bytes 65536 skE
    Maximum amount of output data buffered per stream.
    H2ModernTLSOnly on|off on skE
    Require HTTP/2 connections to be "modern TLS" only
    H2Push on|off on skE
    H2 Server Push Switch
    H2PushPriority mime-type [after|before|interleaved] [weight] * After 16 skE
    H2 Server Push Priority
    H2SerializeHeaders on|off off skE
    Serialize Request/Response Processing Switch
    H2SessionExtraFiles n 5 skE
    Number of Extra File Handles
    H2StreamMaxMemSize bytes 65536 skE
    Maximum amount of output data buffered per stream.
    H2TLSCoolDownSecs seconds 1 skE
    -
    H2TLSWarmUpSize amount 1048576 skE
    -
    H2Upgrade on|off on for h2c, off for +skE
    H2 Upgrade Protocol Switch
    H2WindowSize bytes 65536 skE
    Size of Stream Window for upstream data.
    Header [condition] add|append|echo|edit|edit*|merge|set|setifempty|unset|note header [[expr=]value [replacement] @@ -762,328 +768,330 @@ header
    ProxyTimeout secondsskE
    Network timeout for proxied requests
    ProxyVia On|Off|Full|Block Off skE
    Information provided in the Via HTTP response header for proxied requests
    ReadmeName dosya-ismiskdhT
    Dizin listesinin sonuna yerleştirilecek dosyanın ismini +
    QualifyRedirectURL ON|OFF OFF skdÇ
    Controls whether the REDIRECT_URL environent variable is + fully qualified
    ReadmeName dosya-ismiskdhT
    Dizin listesinin sonuna yerleştirilecek dosyanın ismini belirler.
    ReceiveBufferSize bayt-sayısı 0 sM
    TCP 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 sM
    TCP 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 -URLskdhT
    Geçerli URL ile eşleşen bir düzenli ifadeye dayanarak bir harici +
    RedirectMatch [durum] düzenli-ifade +URLskdhT
    Geç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.
    RedirectTemp URL-yolu URLskdhT
    İstemciyi, geçici bir yönlendirme isteği döndürerek farklı bir +
    RedirectTemp URL-yolu URLskdhT
    İstemciyi, geçici bir yönlendirme isteği döndürerek farklı bir URL’ye yönlendirir.
    ReflectorHeader inputheader [outputheader]skdhT
    Reflect an input header to the output headers
    RemoteIPHeader header-fieldskT
    Declare the header field which should be parsed for useragent IP addresses
    RemoteIPInternalProxy proxy-ip|proxy-ip/subnet|hostname ...skT
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPInternalProxyList filenameskT
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPProxiesHeader HeaderFieldNameskT
    Declare the header field which will record all intermediate IP addresses
    RemoteIPTrustedProxy proxy-ip|proxy-ip/subnet|hostname ...skT
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPTrustedProxyList filenameskT
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoveCharset extension [extension] -...kdhT
    Removes any character set associations for a set of file +
    ReflectorHeader inputheader [outputheader]skdhT
    Reflect an input header to the output headers
    RemoteIPHeader header-fieldskT
    Declare the header field which should be parsed for useragent IP addresses
    RemoteIPInternalProxy proxy-ip|proxy-ip/subnet|hostname ...skT
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPInternalProxyList filenameskT
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPProxiesHeader HeaderFieldNameskT
    Declare the header field which will record all intermediate IP addresses
    RemoteIPTrustedProxy proxy-ip|proxy-ip/subnet|hostname ...skT
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoteIPTrustedProxyList filenameskT
    Declare client intranet IP addresses trusted to present the RemoteIPHeader value
    RemoveCharset extension [extension] +...kdhT
    Removes any character set associations for a set of file extensions
    RemoveEncoding extension [extension] -...kdhT
    Removes any content encoding associations for a set of file +
    RemoveEncoding extension [extension] +...kdhT
    Removes any content encoding associations for a set of file extensions
    RemoveHandler extension [extension] -...kdhT
    Removes any handler associations for a set of file +
    RemoveHandler extension [extension] +...kdhT
    Removes any handler associations for a set of file extensions
    RemoveInputFilter extension [extension] -...kdhT
    Removes any input filter associations for a set of file +
    RemoveInputFilter extension [extension] +...kdhT
    Removes any input filter associations for a set of file extensions
    RemoveLanguage extension [extension] -...kdhT
    Removes any language associations for a set of file +
    RemoveLanguage extension [extension] +...kdhT
    Removes any language associations for a set of file extensions
    RemoveOutputFilter extension [extension] -...kdhT
    Removes any output filter associations for a set of file +
    RemoveOutputFilter extension [extension] +...kdhT
    Removes any output filter associations for a set of file extensions
    RemoveType extension [extension] -...kdhT
    Removes any content type associations for a set of file +
    RemoveType extension [extension] +...kdhT
    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]] -skdhE
    Configure HTTP request headers
    RequestReadTimeout +skdhE
    Configure HTTP request headers
    RequestReadTimeout [header=timeout[-maxtimeout][,MinRate=rate] [body=timeout[-maxtimeout][,MinRate=rate] -skE
    Set timeout values for receiving request headers and body from client. +skE
    Set timeout values for receiving request headers and body from client.
    Require [not] entity-name - [entity-name] ...dhT
    Tests whether an authenticated user is authorized by +
    Require [not] entity-name + [entity-name] ...dhT
    Tests whether an authenticated user is authorized by an authorization provider.
    <RequireAll> ... </RequireAll>dhT
    Enclose a group of authorization directives of which none +
    <RequireAll> ... </RequireAll>dhT
    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>dhT
    Enclose a group of authorization directives of which one +
    <RequireAny> ... </RequireAny>dhT
    Enclose a group of authorization directives of which one must succeed for the enclosing directive to succeed.
    <RequireNone> ... </RequireNone>dhT
    Enclose a group of authorization directives of which none +
    <RequireNone> ... </RequireNone>dhT
    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 CondPatternskdhE
    Defines a condition under which rewriting will take place +
    RewriteBase URL-pathdhE
    Sets the base URL for per-directory rewrites
    RewriteCond + TestString CondPatternskdhE
    Defines a condition under which rewriting will take place
    RewriteEngine on|off off skdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource -skE
    Defines a mapping function for key-lookup
    RewriteOptions OptionsskdhE
    Sets some special options for the rewrite engine
    RewriteRule - Pattern Substitution [flags]skdhE
    Defines 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 +
    RewriteEngine on|off off skdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource +skE
    Defines a mapping function for key-lookup
    RewriteOptions OptionsskdhE
    Sets some special options for the rewrite engine
    RewriteRule + Pattern Substitution [flags]skdhE
    Defines 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 dhE
    Interaction between host-level access control and +
    Satisfy Any|All All dhE
    Interaction 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-scriptskdT
    Activates a CGI script for a particular request +
    Script method cgi-scriptskdT
    Activates a CGI script for a particular request method.
    ScriptAlias URL-yolu -dosya-yolu|dizin-yoluskT
    Bir 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-yoluskT
    Bir URL’yi dosya sistemindeki bir yere düzenli ifade kullanarak +
    ScriptAlias URL-yolu +dosya-yolu|dizin-yoluskT
    Bir 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-yoluskT
    Bir 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-pathskT
    Location of the CGI script error logfile
    ScriptLogBuffer bytes 1024 skT
    Maximum 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-pathskT
    Location of the CGI script error logfile
    ScriptLogBuffer bytes 1024 skT
    Maximum amount of PUT or POST requests that will be recorded in the scriptlog
    ScriptLogLength bytes 10385760 skT
    Size limit of the CGI script logfile
    ScriptSock file-path cgisock sT
    The filename prefix of the socket to use for communication with +
    ScriptLogLength bytes 10385760 skT
    Size limit of the CGI script logfile
    ScriptSock file-path cgisock sT
    The filename prefix of the socket to use for communication with the cgi daemon
    SecureListen [IP-address:]portnumber -Certificate-Name [MUTUAL]sT
    Enables 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]sT
    Enables 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 sM
    TCP tamponu boyu
    ServerAdmin eposta-adresi|URLskÇ
    Sunucunun hata iletilerinde istemciye göstereceği eposta adresi +
    SendBufferSize bayt-sayısı 0 sM
    TCP 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ısM
    Ayarlanabilir süreç sayısının üst sınırını belirler.
    ServerName [şema://]tam-nitelenmiş-alan-adı[: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. +
    ServerLimit sayısM
    Ayarlanabilir süreç sayısının üst sınırını belirler.
    ServerName [şema://]tam-nitelenmiş-alan-adı[: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. +
    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 skdhE
    Enables a session for the current directory or location
    SessionCookieName name attributesskdhE
    Name and attributes for the RFC2109 cookie storing the session
    SessionCookieName2 name attributesskdhE
    Name and attributes for the RFC2965 cookie storing the session
    SessionCookieRemove On|Off Off skdhE
    Control for whether session cookies should be removed from incoming HTTP headers
    SessionCryptoCipher nameskdhD
    The crypto cipher to be used to encrypt the session
    SessionCryptoDriver name [param[=value]]sD
    The crypto driver to be used to encrypt the session
    SessionCryptoPassphrase secret [ secret ... ] skdhD
    The key used to encrypt the session
    SessionCryptoPassphraseFile filenameskdD
    File containing keys used to encrypt the session
    SessionDBDCookieName name attributesskdhE
    Name and attributes for the RFC2109 cookie storing the session ID
    SessionDBDCookieName2 name attributesskdhE
    Name and attributes for the RFC2965 cookie storing the session ID
    SessionDBDCookieRemove On|Off On skdhE
    Control for whether session ID cookies should be removed from incoming HTTP headers
    SessionDBDDeleteLabel label deletesession skdhE
    The SQL query to use to remove sessions from the database
    SessionDBDInsertLabel label insertsession skdhE
    The SQL query to use to insert sessions into the database
    SessionDBDPerUser On|Off Off skdhE
    Enable a per user session
    SessionDBDSelectLabel label selectsession skdhE
    The SQL query to use to select sessions from the database
    SessionDBDUpdateLabel label updatesession skdhE
    The SQL query to use to update existing sessions in the database
    SessionEnv On|Off Off skdhE
    Control whether the contents of the session are written to the +
    Session On|Off Off skdhE
    Enables a session for the current directory or location
    SessionCookieName name attributesskdhE
    Name and attributes for the RFC2109 cookie storing the session
    SessionCookieName2 name attributesskdhE
    Name and attributes for the RFC2965 cookie storing the session
    SessionCookieRemove On|Off Off skdhE
    Control for whether session cookies should be removed from incoming HTTP headers
    SessionCryptoCipher nameskdhD
    The crypto cipher to be used to encrypt the session
    SessionCryptoDriver name [param[=value]]sD
    The crypto driver to be used to encrypt the session
    SessionCryptoPassphrase secret [ secret ... ] skdhD
    The key used to encrypt the session
    SessionCryptoPassphraseFile filenameskdD
    File containing keys used to encrypt the session
    SessionDBDCookieName name attributesskdhE
    Name and attributes for the RFC2109 cookie storing the session ID
    SessionDBDCookieName2 name attributesskdhE
    Name and attributes for the RFC2965 cookie storing the session ID
    SessionDBDCookieRemove On|Off On skdhE
    Control for whether session ID cookies should be removed from incoming HTTP headers
    SessionDBDDeleteLabel label deletesession skdhE
    The SQL query to use to remove sessions from the database
    SessionDBDInsertLabel label insertsession skdhE
    The SQL query to use to insert sessions into the database
    SessionDBDPerUser On|Off Off skdhE
    Enable a per user session
    SessionDBDSelectLabel label selectsession skdhE
    The SQL query to use to select sessions from the database
    SessionDBDUpdateLabel label updatesession skdhE
    The SQL query to use to update existing sessions in the database
    SessionEnv On|Off Off skdhE
    Control whether the contents of the session are written to the HTTP_SESSION environment variable
    SessionExclude pathskdhE
    Define URL prefixes for which a session is ignored
    SessionHeader headerskdhE
    Import session updates from a given HTTP response header
    SessionInclude pathskdhE
    Define URL prefixes for which a session is valid
    SessionMaxAge maxage 0 skdhE
    Define a maximum age in seconds for a session
    SetEnv ortam-deÄŸiÅŸkeni [deÄŸer]skdhT
    Ortam değişkenlerini tanımlar.
    SetEnvIf öznitelik +
    SessionExclude pathskdhE
    Define URL prefixes for which a session is ignored
    SessionHeader headerskdhE
    Import session updates from a given HTTP response header
    SessionInclude pathskdhE
    Define URL prefixes for which a session is valid
    SessionMaxAge maxage 0 skdhE
    Define a maximum age in seconds for a session
    SetEnv ortam-deÄŸiÅŸkeni [deÄŸer]skdhT
    Ortam değişkenlerini tanımlar.
    SetEnvIf öznitelik düzifd [!]ort-değişkeni[=değer] - [[!]ort-değişkeni[=değer]] ...skdhT
    Ortam değişkenlerini isteğin özniteliklerine göre atar. + [[!]ort-değişkeni[=değer]] ...skdhT
    Ortam 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]] ...skdhT
    Bir ap_expr ifadesine dayanarak ortam deÄŸiÅŸkenlerine deÄŸer atar
    SetEnvIfNoCase öznitelik + [[!]ort-değişkeni[=değer]] ...skdhT
    Bir 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]] ...skdhT
    Ortam değişkenlerini isteğin özniteliklerinde harf büyüklüğüne + [[!]ort-değişkeni[=değer]] ...skdhT
    Ortam 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|NoneskdhÇ
    Eşleşen tüm dosyaların belli bir eylemci tarafından işlenmesine +
    SetHandler eylemci-ismi|NoneskdhÇ
    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 "-->" skT
    String that ends an include element
    SSIErrorMsg message "[an error occurred +skdhT
    Error 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 "-->" skT
    String that ends an include element
    SSIErrorMsg message "[an error occurred +skdhT
    Error message displayed when there is an SSI error
    SSIETag on|off off dhT
    Controls whether ETags are generated by the server.
    SSILastModified on|off off dhT
    Controls whether Last-Modified headers are generated by the +
    SSIETag on|off off dhT
    Controls whether ETags are generated by the server.
    SSILastModified on|off off dhT
    Controls whether Last-Modified headers are generated by the server.
    SSILegacyExprParser on|off off dhT
    Enable compatibility mode for conditional expressions.
    SSIStartTag tag "<!--#" skT
    String that starts an include element
    SSITimeFormat formatstring "%A, %d-%b-%Y %H:%M +skdhT
    Configures the format in which date strings are +
    SSILegacyExprParser on|off off dhT
    Enable compatibility mode for conditional expressions.
    SSIStartTag tag "<!--#" skT
    String that starts an include element
    SSITimeFormat formatstring "%A, %d-%b-%Y %H:%M +skdhT
    Configures the format in which date strings are displayed
    SSIUndefinedEcho string "(none)" skdhT
    String displayed when an unset variable is echoed
    SSLCACertificateFile file-pathskE
    File of concatenated PEM-encoded CA Certificates +
    SSIUndefinedEcho string "(none)" skdhT
    String displayed when an unset variable is echoed
    SSLCACertificateFile file-pathskE
    File of concatenated PEM-encoded CA Certificates for Client Auth
    SSLCACertificatePath directory-pathskE
    Directory of PEM-encoded CA Certificates for +
    SSLCACertificatePath directory-pathskE
    Directory of PEM-encoded CA Certificates for Client Auth
    SSLCADNRequestFile file-pathskE
    File of concatenated PEM-encoded CA Certificates +
    SSLCADNRequestFile file-pathskE
    File of concatenated PEM-encoded CA Certificates for defining acceptable CA names
    SSLCADNRequestPath directory-pathskE
    Directory of PEM-encoded CA Certificates for +
    SSLCADNRequestPath directory-pathskE
    Directory of PEM-encoded CA Certificates for defining acceptable CA names
    SSLCARevocationCheck chain|leaf|none none skE
    Enable CRL-based revocation checking
    SSLCARevocationFile file-pathskE
    File of concatenated PEM-encoded CA CRLs for +
    SSLCARevocationCheck chain|leaf|none none skE
    Enable CRL-based revocation checking
    SSLCARevocationFile file-pathskE
    File of concatenated PEM-encoded CA CRLs for Client Auth
    SSLCARevocationPath directory-pathskE
    Directory of PEM-encoded CA CRLs for +
    SSLCARevocationPath directory-pathskE
    Directory of PEM-encoded CA CRLs for Client Auth
    SSLCertificateChainFile file-pathskE
    File of PEM-encoded Server CA Certificates
    SSLCertificateFile file-pathskE
    Server PEM-encoded X.509 certificate data file
    SSLCertificateKeyFile file-pathskE
    Server PEM-encoded private key file
    SSLCipherSuite cipher-spec DEFAULT (depends on +skdhE
    Cipher Suite available for negotiation in SSL +
    SSLCertificateChainFile file-pathskE
    File of PEM-encoded Server CA Certificates
    SSLCertificateFile file-pathskE
    Server PEM-encoded X.509 certificate data file
    SSLCertificateKeyFile file-pathskE
    Server PEM-encoded private key file
    SSLCipherSuite cipher-spec DEFAULT (depends on +skdhE
    Cipher Suite available for negotiation in SSL handshake
    SSLCompression on|off off skE
    Enable compression on the SSL level
    SSLCryptoDevice engine builtin sE
    Enable use of a cryptographic hardware accelerator
    SSLEngine on|off|optional off skE
    SSL Engine Operation Switch
    SSLFIPS on|off off sE
    SSL FIPS mode Switch
    SSLHonorCipherOrder on|off off skE
    Option to prefer the server's cipher preference order
    SSLInsecureRenegotiation on|off off skE
    Option to enable support for insecure renegotiation
    SSLOCSDefaultResponder uriskE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|off off skE
    Enable OCSP validation of the client certificate chain
    SSLOCSPOverrideResponder on|off off skE
    Force use of the default responder URI for OCSP validation
    SSLOCSPResponderTimeout seconds 10 skE
    Timeout for OCSP queries
    SSLOCSPResponseMaxAge seconds -1 skE
    Maximum allowable age for OCSP responses
    SSLOCSPResponseTimeSkew seconds 300 skE
    Maximum allowable time skew for OCSP response validation
    SSLOCSPUseRequestNonce on|off on skE
    Use a nonce within OCSP queries
    SSLOpenSSLConfCmd command-name command-valueskE
    Configure OpenSSL parameters through its SSL_CONF API
    SSLOptions [+|-]option ...skdhE
    Configure various SSL engine run-time options
    SSLPassPhraseDialog type builtin sE
    Type of pass phrase dialog for encrypted private +
    SSLCompression on|off off skE
    Enable compression on the SSL level
    SSLCryptoDevice engine builtin sE
    Enable use of a cryptographic hardware accelerator
    SSLEngine on|off|optional off skE
    SSL Engine Operation Switch
    SSLFIPS on|off off sE
    SSL FIPS mode Switch
    SSLHonorCipherOrder on|off off skE
    Option to prefer the server's cipher preference order
    SSLInsecureRenegotiation on|off off skE
    Option to enable support for insecure renegotiation
    SSLOCSDefaultResponder uriskE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|off off skE
    Enable OCSP validation of the client certificate chain
    SSLOCSPOverrideResponder on|off off skE
    Force use of the default responder URI for OCSP validation
    SSLOCSPResponderTimeout seconds 10 skE
    Timeout for OCSP queries
    SSLOCSPResponseMaxAge seconds -1 skE
    Maximum allowable age for OCSP responses
    SSLOCSPResponseTimeSkew seconds 300 skE
    Maximum allowable time skew for OCSP response validation
    SSLOCSPUseRequestNonce on|off on skE
    Use a nonce within OCSP queries
    SSLOpenSSLConfCmd command-name command-valueskE
    Configure OpenSSL parameters through its SSL_CONF API
    SSLOptions [+|-]option ...skdhE
    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 +skE
    Configure usable SSL/TLS protocol versions
    SSLProxyCACertificateFile file-pathskE
    File of concatenated PEM-encoded CA Certificates +
    SSLProtocol [+|-]protocol ... all -SSLv3 (up to 2 +skE
    Configure usable SSL/TLS protocol versions
    SSLProxyCACertificateFile file-pathskE
    File of concatenated PEM-encoded CA Certificates for Remote Server Auth
    SSLProxyCACertificatePath directory-pathskE
    Directory of PEM-encoded CA Certificates for +
    SSLProxyCACertificatePath directory-pathskE
    Directory of PEM-encoded CA Certificates for Remote Server Auth
    SSLProxyCARevocationCheck chain|leaf|none none skE
    Enable CRL-based revocation checking for Remote Server Auth
    SSLProxyCARevocationFile file-pathskE
    File of concatenated PEM-encoded CA CRLs for +
    SSLProxyCARevocationCheck chain|leaf|none none skE
    Enable CRL-based revocation checking for Remote Server Auth
    SSLProxyCARevocationFile file-pathskE
    File of concatenated PEM-encoded CA CRLs for Remote Server Auth
    SSLProxyCARevocationPath directory-pathskE
    Directory of PEM-encoded CA CRLs for +
    SSLProxyCARevocationPath directory-pathskE
    Directory of PEM-encoded CA CRLs for Remote Server Auth
    SSLProxyCheckPeerCN on|off on skE
    Whether to check the remote server certificate's CN field +
    SSLProxyCheckPeerCN on|off on skE
    Whether to check the remote server certificate's CN field
    SSLProxyCheckPeerExpire on|off on skE
    Whether to check if remote server certificate is expired +
    SSLProxyCheckPeerExpire on|off on skE
    Whether to check if remote server certificate is expired
    SSLProxyCheckPeerName on|off on skE
    Configure host name checking for remote server certificates +
    SSLProxyCheckPeerName on|off on skE
    Configure host name checking for remote server certificates
    SSLProxyCipherSuite cipher-spec ALL:!ADH:RC4+RSA:+H +skdhE
    Cipher Suite available for negotiation in SSL +
    SSLProxyCipherSuite cipher-spec ALL:!ADH:RC4+RSA:+H +skdhE
    Cipher Suite available for negotiation in SSL proxy handshake
    SSLProxyEngine on|off off skE
    SSL Proxy Engine Operation Switch
    SSLProxyMachineCertificateChainFile filenamesE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysE
    Directory of PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyProtocol [+|-]protocol ... all -SSLv3 (up to 2 +skE
    Configure usable SSL protocol flavors for proxy usage
    SSLProxyVerify level none skE
    Type of remote server Certificate verification
    SSLProxyVerifyDepth number 1 skE
    Maximum depth of CA Certificates in Remote Server +
    SSLProxyEngine on|off off skE
    SSL Proxy Engine Operation Switch
    SSLProxyMachineCertificateChainFile filenamesE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysE
    Directory of PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyProtocol [+|-]protocol ... all -SSLv3 (up to 2 +skE
    Configure usable SSL protocol flavors for proxy usage
    SSLProxyVerify level none skE
    Type of remote server Certificate verification
    SSLProxyVerifyDepth number 1 skE
    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 skE
    Number of seconds before an SSL session expires +
    SSLSessionCacheTimeout seconds 300 skE
    Number of seconds before an SSL session expires in the Session Cache
    SSLSessionTicketKeyFile file-pathskE
    Persistent encryption/decryption key for TLS session tickets
    SSLSessionTickets on|off on skE
    Enable or disable use of TLS session tickets
    SSLSRPUnknownUserSeed secret-stringskE
    SRP unknown user seed
    SSLSRPVerifierFile file-pathskE
    Path to SRP verifier file
    SSLStaplingCache typesE
    Configures the OCSP stapling cache
    SSLStaplingErrorCacheTimeout seconds 600 skE
    Number of seconds before expiring invalid responses in the OCSP stapling cache
    SSLStaplingFakeTryLater on|off on skE
    Synthesize "tryLater" responses for failed OCSP stapling queries
    SSLStaplingForceURL uriskE
    Override the OCSP responder URI specified in the certificate's AIA extension
    SSLStaplingResponderTimeout seconds 10 skE
    Timeout for OCSP stapling queries
    SSLStaplingResponseMaxAge seconds -1 skE
    Maximum allowable age for OCSP stapling responses
    SSLStaplingResponseTimeSkew seconds 300 skE
    Maximum allowable time skew for OCSP stapling response validation
    SSLStaplingReturnResponderErrors on|off on skE
    Pass stapling related OCSP errors on to client
    SSLStaplingStandardCacheTimeout seconds 3600 skE
    Number of seconds before expiring responses in the OCSP stapling cache
    SSLStrictSNIVHostCheck on|off off skE
    Whether to allow non-SNI clients to access a name-based virtual +
    SSLSessionTicketKeyFile file-pathskE
    Persistent encryption/decryption key for TLS session tickets
    SSLSessionTickets on|off on skE
    Enable or disable use of TLS session tickets
    SSLSRPUnknownUserSeed secret-stringskE
    SRP unknown user seed
    SSLSRPVerifierFile file-pathskE
    Path to SRP verifier file
    SSLStaplingCache typesE
    Configures the OCSP stapling cache
    SSLStaplingErrorCacheTimeout seconds 600 skE
    Number of seconds before expiring invalid responses in the OCSP stapling cache
    SSLStaplingFakeTryLater on|off on skE
    Synthesize "tryLater" responses for failed OCSP stapling queries
    SSLStaplingForceURL uriskE
    Override the OCSP responder URI specified in the certificate's AIA extension
    SSLStaplingResponderTimeout seconds 10 skE
    Timeout for OCSP stapling queries
    SSLStaplingResponseMaxAge seconds -1 skE
    Maximum allowable age for OCSP stapling responses
    SSLStaplingResponseTimeSkew seconds 300 skE
    Maximum allowable time skew for OCSP stapling response validation
    SSLStaplingReturnResponderErrors on|off on skE
    Pass stapling related OCSP errors on to client
    SSLStaplingStandardCacheTimeout seconds 3600 skE
    Number of seconds before expiring responses in the OCSP stapling cache
    SSLStrictSNIVHostCheck on|off off skE
    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 skE
    Enable stapling of OCSP responses in the TLS handshake
    SSLVerifyClient level none skdhE
    Type of Client Certificate verification
    SSLVerifyDepth number 1 skdhE
    Maximum depth of CA Certificates in Client +
    SSLUserName varnamesdhE
    Variable name to determine user name
    SSLUseStapling on|off off skE
    Enable stapling of OCSP responses in the TLS handshake
    SSLVerifyClient level none skdhE
    Type of Client Certificate verification
    SSLVerifyDepth number 1 skdhE
    Maximum depth of CA Certificates in Client Certificate verification
    StartServers sayısM
    Sunucunun başlatılması sırasında oluşturulan çocuk süreçlerin +
    StartServers sayısM
    Sunucunun başlatılması sırasında oluşturulan çocuk süreçlerin sayısını belirler.
    StartThreads sayısM
    Sunucunun başlatılması sırasında oluşturulan evrelerin sayısını +
    StartThreads sayısM
    Sunucunun başlatılması sırasında oluşturulan evrelerin sayısını belirler.
    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|OffsT
    suEXEC özelliğini etkin veya etkisiz yapar
    SuexecUserGroup Kullanıcı GrupskE
    CGI betiklerini çalıştıracak kullanıcı ve grup belirtilir. +
    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|OffsT
    suEXEC özelliğini etkin veya etkisiz yapar
    SuexecUserGroup Kullanıcı GrupskE
    CGI 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ısM
    Her çocuk süreç tarafından oluşturulan evrelerin sayısını +
    ThreadsPerChild sayısM
    Her ç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]skT
    Bir günlük dosyasının yerini belirtir.
    TypesConfig file-path conf/mime.types sT
    The location of the mime.types file
    UnDefine değişken-ismisÇ
    Bir değişkeni tanımsız yapar
    UndefMacro nameskdT
    Undefine a macro
    UnsetEnv ortam-deÄŸiÅŸkeni [ortam-deÄŸiÅŸkeni] -...skdhT
    Ortamdaki değişkenleri tanımsız hale getirir.
    Use name [value1 ... valueN] -skdT
    Use 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]skT
    Bir günlük dosyasının yerini belirtir.
    TypesConfig file-path conf/mime.types sT
    The location of the mime.types file
    UnDefine değişken-ismisÇ
    Bir değişkeni tanımsız yapar
    UndefMacro nameskdT
    Undefine a macro
    UnsetEnv ortam-deÄŸiÅŸkeni [ortam-deÄŸiÅŸkeni] +...skdhT
    Ortamdaki değişkenleri tanımsız hale getirir.
    Use name [value1 ... valueN] +skdT
    Use 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] ...skT
    Kullanıcıya özel dizinlerin yeri
    VHostCGIMode On|Off|Secure On kD
    Determines whether the virtualhost can run +
    UserDir dizin [dizin] ...skT
    Kullanıcıya özel dizinlerin yeri
    VHostCGIMode On|Off|Secure On kD
    Determines whether the virtualhost can run subprocesses, and the privileges available to subprocesses.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...kD
    Assign arbitrary privileges to subprocesses created +
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...kD
    Assign arbitrary privileges to subprocesses created by a virtual host.
    VHostGroup unix-groupidkD
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...kD
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On kD
    Determines whether the server runs with enhanced security +
    VHostGroup unix-groupidkD
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...kD
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On kD
    Determines whether the server runs with enhanced security for the virtualhost.
    VHostUser unix-useridkD
    Sets the User ID under which a virtual host runs.
    VirtualDocumentRoot hesaplanan-dizin|none none skE
    Bir sanal konağın belge kök dizinini devingen olarak yapılandırır. +
    VHostUser unix-useridkD
    Sets the User ID under which a virtual host runs.
    VirtualDocumentRoot hesaplanan-dizin|none none skE
    Bir sanal konağın belge kök dizinini devingen olarak yapılandırır.
    VirtualDocumentRootIP hesaplanan-dizin|none none skE
    Bir sanal konağın belge kök dizinini devingen olarak yapılandırır. +
    VirtualDocumentRootIP hesaplanan-dizin|none none skE
    Bir 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 skE
    Bir 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 skE
    Bir sanal konağın CGI dizinini devingen olarak yapılandırır.
    VirtualScriptAliasIP hesaplanan-dizin|none none skE
    Bir sanal konağın CGI dizinini devingen olarak yapılandırır. +
    VirtualScriptAliasIP hesaplanan-dizin|none none skE
    Bir sanal konağın CGI dizinini devingen olarak yapılandırır.
    WatchdogInterval number-of-seconds 1 sT
    Watchdog interval in seconds
    XBitHack on|off|full off skdhT
    Parse SSI directives in files with the execute bit +
    WatchdogInterval number-of-seconds 1 sT
    Watchdog interval in seconds
    XBitHack on|off|full off skdhT
    Parse SSI directives in files with the execute bit set
    xml2EncAlias charset alias [alias ...]sT
    Recognise Aliases for encoding values
    xml2EncDefault nameskdhT
    Sets a default encoding to assume when absolutely no information +
    xml2EncAlias charset alias [alias ...]sT
    Recognise Aliases for encoding values
    xml2EncDefault nameskdhT
    Sets a default encoding to assume when absolutely no information can be automatically detected
    xml2StartParse element [element ...]skdhT
    Advise the parser to skip leading junk.
    xml2StartParse element [element ...]skdhT
    Advise the parser to skip leading junk.

    Mevcut Diller:  de  | diff --git a/docs/manual/mod/quickreference.html.zh-cn.utf8 b/docs/manual/mod/quickreference.html.zh-cn.utf8 index 62843b5f..be1c123b 100644 --- a/docs/manual/mod/quickreference.html.zh-cn.utf8 +++ b/docs/manual/mod/quickreference.html.zh-cn.utf8 @@ -44,7 +44,7 @@

    第三列显示å…许此指令的上下文,第四列显示指令的状æ€ã€‚

    - +
     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  R  |  S  |  T  |  U  |  V  |  W  |  X 
     A  |  B  |  C  |  D  |  E  |  F  |  G  |  H  |  I  |  K  |  L  |  M  |  N  |  O  |  P  |  Q  |  R  |  S  |  T  |  U  |  V  |  W  |  X  @@ -462,14 +462,20 @@ media type in the HTTP Content-Type header field will exit. - + - - - + + + + + + + + + - + - - + - - - - - - - - - - - + + + + + + + - - - - - - - - + - - - - - - + - - - - - + + + + - - - - - - - - - - + - - + - - + - - + - - - + - - - - + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - + + + + + - - + - - - - - + - - + - - - + + - - + - - - - - + - - - - - + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + - - - + - - - - - - - - - - - - + + + + + + - - - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - + + + - - - - - - - - + + + + + + + - - + - - - - - - - - - + + + + + + + - - + - - + - - - - + + - - + - - - - - - + - - + - +
    sæœåС噍é…ç½®
    v虚拟主机
    d目录
    Group unix-group #-1 sB
    Group under which the server will answer requests
    H2Direct on|off on (for non TLS) svE
    H2 Direct Protocol Switch
    H2Direct on|off on for h2c, off for +svE
    H2 Direct Protocol Switch
    H2MaxSessionStreams n 100 svE
    Maximum number of active streams per HTTP/2 session.
    H2MaxWorkerIdleSeconds n 600 sE
    Maximum number of seconds h2 workers remain idle until shut down.
    H2MaxWorkers nsE
    Maximum number of worker threads to use per child process.
    H2MinWorkers nsE
    Minimal number of worker threads to use per child process.
    H2SerializeHeaders on|off off svE
    Serialize Request/Resoonse Processing Switch
    H2SessionExtraFiles n 5 svE
    Number of Extra File Handles
    H2StreamMaxMemSize bytes 65536 svE
    Maximum amount of output data buffered per stream.
    H2ModernTLSOnly on|off on svE
    Require HTTP/2 connections to be "modern TLS" only
    H2Push on|off on svE
    H2 Server Push Switch
    H2PushPriority mime-type [after|before|interleaved] [weight] * After 16 svE
    H2 Server Push Priority
    H2SerializeHeaders on|off off svE
    Serialize Request/Response Processing Switch
    H2SessionExtraFiles n 5 svE
    Number of Extra File Handles
    H2StreamMaxMemSize bytes 65536 svE
    Maximum amount of output data buffered per stream.
    H2TLSCoolDownSecs seconds 1 svE
    -
    H2TLSWarmUpSize amount 1048576 svE
    -
    H2Upgrade on|off on for h2c, off for +svE
    H2 Upgrade Protocol Switch
    H2WindowSize bytes 65536 svE
    Size of Stream Window for upstream data.
    Header [condition] add|append|echo|edit|edit*|merge|set|setifempty|unset|note header [[expr=]value [replacement] @@ -759,324 +765,326 @@ header
    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
    ReadmeName filenamesvdhB
    Name of the file that will be inserted at the end +
    QualifyRedirectURL ON|OFF OFF svdC
    Controls whether the REDIRECT_URL environent variable is + fully qualified
    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
    RedirectTemp URL-path URLsvdhB
    Sends an external temporary redirect asking the client to fetch +
    RedirectTemp URL-path URLsvdhB
    Sends an external temporary redirect asking the client to fetch a different URL
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    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
    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 +
    ReflectorHeader inputheader [outputheader]svdhB
    Reflect an input header to the output headers
    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
    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 [header=timeout[-maxtimeout][,MinRate=rate] [body=timeout[-maxtimeout][,MinRate=rate] -svE
    Set timeout values for receiving request headers and body from client. +svE
    Set timeout values for receiving request headers and 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 CondPatternsvdhE
    Defines a condition under which rewriting will take place +
    RewriteBase URL-pathdhE
    Sets the base URL for per-directory rewrites
    RewriteCond + TestString CondPatternsvdhE
    Defines a condition under which rewriting will take place
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource -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 +
    RewriteEngine on|off off svdhE
    Enables or disables runtime rewriting engine
    RewriteMap MapName MapType:MapSource +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 dhE
    Interaction between host-level access control and +
    Satisfy Any|All All dhE
    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-pathsvB
    Maps a URL to a filesystem location and designates the +
    ScriptAlias URL-path +file-path|directory-pathsvB
    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 namesvdhX
    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 namesvdhX
    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
    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 +
    SessionExclude pathsvdhE
    Define URL prefixes for which a session is ignored
    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 none svE
    Enable CRL-based revocation checking
    SSLCARevocationFile file-pathsvE
    File of concatenated PEM-encoded CA CRLs for +
    SSLCARevocationCheck chain|leaf|none 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-pathsvE
    Server PEM-encoded X.509 certificate data file
    SSLCertificateKeyFile file-pathsvE
    Server PEM-encoded private key file
    SSLCipherSuite 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-pathsvE
    Server PEM-encoded X.509 certificate data file
    SSLCertificateKeyFile file-pathsvE
    Server PEM-encoded private key file
    SSLCipherSuite 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|optional 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
    SSLOCSDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|off off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    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|optional 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
    SSLOCSDefaultResponder urisvE
    Set the default responder URI for OCSP validation
    SSLOCSPEnable on|off off svE
    Enable OCSP validation of the client certificate chain
    SSLOCSPOverrideResponder on|off off svE
    Force use of the default responder URI for OCSP validation
    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 cipher-spec ALL:!ADH:RC4+RSA:+H +svdhE
    Cipher Suite available for negotiation in SSL +
    SSLProxyCipherSuite cipher-spec ALL:!ADH:RC4+RSA:+H +svdhE
    Cipher Suite available for negotiation in SSL proxy handshake
    SSLProxyEngine on|off off svE
    SSL Proxy Engine Operation Switch
    SSLProxyMachineCertificateChainFile filenamesE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysE
    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 filenamesE
    File of concatenated PEM-encoded CA certificates to be used by the proxy for choosing a certificate
    SSLProxyMachineCertificateFile filenamesE
    File of concatenated PEM-encoded client certificates and keys to be used by the proxy
    SSLProxyMachineCertificatePath directorysE
    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
    StartServers numbersM
    Number of child server processes created at startup
    StartThreads numbersM
    Number of threads created on startup
    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 +
    StartServers numbersM
    Number of child server processes created at startup
    StartThreads numbersM
    Number of threads created on startup
    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
    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 +
    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
    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 vX
    Determines whether the virtualhost can run +
    UserDir directory-filename [directory-filename] ... +svB
    Location of the user-specific directories
    VHostCGIMode On|Off|Secure On vX
    Determines whether the virtualhost can run subprocesses, and the privileges available to subprocesses.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to subprocesses created +
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to subprocesses created by a virtual host.
    VHostGroup unix-groupidvX
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vX
    Determines whether the server runs with enhanced security +
    VHostGroup unix-groupidvX
    Sets the Group ID under which a virtual host runs.
    VHostPrivs [+-]?privilege-name [[+-]?privilege-name] ...vX
    Assign arbitrary privileges to a virtual host.
    VHostSecure On|Off On vX
    Determines whether the server runs with enhanced security for the virtualhost.
    VHostUser unix-useridvX
    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-useridvX
    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 number-of-seconds 1 sB
    Watchdog interval in seconds
    XBitHack on|off|full off svdhB
    Parse SSI directives in files with the execute bit +
    WatchdogInterval number-of-seconds 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
    Recognise Aliases for encoding values
    xml2EncDefault namesvdhB
    Sets a default encoding to assume when absolutely no information +
    xml2EncAlias charset alias [alias ...]sB
    Recognise 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 --git a/docs/manual/style/version.ent b/docs/manual/style/version.ent index bf0e2316..5ae59604 100644 --- a/docs/manual/style/version.ent +++ b/docs/manual/style/version.ent @@ -19,6 +19,6 @@ - + diff --git a/httpd.spec b/httpd.spec index 911f4dcc..bfe87856 100644 --- a/httpd.spec +++ b/httpd.spec @@ -4,7 +4,7 @@ Summary: Apache HTTP Server Name: httpd -Version: 2.4.17 +Version: 2.4.18 Release: 1 URL: http://httpd.apache.org/ Vendor: Apache Software Foundation diff --git a/include/ap_config_auto.h.in b/include/ap_config_auto.h.in index eecd4aad..5a95d95a 100644 --- a/include/ap_config_auto.h.in +++ b/include/ap_config_auto.h.in @@ -115,9 +115,16 @@ /* Define to 1 if you have the header file. */ #undef HAVE_NGHTTP2_NGHTTP2_H +/* Define to 1 if you have the `nghttp2_session_change_stream_priority' + function. */ +#undef HAVE_NGHTTP2_SESSION_CHANGE_STREAM_PRIORITY + /* Define to 1 if you have the `nghttp2_session_server_new2' function. */ #undef HAVE_NGHTTP2_SESSION_SERVER_NEW2 +/* Define to 1 if you have the `nghttp2_stream_get_weight' function. */ +#undef HAVE_NGHTTP2_STREAM_GET_WEIGHT + /* Define if OpenSSL is available */ #undef HAVE_OPENSSL diff --git a/include/ap_mmn.h b/include/ap_mmn.h index 015ff6d3..3233ef80 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -456,6 +456,7 @@ * ap_select_protocol(), ap_switch_protocol(), * ap_get_protocol(). Add HTTP_MISDIRECTED_REQUEST. * Added ap_parse_token_list_strict() to httpd.h + * 20120211.52 (2.4.17-dev) Add master conn_rec* member in conn_rec. */ #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */ @@ -463,7 +464,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20120211 #endif -#define MODULE_MAGIC_NUMBER_MINOR 51 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 52 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/include/ap_release.h b/include/ap_release.h index 0b49ada5..79eeee39 100644 --- a/include/ap_release.h +++ b/include/ap_release.h @@ -43,7 +43,7 @@ #define AP_SERVER_MAJORVERSION_NUMBER 2 #define AP_SERVER_MINORVERSION_NUMBER 4 -#define AP_SERVER_PATCHLEVEL_NUMBER 17 +#define AP_SERVER_PATCHLEVEL_NUMBER 18 #define AP_SERVER_DEVBUILD_BOOLEAN 0 /* Synchronize the above with docs/manual/style/version.ent */ diff --git a/include/http_core.h b/include/http_core.h index 6ca53f76..85354552 100644 --- a/include/http_core.h +++ b/include/http_core.h @@ -465,6 +465,17 @@ typedef unsigned long etag_components_t; /* This is the default value used */ #define ETAG_BACKWARD (ETAG_MTIME | ETAG_SIZE) +/* Generic ON/OFF/UNSET for unsigned int foo :2 */ +#define AP_CORE_CONFIG_OFF (0) +#define AP_CORE_CONFIG_ON (1) +#define AP_CORE_CONFIG_UNSET (2) + +/* Generic merge of flag */ +#define AP_CORE_MERGE_FLAG(field, to, base, over) to->field = \ + over->field != AP_CORE_CONFIG_UNSET \ + ? over->field \ + : base->field + /** * @brief Server Signature Enumeration */ @@ -630,6 +641,8 @@ typedef struct { * advice */ unsigned int cgi_pass_auth : 2; + unsigned int qualify_redirect_url :2; + } core_dir_config; /* macro to implement off by default behaviour */ diff --git a/include/http_protocol.h b/include/http_protocol.h index 64ed0136..316850e3 100644 --- a/include/http_protocol.h +++ b/include/http_protocol.h @@ -736,6 +736,10 @@ AP_DECLARE_HOOK(apr_port_t,default_port,(const request_rec *r)) * NULL to indicated that the hooks are free to propose * @param proposals The list of protocol identifiers proposed by the hooks * @return OK or DECLINED + * @bug This API or implementation and order of operations should be considered + * experimental and will continue to evolve in future 2.4 releases, with + * a corresponding minor module magic number (MMN) bump to indicate the + * API revision level. */ AP_DECLARE_HOOK(int,protocol_propose,(conn_rec *c, request_rec *r, server_rec *s, @@ -765,6 +769,10 @@ AP_DECLARE_HOOK(int,protocol_propose,(conn_rec *c, request_rec *r, * @param choices A list of protocol identifiers, normally the clients whishes * @param proposals the list of protocol identifiers proposed by the hooks * @return OK or DECLINED + * @bug This API or implementation and order of operations should be considered + * experimental and will continue to evolve in future 2.4 releases, with + * a corresponding minor module magic number (MMN) bump to indicate the + * API revision level. */ AP_DECLARE_HOOK(int,protocol_switch,(conn_rec *c, request_rec *r, server_rec *s, @@ -780,9 +788,37 @@ AP_DECLARE_HOOK(int,protocol_switch,(conn_rec *c, request_rec *r, * * @param c The current connection * @return The identifier of the protocol in place or NULL + * @bug This API or implementation and order of operations should be considered + * experimental and will continue to evolve in future 2.4 releases, with + * a corresponding minor module magic number (MMN) bump to indicate the + * API revision level. */ AP_DECLARE_HOOK(const char *,protocol_get,(const conn_rec *c)) - + +/** + * Get the protocols that the connection and optional request may + * upgrade to - besides the protocol currently active on the connection. These + * values may be used to announce to a client what choices it has. + * + * If report_all == 0, only protocols more preferable than the one currently + * being used, are reported. Otherwise, all available protocols beside the + * current one are being reported. + * + * @param c The current connection + * @param r The current request or NULL + * @param s The server/virtual host selected or NULL + * @param report_all include also protocols less preferred than the current one + * @param pupgrades on return, possible protocols to upgrade to in descending order + * of preference. Maybe NULL if none are available. + * @bug This API or implementation and order of operations should be considered + * experimental and will continue to evolve in future 2.4 releases, with + * a corresponding minor module magic number (MMN) bump to indicate the + * API revision level. + */ +AP_DECLARE(apr_status_t) ap_get_protocol_upgrades(conn_rec *c, request_rec *r, + server_rec *s, int report_all, + const apr_array_header_t **pupgrades); + /** * Select a protocol for the given connection and optional request. Will return * the protocol identifier selected which may be the protocol already in place @@ -797,6 +833,10 @@ AP_DECLARE_HOOK(const char *,protocol_get,(const conn_rec *c)) * @param s The server/virtual host selected * @param choices A list of protocol identifiers, normally the clients whishes * @return The selected protocol or NULL if no protocol could be agreed upon + * @bug This API or implementation and order of operations should be considered + * experimental and will continue to evolve in future 2.4 releases, with + * a corresponding minor module magic number (MMN) bump to indicate the + * API revision level. */ AP_DECLARE(const char *) ap_select_protocol(conn_rec *c, request_rec *r, server_rec *s, @@ -815,6 +855,10 @@ AP_DECLARE(const char *) ap_select_protocol(conn_rec *c, request_rec *r, * APR_EINVAL, if the protocol is already in place * APR_NOTIMPL, if no module performed the switch * Other errors where appropriate + * @bug This API or implementation and order of operations should be considered + * experimental and will continue to evolve in future 2.4 releases, with + * a corresponding minor module magic number (MMN) bump to indicate the + * API revision level. */ AP_DECLARE(apr_status_t) ap_switch_protocol(conn_rec *c, request_rec *r, server_rec *s, @@ -830,9 +874,34 @@ AP_DECLARE(apr_status_t) ap_switch_protocol(conn_rec *c, request_rec *r, * * @param c The connection to determine the protocol for * @return the protocol in use, never NULL + * @bug This API or implementation and order of operations should be considered + * experimental and will continue to evolve in future 2.4 releases, with + * a corresponding minor module magic number (MMN) bump to indicate the + * API revision level. */ AP_DECLARE(const char *) ap_get_protocol(conn_rec *c); +/** + * Check if the given protocol is an allowed choice on the given + * combination of connection, request and server. + * + * When server is NULL, it is taken from request_rec, unless + * request_rec is NULL. Then it is taken from the connection base + * server. + * + * @param c The current connection + * @param r The current request or NULL + * @param s The server/virtual host selected or NULL + * @param protocol the protocol to switch to + * @return != 0 iff protocol is allowed + * @bug This API or implementation and order of operations should be considered + * experimental and will continue to evolve in future 2.4 releases, with + * a corresponding minor module magic number (MMN) bump to indicate the + * API revision level. + */ +AP_DECLARE(int) ap_is_allowed_protocol(conn_rec *c, request_rec *r, + server_rec *s, const char *protocol); + /** @see ap_bucket_type_error */ typedef struct ap_bucket_error ap_bucket_error; diff --git a/include/httpd.h b/include/httpd.h index 4204cd04..1cd71d66 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -200,6 +200,10 @@ extern "C" { #ifndef DEFAULT_LIMIT_REQUEST_FIELDS #define DEFAULT_LIMIT_REQUEST_FIELDS 100 #endif +/** default/hard limit on number of leading/trailing empty lines */ +#ifndef DEFAULT_LIMIT_BLANK_LINES +#define DEFAULT_LIMIT_BLANK_LINES 10 +#endif /** * The default default character set name to add if AddDefaultCharset is @@ -1167,6 +1171,9 @@ struct conn_rec { #if APR_HAS_THREADS apr_thread_t *current_thread; #endif + + /** The "real" master connection. NULL if I am the master. */ + conn_rec *master; }; /** diff --git a/modules/aaa/mod_auth_basic.c b/modules/aaa/mod_auth_basic.c index dc03b438..1d72ff68 100644 --- a/modules/aaa/mod_auth_basic.c +++ b/modules/aaa/mod_auth_basic.c @@ -254,7 +254,6 @@ static int get_basic_auth(request_rec *r, const char **user, { const char *auth_line; char *decoded_line; - int length; /* Get the appropriate header */ auth_line = apr_table_get(r->headers_in, (PROXYREQ_PROXY == r->proxyreq) @@ -279,10 +278,7 @@ static int get_basic_auth(request_rec *r, const char **user, auth_line++; } - decoded_line = apr_palloc(r->pool, apr_base64_decode_len(auth_line) + 1); - length = apr_base64_decode(decoded_line, auth_line); - /* Null-terminate the string. */ - decoded_line[length] = '\0'; + decoded_line = ap_pbase64decode(r->pool, auth_line); *user = ap_getword_nulls(r->pool, (const char**)&decoded_line, ':'); *pw = decoded_line; diff --git a/modules/aaa/mod_authn_anon.c b/modules/aaa/mod_authn_anon.c index 21e0da85..82559bcc 100644 --- a/modules/aaa/mod_authn_anon.c +++ b/modules/aaa/mod_authn_anon.c @@ -57,7 +57,7 @@ #include "mod_auth.h" typedef struct anon_auth_user { - char *user; + const char *user; struct anon_auth_user *next; } anon_auth_user; @@ -103,7 +103,7 @@ static const char *anon_set_string_slots(cmd_parms *cmd, else { first = conf->users; conf->users = apr_palloc(cmd->pool, sizeof(*conf->users)); - conf->users->user = apr_pstrdup(cmd->pool, arg); + conf->users->user = arg; conf->users->next = first; } } diff --git a/modules/aaa/mod_authnz_ldap.c b/modules/aaa/mod_authnz_ldap.c index 211e4f74..370016f7 100644 --- a/modules/aaa/mod_authnz_ldap.c +++ b/modules/aaa/mod_authnz_ldap.c @@ -1627,7 +1627,7 @@ static const char *set_bind_pattern(cmd_parms *cmd, void *_cfg, const char *exp, } sec->bind_regex = regexp; - sec->bind_subst = apr_pstrdup(cmd->pool, subst); + sec->bind_subst = subst; return NULL; } @@ -1655,7 +1655,7 @@ static const char *set_bind_password(cmd_parms *cmd, void *_cfg, const char *arg result = ap_get_exec_line(cmd->pool, (const char*)argv[0], (const char * const *)argv); - if(!result) { + if (!result) { return apr_pstrcat(cmd->pool, "Unable to get bind password from exec of ", arg+5, NULL); diff --git a/modules/arch/win32/mod_isapi.c b/modules/arch/win32/mod_isapi.c index ec0d35e7..66cc8da6 100644 --- a/modules/arch/win32/mod_isapi.c +++ b/modules/arch/win32/mod_isapi.c @@ -1582,9 +1582,10 @@ static apr_status_t isapi_handler (request_rec *r) rv = (*isa->HttpExtensionProc)(cid->ecb); /* Check for a log message - and log it */ - if (cid->ecb->lpszLogData && *cid->ecb->lpszLogData) + if (*cid->ecb->lpszLogData) { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02113) "%s: %s", r->filename, cid->ecb->lpszLogData); + } switch(rv) { case 0: /* Strange, but MS isapi accepts this as success */ diff --git a/modules/cache/cache_util.h b/modules/cache/cache_util.h index 59436c42..397efb90 100644 --- a/modules/cache/cache_util.h +++ b/modules/cache/cache_util.h @@ -99,7 +99,7 @@ extern "C" { #define CACHE_LOCKNAME_KEY "mod_cache-lockname" #define CACHE_LOCKFILE_KEY "mod_cache-lockfile" #define CACHE_CTX_KEY "mod_cache-ctx" -#define CACHE_SEPARATOR ", " +#define CACHE_SEPARATOR ", \t" /** * cache_util.c diff --git a/modules/http/http_request.c b/modules/http/http_request.c index 70bf2937..5b2c1e8a 100644 --- a/modules/http/http_request.c +++ b/modules/http/http_request.c @@ -230,21 +230,93 @@ AP_DECLARE(void) ap_die(int type, request_rec *r) static void check_pipeline(conn_rec *c, apr_bucket_brigade *bb) { - if (c->keepalive != AP_CONN_CLOSE && !c->aborted) { - apr_status_t rv; - - AP_DEBUG_ASSERT(APR_BRIGADE_EMPTY(bb)); - rv = ap_get_brigade(c->input_filters, bb, AP_MODE_SPECULATIVE, - APR_NONBLOCK_READ, 1); + apr_status_t rv; + int num_blank_lines = DEFAULT_LIMIT_BLANK_LINES; + ap_input_mode_t mode = AP_MODE_SPECULATIVE; + apr_size_t cr = 0; + char buf[2]; + + c->data_in_input_filters = 0; + while (c->keepalive != AP_CONN_CLOSE && !c->aborted) { + apr_size_t len = cr + 1; + + apr_brigade_cleanup(bb); + rv = ap_get_brigade(c->input_filters, bb, mode, + APR_NONBLOCK_READ, len); if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(bb)) { /* * Error or empty brigade: There is no data present in the input * filter */ - c->data_in_input_filters = 0; + if (mode == AP_MODE_READBYTES) { + /* Unexpected error, stop with this connection */ + ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(02967) + "Can't consume pipelined empty lines"); + c->keepalive = AP_CONN_CLOSE; + } + break; + } + + /* Ignore trailing blank lines (which must not be interpreted as + * pipelined requests) up to the limit, otherwise we would block + * on the next read without flushing data, and hence possibly delay + * pending response(s) until the next/real request comes in or the + * keepalive timeout expires. + */ + rv = apr_brigade_flatten(bb, buf, &len); + if (rv != APR_SUCCESS || len != cr + 1) { + int log_level; + if (mode == AP_MODE_READBYTES) { + /* Unexpected error, stop with this connection */ + c->keepalive = AP_CONN_CLOSE; + log_level = APLOG_ERR; + } + else { + /* Let outside (non-speculative/blocking) read determine + * where this possible failure comes from (metadata, + * morphed EOF socket => empty bucket? debug only here). + */ + c->data_in_input_filters = 1; + log_level = APLOG_DEBUG; + } + ap_log_cerror(APLOG_MARK, log_level, rv, c, APLOGNO(02968) + "Can't check pipelined data"); + break; + } + + if (mode == AP_MODE_READBYTES) { + mode = AP_MODE_SPECULATIVE; + cr = 0; + } + else if (cr) { + AP_DEBUG_ASSERT(len == 2 && buf[0] == APR_ASCII_CR); + if (buf[1] == APR_ASCII_LF) { + mode = AP_MODE_READBYTES; + num_blank_lines--; + } + else { + c->data_in_input_filters = 1; + break; + } } else { - c->data_in_input_filters = 1; + if (buf[0] == APR_ASCII_LF) { + mode = AP_MODE_READBYTES; + num_blank_lines--; + } + else if (buf[0] == APR_ASCII_CR) { + cr = 1; + } + else { + c->data_in_input_filters = 1; + break; + } + } + /* Enough blank lines with this connection? + * Stop and don't recycle it. + */ + if (num_blank_lines < 0) { + c->keepalive = AP_CONN_CLOSE; } } } diff --git a/modules/http2/config.m4 b/modules/http2/config.m4 index 9c5eb867..05cf2ba3 100644 --- a/modules/http2/config.m4 +++ b/modules/http2/config.m4 @@ -20,6 +20,8 @@ dnl # list of module object files http2_objs="dnl mod_http2.lo dnl h2_alt_svc.lo dnl +h2_bucket_eoc.lo dnl +h2_bucket_eos.lo dnl h2_config.lo dnl h2_conn.lo dnl h2_conn_io.lo dnl @@ -29,6 +31,7 @@ h2_h2.lo dnl h2_io.lo dnl h2_io_set.lo dnl h2_mplx.lo dnl +h2_push.lo dnl h2_request.lo dnl h2_response.lo dnl h2_session.lo dnl @@ -39,7 +42,6 @@ h2_task.lo dnl h2_task_input.lo dnl h2_task_output.lo dnl h2_task_queue.lo dnl -h2_to_h1.lo dnl h2_util.lo dnl h2_worker.lo dnl h2_workers.lo dnl @@ -125,12 +127,12 @@ AC_DEFUN([APACHE_CHECK_NGHTTP2],[ fi fi - AC_MSG_CHECKING([for nghttp2 version >= 1.0.0]) + AC_MSG_CHECKING([for nghttp2 version >= 1.2.1]) AC_TRY_COMPILE([#include ],[ #if !defined(NGHTTP2_VERSION_NUM) #error "Missing nghttp2 version" #endif -#if NGHTTP2_VERSION_NUM < 0x010000 +#if NGHTTP2_VERSION_NUM < 0x010201 #error "Unsupported nghttp2 version " NGHTTP2_VERSION_TEXT #endif], [AC_MSG_RESULT(OK) @@ -152,6 +154,12 @@ AC_DEFUN([APACHE_CHECK_NGHTTP2],[ if test "x$liberrors" != "x"; then AC_MSG_WARN([nghttp2 library is unusable]) fi +dnl # nghttp2 >= 1.3.0: access to stream weights + AC_CHECK_FUNCS([nghttp2_stream_get_weight], + [APR_ADDTO(MOD_CPPFLAGS, ["-DH2_NG2_STREAM_API"])], []) +dnl # nghttp2 >= 1.5.0: changing stream priorities + AC_CHECK_FUNCS([nghttp2_session_change_stream_priority], + [APR_ADDTO(MOD_CPPFLAGS, ["-DH2_NG2_CHANGE_PRIO"])], []) else AC_MSG_WARN([nghttp2 version is too old]) fi diff --git a/modules/http2/h2_alt_svc.c b/modules/http2/h2_alt_svc.c index d18ae5f2..6bc3cd0d 100644 --- a/modules/http2/h2_alt_svc.c +++ b/modules/http2/h2_alt_svc.c @@ -74,7 +74,7 @@ h2_alt_svc *h2_alt_svc_parse(const char *s, apr_pool_t *pool) { static int h2_alt_svc_handler(request_rec *r) { h2_ctx *ctx; - h2_config *cfg; + const h2_config *cfg; int i; if (r->connection->keepalives > 0) { diff --git a/modules/http2/h2_bucket_eoc.c b/modules/http2/h2_bucket_eoc.c new file mode 100644 index 00000000..3ddb54d6 --- /dev/null +++ b/modules/http2/h2_bucket_eoc.c @@ -0,0 +1,109 @@ +/* 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 +#include + +#include +#include +#include +#include + +#include "h2_private.h" +#include "h2_mplx.h" +#include "h2_session.h" +#include "h2_bucket_eoc.h" + +typedef struct { + apr_bucket_refcount refcount; + h2_session *session; +} h2_bucket_eoc; + +static apr_status_t bucket_cleanup(void *data) +{ + h2_session **psession = data; + + if (*psession) { + /* + * If bucket_destroy is called after us, this prevents + * bucket_destroy from trying to destroy the pool again. + */ + *psession = NULL; + } + return APR_SUCCESS; +} + +static apr_status_t bucket_read(apr_bucket *b, const char **str, + apr_size_t *len, apr_read_type_e block) +{ + (void)b; + (void)block; + *str = NULL; + *len = 0; + return APR_SUCCESS; +} + +apr_bucket * h2_bucket_eoc_make(apr_bucket *b, h2_session *session) +{ + h2_bucket_eoc *h; + + h = apr_bucket_alloc(sizeof(*h), b->list); + h->session = session; + + b = apr_bucket_shared_make(b, h, 0, 0); + b->type = &h2_bucket_type_eoc; + + return b; +} + +apr_bucket * h2_bucket_eoc_create(apr_bucket_alloc_t *list, h2_session *session) +{ + apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); + + APR_BUCKET_INIT(b); + b->free = apr_bucket_free; + b->list = list; + b = h2_bucket_eoc_make(b, session); + if (session) { + h2_bucket_eoc *h = b->data; + apr_pool_pre_cleanup_register(session->pool, &h->session, bucket_cleanup); + } + return b; +} + +static void bucket_destroy(void *data) +{ + h2_bucket_eoc *h = data; + + if (apr_bucket_shared_destroy(h)) { + h2_session *session = h->session; + apr_bucket_free(h); + if (session) { + h2_session_eoc_callback(session); + /* all is gone now */ + } + } +} + +const apr_bucket_type_t h2_bucket_type_eoc = { + "H2EOC", 5, APR_BUCKET_METADATA, + bucket_destroy, + bucket_read, + apr_bucket_setaside_noop, + apr_bucket_split_notimpl, + apr_bucket_shared_copy +}; + diff --git a/modules/http2/h2_bucket_eoc.h b/modules/http2/h2_bucket_eoc.h new file mode 100644 index 00000000..f1cd6f81 --- /dev/null +++ b/modules/http2/h2_bucket_eoc.h @@ -0,0 +1,31 @@ +/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef mod_http2_h2_bucket_eoc_h +#define mod_http2_h2_bucket_eoc_h + +struct h2_session; + +/** End Of HTTP/2 SESSION (H2EOC) bucket */ +extern const apr_bucket_type_t h2_bucket_type_eoc; + + +apr_bucket * h2_bucket_eoc_make(apr_bucket *b, + struct h2_session *session); + +apr_bucket * h2_bucket_eoc_create(apr_bucket_alloc_t *list, + struct h2_session *session); + +#endif /* mod_http2_h2_bucket_eoc_h */ diff --git a/modules/http2/h2_bucket_eos.c b/modules/http2/h2_bucket_eos.c new file mode 100644 index 00000000..98a0b365 --- /dev/null +++ b/modules/http2/h2_bucket_eos.c @@ -0,0 +1,109 @@ +/* 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 +#include + +#include +#include +#include +#include + +#include "h2_private.h" +#include "h2_mplx.h" +#include "h2_stream.h" +#include "h2_bucket_eos.h" + +typedef struct { + apr_bucket_refcount refcount; + h2_stream *stream; +} h2_bucket_eos; + +static apr_status_t bucket_cleanup(void *data) +{ + h2_stream **pstream = data; + + if (*pstream) { + /* + * If bucket_destroy is called after us, this prevents + * bucket_destroy from trying to destroy the pool again. + */ + *pstream = NULL; + } + return APR_SUCCESS; +} + +static apr_status_t bucket_read(apr_bucket *b, const char **str, + apr_size_t *len, apr_read_type_e block) +{ + (void)b; + (void)block; + *str = NULL; + *len = 0; + return APR_SUCCESS; +} + +apr_bucket *h2_bucket_eos_make(apr_bucket *b, h2_stream *stream) +{ + h2_bucket_eos *h; + + h = apr_bucket_alloc(sizeof(*h), b->list); + h->stream = stream; + + b = apr_bucket_shared_make(b, h, 0, 0); + b->type = &h2_bucket_type_eos; + + return b; +} + +apr_bucket *h2_bucket_eos_create(apr_bucket_alloc_t *list, + h2_stream *stream) +{ + apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); + + APR_BUCKET_INIT(b); + b->free = apr_bucket_free; + b->list = list; + b = h2_bucket_eos_make(b, stream); + if (stream) { + h2_bucket_eos *h = b->data; + apr_pool_pre_cleanup_register(stream->pool, &h->stream, bucket_cleanup); + } + return b; +} + +static void bucket_destroy(void *data) +{ + h2_bucket_eos *h = data; + + if (apr_bucket_shared_destroy(h)) { + h2_stream *stream = h->stream; + if (stream) { + h2_stream_cleanup(stream); + } + apr_bucket_free(h); + } +} + +const apr_bucket_type_t h2_bucket_type_eos = { + "H2EOS", 5, APR_BUCKET_METADATA, + bucket_destroy, + bucket_read, + apr_bucket_setaside_noop, + apr_bucket_split_notimpl, + apr_bucket_shared_copy +}; + diff --git a/modules/http2/h2_bucket_eos.h b/modules/http2/h2_bucket_eos.h new file mode 100644 index 00000000..bd3360db --- /dev/null +++ b/modules/http2/h2_bucket_eos.h @@ -0,0 +1,30 @@ +/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef mod_http2_h2_bucket_stream_eos_h +#define mod_http2_h2_bucket_stream_eos_h + +struct h2_stream; + +/** End Of HTTP/2 STREAM (H2EOS) bucket */ +extern const apr_bucket_type_t h2_bucket_type_eos; + + +apr_bucket *h2_bucket_eos_make(apr_bucket *b, struct h2_stream *stream); + +apr_bucket *h2_bucket_eos_create(apr_bucket_alloc_t *list, + struct h2_stream *stream); + +#endif /* mod_http2_h2_bucket_stream_eos_h */ diff --git a/modules/http2/h2_config.c b/modules/http2/h2_config.c index 6db702ec..04714247 100644 --- a/modules/http2/h2_config.c +++ b/modules/http2/h2_config.c @@ -15,6 +15,9 @@ #include +#include +#include + #include #include #include @@ -29,6 +32,7 @@ #include "h2_ctx.h" #include "h2_conn.h" #include "h2_config.h" +#include "h2_h2.h" #include "h2_private.h" #define DEF_VAL (-1) @@ -38,17 +42,23 @@ static h2_config defconf = { "default", - 100, /* max_streams */ - 64 * 1024, /* window_size */ - -1, /* min workers */ - -1, /* max workers */ - 10 * 60, /* max workers idle secs */ - 64 * 1024, /* stream max mem size */ - NULL, /* no alt-svcs */ - -1, /* alt-svc max age */ - 0, /* serialize headers */ - -1, /* h2 direct mode */ - -1, /* # session extra files */ + 100, /* max_streams */ + H2_INITIAL_WINDOW_SIZE, /* window_size */ + -1, /* min workers */ + -1, /* max workers */ + 10 * 60, /* max workers idle secs */ + 64 * 1024, /* stream max mem size */ + NULL, /* no alt-svcs */ + -1, /* alt-svc max age */ + 0, /* serialize headers */ + -1, /* h2 direct mode */ + -1, /* # session extra files */ + 1, /* modern TLS only */ + -1, /* HTTP/1 Upgrade support */ + 1024*1024, /* TLS warmup size */ + 1, /* TLS cooldown secs */ + 1, /* HTTP/2 server push enabled */ + NULL, /* map of content-type to priorities */ }; static int files_per_session = 0; @@ -100,6 +110,13 @@ static void *h2_config_create(apr_pool_t *pool, conf->serialize_headers = DEF_VAL; conf->h2_direct = DEF_VAL; conf->session_extra_files = DEF_VAL; + conf->modern_tls_only = DEF_VAL; + conf->h2_upgrade = DEF_VAL; + conf->tls_warmup_size = DEF_VAL; + conf->tls_cooldown_secs = DEF_VAL; + conf->h2_push = DEF_VAL; + conf->priorities = NULL; + return conf; } @@ -127,22 +144,38 @@ void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv) strcat(name, "]"); n->name = name; - n->h2_max_streams = H2_CONFIG_GET(add, base, h2_max_streams); - n->h2_window_size = H2_CONFIG_GET(add, base, h2_window_size); - n->min_workers = H2_CONFIG_GET(add, base, min_workers); - n->max_workers = H2_CONFIG_GET(add, base, max_workers); + n->h2_max_streams = H2_CONFIG_GET(add, base, h2_max_streams); + n->h2_window_size = H2_CONFIG_GET(add, base, h2_window_size); + n->min_workers = H2_CONFIG_GET(add, base, min_workers); + n->max_workers = H2_CONFIG_GET(add, base, max_workers); n->max_worker_idle_secs = H2_CONFIG_GET(add, base, max_worker_idle_secs); - n->stream_max_mem_size = H2_CONFIG_GET(add, base, stream_max_mem_size); - n->alt_svcs = add->alt_svcs? add->alt_svcs : base->alt_svcs; - n->alt_svc_max_age = H2_CONFIG_GET(add, base, alt_svc_max_age); - n->serialize_headers = H2_CONFIG_GET(add, base, serialize_headers); - n->h2_direct = H2_CONFIG_GET(add, base, h2_direct); - n->session_extra_files = H2_CONFIG_GET(add, base, session_extra_files); + n->stream_max_mem_size = H2_CONFIG_GET(add, base, stream_max_mem_size); + n->alt_svcs = add->alt_svcs? add->alt_svcs : base->alt_svcs; + n->alt_svc_max_age = H2_CONFIG_GET(add, base, alt_svc_max_age); + n->serialize_headers = H2_CONFIG_GET(add, base, serialize_headers); + n->h2_direct = H2_CONFIG_GET(add, base, h2_direct); + n->session_extra_files = H2_CONFIG_GET(add, base, session_extra_files); + n->modern_tls_only = H2_CONFIG_GET(add, base, modern_tls_only); + n->h2_upgrade = H2_CONFIG_GET(add, base, h2_upgrade); + n->tls_warmup_size = H2_CONFIG_GET(add, base, tls_warmup_size); + n->tls_cooldown_secs = H2_CONFIG_GET(add, base, tls_cooldown_secs); + n->h2_push = H2_CONFIG_GET(add, base, h2_push); + if (add->priorities && base->priorities) { + n->priorities = apr_hash_overlay(pool, add->priorities, base->priorities); + } + else { + n->priorities = add->priorities? add->priorities : base->priorities; + } return n; } -int h2_config_geti(h2_config *conf, h2_config_var_t var) +int h2_config_geti(const h2_config *conf, h2_config_var_t var) +{ + return (int)h2_config_geti64(conf, var); +} + +apr_int64_t h2_config_geti64(const h2_config *conf, h2_config_var_t var) { int n; switch(var) { @@ -162,6 +195,10 @@ int h2_config_geti(h2_config *conf, h2_config_var_t var) return H2_CONFIG_GET(conf, &defconf, alt_svc_max_age); case H2_CONF_SER_HEADERS: return H2_CONFIG_GET(conf, &defconf, serialize_headers); + case H2_CONF_MODERN_TLS_ONLY: + return H2_CONFIG_GET(conf, &defconf, modern_tls_only); + case H2_CONF_UPGRADE: + return H2_CONFIG_GET(conf, &defconf, h2_upgrade); case H2_CONF_DIRECT: return H2_CONFIG_GET(conf, &defconf, h2_direct); case H2_CONF_SESSION_FILES: @@ -170,12 +207,18 @@ int h2_config_geti(h2_config *conf, h2_config_var_t var) n = files_per_session; } return n; + case H2_CONF_TLS_WARMUP_SIZE: + return H2_CONFIG_GET(conf, &defconf, tls_warmup_size); + case H2_CONF_TLS_COOLDOWN_SECS: + return H2_CONFIG_GET(conf, &defconf, tls_cooldown_secs); + case H2_CONF_PUSH: + return H2_CONFIG_GET(conf, &defconf, h2_push); default: return DEF_VAL; } } -h2_config *h2_config_sget(server_rec *s) +const h2_config *h2_config_sget(server_rec *s) { h2_config *cfg = (h2_config *)ap_get_module_config(s->module_config, &http2_module); @@ -183,11 +226,21 @@ h2_config *h2_config_sget(server_rec *s) return cfg; } +const struct h2_priority *h2_config_get_priority(const h2_config *conf, + const char *content_type) +{ + if (content_type && conf->priorities) { + size_t len = strcspn(content_type, "; \t"); + h2_priority *prio = apr_hash_get(conf->priorities, content_type, len); + return prio? prio : apr_hash_get(conf->priorities, "*", 1); + } + return NULL; +} static const char *h2_conf_set_max_streams(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); cfg->h2_max_streams = (int)apr_atoi64(value); (void)arg; if (cfg->h2_max_streams < 1) { @@ -199,7 +252,7 @@ static const char *h2_conf_set_max_streams(cmd_parms *parms, static const char *h2_conf_set_window_size(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); cfg->h2_window_size = (int)apr_atoi64(value); (void)arg; if (cfg->h2_window_size < 1024) { @@ -211,7 +264,7 @@ static const char *h2_conf_set_window_size(cmd_parms *parms, static const char *h2_conf_set_min_workers(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); cfg->min_workers = (int)apr_atoi64(value); (void)arg; if (cfg->min_workers < 1) { @@ -223,7 +276,7 @@ static const char *h2_conf_set_min_workers(cmd_parms *parms, static const char *h2_conf_set_max_workers(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); cfg->max_workers = (int)apr_atoi64(value); (void)arg; if (cfg->max_workers < 1) { @@ -235,7 +288,7 @@ static const char *h2_conf_set_max_workers(cmd_parms *parms, static const char *h2_conf_set_max_worker_idle_secs(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); cfg->max_worker_idle_secs = (int)apr_atoi64(value); (void)arg; if (cfg->max_worker_idle_secs < 1) { @@ -247,7 +300,7 @@ static const char *h2_conf_set_max_worker_idle_secs(cmd_parms *parms, static const char *h2_conf_set_stream_max_mem_size(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); cfg->stream_max_mem_size = (int)apr_atoi64(value); @@ -262,7 +315,7 @@ static const char *h2_add_alt_svc(cmd_parms *parms, void *arg, const char *value) { if (value && strlen(value)) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); h2_alt_svc *as = h2_alt_svc_parse(value, parms->pool); if (!as) { return "unable to parse alt-svc specifier"; @@ -279,7 +332,7 @@ static const char *h2_add_alt_svc(cmd_parms *parms, static const char *h2_conf_set_alt_svc_max_age(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); cfg->alt_svc_max_age = (int)apr_atoi64(value); (void)arg; return NULL; @@ -288,10 +341,10 @@ static const char *h2_conf_set_alt_svc_max_age(cmd_parms *parms, static const char *h2_conf_set_session_extra_files(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); apr_int64_t max = (int)apr_atoi64(value); - if (max <= 0) { - return "value must be a positive number"; + if (max < 0) { + return "value must be a non-negative number"; } cfg->session_extra_files = (int)max; (void)arg; @@ -301,7 +354,7 @@ static const char *h2_conf_set_session_extra_files(cmd_parms *parms, static const char *h2_conf_set_serialize_headers(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); if (!strcasecmp(value, "On")) { cfg->serialize_headers = 1; return NULL; @@ -318,7 +371,7 @@ static const char *h2_conf_set_serialize_headers(cmd_parms *parms, static const char *h2_conf_set_direct(cmd_parms *parms, void *arg, const char *value) { - h2_config *cfg = h2_config_sget(parms->server); + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); if (!strcasecmp(value, "On")) { cfg->h2_direct = 1; return NULL; @@ -332,9 +385,134 @@ static const char *h2_conf_set_direct(cmd_parms *parms, return "value must be On or Off"; } -#define AP_END_CMD AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL) +static const char *h2_conf_set_push(cmd_parms *parms, + void *arg, const char *value) +{ + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); + if (!strcasecmp(value, "On")) { + cfg->h2_push = 1; + return NULL; + } + else if (!strcasecmp(value, "Off")) { + cfg->h2_push = 0; + return NULL; + } + + (void)arg; + return "value must be On or Off"; +} + +static const char *h2_conf_add_push_priority(cmd_parms *cmd, void *_cfg, + const char *ctype, const char *sdependency, + const char *sweight) +{ + h2_config *cfg = (h2_config *)h2_config_sget(cmd->server); + const char *sdefweight = "16"; /* default AFTER weight */ + h2_dependency dependency; + h2_priority *priority; + int weight; + + if (!strlen(ctype)) { + return "1st argument must be a mime-type, like 'text/css' or '*'"; + } + + if (!sweight) { + /* 2 args only, but which one? */ + if (apr_isdigit(sdependency[0])) { + sweight = sdependency; + sdependency = "AFTER"; /* default dependency */ + } + } + + if (!strcasecmp("AFTER", sdependency)) { + dependency = H2_DEPENDANT_AFTER; + } + else if (!strcasecmp("BEFORE", sdependency)) { + dependency = H2_DEPENDANT_BEFORE; + if (sweight) { + return "dependecy 'Before' does not allow a weight"; + } + } + else if (!strcasecmp("INTERLEAVED", sdependency)) { + dependency = H2_DEPENDANT_INTERLEAVED; + sdefweight = "256"; /* default INTERLEAVED weight */ + } + else { + return "dependency must be one of 'After', 'Before' or 'Interleaved'"; + } + + weight = (int)apr_atoi64(sweight? sweight : sdefweight); + if (weight < NGHTTP2_MIN_WEIGHT) { + return apr_psprintf(cmd->pool, "weight must be a number >= %d", + NGHTTP2_MIN_WEIGHT); + } + + priority = apr_pcalloc(cmd->pool, sizeof(*priority)); + priority->dependency = dependency; + priority->weight = weight; + + if (!cfg->priorities) { + cfg->priorities = apr_hash_make(cmd->pool); + } + apr_hash_set(cfg->priorities, ctype, strlen(ctype), priority); + return NULL; +} + +static const char *h2_conf_set_modern_tls_only(cmd_parms *parms, + void *arg, const char *value) +{ + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); + if (!strcasecmp(value, "On")) { + cfg->modern_tls_only = 1; + return NULL; + } + else if (!strcasecmp(value, "Off")) { + cfg->modern_tls_only = 0; + return NULL; + } + + (void)arg; + return "value must be On or Off"; +} + +static const char *h2_conf_set_upgrade(cmd_parms *parms, + void *arg, const char *value) +{ + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); + if (!strcasecmp(value, "On")) { + cfg->h2_upgrade = 1; + return NULL; + } + else if (!strcasecmp(value, "Off")) { + cfg->h2_upgrade = 0; + return NULL; + } + + (void)arg; + return "value must be On or Off"; +} + +static const char *h2_conf_set_tls_warmup_size(cmd_parms *parms, + void *arg, const char *value) +{ + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); + cfg->tls_warmup_size = apr_atoi64(value); + (void)arg; + return NULL; +} + +static const char *h2_conf_set_tls_cooldown_secs(cmd_parms *parms, + void *arg, const char *value) +{ + h2_config *cfg = (h2_config *)h2_config_sget(parms->server); + cfg->tls_cooldown_secs = (int)apr_atoi64(value); + (void)arg; + return NULL; +} +#define AP_END_CMD AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL) + const command_rec h2_cmds[] = { AP_INIT_TAKE1("H2MaxSessionStreams", h2_conf_set_max_streams, NULL, RSRC_CONF, "maximum number of open streams per session"), @@ -354,22 +532,34 @@ const command_rec h2_cmds[] = { RSRC_CONF, "set the maximum age (in seconds) that client can rely on alt-svc information"), AP_INIT_TAKE1("H2SerializeHeaders", h2_conf_set_serialize_headers, NULL, RSRC_CONF, "on to enable header serialization for compatibility"), + AP_INIT_TAKE1("H2ModernTLSOnly", h2_conf_set_modern_tls_only, NULL, + RSRC_CONF, "off to not impose RFC 7540 restrictions on TLS"), + AP_INIT_TAKE1("H2Upgrade", h2_conf_set_upgrade, NULL, + RSRC_CONF, "on to allow HTTP/1 Upgrades to h2/h2c"), AP_INIT_TAKE1("H2Direct", h2_conf_set_direct, NULL, RSRC_CONF, "on to enable direct HTTP/2 mode"), AP_INIT_TAKE1("H2SessionExtraFiles", h2_conf_set_session_extra_files, NULL, RSRC_CONF, "number of extra file a session might keep open"), + AP_INIT_TAKE1("H2TLSWarmUpSize", h2_conf_set_tls_warmup_size, NULL, + RSRC_CONF, "number of bytes on TLS connection before doing max writes"), + AP_INIT_TAKE1("H2TLSCoolDownSecs", h2_conf_set_tls_cooldown_secs, NULL, + RSRC_CONF, "seconds of idle time on TLS before shrinking writes"), + AP_INIT_TAKE1("H2Push", h2_conf_set_push, NULL, + RSRC_CONF, "off to disable HTTP/2 server push"), + AP_INIT_TAKE23("H2PushPriority", h2_conf_add_push_priority, NULL, + RSRC_CONF, "define priority of PUSHed resources per content type"), AP_END_CMD }; -h2_config *h2_config_rget(request_rec *r) +const h2_config *h2_config_rget(request_rec *r) { h2_config *cfg = (h2_config *)ap_get_module_config(r->per_dir_config, &http2_module); return cfg? cfg : h2_config_sget(r->server); } -h2_config *h2_config_get(conn_rec *c) +const h2_config *h2_config_get(conn_rec *c) { h2_ctx *ctx = h2_ctx_get(c); diff --git a/modules/http2/h2_config.h b/modules/http2/h2_config.h index 931af59d..3d85ec24 100644 --- a/modules/http2/h2_config.h +++ b/modules/http2/h2_config.h @@ -34,8 +34,16 @@ typedef enum { H2_CONF_SER_HEADERS, H2_CONF_DIRECT, H2_CONF_SESSION_FILES, + H2_CONF_MODERN_TLS_ONLY, + H2_CONF_UPGRADE, + H2_CONF_TLS_WARMUP_SIZE, + H2_CONF_TLS_COOLDOWN_SECS, + H2_CONF_PUSH, } h2_config_var_t; +struct apr_hash_t; +struct h2_priority; + /* Apache httpd module configuration for h2. */ typedef struct h2_config { const char *name; @@ -51,6 +59,12 @@ typedef struct h2_config { processing, better compatibility */ int h2_direct; /* if mod_h2 is active directly */ int session_extra_files; /* # of extra files a session may keep open */ + int modern_tls_only; /* Accept only modern TLS in HTTP/2 connections */ + int h2_upgrade; /* Allow HTTP/1 upgrade to h2/h2c */ + apr_int64_t tls_warmup_size; /* Amount of TLS data to send before going full write size */ + int tls_cooldown_secs; /* Seconds of idle time before going back to small TLS records */ + int h2_push; /* if HTTP/2 server push is enabled */ + struct apr_hash_t *priorities;/* map of content-type to h2_priority records */ } h2_config; @@ -58,17 +72,21 @@ void *h2_config_create_dir(apr_pool_t *pool, char *x); void *h2_config_create_svr(apr_pool_t *pool, server_rec *s); void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv); -apr_status_t h2_config_apply_header(h2_config *config, request_rec *r); +apr_status_t h2_config_apply_header(const h2_config *config, request_rec *r); extern const command_rec h2_cmds[]; -h2_config *h2_config_get(conn_rec *c); -h2_config *h2_config_sget(server_rec *s); -h2_config *h2_config_rget(request_rec *r); +const h2_config *h2_config_get(conn_rec *c); +const h2_config *h2_config_sget(server_rec *s); +const h2_config *h2_config_rget(request_rec *r); -int h2_config_geti(h2_config *conf, h2_config_var_t var); +int h2_config_geti(const h2_config *conf, h2_config_var_t var); +apr_int64_t h2_config_geti64(const h2_config *conf, h2_config_var_t var); void h2_config_init(apr_pool_t *pool); +const struct h2_priority *h2_config_get_priority(const h2_config *conf, + const char *content_type); + #endif /* __mod_h2__h2_config_h__ */ diff --git a/modules/http2/h2_conn.c b/modules/http2/h2_conn.c index 8bffbc42..b9b6e87b 100644 --- a/modules/http2/h2_conn.c +++ b/modules/http2/h2_conn.c @@ -32,6 +32,7 @@ #include "h2_session.h" #include "h2_stream.h" #include "h2_stream_set.h" +#include "h2_h2.h" #include "h2_task.h" #include "h2_worker.h" #include "h2_workers.h" @@ -39,11 +40,8 @@ static struct h2_workers *workers; -static apr_status_t h2_session_process(h2_session *session); - static h2_mpm_type_t mpm_type = H2_MPM_UNKNOWN; static module *mpm_module; -static module *ssl_module; static int checked; static void check_modules(void) @@ -64,9 +62,6 @@ static void check_modules(void) mpm_type = H2_MPM_PREFORK; mpm_module = m; } - else if (!strcmp("mod_ssl.c", m->name)) { - ssl_module = m; - } } checked = 1; } @@ -74,7 +69,7 @@ static void check_modules(void) apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s) { - h2_config *config = h2_config_sget(s); + const h2_config *config = h2_config_sget(s); apr_status_t status = APR_SUCCESS; int minw = h2_config_geti(config, H2_CONF_MIN_WORKERS); int maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS); @@ -103,9 +98,6 @@ apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s) mpm_type = H2_MPM_PREFORK; mpm_module = m; } - else if (!strcmp("mod_ssl.c", m->name)) { - ssl_module = m; - } } if (minw <= 0) { @@ -139,268 +131,103 @@ static module *h2_conn_mpm_module(void) { return mpm_module; } -apr_status_t h2_conn_rprocess(request_rec *r) +apr_status_t h2_conn_process(conn_rec *c, request_rec *r, server_rec *s) { - h2_config *config = h2_config_rget(r); + apr_status_t status; h2_session *session; + const h2_config *config; + int rv; - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "h2_conn_process start"); if (!workers) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02911) + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02911) "workers not initialized"); return APR_EGENERAL; } - session = h2_session_rcreate(r, config, workers); - if (!session) { - return APR_EGENERAL; - } - - return h2_session_process(session); -} - -apr_status_t h2_conn_main(conn_rec *c) -{ - h2_config *config = h2_config_get(c); - h2_session *session; - apr_status_t status; + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "h2_conn_process start"); - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "h2_conn_main start"); - if (!workers) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02912) - "workers not initialized"); - return APR_EGENERAL; + if (!s && r) { + s = r->server; } - session = h2_session_create(c, config, workers); - if (!session) { - return APR_EGENERAL; + config = s? h2_config_sget(s) : h2_config_get(c); + if (r) { + session = h2_session_rcreate(r, config, workers); } - - status = h2_session_process(session); - - /* Make sure this connection gets closed properly. */ - c->keepalive = AP_CONN_CLOSE; - if (c->cs) { - c->cs->state = CONN_STATE_WRITE_COMPLETION; + else { + session = h2_session_create(c, config, workers); } - - return status; -} - -apr_status_t h2_session_process(h2_session *session) -{ - apr_status_t status = APR_SUCCESS; - int rv = 0; - apr_interval_time_t wait_micros = 0; - static const int MAX_WAIT_MICROS = 200 * 1000; - /* Start talking to the client. Apart from protocol meta data, - * we mainly will see new http/2 streams opened by the client, which - * basically are http requests we need to dispatch. - * - * There will be bursts of new streams, to be served concurrently, - * followed by long pauses of no activity. - * - * Since the purpose of http/2 is to allow siumultaneous streams, we - * need to dispatch the handling of each stream into a separate worker - * thread, keeping this thread open for sending responses back as - * soon as they arrive. - * At the same time, we need to continue reading new frames from - * our client, which may be meta (WINDOWS_UPDATEs, PING, SETTINGS) or - * new streams. - * - * As long as we have streams open in this session, we cannot really rest - * since there are two conditions to wait on: 1. new data from the client, - * 2. new data from the open streams to send back. - * - * Only when we have no more streams open, can we do a blocking read - * on our connection. - * - * TODO: implement graceful GO_AWAY after configurable idle time - */ - - ap_update_child_status_from_conn(session->c->sbh, SERVER_BUSY_READ, - session->c); + if (!h2_is_acceptable_connection(c, 1)) { + nghttp2_submit_goaway(session->ngh2, NGHTTP2_FLAG_NONE, 0, + NGHTTP2_INADEQUATE_SECURITY, NULL, 0); + } - if (APLOGctrace2(session->c)) { - ap_filter_t *filter = session->c->input_filters; - while (filter) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c, - "h2_conn(%ld), has connection filter %s", - session->id, filter->frec->name); - filter = filter->next; - } - } - + ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_READ, c); status = h2_session_start(session, &rv); ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, "h2_session(%ld): starting on %s:%d", session->id, - session->c->base_server->defn_name, + session->c->base_server->server_hostname, session->c->local_addr->port); if (status != APR_SUCCESS) { h2_session_abort(session, status, rv); - h2_session_destroy(session); + h2_session_eoc_callback(session); return status; } - while (!h2_session_is_done(session)) { - int have_written = 0; - int have_read = 0; - int got_streams; - - status = h2_session_write(session, wait_micros); - if (status == APR_SUCCESS) { - have_written = 1; - wait_micros = 0; - } - else if (status == APR_EAGAIN) { - /* nop */ - } - else if (status == APR_TIMEUP) { - wait_micros *= 2; - if (wait_micros > MAX_WAIT_MICROS) { - wait_micros = MAX_WAIT_MICROS; - } - } - else { - ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c, - "h2_session(%ld): writing, terminating", - session->id); - h2_session_abort(session, status, 0); - break; - } - - /* We would like to do blocking reads as often as possible as they - * are more efficient in regard to server resources. - * We can do them under the following circumstances: - * - we have no open streams and therefore have nothing to write - * - we have just started the session and are waiting for the first - * two frames to come in. There will always be at least 2 frames as - * * h2 will send SETTINGS and SETTINGS-ACK - * * h2c will count the header settings as one frame and we - * submit our settings and need the ACK. - */ - got_streams = !h2_stream_set_is_empty(session->streams); - status = h2_session_read(session, - (!got_streams - || session->frames_received <= 1)? - APR_BLOCK_READ : APR_NONBLOCK_READ); - switch (status) { - case APR_SUCCESS: /* successful read, reset our idle timers */ - have_read = 1; - wait_micros = 0; - break; - case APR_EAGAIN: /* non-blocking read, nothing there */ - break; - case APR_EBADF: /* connection is not there any more */ - case APR_EOF: - case APR_ECONNABORTED: - case APR_ECONNRESET: - case APR_TIMEUP: /* blocked read, timed out */ - ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c, - "h2_session(%ld): reading", - session->id); - h2_session_abort(session, status, 0); - break; - default: - ap_log_cerror( APLOG_MARK, APLOG_INFO, status, session->c, - APLOGNO(02950) - "h2_session(%ld): error reading, terminating", - session->id); - h2_session_abort(session, status, 0); - break; - } - - if (!have_read && !have_written - && !h2_stream_set_is_empty(session->streams)) { - /* Nothing to read or write, we have streams, but - * the have no data yet ready to be delivered. Slowly - * back off to give others a chance to do their work. - */ - if (wait_micros == 0) { - wait_micros = 10; - } - } - } - + status = h2_session_process(session); + ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c, "h2_session(%ld): done", session->id); - - ap_update_child_status_from_conn(session->c->sbh, SERVER_CLOSING, - session->c); + /* Make sure this connection gets closed properly. */ + ap_update_child_status_from_conn(c->sbh, SERVER_CLOSING, c); + c->keepalive = AP_CONN_CLOSE; + if (c->cs) { + c->cs->state = CONN_STATE_WRITE_COMPLETION; + } h2_session_close(session); - h2_session_destroy(session); - - return DONE; + /* hereafter session will be gone */ + return status; } static void fix_event_conn(conn_rec *c, conn_rec *master); -/* - * We would like to create the connection more lightweight like - * slave connections in 2.5-DEV. But we get 500 responses on long - * cgi tests in modules/h2.t as the script parsing seems to see an - * EOF from the cgi before anything is sent. - * conn_rec *h2_conn_create(conn_rec *master, apr_pool_t *pool) { - conn_rec *c = (conn_rec *) apr_palloc(pool, sizeof(conn_rec)); - - memcpy(c, master, sizeof(conn_rec)); - c->id = (master->id & (long)pool); - c->slaves = NULL; - c->master = master; - c->input_filters = NULL; - c->output_filters = NULL; - c->pool = pool; - - return c; -} -*/ - -conn_rec *h2_conn_create(conn_rec *master, apr_pool_t *pool) -{ - apr_socket_t *socket; conn_rec *c; AP_DEBUG_ASSERT(master); - - /* CAVEAT: it seems necessary to setup the conn_rec in the master - * connection thread. Other attempts crashed. - * HOWEVER: we setup the connection using the pools and other items - * from the master connection, since we do not want to allocate - * lots of resources here. - * Lets allocated pools and everything else when we actually start - * working on this new connection. - */ - /* Not sure about the scoreboard handle. Reusing the one from the main - * connection could make sense, is not really correct, but we cannot - * easily create new handles for our worker threads either. - * TODO + + /* This is like the slave connection creation from 2.5-DEV. A + * very efficient way - not sure how compatible this is, since + * the core hooks are no longer run. + * But maybe it's is better this way, not sure yet. */ - socket = ap_get_module_config(master->conn_config, &core_module); - c = ap_run_create_connection(pool, master->base_server, - socket, - master->id^((long)pool), - master->sbh, - master->bucket_alloc); + c = (conn_rec *) apr_palloc(pool, sizeof(conn_rec)); if (c == NULL) { ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, pool, APLOGNO(02913) "h2_task: creating conn"); return NULL; } + + memcpy(c, master, sizeof(conn_rec)); + c->id = (master->id & (long)pool); + c->master = master; + c->input_filters = NULL; + c->output_filters = NULL; + c->pool = pool; return c; } -apr_status_t h2_conn_setup(h2_task_env *env, struct h2_worker *worker) +apr_status_t h2_conn_setup(h2_task *task, apr_bucket_alloc_t *bucket_alloc, + apr_thread_t *thread, apr_socket_t *socket) { - conn_rec *master = env->mplx->c; + conn_rec *master = task->mplx->c; - ap_log_perror(APLOG_MARK, APLOG_TRACE3, 0, env->pool, + ap_log_perror(APLOG_MARK, APLOG_TRACE3, 0, task->pool, "h2_conn(%ld): created from master", master->id); /* Ok, we are just about to start processing the connection and @@ -409,28 +236,17 @@ apr_status_t h2_conn_setup(h2_task_env *env, struct h2_worker *worker) * sub-resources from it, so that we get a nice reuse of * pools. */ - env->c.pool = env->pool; - env->c.bucket_alloc = h2_worker_get_bucket_alloc(worker); - env->c.current_thread = h2_worker_get_thread(worker); + task->c->pool = task->pool; + task->c->current_thread = thread; + task->c->bucket_alloc = bucket_alloc; - env->c.conn_config = ap_create_conn_config(env->pool); - env->c.notes = apr_table_make(env->pool, 5); + task->c->conn_config = ap_create_conn_config(task->pool); + task->c->notes = apr_table_make(task->pool, 5); - ap_set_module_config(env->c.conn_config, &core_module, - h2_worker_get_socket(worker)); + /* In order to do this in 2.4.x, we need to add a member to conn_rec */ + task->c->master = master; - /* If we serve http:// requests over a TLS connection, we do - * not want any mod_ssl vars to be visible. - */ - if (ssl_module && (!env->scheme || strcmp("http", env->scheme))) { - /* See #19, there is a range of SSL variables to be gotten from - * the main connection that should be available in request handlers - */ - void *sslcfg = ap_get_module_config(master->conn_config, ssl_module); - if (sslcfg) { - ap_set_module_config(env->c.conn_config, ssl_module, sslcfg); - } - } + ap_set_module_config(task->c->conn_config, &core_module, socket); /* This works for mpm_worker so far. Other mpm modules have * different needs, unfortunately. The most interesting one @@ -441,7 +257,7 @@ apr_status_t h2_conn_setup(h2_task_env *env, struct h2_worker *worker) /* all fine */ break; case H2_MPM_EVENT: - fix_event_conn(&env->c, master); + fix_event_conn(task->c, master); break; default: /* fingers crossed */ @@ -453,26 +269,7 @@ apr_status_t h2_conn_setup(h2_task_env *env, struct h2_worker *worker) * 400 Bad Request * when names do not match. We prefer a predictable 421 status. */ - env->c.keepalives = 1; - - return APR_SUCCESS; -} - -apr_status_t h2_conn_post(conn_rec *c, h2_worker *worker) -{ - (void)worker; - - /* be sure no one messes with this any more */ - memset(c, 0, sizeof(*c)); - return APR_SUCCESS; -} - -apr_status_t h2_conn_process(conn_rec *c, apr_socket_t *socket) -{ - AP_DEBUG_ASSERT(c); - - c->clogging_input_filters = 1; - ap_process_connection(c, socket); + task->c->keepalives = 1; return APR_SUCCESS; } diff --git a/modules/http2/h2_conn.h b/modules/http2/h2_conn.h index 49a70db8..917a57e0 100644 --- a/modules/http2/h2_conn.h +++ b/modules/http2/h2_conn.h @@ -17,20 +17,17 @@ #define __mod_h2__h2_conn__ struct h2_task; -struct h2_task_env; -struct h2_worker; -/* Process the connection that is now starting the HTTP/2 - * conversation. Return when the HTTP/2 session is done - * and the connection will close. - */ -apr_status_t h2_conn_main(conn_rec *c); - -/* Process the request that has been upgraded to a HTTP/2 +/** + * Process the connection that is now starting the HTTP/2 * conversation. Return when the HTTP/2 session is done * and the connection will close. + * + * @param c the connection HTTP/2 is starting on + * @param r the upgrade request that still awaits an answer, optional + * @param s the server selected by request or, if NULL, connection */ -apr_status_t h2_conn_rprocess(request_rec *r); +apr_status_t h2_conn_process(conn_rec *c, request_rec *r, server_rec *s); /* Initialize this child process for h2 connection work, * to be called once during child init before multi processing @@ -52,9 +49,7 @@ h2_mpm_type_t h2_conn_mpm_type(void); conn_rec *h2_conn_create(conn_rec *master, apr_pool_t *stream_pool); -apr_status_t h2_conn_setup(struct h2_task_env *env, struct h2_worker *worker); -apr_status_t h2_conn_post(conn_rec *c, struct h2_worker *worker); - -apr_status_t h2_conn_process(conn_rec *c, apr_socket_t *socket); +apr_status_t h2_conn_setup(struct h2_task *task, apr_bucket_alloc_t *bucket_alloc, + apr_thread_t *thread, apr_socket_t *socket); #endif /* defined(__mod_h2__h2_conn__) */ diff --git a/modules/http2/h2_conn_io.c b/modules/http2/h2_conn_io.c index 08d00a4f..a67d025f 100644 --- a/modules/http2/h2_conn_io.c +++ b/modules/http2/h2_conn_io.c @@ -23,51 +23,74 @@ #include #include "h2_private.h" +#include "h2_bucket_eoc.h" #include "h2_config.h" #include "h2_conn_io.h" #include "h2_h2.h" #include "h2_util.h" -#define WRITE_BUFFER_SIZE (64*1024) +#define TLS_DATA_MAX (16*1024) + +/* Calculated like this: assuming MTU 1500 bytes + * 1500 - 40 (IP) - 20 (TCP) - 40 (TCP options) + * - TLS overhead (60-100) + * ~= 1300 bytes */ #define WRITE_SIZE_INITIAL 1300 -#define WRITE_SIZE_MAX (16*1024) -#define WRITE_SIZE_IDLE_USEC (1*APR_USEC_PER_SEC) -#define WRITE_SIZE_THRESHOLD (1*1024*1024) +/* Calculated like this: max TLS record size 16*1024 + * - 40 (IP) - 20 (TCP) - 40 (TCP options) + * - TLS overhead (60-100) + * which seems to create less TCP packets overall + */ +#define WRITE_SIZE_MAX (TLS_DATA_MAX - 100) -apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c) -{ - io->connection = c; - io->input = apr_brigade_create(c->pool, c->bucket_alloc); - io->output = apr_brigade_create(c->pool, c->bucket_alloc); - io->buflen = 0; - /* That is where we start with, - * see https://issues.apache.org/jira/browse/TS-2503 */ - io->write_size = WRITE_SIZE_INITIAL; - io->last_write = 0; - io->buffer_output = h2_h2_is_tls(c); +#define WRITE_BUFFER_SIZE (8*WRITE_SIZE_MAX) - /* Currently we buffer only for TLS output. The reason this gives - * improved performance is that buckets send to the mod_ssl network - * filter will be encrypted in chunks. There is a special filter - * that tries to aggregate data, but that does not work well when - * bucket sizes alternate between tiny frame headers and large data - * chunks. - */ +apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, + const h2_config *cfg, + apr_pool_t *pool) +{ + io->connection = c; + io->input = apr_brigade_create(pool, c->bucket_alloc); + io->output = apr_brigade_create(pool, c->bucket_alloc); + io->buflen = 0; + io->is_tls = h2_h2_is_tls(c); + io->buffer_output = io->is_tls; + if (io->buffer_output) { io->bufsize = WRITE_BUFFER_SIZE; - io->buffer = apr_pcalloc(c->pool, io->bufsize); + io->buffer = apr_pcalloc(pool, io->bufsize); } else { io->bufsize = 0; } + if (io->is_tls) { + /* That is where we start with, + * see https://issues.apache.org/jira/browse/TS-2503 */ + io->warmup_size = h2_config_geti64(cfg, H2_CONF_TLS_WARMUP_SIZE); + io->cooldown_usecs = (h2_config_geti(cfg, H2_CONF_TLS_COOLDOWN_SECS) + * APR_USEC_PER_SEC); + io->write_size = WRITE_SIZE_INITIAL; + } + else { + io->warmup_size = 0; + io->cooldown_usecs = 0; + io->write_size = io->bufsize; + } + + if (APLOGctrace1(c)) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, io->connection, + "h2_conn_io(%ld): init, buffering=%d, warmup_size=%ld, cd_secs=%f", + io->connection->id, io->buffer_output, (long)io->warmup_size, + ((float)io->cooldown_usecs/APR_USEC_PER_SEC)); + } + return APR_SUCCESS; } -void h2_conn_io_destroy(h2_conn_io *io) +int h2_conn_io_is_buffered(h2_conn_io *io) { - io->input = NULL; - io->output = NULL; + return io->bufsize > 0; } static apr_status_t h2_conn_io_bucket_read(h2_conn_io *io, @@ -93,6 +116,8 @@ static apr_status_t h2_conn_io_bucket_read(h2_conn_io *io, &bucket_length, block); if (status == APR_SUCCESS && bucket_length > 0) { + apr_size_t consumed = 0; + if (APLOGctrace2(io->connection)) { char buffer[32]; h2_util_hex_dump(buffer, sizeof(buffer)/sizeof(buffer[0]), @@ -102,20 +127,18 @@ static apr_status_t h2_conn_io_bucket_read(h2_conn_io *io, io->connection->id, (int)bucket_length, buffer); } - if (bucket_length > 0) { - apr_size_t consumed = 0; - status = on_read_cb(bucket_data, bucket_length, - &consumed, pdone, puser); - if (status == APR_SUCCESS && bucket_length > consumed) { - /* We have data left in the bucket. Split it. */ - status = apr_bucket_split(bucket, consumed); - } - readlen += consumed; + status = on_read_cb(bucket_data, bucket_length, &consumed, + pdone, puser); + if (status == APR_SUCCESS && bucket_length > consumed) { + /* We have data left in the bucket. Split it. */ + status = apr_bucket_split(bucket, consumed); } + readlen += consumed; } } apr_bucket_delete(bucket); } + if (readlen == 0 && status == APR_SUCCESS && block == APR_NONBLOCK_READ) { return APR_EAGAIN; } @@ -136,10 +159,10 @@ apr_status_t h2_conn_io_read(h2_conn_io *io, /* Seems something is left from a previous read, lets * satisfy our caller with the data we already have. */ status = h2_conn_io_bucket_read(io, block, on_read_cb, puser, &done); + apr_brigade_cleanup(io->input); if (status != APR_SUCCESS || done) { return status; } - apr_brigade_cleanup(io->input); } /* We only do a blocking read when we have no streams to process. So, @@ -157,9 +180,12 @@ apr_status_t h2_conn_io_read(h2_conn_io *io, ap_update_child_status(io->connection->sbh, SERVER_BUSY_READ, NULL); } + /* TODO: replace this with a connection filter itself, so that we + * no longer need to transfer incoming buckets to our own brigade. + */ status = ap_get_brigade(io->connection->input_filters, io->input, AP_MODE_READBYTES, - block, 16 * 4096); + block, 64 * 4096); switch (status) { case APR_SUCCESS: return h2_conn_io_bucket_read(io, block, on_read_cb, puser, &done); @@ -174,15 +200,22 @@ apr_status_t h2_conn_io_read(h2_conn_io *io, return status; } -static apr_status_t flush_out(apr_bucket_brigade *bb, void *ctx) +static apr_status_t pass_out(apr_bucket_brigade *bb, void *ctx) { h2_conn_io *io = (h2_conn_io*)ctx; apr_status_t status; apr_off_t bblen; + if (APR_BRIGADE_EMPTY(bb)) { + return APR_SUCCESS; + } + ap_update_child_status(io->connection->sbh, SERVER_BUSY_WRITE, NULL); - status = apr_brigade_length(bb, 1, &bblen); + status = apr_brigade_length(bb, 0, &bblen); if (status == APR_SUCCESS) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, io->connection, + "h2_conn_io(%ld): pass_out brigade %ld bytes", + io->connection->id, (long)bblen); status = ap_pass_brigade(io->connection->output_filters, bb); if (status == APR_SUCCESS) { io->bytes_written += (apr_size_t)bblen; @@ -193,14 +226,18 @@ static apr_status_t flush_out(apr_bucket_brigade *bb, void *ctx) return status; } +/* Bring the current buffer content into the output brigade, appropriately + * chunked. + */ static apr_status_t bucketeer_buffer(h2_conn_io *io) { const char *data = io->buffer; apr_size_t remaining = io->buflen; apr_bucket *b; int bcount, i; - if (io->write_size > WRITE_SIZE_INITIAL - && (apr_time_now() - io->last_write) >= WRITE_SIZE_IDLE_USEC) { + if (io->write_size > WRITE_SIZE_INITIAL + && (io->cooldown_usecs > 0) + && (apr_time_now() - io->last_write) >= io->cooldown_usecs) { /* long time not written, reset write size */ io->write_size = WRITE_SIZE_INITIAL; io->bytes_written = 0; @@ -209,7 +246,7 @@ static apr_status_t bucketeer_buffer(h2_conn_io *io) { (long)io->connection->id, (long)io->write_size); } else if (io->write_size < WRITE_SIZE_MAX - && io->bytes_written >= WRITE_SIZE_THRESHOLD) { + && io->bytes_written >= io->warmup_size) { /* connection is hot, use max size */ io->write_size = WRITE_SIZE_MAX; ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, io->connection, @@ -238,16 +275,22 @@ apr_status_t h2_conn_io_write(h2_conn_io *io, const char *buf, size_t length) { apr_status_t status = APR_SUCCESS; - io->unflushed = 1; - if (io->buffer_output) { + io->unflushed = 1; + if (io->bufsize > 0) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, io->connection, "h2_conn_io: buffering %ld bytes", (long)length); + + if (!APR_BRIGADE_EMPTY(io->output)) { + status = h2_conn_io_pass(io); + io->unflushed = 1; + } + while (length > 0 && (status == APR_SUCCESS)) { apr_size_t avail = io->bufsize - io->buflen; if (avail <= 0) { bucketeer_buffer(io); - status = flush_out(io->output, io); + status = pass_out(io->output, io); io->buflen = 0; } else if (length > avail) { @@ -266,47 +309,93 @@ apr_status_t h2_conn_io_write(h2_conn_io *io, } else { - status = apr_brigade_write(io->output, flush_out, io, buf, length); - if (status != APR_SUCCESS) { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, io->connection, - "h2_conn_io: write error"); - } + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, io->connection, + "h2_conn_io: writing %ld bytes to brigade", (long)length); + status = apr_brigade_write(io->output, pass_out, io, buf, length); } return status; } +apr_status_t h2_conn_io_writeb(h2_conn_io *io, apr_bucket *b) +{ + APR_BRIGADE_INSERT_TAIL(io->output, b); + io->unflushed = 1; + return APR_SUCCESS; +} -apr_status_t h2_conn_io_flush(h2_conn_io *io) +apr_status_t h2_conn_io_consider_flush(h2_conn_io *io) { + apr_status_t status = APR_SUCCESS; + + /* The HTTP/1.1 network output buffer/flush behaviour does not + * give optimal performance in the HTTP/2 case, as the pattern of + * buckets (data/eor/eos) is different. + * As long as we have not found out the "best" way to deal with + * this, force a flush at least every WRITE_BUFFER_SIZE amount + * of data. + */ if (io->unflushed) { - apr_status_t status; + apr_off_t len = 0; + if (!APR_BRIGADE_EMPTY(io->output)) { + apr_brigade_length(io->output, 0, &len); + } + len += io->buflen; + if (len >= WRITE_BUFFER_SIZE) { + return h2_conn_io_pass(io); + } + } + return status; +} + +static apr_status_t h2_conn_io_flush_int(h2_conn_io *io, int force) +{ + if (io->unflushed || force) { if (io->buflen > 0) { + /* something in the buffer, put it in the output brigade */ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, io->connection, "h2_conn_io: flush, flushing %ld bytes", (long)io->buflen); bucketeer_buffer(io); io->buflen = 0; } - /* Append flush. - */ - APR_BRIGADE_INSERT_TAIL(io->output, - apr_bucket_flush_create(io->output->bucket_alloc)); - - /* Send it out through installed filters (TLS) to the client */ - status = flush_out(io->output, io); - if (status == APR_SUCCESS) { - /* These are all fine and no reason for concern. Everything else - * is interesting. */ - io->unflushed = 0; - } - else { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, io->connection, - "h2_conn_io: flush error"); + if (force) { + APR_BRIGADE_INSERT_TAIL(io->output, + apr_bucket_flush_create(io->output->bucket_alloc)); } - return status; + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, io->connection, + "h2_conn_io: flush"); + /* Send it out */ + io->unflushed = 0; + return pass_out(io->output, io); + /* no more access after this, as we might have flushed an EOC bucket + * that de-allocated us all. */ } return APR_SUCCESS; } +apr_status_t h2_conn_io_flush(h2_conn_io *io) +{ + return h2_conn_io_flush_int(io, 1); +} + +apr_status_t h2_conn_io_pass(h2_conn_io *io) +{ + return h2_conn_io_flush_int(io, 0); +} + +apr_status_t h2_conn_io_close(h2_conn_io *io, void *session) +{ + apr_bucket *b; + + /* Send out anything in our buffers */ + h2_conn_io_flush_int(io, 0); + + b = h2_bucket_eoc_create(io->connection->bucket_alloc, session); + APR_BRIGADE_INSERT_TAIL(io->output, b); + b = apr_bucket_flush_create(io->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(io->output, b); + return ap_pass_brigade(io->connection->output_filters, io->output); + /* and all is gone */ +} \ No newline at end of file diff --git a/modules/http2/h2_conn_io.h b/modules/http2/h2_conn_io.h index 084445ef..f051c6c3 100644 --- a/modules/http2/h2_conn_io.h +++ b/modules/http2/h2_conn_io.h @@ -16,6 +16,8 @@ #ifndef __mod_h2__h2_conn_io__ #define __mod_h2__h2_conn_io__ +struct h2_config; + /* h2_io is the basic handler of a httpd connection. It keeps two brigades, * one for input, one for output and works with the installed connection * filters. @@ -26,19 +28,27 @@ typedef struct { conn_rec *connection; apr_bucket_brigade *input; apr_bucket_brigade *output; - int buffer_output; - int write_size; + + int is_tls; + apr_time_t cooldown_usecs; + apr_int64_t warmup_size; + + apr_size_t write_size; apr_time_t last_write; - apr_size_t bytes_written; + apr_int64_t bytes_written; + int buffer_output; char *buffer; apr_size_t buflen; apr_size_t bufsize; int unflushed; } h2_conn_io; -apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c); -void h2_conn_io_destroy(h2_conn_io *io); +apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, + const struct h2_config *cfg, + apr_pool_t *pool); + +int h2_conn_io_is_buffered(h2_conn_io *io); typedef apr_status_t (*h2_conn_io_on_read_cb)(const char *data, apr_size_t len, apr_size_t *readlen, int *done, @@ -52,7 +62,13 @@ apr_status_t h2_conn_io_read(h2_conn_io *io, apr_status_t h2_conn_io_write(h2_conn_io *io, const char *buf, size_t length); + +apr_status_t h2_conn_io_writeb(h2_conn_io *io, apr_bucket *b); + +apr_status_t h2_conn_io_consider_flush(h2_conn_io *io); +apr_status_t h2_conn_io_pass(h2_conn_io *io); apr_status_t h2_conn_io_flush(h2_conn_io *io); +apr_status_t h2_conn_io_close(h2_conn_io *io, void *session); #endif /* defined(__mod_h2__h2_conn_io__) */ diff --git a/modules/http2/h2_ctx.c b/modules/http2/h2_ctx.c index 422835c2..08bdd861 100644 --- a/modules/http2/h2_ctx.c +++ b/modules/http2/h2_ctx.c @@ -32,11 +32,11 @@ static h2_ctx *h2_ctx_create(const conn_rec *c) return ctx; } -h2_ctx *h2_ctx_create_for(const conn_rec *c, h2_task_env *env) +h2_ctx *h2_ctx_create_for(const conn_rec *c, h2_task *task) { h2_ctx *ctx = h2_ctx_create(c); if (ctx) { - ctx->task_env = env; + ctx->task = task; } return ctx; } @@ -76,7 +76,7 @@ h2_ctx *h2_ctx_server_set(h2_ctx *ctx, server_rec *s) int h2_ctx_is_task(h2_ctx *ctx) { - return ctx && !!ctx->task_env; + return ctx && !!ctx->task; } int h2_ctx_is_active(h2_ctx *ctx) @@ -84,7 +84,7 @@ int h2_ctx_is_active(h2_ctx *ctx) return ctx && ctx->is_h2; } -struct h2_task_env *h2_ctx_get_task(h2_ctx *ctx) +struct h2_task *h2_ctx_get_task(h2_ctx *ctx) { - return ctx->task_env; + return ctx->task; } diff --git a/modules/http2/h2_ctx.h b/modules/http2/h2_ctx.h index 86c59206..7f8f2b59 100644 --- a/modules/http2/h2_ctx.h +++ b/modules/http2/h2_ctx.h @@ -16,7 +16,7 @@ #ifndef __mod_h2__h2_ctx__ #define __mod_h2__h2_ctx__ -struct h2_task_env; +struct h2_task; struct h2_config; /** @@ -28,17 +28,17 @@ struct h2_config; * - those created by ourself to perform work on HTTP/2 streams */ typedef struct h2_ctx { - int is_h2; /* h2 engine is used */ - const char *protocol; /* the protocol negotiated */ - struct h2_task_env *task_env; /* the h2_task environment or NULL */ - const char *hostname; /* hostname negotiated via SNI, optional */ - server_rec *server; /* httpd server config selected. */ - struct h2_config *config; /* effective config in this context */ + int is_h2; /* h2 engine is used */ + const char *protocol; /* the protocol negotiated */ + struct h2_task *task; /* the h2_task executing or NULL */ + const char *hostname; /* hostname negotiated via SNI, optional */ + server_rec *server; /* httpd server config selected. */ + const struct h2_config *config; /* effective config in this context */ } h2_ctx; h2_ctx *h2_ctx_get(const conn_rec *c); h2_ctx *h2_ctx_rget(const request_rec *r); -h2_ctx *h2_ctx_create_for(const conn_rec *c, struct h2_task_env *env); +h2_ctx *h2_ctx_create_for(const conn_rec *c, struct h2_task *task); /* Set the h2 protocol established on this connection context or @@ -58,6 +58,6 @@ const char *h2_ctx_protocol_get(const conn_rec *c); int h2_ctx_is_task(h2_ctx *ctx); int h2_ctx_is_active(h2_ctx *ctx); -struct h2_task_env *h2_ctx_get_task(h2_ctx *ctx); +struct h2_task *h2_ctx_get_task(h2_ctx *ctx); #endif /* defined(__mod_h2__h2_ctx__) */ diff --git a/modules/http2/h2_from_h1.c b/modules/http2/h2_from_h1.c index be11f5c3..0b5060d4 100644 --- a/modules/http2/h2_from_h1.c +++ b/modules/http2/h2_from_h1.c @@ -51,19 +51,10 @@ h2_from_h1 *h2_from_h1_create(int stream_id, apr_pool_t *pool) apr_status_t h2_from_h1_destroy(h2_from_h1 *from_h1) { - if (from_h1->response) { - h2_response_destroy(from_h1->response); - from_h1->response = NULL; - } from_h1->bb = NULL; return APR_SUCCESS; } -h2_from_h1_state_t h2_from_h1_get_state(h2_from_h1 *from_h1) -{ - return from_h1->state; -} - static void set_state(h2_from_h1 *from_h1, h2_from_h1_state_t state) { if (from_h1->state != state) { @@ -78,16 +69,9 @@ h2_response *h2_from_h1_get_response(h2_from_h1 *from_h1) static apr_status_t make_h2_headers(h2_from_h1 *from_h1, request_rec *r) { - from_h1->response = h2_response_create(from_h1->stream_id, - from_h1->status, from_h1->hlines, - from_h1->pool); - if (from_h1->response == NULL) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EINVAL, r->connection, - APLOGNO(02915) - "h2_from_h1(%d): unable to create resp_head", - from_h1->stream_id); - return APR_EINVAL; - } + from_h1->response = h2_response_create(from_h1->stream_id, 0, + from_h1->http_status, from_h1->hlines, + from_h1->pool); from_h1->content_length = from_h1->response->content_length; from_h1->chunked = r->chunked; @@ -202,8 +186,7 @@ apr_status_t h2_from_h1_read_response(h2_from_h1 *from_h1, ap_filter_t* f, } if (from_h1->state == H2_RESP_ST_STATUS_LINE) { /* instead of parsing, just take it directly */ - from_h1->status = apr_psprintf(from_h1->pool, - "%d", f->r->status); + from_h1->http_status = f->r->status; from_h1->state = H2_RESP_ST_HEADERS; } else if (line[0] == '\0') { @@ -375,6 +358,7 @@ static h2_response *create_response(h2_from_h1 *from_h1, request_rec *r) if (!apr_is_empty_table(r->err_headers_out)) { r->headers_out = apr_table_overlay(r->pool, r->err_headers_out, r->headers_out); + apr_table_clear(r->err_headers_out); } /* @@ -417,7 +401,7 @@ static h2_response *create_response(h2_from_h1 *from_h1, request_rec *r) } if (!apr_is_empty_array(r->content_languages)) { - int i; + unsigned int i; char *token; char **languages = (char **)(r->content_languages->elts); const char *field = apr_table_get(r->headers_out, "Content-Language"); @@ -492,8 +476,8 @@ static h2_response *create_response(h2_from_h1 *from_h1, request_rec *r) apr_status_t h2_response_output_filter(ap_filter_t *f, apr_bucket_brigade *bb) { - h2_task_env *env = f->ctx; - h2_from_h1 *from_h1 = env->output? env->output->from_h1 : NULL; + h2_task *task = f->ctx; + h2_from_h1 *from_h1 = task->output? task->output->from_h1 : NULL; request_rec *r = f->r; apr_bucket *b; ap_bucket_error *eb = NULL; @@ -503,7 +487,7 @@ apr_status_t h2_response_output_filter(ap_filter_t *f, apr_bucket_brigade *bb) ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, "h2_from_h1(%d): output_filter called", from_h1->stream_id); - if (r->header_only && env->output && from_h1->response) { + if (r->header_only && task->output && from_h1->response) { /* throw away any data after we have compiled the response */ apr_brigade_cleanup(bb); return OK; @@ -533,7 +517,7 @@ apr_status_t h2_response_output_filter(ap_filter_t *f, apr_bucket_brigade *bb) if (eb) { int st = eb->status; apr_brigade_cleanup(bb); - ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c, + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, "h2_from_h1(%d): err bucket status=%d", from_h1->stream_id, st); ap_die(st, r); @@ -568,3 +552,38 @@ apr_status_t h2_response_output_filter(ap_filter_t *f, apr_bucket_brigade *bb) } return ap_pass_brigade(f->next, bb); } + +apr_status_t h2_response_trailers_filter(ap_filter_t *f, apr_bucket_brigade *bb) +{ + h2_task *task = f->ctx; + h2_from_h1 *from_h1 = task->output? task->output->from_h1 : NULL; + request_rec *r = f->r; + apr_bucket *b; + + if (from_h1 && from_h1->response) { + /* Detect the EOR bucket and forward any trailers that may have + * been set to our h2_response. + */ + for (b = APR_BRIGADE_FIRST(bb); + b != APR_BRIGADE_SENTINEL(bb); + b = APR_BUCKET_NEXT(b)) + { + if (AP_BUCKET_IS_EOR(b)) { + /* FIXME: need a better test case than this. + apr_table_setn(r->trailers_out, "X", "1"); */ + if (r->trailers_out && !apr_is_empty_table(r->trailers_out)) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, + "h2_from_h1(%d): trailers filter, saving trailers", + from_h1->stream_id); + h2_response_set_trailers(from_h1->response, + apr_table_clone(from_h1->pool, + r->trailers_out)); + } + break; + } + } + } + + return ap_pass_brigade(f->next, bb); +} + diff --git a/modules/http2/h2_from_h1.h b/modules/http2/h2_from_h1.h index 115a3142..cdd38ca6 100644 --- a/modules/http2/h2_from_h1.h +++ b/modules/http2/h2_from_h1.h @@ -48,35 +48,27 @@ struct h2_from_h1 { apr_pool_t *pool; apr_bucket_brigade *bb; - apr_size_t content_length; + apr_off_t content_length; int chunked; - const char *status; + int http_status; apr_array_header_t *hlines; struct h2_response *response; }; -typedef void h2_from_h1_state_change_cb(struct h2_from_h1 *resp, - h2_from_h1_state_t prevstate, - void *cb_ctx); - h2_from_h1 *h2_from_h1_create(int stream_id, apr_pool_t *pool); apr_status_t h2_from_h1_destroy(h2_from_h1 *response); -void h2_from_h1_set_state_change_cb(h2_from_h1 *from_h1, - h2_from_h1_state_change_cb *callback, - void *cb_ctx); - apr_status_t h2_from_h1_read_response(h2_from_h1 *from_h1, ap_filter_t* f, apr_bucket_brigade* bb); struct h2_response *h2_from_h1_get_response(h2_from_h1 *from_h1); -h2_from_h1_state_t h2_from_h1_get_state(h2_from_h1 *from_h1); - apr_status_t h2_response_output_filter(ap_filter_t *f, apr_bucket_brigade *bb); +apr_status_t h2_response_trailers_filter(ap_filter_t *f, apr_bucket_brigade *bb); + #endif /* defined(__mod_h2__h2_from_h1__) */ diff --git a/modules/http2/h2_h2.c b/modules/http2/h2_h2.c index 221f5118..2f93340d 100644 --- a/modules/http2/h2_h2.c +++ b/modules/http2/h2_h2.c @@ -53,6 +53,383 @@ APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec*)); static int (*opt_ssl_engine_disable)(conn_rec*); static int (*opt_ssl_is_https)(conn_rec*); +/******************************************************************************* + * SSL var lookup + */ +APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup, + (apr_pool_t *, server_rec *, + conn_rec *, request_rec *, + char *)); +static char *(*opt_ssl_var_lookup)(apr_pool_t *, server_rec *, + conn_rec *, request_rec *, + char *); + + +/******************************************************************************* + * HTTP/2 error stuff + */ +static const char *h2_err_descr[] = { + "no error", /* 0x0 */ + "protocol error", + "internal error", + "flow control error", + "settings timeout", + "stream closed", /* 0x5 */ + "frame size error", + "refused stream", + "cancel", + "compression error", + "connect error", /* 0xa */ + "enhance your calm", + "inadequate security", + "http/1.1 required", +}; + +const char *h2_h2_err_description(unsigned int h2_error) +{ + if (h2_error < (sizeof(h2_err_descr)/sizeof(h2_err_descr[0]))) { + return h2_err_descr[h2_error]; + } + return "unknown http/2 errotr code"; +} + +/******************************************************************************* + * Check connection security requirements of RFC 7540 + */ + +/* + * Black Listed Ciphers from RFC 7549 Appendix A + * + */ +static const char *RFC7540_names[] = { + /* ciphers with NULL encrpytion */ + "NULL-MD5", /* TLS_NULL_WITH_NULL_NULL */ + /* same */ /* TLS_RSA_WITH_NULL_MD5 */ + "NULL-SHA", /* TLS_RSA_WITH_NULL_SHA */ + "NULL-SHA256", /* TLS_RSA_WITH_NULL_SHA256 */ + "PSK-NULL-SHA", /* TLS_PSK_WITH_NULL_SHA */ + "DHE-PSK-NULL-SHA", /* TLS_DHE_PSK_WITH_NULL_SHA */ + "RSA-PSK-NULL-SHA", /* TLS_RSA_PSK_WITH_NULL_SHA */ + "PSK-NULL-SHA256", /* TLS_PSK_WITH_NULL_SHA256 */ + "PSK-NULL-SHA384", /* TLS_PSK_WITH_NULL_SHA384 */ + "DHE-PSK-NULL-SHA256", /* TLS_DHE_PSK_WITH_NULL_SHA256 */ + "DHE-PSK-NULL-SHA384", /* TLS_DHE_PSK_WITH_NULL_SHA384 */ + "RSA-PSK-NULL-SHA256", /* TLS_RSA_PSK_WITH_NULL_SHA256 */ + "RSA-PSK-NULL-SHA384", /* TLS_RSA_PSK_WITH_NULL_SHA384 */ + "ECDH-ECDSA-NULL-SHA", /* TLS_ECDH_ECDSA_WITH_NULL_SHA */ + "ECDHE-ECDSA-NULL-SHA", /* TLS_ECDHE_ECDSA_WITH_NULL_SHA */ + "ECDH-RSA-NULL-SHA", /* TLS_ECDH_RSA_WITH_NULL_SHA */ + "ECDHE-RSA-NULL-SHA", /* TLS_ECDHE_RSA_WITH_NULL_SHA */ + "AECDH-NULL-SHA", /* TLS_ECDH_anon_WITH_NULL_SHA */ + "ECDHE-PSK-NULL-SHA", /* TLS_ECDHE_PSK_WITH_NULL_SHA */ + "ECDHE-PSK-NULL-SHA256", /* TLS_ECDHE_PSK_WITH_NULL_SHA256 */ + "ECDHE-PSK-NULL-SHA384", /* TLS_ECDHE_PSK_WITH_NULL_SHA384 */ + + /* DES/3DES ciphers */ + "PSK-3DES-EDE-CBC-SHA", /* TLS_PSK_WITH_3DES_EDE_CBC_SHA */ + "DHE-PSK-3DES-EDE-CBC-SHA", /* TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA */ + "RSA-PSK-3DES-EDE-CBC-SHA", /* TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA */ + "ECDH-ECDSA-DES-CBC3-SHA", /* TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA */ + "ECDHE-ECDSA-DES-CBC3-SHA", /* TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA */ + "ECDH-RSA-DES-CBC3-SHA", /* TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA */ + "ECDHE-RSA-DES-CBC3-SHA", /* TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA */ + "AECDH-DES-CBC3-SHA", /* TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA */ + "SRP-3DES-EDE-CBC-SHA", /* TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA */ + "SRP-RSA-3DES-EDE-CBC-SHA", /* TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA */ + "SRP-DSS-3DES-EDE-CBC-SHA", /* TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA */ + "ECDHE-PSK-3DES-EDE-CBC-SHA", /* TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA */ + "DES-CBC-SHA", /* TLS_RSA_WITH_DES_CBC_SHA */ + "DES-CBC3-SHA", /* TLS_RSA_WITH_3DES_EDE_CBC_SHA */ + "DHE-DSS-DES-CBC3-SHA", /* TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA */ + "DHE-RSA-DES-CBC-SHA", /* TLS_DHE_RSA_WITH_DES_CBC_SHA */ + "DHE-RSA-DES-CBC3-SHA", /* TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA */ + "ADH-DES-CBC-SHA", /* TLS_DH_anon_WITH_DES_CBC_SHA */ + "ADH-DES-CBC3-SHA", /* TLS_DH_anon_WITH_3DES_EDE_CBC_SHA */ + "EXP-DH-DSS-DES-CBC-SHA", /* TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA */ + "DH-DSS-DES-CBC-SHA", /* TLS_DH_DSS_WITH_DES_CBC_SHA */ + "DH-DSS-DES-CBC3-SHA", /* TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA */ + "EXP-DH-RSA-DES-CBC-SHA", /* TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA */ + "DH-RSA-DES-CBC-SHA", /* TLS_DH_RSA_WITH_DES_CBC_SHA */ + "DH-RSA-DES-CBC3-SHA", /* TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA */ + + /* blacklisted EXPORT ciphers */ + "EXP-RC4-MD5", /* TLS_RSA_EXPORT_WITH_RC4_40_MD5 */ + "EXP-RC2-CBC-MD5", /* TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 */ + "EXP-DES-CBC-SHA", /* TLS_RSA_EXPORT_WITH_DES40_CBC_SHA */ + "EXP-DHE-DSS-DES-CBC-SHA", /* TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA */ + "EXP-DHE-RSA-DES-CBC-SHA", /* TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA */ + "EXP-ADH-DES-CBC-SHA", /* TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA */ + "EXP-ADH-RC4-MD5", /* TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 */ + + /* blacklisted RC4 encryption */ + "RC4-MD5", /* TLS_RSA_WITH_RC4_128_MD5 */ + "RC4-SHA", /* TLS_RSA_WITH_RC4_128_SHA */ + "ADH-RC4-MD5", /* TLS_DH_anon_WITH_RC4_128_MD5 */ + "KRB5-RC4-SHA", /* TLS_KRB5_WITH_RC4_128_SHA */ + "KRB5-RC4-MD5", /* TLS_KRB5_WITH_RC4_128_MD5 */ + "EXP-KRB5-RC4-SHA", /* TLS_KRB5_EXPORT_WITH_RC4_40_SHA */ + "EXP-KRB5-RC4-MD5", /* TLS_KRB5_EXPORT_WITH_RC4_40_MD5 */ + "PSK-RC4-SHA", /* TLS_PSK_WITH_RC4_128_SHA */ + "DHE-PSK-RC4-SHA", /* TLS_DHE_PSK_WITH_RC4_128_SHA */ + "RSA-PSK-RC4-SHA", /* TLS_RSA_PSK_WITH_RC4_128_SHA */ + "ECDH-ECDSA-RC4-SHA", /* TLS_ECDH_ECDSA_WITH_RC4_128_SHA */ + "ECDHE-ECDSA-RC4-SHA", /* TLS_ECDHE_ECDSA_WITH_RC4_128_SHA */ + "ECDH-RSA-RC4-SHA", /* TLS_ECDH_RSA_WITH_RC4_128_SHA */ + "ECDHE-RSA-RC4-SHA", /* TLS_ECDHE_RSA_WITH_RC4_128_SHA */ + "AECDH-RC4-SHA", /* TLS_ECDH_anon_WITH_RC4_128_SHA */ + "ECDHE-PSK-RC4-SHA", /* TLS_ECDHE_PSK_WITH_RC4_128_SHA */ + + /* blacklisted AES128 encrpytion ciphers */ + "AES128-SHA256", /* TLS_RSA_WITH_AES_128_CBC_SHA */ + "DH-DSS-AES128-SHA", /* TLS_DH_DSS_WITH_AES_128_CBC_SHA */ + "DH-RSA-AES128-SHA", /* TLS_DH_RSA_WITH_AES_128_CBC_SHA */ + "DHE-DSS-AES128-SHA", /* TLS_DHE_DSS_WITH_AES_128_CBC_SHA */ + "DHE-RSA-AES128-SHA", /* TLS_DHE_RSA_WITH_AES_128_CBC_SHA */ + "ADH-AES128-SHA", /* TLS_DH_anon_WITH_AES_128_CBC_SHA */ + "AES128-SHA256", /* TLS_RSA_WITH_AES_128_CBC_SHA256 */ + "DH-DSS-AES128-SHA256", /* TLS_DH_DSS_WITH_AES_128_CBC_SHA256 */ + "DH-RSA-AES128-SHA256", /* TLS_DH_RSA_WITH_AES_128_CBC_SHA256 */ + "DHE-DSS-AES128-SHA256", /* TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 */ + "DHE-RSA-AES128-SHA256", /* TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 */ + "ECDH-ECDSA-AES128-SHA", /* TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA */ + "ECDHE-ECDSA-AES128-SHA", /* TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA */ + "ECDH-RSA-AES128-SHA", /* TLS_ECDH_RSA_WITH_AES_128_CBC_SHA */ + "ECDHE-RSA-AES128-SHA", /* TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA */ + "AECDH-AES128-SHA", /* TLS_ECDH_anon_WITH_AES_128_CBC_SHA */ + "ECDHE-ECDSA-AES128-SHA256", /* TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 */ + "ECDH-ECDSA-AES128-SHA256", /* TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 */ + "ECDHE-RSA-AES128-SHA256", /* TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 */ + "ECDH-RSA-AES128-SHA256", /* TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 */ + "ADH-AES128-SHA256", /* TLS_DH_anon_WITH_AES_128_CBC_SHA256 */ + "PSK-AES128-CBC-SHA", /* TLS_PSK_WITH_AES_128_CBC_SHA */ + "DHE-PSK-AES128-CBC-SHA", /* TLS_DHE_PSK_WITH_AES_128_CBC_SHA */ + "RSA-PSK-AES128-CBC-SHA", /* TLS_RSA_PSK_WITH_AES_128_CBC_SHA */ + "PSK-AES128-CBC-SHA256", /* TLS_PSK_WITH_AES_128_CBC_SHA256 */ + "DHE-PSK-AES128-CBC-SHA256", /* TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 */ + "RSA-PSK-AES128-CBC-SHA256", /* TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 */ + "ECDHE-PSK-AES128-CBC-SHA", /* TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA */ + "ECDHE-PSK-AES128-CBC-SHA256", /* TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 */ + "AES128-CCM", /* TLS_RSA_WITH_AES_128_CCM */ + "AES128-CCM8", /* TLS_RSA_WITH_AES_128_CCM_8 */ + "PSK-AES128-CCM", /* TLS_PSK_WITH_AES_128_CCM */ + "PSK-AES128-CCM8", /* TLS_PSK_WITH_AES_128_CCM_8 */ + "AES128-GCM-SHA256", /* TLS_RSA_WITH_AES_128_GCM_SHA256 */ + "DH-RSA-AES128-GCM-SHA256", /* TLS_DH_RSA_WITH_AES_128_GCM_SHA256 */ + "DH-DSS-AES128-GCM-SHA256", /* TLS_DH_DSS_WITH_AES_128_GCM_SHA256 */ + "ADH-AES128-GCM-SHA256", /* TLS_DH_anon_WITH_AES_128_GCM_SHA256 */ + "PSK-AES128-GCM-SHA256", /* TLS_PSK_WITH_AES_128_GCM_SHA256 */ + "RSA-PSK-AES128-GCM-SHA256", /* TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 */ + "ECDH-ECDSA-AES128-GCM-SHA256", /* TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 */ + "ECDH-RSA-AES128-GCM-SHA256", /* TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 */ + "SRP-AES-128-CBC-SHA", /* TLS_SRP_SHA_WITH_AES_128_CBC_SHA */ + "SRP-RSA-AES-128-CBC-SHA", /* TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA */ + "SRP-DSS-AES-128-CBC-SHA", /* TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA */ + + /* blacklisted AES256 encrpytion ciphers */ + "AES256-SHA", /* TLS_RSA_WITH_AES_256_CBC_SHA */ + "DH-DSS-AES256-SHA", /* TLS_DH_DSS_WITH_AES_256_CBC_SHA */ + "DH-RSA-AES256-SHA", /* TLS_DH_RSA_WITH_AES_256_CBC_SHA */ + "DHE-DSS-AES256-SHA", /* TLS_DHE_DSS_WITH_AES_256_CBC_SHA */ + "DHE-RSA-AES256-SHA", /* TLS_DHE_RSA_WITH_AES_256_CBC_SHA */ + "ADH-AES256-SHA", /* TLS_DH_anon_WITH_AES_256_CBC_SHA */ + "AES256-SHA256", /* TLS_RSA_WITH_AES_256_CBC_SHA256 */ + "DH-DSS-AES256-SHA256", /* TLS_DH_DSS_WITH_AES_256_CBC_SHA256 */ + "DH-RSA-AES256-SHA256", /* TLS_DH_RSA_WITH_AES_256_CBC_SHA256 */ + "DHE-DSS-AES256-SHA256", /* TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 */ + "DHE-RSA-AES256-SHA256", /* TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 */ + "ADH-AES256-SHA256", /* TLS_DH_anon_WITH_AES_256_CBC_SHA256 */ + "ECDH-ECDSA-AES256-SHA", /* TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA */ + "ECDHE-ECDSA-AES256-SHA", /* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA */ + "ECDH-RSA-AES256-SHA", /* TLS_ECDH_RSA_WITH_AES_256_CBC_SHA */ + "ECDHE-RSA-AES256-SHA", /* TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA */ + "AECDH-AES256-SHA", /* TLS_ECDH_anon_WITH_AES_256_CBC_SHA */ + "ECDHE-ECDSA-AES256-SHA384", /* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 */ + "ECDH-ECDSA-AES256-SHA384", /* TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 */ + "ECDHE-RSA-AES256-SHA384", /* TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 */ + "ECDH-RSA-AES256-SHA384", /* TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 */ + "PSK-AES256-CBC-SHA", /* TLS_PSK_WITH_AES_256_CBC_SHA */ + "DHE-PSK-AES256-CBC-SHA", /* TLS_DHE_PSK_WITH_AES_256_CBC_SHA */ + "RSA-PSK-AES256-CBC-SHA", /* TLS_RSA_PSK_WITH_AES_256_CBC_SHA */ + "PSK-AES256-CBC-SHA384", /* TLS_PSK_WITH_AES_256_CBC_SHA384 */ + "DHE-PSK-AES256-CBC-SHA384", /* TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 */ + "RSA-PSK-AES256-CBC-SHA384", /* TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 */ + "ECDHE-PSK-AES256-CBC-SHA", /* TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA */ + "ECDHE-PSK-AES256-CBC-SHA384", /* TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 */ + "SRP-AES-256-CBC-SHA", /* TLS_SRP_SHA_WITH_AES_256_CBC_SHA */ + "SRP-RSA-AES-256-CBC-SHA", /* TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA */ + "SRP-DSS-AES-256-CBC-SHA", /* TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA */ + "AES256-CCM", /* TLS_RSA_WITH_AES_256_CCM */ + "AES256-CCM8", /* TLS_RSA_WITH_AES_256_CCM_8 */ + "PSK-AES256-CCM", /* TLS_PSK_WITH_AES_256_CCM */ + "PSK-AES256-CCM8", /* TLS_PSK_WITH_AES_256_CCM_8 */ + "AES256-GCM-SHA384", /* TLS_RSA_WITH_AES_256_GCM_SHA384 */ + "DH-RSA-AES256-GCM-SHA384", /* TLS_DH_RSA_WITH_AES_256_GCM_SHA384 */ + "DH-DSS-AES256-GCM-SHA384", /* TLS_DH_DSS_WITH_AES_256_GCM_SHA384 */ + "ADH-AES256-GCM-SHA384", /* TLS_DH_anon_WITH_AES_256_GCM_SHA384 */ + "PSK-AES256-GCM-SHA384", /* TLS_PSK_WITH_AES_256_GCM_SHA384 */ + "RSA-PSK-AES256-GCM-SHA384", /* TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 */ + "ECDH-ECDSA-AES256-GCM-SHA384", /* TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 */ + "ECDH-RSA-AES256-GCM-SHA384", /* TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 */ + + /* blacklisted CAMELLIA128 encrpytion ciphers */ + "CAMELLIA128-SHA", /* TLS_RSA_WITH_CAMELLIA_128_CBC_SHA */ + "DH-DSS-CAMELLIA128-SHA", /* TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA */ + "DH-RSA-CAMELLIA128-SHA", /* TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA */ + "DHE-DSS-CAMELLIA128-SHA", /* TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA */ + "DHE-RSA-CAMELLIA128-SHA", /* TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA */ + "ADH-CAMELLIA128-SHA", /* TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA */ + "ECDHE-ECDSA-CAMELLIA128-SHA256", /* TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 */ + "ECDH-ECDSA-CAMELLIA128-SHA256", /* TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 */ + "ECDHE-RSA-CAMELLIA128-SHA256", /* TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 */ + "ECDH-RSA-CAMELLIA128-SHA256", /* TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 */ + "PSK-CAMELLIA128-SHA256", /* TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 */ + "DHE-PSK-CAMELLIA128-SHA256", /* TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 */ + "RSA-PSK-CAMELLIA128-SHA256", /* TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 */ + "ECDHE-PSK-CAMELLIA128-SHA256", /* TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 */ + "CAMELLIA128-GCM-SHA256", /* TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 */ + "DH-RSA-CAMELLIA128-GCM-SHA256", /* TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 */ + "DH-DSS-CAMELLIA128-GCM-SHA256", /* TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 */ + "ADH-CAMELLIA128-GCM-SHA256", /* TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 */ + "ECDH-ECDSA-CAMELLIA128-GCM-SHA256",/* TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 */ + "ECDH-RSA-CAMELLIA128-GCM-SHA256", /* TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 */ + "PSK-CAMELLIA128-GCM-SHA256", /* TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 */ + "RSA-PSK-CAMELLIA128-GCM-SHA256", /* TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 */ + "CAMELLIA128-SHA256", /* TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 */ + "DH-DSS-CAMELLIA128-SHA256", /* TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 */ + "DH-RSA-CAMELLIA128-SHA256", /* TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 */ + "DHE-DSS-CAMELLIA128-SHA256", /* TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 */ + "DHE-RSA-CAMELLIA128-SHA256", /* TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 */ + "ADH-CAMELLIA128-SHA256", /* TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 */ + + /* blacklisted CAMELLIA256 encrpytion ciphers */ + "CAMELLIA256-SHA", /* TLS_RSA_WITH_CAMELLIA_256_CBC_SHA */ + "DH-RSA-CAMELLIA256-SHA", /* TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA */ + "DH-DSS-CAMELLIA256-SHA", /* TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA */ + "DHE-DSS-CAMELLIA256-SHA", /* TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA */ + "DHE-RSA-CAMELLIA256-SHA", /* TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA */ + "ADH-CAMELLIA256-SHA", /* TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA */ + "ECDHE-ECDSA-CAMELLIA256-SHA384", /* TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 */ + "ECDH-ECDSA-CAMELLIA256-SHA384", /* TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 */ + "ECDHE-RSA-CAMELLIA256-SHA384", /* TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 */ + "ECDH-RSA-CAMELLIA256-SHA384", /* TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 */ + "PSK-CAMELLIA256-SHA384", /* TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 */ + "DHE-PSK-CAMELLIA256-SHA384", /* TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 */ + "RSA-PSK-CAMELLIA256-SHA384", /* TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 */ + "ECDHE-PSK-CAMELLIA256-SHA384", /* TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 */ + "CAMELLIA256-SHA256", /* TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 */ + "DH-DSS-CAMELLIA256-SHA256", /* TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 */ + "DH-RSA-CAMELLIA256-SHA256", /* TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 */ + "DHE-DSS-CAMELLIA256-SHA256", /* TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 */ + "DHE-RSA-CAMELLIA256-SHA256", /* TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 */ + "ADH-CAMELLIA256-SHA256", /* TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 */ + "CAMELLIA256-GCM-SHA384", /* TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 */ + "DH-RSA-CAMELLIA256-GCM-SHA384", /* TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 */ + "DH-DSS-CAMELLIA256-GCM-SHA384", /* TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 */ + "ADH-CAMELLIA256-GCM-SHA384", /* TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 */ + "ECDH-ECDSA-CAMELLIA256-GCM-SHA384",/* TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 */ + "ECDH-RSA-CAMELLIA256-GCM-SHA384", /* TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 */ + "PSK-CAMELLIA256-GCM-SHA384", /* TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 */ + "RSA-PSK-CAMELLIA256-GCM-SHA384", /* TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 */ + + /* The blacklisted ARIA encrpytion ciphers */ + "ARIA128-SHA256", /* TLS_RSA_WITH_ARIA_128_CBC_SHA256 */ + "ARIA256-SHA384", /* TLS_RSA_WITH_ARIA_256_CBC_SHA384 */ + "DH-DSS-ARIA128-SHA256", /* TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 */ + "DH-DSS-ARIA256-SHA384", /* TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 */ + "DH-RSA-ARIA128-SHA256", /* TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 */ + "DH-RSA-ARIA256-SHA384", /* TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 */ + "DHE-DSS-ARIA128-SHA256", /* TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 */ + "DHE-DSS-ARIA256-SHA384", /* TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 */ + "DHE-RSA-ARIA128-SHA256", /* TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 */ + "DHE-RSA-ARIA256-SHA384", /* TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 */ + "ADH-ARIA128-SHA256", /* TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 */ + "ADH-ARIA256-SHA384", /* TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 */ + "ECDHE-ECDSA-ARIA128-SHA256", /* TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 */ + "ECDHE-ECDSA-ARIA256-SHA384", /* TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 */ + "ECDH-ECDSA-ARIA128-SHA256", /* TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 */ + "ECDH-ECDSA-ARIA256-SHA384", /* TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 */ + "ECDHE-RSA-ARIA128-SHA256", /* TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 */ + "ECDHE-RSA-ARIA256-SHA384", /* TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 */ + "ECDH-RSA-ARIA128-SHA256", /* TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 */ + "ECDH-RSA-ARIA256-SHA384", /* TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 */ + "ARIA128-GCM-SHA256", /* TLS_RSA_WITH_ARIA_128_GCM_SHA256 */ + "ARIA256-GCM-SHA384", /* TLS_RSA_WITH_ARIA_256_GCM_SHA384 */ + "DH-DSS-ARIA128-GCM-SHA256", /* TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 */ + "DH-DSS-ARIA256-GCM-SHA384", /* TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 */ + "DH-RSA-ARIA128-GCM-SHA256", /* TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 */ + "DH-RSA-ARIA256-GCM-SHA384", /* TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 */ + "ADH-ARIA128-GCM-SHA256", /* TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 */ + "ADH-ARIA256-GCM-SHA384", /* TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 */ + "ECDH-ECDSA-ARIA128-GCM-SHA256", /* TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 */ + "ECDH-ECDSA-ARIA256-GCM-SHA384", /* TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 */ + "ECDH-RSA-ARIA128-GCM-SHA256", /* TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 */ + "ECDH-RSA-ARIA256-GCM-SHA384", /* TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 */ + "PSK-ARIA128-SHA256", /* TLS_PSK_WITH_ARIA_128_CBC_SHA256 */ + "PSK-ARIA256-SHA384", /* TLS_PSK_WITH_ARIA_256_CBC_SHA384 */ + "DHE-PSK-ARIA128-SHA256", /* TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 */ + "DHE-PSK-ARIA256-SHA384", /* TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 */ + "RSA-PSK-ARIA128-SHA256", /* TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 */ + "RSA-PSK-ARIA256-SHA384", /* TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 */ + "ARIA128-GCM-SHA256", /* TLS_PSK_WITH_ARIA_128_GCM_SHA256 */ + "ARIA256-GCM-SHA384", /* TLS_PSK_WITH_ARIA_256_GCM_SHA384 */ + "RSA-PSK-ARIA128-GCM-SHA256", /* TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 */ + "RSA-PSK-ARIA256-GCM-SHA384", /* TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 */ + "ECDHE-PSK-ARIA128-SHA256", /* TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 */ + "ECDHE-PSK-ARIA256-SHA384", /* TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 */ + + /* blacklisted SEED encryptions */ + "SEED-SHA", /*TLS_RSA_WITH_SEED_CBC_SHA */ + "DH-DSS-SEED-SHA", /* TLS_DH_DSS_WITH_SEED_CBC_SHA */ + "DH-RSA-SEED-SHA", /* TLS_DH_RSA_WITH_SEED_CBC_SHA */ + "DHE-DSS-SEED-SHA", /* TLS_DHE_DSS_WITH_SEED_CBC_SHA */ + "DHE-RSA-SEED-SHA", /* TLS_DHE_RSA_WITH_SEED_CBC_SHA */ + "ADH-SEED-SHA", /* TLS_DH_anon_WITH_SEED_CBC_SHA */ + + /* blacklisted KRB5 ciphers */ + "KRB5-DES-CBC-SHA", /* TLS_KRB5_WITH_DES_CBC_SHA */ + "KRB5-DES-CBC3-SHA", /* TLS_KRB5_WITH_3DES_EDE_CBC_SHA */ + "KRB5-IDEA-CBC-SHA", /* TLS_KRB5_WITH_IDEA_CBC_SHA */ + "KRB5-DES-CBC-MD5", /* TLS_KRB5_WITH_DES_CBC_MD5 */ + "KRB5-DES-CBC3-MD5", /* TLS_KRB5_WITH_3DES_EDE_CBC_MD5 */ + "KRB5-IDEA-CBC-MD5", /* TLS_KRB5_WITH_IDEA_CBC_MD5 */ + "EXP-KRB5-DES-CBC-SHA", /* TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA */ + "EXP-KRB5-DES-CBC-MD5", /* TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 */ + "EXP-KRB5-RC2-CBC-SHA", /* TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA */ + "EXP-KRB5-RC2-CBC-MD5", /* TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 */ + + /* blacklisted exoticas */ + "DHE-DSS-CBC-SHA", /* TLS_DHE_DSS_WITH_DES_CBC_SHA */ + "IDEA-CBC-SHA", /* TLS_RSA_WITH_IDEA_CBC_SHA */ + + /* not really sure if the following names are correct */ + "SSL3_CK_SCSV", /* TLS_EMPTY_RENEGOTIATION_INFO_SCSV */ + "SSL3_CK_FALLBACK_SCSV" +}; +static size_t RFC7540_names_LEN = sizeof(RFC7540_names)/sizeof(RFC7540_names[0]); + + +static apr_hash_t *BLCNames; + +static void cipher_init(apr_pool_t *pool) +{ + apr_hash_t *hash = apr_hash_make(pool); + const char *source; + unsigned int i; + + source = "rfc7540"; + for (i = 0; i < RFC7540_names_LEN; ++i) { + apr_hash_set(hash, RFC7540_names[i], APR_HASH_KEY_STRING, source); + } + + BLCNames = hash; +} + +static int cipher_is_blacklisted(const char *cipher, const char **psource) +{ + *psource = apr_hash_get(BLCNames, cipher, APR_HASH_KEY_STRING); + return !!*psource; +} + /******************************************************************************* * Hooks for processing incoming connections: * - pre_conn_before_tls switches SSL off for stream connections @@ -72,12 +449,15 @@ apr_status_t h2_h2_init(apr_pool_t *pool, server_rec *s) ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "h2_h2, child_init"); opt_ssl_engine_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable); opt_ssl_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https); + opt_ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup); - if (!opt_ssl_is_https) { + if (!opt_ssl_is_https || !opt_ssl_var_lookup) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(02951) "mod_ssl does not seem to be enabled"); } + cipher_init(pool); + return APR_SUCCESS; } @@ -86,18 +466,95 @@ int h2_h2_is_tls(conn_rec *c) return opt_ssl_is_https && opt_ssl_is_https(c); } -int h2_tls_disable(conn_rec *c) +int h2_is_acceptable_connection(conn_rec *c, int require_all) { - if (opt_ssl_engine_disable) { - return opt_ssl_engine_disable(c); + int is_tls = h2_h2_is_tls(c); + const h2_config *cfg = h2_config_get(c); + + if (is_tls && h2_config_geti(cfg, H2_CONF_MODERN_TLS_ONLY) > 0) { + /* Check TLS connection for modern TLS parameters, as defined in + * RFC 7540 and https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility + */ + apr_pool_t *pool = c->pool; + server_rec *s = c->base_server; + char *val; + + if (!opt_ssl_var_lookup) { + /* unable to check */ + return 0; + } + + /* Need Tlsv1.2 or higher, rfc 7540, ch. 9.2 + */ + val = opt_ssl_var_lookup(pool, s, c, NULL, (char*)"SSL_PROTOCOL"); + if (val && *val) { + if (strncmp("TLS", val, 3) + || !strcmp("TLSv1", val) + || !strcmp("TLSv1.1", val)) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + "h2_h2(%ld): tls protocol not suitable: %s", + (long)c->id, val); + return 0; + } + } + else if (require_all) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + "h2_h2(%ld): tls protocol is indetermined", (long)c->id); + return 0; + } + + /* Check TLS cipher blacklist + */ + val = opt_ssl_var_lookup(pool, s, c, NULL, (char*)"SSL_CIPHER"); + if (val && *val) { + const char *source; + if (cipher_is_blacklisted(val, &source)) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + "h2_h2(%ld): tls cipher %s blacklisted by %s", + (long)c->id, val, source); + return 0; + } + } + else if (require_all) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + "h2_h2(%ld): tls cipher is indetermined", (long)c->id); + return 0; + } + } + return 1; +} + +int h2_allows_h2_direct(conn_rec *c) +{ + const h2_config *cfg = h2_config_get(c); + int h2_direct = h2_config_geti(cfg, H2_CONF_DIRECT); + + if (h2_direct < 0) { + if (h2_h2_is_tls(c)) { + /* disabled by default on TLS */ + h2_direct = 0; + } + else { + /* enabled if "Protocols h2c" is configured */ + h2_direct = ap_is_allowed_protocol(c, NULL, NULL, "h2c"); + } } - return 0; + return !!h2_direct; +} + +int h2_allows_h2_upgrade(conn_rec *c) +{ + const h2_config *cfg = h2_config_get(c); + int h2_upgrade = h2_config_geti(cfg, H2_CONF_UPGRADE); + + return h2_upgrade > 0 || (h2_upgrade < 0 && !h2_h2_is_tls(c)); } /******************************************************************************* * Register various hooks */ static const char *const mod_reqtimeout[] = { "reqtimeout.c", NULL}; +static const char* const mod_ssl[] = {"mod_ssl.c", NULL}; void h2_h2_register_hooks(void) { @@ -105,7 +562,8 @@ void h2_h2_register_hooks(void) * take over, if h2* was selected as protocol. */ ap_hook_process_connection(h2_h2_process_conn, - NULL, NULL, APR_HOOK_FIRST); + mod_ssl, NULL, APR_HOOK_MIDDLE); + /* Perform connection cleanup before the actual processing happens. */ ap_hook_process_connection(h2_h2_remove_timeout, @@ -119,7 +577,7 @@ void h2_h2_register_hooks(void) ap_hook_post_read_request(h2_h2_post_read_req, NULL, NULL, APR_HOOK_REALLY_FIRST); } -int h2_h2_remove_timeout(conn_rec* c) +static int h2_h2_remove_timeout(conn_rec* c) { h2_ctx *ctx = h2_ctx_get(c); @@ -134,9 +592,6 @@ int h2_h2_remove_timeout(conn_rec* c) int h2_h2_process_conn(conn_rec* c) { h2_ctx *ctx = h2_ctx_get(c); - h2_config *cfg = h2_config_get(c); - apr_bucket_brigade* temp; - int is_tls = h2_h2_is_tls(c); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, process_conn"); if (h2_ctx_is_task(ctx)) { @@ -144,53 +599,50 @@ int h2_h2_process_conn(conn_rec* c) return DECLINED; } - /* If we have not already switched to a h2* protocol and the connection - * is on "http/1.1" - * -> sniff for the magic PRIamble. On TLS, this might trigger the ALPN. - */ - if (!h2_ctx_protocol_get(c) - && !strcmp(AP_PROTOCOL_HTTP1, ap_get_protocol(c))) { + if (h2_ctx_protocol_get(c)) { + /* Something has been negotiated */ + } + else if (!strcmp(AP_PROTOCOL_HTTP1, ap_get_protocol(c)) + && h2_allows_h2_direct(c) + && h2_is_acceptable_connection(c, 1)) { + /* connection still is on http/1.1 and H2Direct is enabled. + * Otherwise connection is in a fully acceptable state. + * -> peek at the first 24 incoming bytes + */ + apr_bucket_brigade *temp; apr_status_t status; + char *s = NULL; + apr_size_t slen; temp = apr_brigade_create(c->pool, c->bucket_alloc); status = ap_get_brigade(c->input_filters, temp, AP_MODE_SPECULATIVE, APR_BLOCK_READ, 24); - - if (status == APR_SUCCESS) { - if (h2_ctx_protocol_get(c) - || strcmp(AP_PROTOCOL_HTTP1, ap_get_protocol(c))) { - /* h2 or another protocol has been selected. */ - } - else { - /* ALPN might have been triggered, but we're still on - * http/1.1. Check the actual bytes read for the H2 Magic - * Token, *if* H2Direct mode is enabled here. - */ - int direct_mode = h2_config_geti(cfg, H2_CONF_DIRECT); - if (direct_mode > 0 || (direct_mode < 0 && !is_tls)) { - char *s = NULL; - apr_size_t slen; - - apr_brigade_pflatten(temp, &s, &slen, c->pool); - if ((slen >= 24) && !memcmp(H2_MAGIC_TOKEN, s, 24)) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, - "h2_h2, direct mode detected"); - h2_ctx_protocol_set(ctx, is_tls? "h2" : "h2c"); - } - else { - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, - "h2_h2, not detected in %d bytes: %s", - (int)slen, s); - } - } - } - } - else { + + if (status != APR_SUCCESS) { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, "h2_h2, error reading 24 bytes speculative"); + apr_brigade_destroy(temp); + return DECLINED; + } + + apr_brigade_pflatten(temp, &s, &slen, c->pool); + if ((slen >= 24) && !memcmp(H2_MAGIC_TOKEN, s, 24)) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, + "h2_h2, direct mode detected"); + h2_ctx_protocol_set(ctx, h2_h2_is_tls(c)? "h2" : "h2c"); } + else { + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, + "h2_h2, not detected in %d bytes: %s", + (int)slen, s); + } + apr_brigade_destroy(temp); } + else { + /* the connection is not HTTP/1.1 or not for us, don't touch it */ + return DECLINED; + } /* If "h2" was selected as protocol (by whatever mechanism), take over * the connection. @@ -199,7 +651,7 @@ int h2_h2_process_conn(conn_rec* c) ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, connection, h2 active"); - return h2_conn_main(c); + return h2_conn_process(c, NULL, ctx->server); } ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, declined"); @@ -209,22 +661,25 @@ int h2_h2_process_conn(conn_rec* c) static int h2_h2_post_read_req(request_rec *r) { h2_ctx *ctx = h2_ctx_rget(r); - struct h2_task_env *env = h2_ctx_get_task(ctx); - if (env) { + struct h2_task *task = h2_ctx_get_task(ctx); + if (task) { + /* FIXME: sometimes, this hook gets called twice for a single request. + * This should not be, right? */ /* h2_task connection for a stream, not for h2c */ - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "adding h1_to_h2_resp output filter"); - if (env->serialize_headers) { + if (task->serialize_headers) { ap_remove_output_filter_byhandle(r->output_filters, "H1_TO_H2_RESP"); - ap_add_output_filter("H1_TO_H2_RESP", env, r, r->connection); + ap_add_output_filter("H1_TO_H2_RESP", task, r, r->connection); } else { /* replace the core http filter that formats response headers * in HTTP/1 with our own that collects status and headers */ ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER"); ap_remove_output_filter_byhandle(r->output_filters, "H2_RESPONSE"); - ap_add_output_filter("H2_RESPONSE", env, r, r->connection); + ap_add_output_filter("H2_RESPONSE", task, r, r->connection); } + ap_add_output_filter("H2_TRAILERS", task, r, r->connection); } return DECLINED; } diff --git a/modules/http2/h2_h2.h b/modules/http2/h2_h2.h index 9a1184d8..563abe3f 100644 --- a/modules/http2/h2_h2.h +++ b/modules/http2/h2_h2.h @@ -34,6 +34,48 @@ extern const char *h2_tls_protos[]; */ extern const char *H2_MAGIC_TOKEN; +#define H2_ERR_NO_ERROR (0x00) +#define H2_ERR_PROTOCOL_ERROR (0x01) +#define H2_ERR_INTERNAL_ERROR (0x02) +#define H2_ERR_FLOW_CONTROL_ERROR (0x03) +#define H2_ERR_SETTINGS_TIMEOUT (0x04) +#define H2_ERR_STREAM_CLOSED (0x05) +#define H2_ERR_FRAME_SIZE_ERROR (0x06) +#define H2_ERR_REFUSED_STREAM (0x07) +#define H2_ERR_CANCEL (0x08) +#define H2_ERR_COMPRESSION_ERROR (0x09) +#define H2_ERR_CONNECT_ERROR (0x0a) +#define H2_ERR_ENHANCE_YOUR_CALM (0x0b) +#define H2_ERR_INADEQUATE_SECURITY (0x0c) +#define H2_ERR_HTTP_1_1_REQUIRED (0x0d) + +/* Maximum number of padding bytes in a frame, rfc7540 */ +#define H2_MAX_PADLEN 256 +/* Initial default window size, RFC 7540 ch. 6.5.2 */ +#define H2_INITIAL_WINDOW_SIZE ((64*1024)-1) + +#define H2_HTTP_2XX(a) ((a) >= 200 && (a) < 300) + +#define H2_STREAM_CLIENT_INITIATED(id) (id&0x01) + +typedef enum { + H2_DEPENDANT_AFTER, + H2_DEPENDANT_INTERLEAVED, + H2_DEPENDANT_BEFORE, +} h2_dependency; + +typedef struct h2_priority { + h2_dependency dependency; + int weight; +} h2_priority; + +/** + * Provide a user readable description of the HTTP/2 error code- + * @param h2_error http/2 error code, as in rfc 7540, ch. 7 + * @return textual description of code or that it is unknown. + */ +const char *h2_h2_err_description(unsigned int h2_error); + /* * One time, post config intialization. */ @@ -43,15 +85,35 @@ apr_status_t h2_h2_init(apr_pool_t *pool, server_rec *s); */ int h2_h2_is_tls(conn_rec *c); -/* Disable SSL for this connection, can only be invoked in a pre- - * connection hook before mod_ssl. - * @return != 0 iff disable worked - */ -int h2_tls_disable(conn_rec *c); - /* Register apache hooks for h2 protocol */ void h2_h2_register_hooks(void); +/** + * Check if the given connection fulfills the requirements as configured. + * @param c the connection + * @param require_all != 0 iff any missing connection properties make + * the test fail. For example, a cipher might not have been selected while + * the handshake is still ongoing. + * @return != 0 iff connection requirements are met + */ +int h2_is_acceptable_connection(conn_rec *c, int require_all); + +/** + * Check if the "direct" HTTP/2 mode of protocol handling is enabled + * for the given connection. + * @param c the connection to check + * @return != 0 iff direct mode is enabled + */ +int h2_allows_h2_direct(conn_rec *c); + +/** + * Check if the "Upgrade" HTTP/1.1 mode of protocol switching is enabled + * for the given connection. + * @param c the connection to check + * @return != 0 iff Upgrade switching is enabled + */ +int h2_allows_h2_upgrade(conn_rec *c); + #endif /* defined(__mod_h2__h2_h2__) */ diff --git a/modules/http2/h2_io.c b/modules/http2/h2_io.c index 42734430..98ba60e6 100644 --- a/modules/http2/h2_io.c +++ b/modules/http2/h2_io.c @@ -23,6 +23,7 @@ #include "h2_private.h" #include "h2_io.h" #include "h2_response.h" +#include "h2_task.h" #include "h2_util.h" h2_io *h2_io_create(int id, apr_pool_t *pool, apr_bucket_alloc_t *bucket_alloc) @@ -31,33 +32,50 @@ h2_io *h2_io_create(int id, apr_pool_t *pool, apr_bucket_alloc_t *bucket_alloc) if (io) { io->id = id; io->pool = pool; + io->bucket_alloc = bucket_alloc; io->bbin = NULL; - io->bbout = apr_brigade_create(pool, bucket_alloc); + io->bbout = NULL; } return io; } -static void h2_io_cleanup(h2_io *io) +void h2_io_destroy(h2_io *io) { - (void)io; + if (io->pool) { + apr_pool_destroy(io->pool); + /* gone */ + } } -void h2_io_destroy(h2_io *io) +void h2_io_set_response(h2_io *io, h2_response *response) { - h2_io_cleanup(io); + AP_DEBUG_ASSERT(io->pool); + AP_DEBUG_ASSERT(response); + AP_DEBUG_ASSERT(!io->response); + io->response = h2_response_clone(io->pool, response); + if (response->rst_error) { + h2_io_rst(io, response->rst_error); + } +} + + +void h2_io_rst(h2_io *io, int error) +{ + io->rst_error = error; + io->eos_in = 1; } int h2_io_in_has_eos_for(h2_io *io) { - return io->eos_in || (io->bbin && h2_util_has_eos(io->bbin, 0)); + return io->eos_in || (io->bbin && h2_util_has_eos(io->bbin, -1)); } int h2_io_out_has_data(h2_io *io) { - return h2_util_bb_has_data_or_eos(io->bbout); + return io->bbout && h2_util_bb_has_data_or_eos(io->bbout); } -apr_size_t h2_io_out_length(h2_io *io) +apr_off_t h2_io_out_length(h2_io *io) { if (io->bbout) { apr_off_t len = 0; @@ -67,6 +85,17 @@ apr_size_t h2_io_out_length(h2_io *io) return 0; } +apr_status_t h2_io_in_shutdown(h2_io *io) +{ + if (io->bbin) { + apr_off_t end_len = 0; + apr_brigade_length(io->bbin, 1, &end_len); + io->input_consumed += end_len; + apr_brigade_cleanup(io->bbin); + } + return h2_io_in_close(io); +} + apr_status_t h2_io_in_read(h2_io *io, apr_bucket_brigade *bb, apr_size_t maxlen) { @@ -74,14 +103,17 @@ apr_status_t h2_io_in_read(h2_io *io, apr_bucket_brigade *bb, apr_bucket *last; apr_status_t status; + if (io->rst_error) { + return APR_ECONNABORTED; + } + if (!io->bbin || APR_BRIGADE_EMPTY(io->bbin)) { return io->eos_in? APR_EOF : APR_EAGAIN; } apr_brigade_length(bb, 1, &start_len); last = APR_BRIGADE_LAST(bb); - status = h2_util_move(bb, io->bbin, maxlen, 0, - "h2_io_in_read"); + status = h2_util_move(bb, io->bbin, maxlen, NULL, "h2_io_in_read"); if (status == APR_SUCCESS) { apr_bucket *nlast = APR_BRIGADE_LAST(bb); apr_off_t end_len = 0; @@ -96,22 +128,29 @@ apr_status_t h2_io_in_read(h2_io *io, apr_bucket_brigade *bb, apr_status_t h2_io_in_write(h2_io *io, apr_bucket_brigade *bb) { + if (io->rst_error) { + return APR_ECONNABORTED; + } + if (io->eos_in) { return APR_EOF; } - io->eos_in = h2_util_has_eos(bb, 0); + io->eos_in = h2_util_has_eos(bb, -1); if (!APR_BRIGADE_EMPTY(bb)) { if (!io->bbin) { - io->bbin = apr_brigade_create(io->bbout->p, - io->bbout->bucket_alloc); + io->bbin = apr_brigade_create(io->pool, io->bucket_alloc); } - return h2_util_move(io->bbin, bb, 0, 0, "h2_io_in_write"); + return h2_util_move(io->bbin, bb, -1, NULL, "h2_io_in_write"); } return APR_SUCCESS; } apr_status_t h2_io_in_close(h2_io *io) { + if (io->rst_error) { + return APR_ECONNABORTED; + } + if (io->bbin) { APR_BRIGADE_INSERT_TAIL(io->bbin, apr_bucket_eos_create(io->bbin->bucket_alloc)); @@ -122,18 +161,103 @@ apr_status_t h2_io_in_close(h2_io *io) apr_status_t h2_io_out_readx(h2_io *io, h2_io_data_cb *cb, void *ctx, - apr_size_t *plen, int *peos) + apr_off_t *plen, int *peos) { + apr_status_t status; + + if (io->rst_error) { + return APR_ECONNABORTED; + } + + if (io->eos_out) { + *plen = 0; + *peos = 1; + return APR_SUCCESS; + } + else if (!io->bbout) { + *plen = 0; + *peos = 0; + return APR_EAGAIN; + } + if (cb == NULL) { /* just checking length available */ - return h2_util_bb_avail(io->bbout, plen, peos); + status = h2_util_bb_avail(io->bbout, plen, peos); + } + else { + status = h2_util_bb_readx(io->bbout, cb, ctx, plen, peos); + if (status == APR_SUCCESS) { + io->eos_out = *peos; + } + } + + return status; +} + +apr_status_t h2_io_out_read_to(h2_io *io, apr_bucket_brigade *bb, + apr_off_t *plen, int *peos) +{ + if (io->rst_error) { + return APR_ECONNABORTED; + } + + if (io->eos_out) { + *plen = 0; + *peos = 1; + return APR_SUCCESS; + } + else if (!io->bbout) { + *plen = 0; + *peos = 0; + return APR_EAGAIN; + } + + io->eos_out = *peos = h2_util_has_eos(io->bbout, *plen); + return h2_util_move(bb, io->bbout, *plen, NULL, "h2_io_read_to"); +} + +static void process_trailers(h2_io *io, apr_table_t *trailers) +{ + if (trailers && io->response) { + h2_response_set_trailers(io->response, + apr_table_clone(io->pool, trailers)); } - return h2_util_bb_readx(io->bbout, cb, ctx, plen, peos); } apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, - apr_size_t maxlen, int *pfile_handles_allowed) + apr_size_t maxlen, apr_table_t *trailers, + int *pfile_handles_allowed) { + apr_status_t status; + int start_allowed; + + if (io->rst_error) { + return APR_ECONNABORTED; + } + + if (io->eos_out) { + apr_off_t len; + /* We have already delivered an EOS bucket to a reader, no + * sense in storing anything more here. + */ + status = apr_brigade_length(bb, 1, &len); + if (status == APR_SUCCESS) { + if (len > 0) { + /* someone tries to write real data after EOS, that + * does not look right. */ + status = APR_EOF; + } + /* cleanup, as if we had moved the data */ + apr_brigade_cleanup(bb); + } + return status; + } + + process_trailers(io, trailers); + if (!io->bbout) { + io->bbout = apr_brigade_create(io->pool, io->bucket_alloc); + } + /* Let's move the buckets from the request processing in here, so * that the main thread can read them when it has time/capacity. * @@ -144,8 +268,7 @@ apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, * many open files already buffered. Otherwise we will run out of * file handles. */ - int start_allowed = *pfile_handles_allowed; - apr_status_t status; + start_allowed = *pfile_handles_allowed; status = h2_util_move(io->bbout, bb, maxlen, pfile_handles_allowed, "h2_io_out_write"); /* track # file buckets moved into our pool */ @@ -156,9 +279,20 @@ apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, } -apr_status_t h2_io_out_close(h2_io *io) +apr_status_t h2_io_out_close(h2_io *io, apr_table_t *trailers) { - APR_BRIGADE_INSERT_TAIL(io->bbout, - apr_bucket_eos_create(io->bbout->bucket_alloc)); + if (io->rst_error) { + return APR_ECONNABORTED; + } + if (!io->eos_out) { /* EOS has not been read yet */ + process_trailers(io, trailers); + if (!io->bbout) { + io->bbout = apr_brigade_create(io->pool, io->bucket_alloc); + } + if (!h2_util_has_eos(io->bbout, -1)) { + APR_BRIGADE_INSERT_TAIL(io->bbout, + apr_bucket_eos_create(io->bbout->bucket_alloc)); + } + } return APR_SUCCESS; } diff --git a/modules/http2/h2_io.h b/modules/http2/h2_io.h index 946ee443..08f3aa3d 100644 --- a/modules/http2/h2_io.h +++ b/modules/http2/h2_io.h @@ -18,11 +18,12 @@ struct h2_response; struct apr_thread_cond_t; -struct h2_task; +struct h2_request; -typedef apr_status_t h2_io_data_cb(void *ctx, - const char *data, apr_size_t len); +typedef apr_status_t h2_io_data_cb(void *ctx, const char *data, apr_off_t len); + +typedef int h2_stream_pri_cmp(int stream_id1, int stream_id2, void *ctx); typedef struct h2_io h2_io; @@ -30,17 +31,24 @@ typedef struct h2_io h2_io; struct h2_io { int id; /* stream identifier */ apr_pool_t *pool; /* stream pool */ - apr_bucket_brigade *bbin; /* input data for stream */ - int eos_in; - int task_done; + int orphaned; /* h2_stream is gone for this io */ - apr_size_t input_consumed; /* how many bytes have been read */ + int task_done; + const struct h2_request *request; /* request on this io */ + int request_body; /* == 0 iff request has no body */ + struct h2_response *response;/* response for submit, once created */ + int rst_error; + + int eos_in; + apr_bucket_brigade *bbin; /* input data for stream */ struct apr_thread_cond_t *input_arrived; /* block on reading */ + apr_size_t input_consumed; /* how many bytes have been read */ + int eos_out; apr_bucket_brigade *bbout; /* output data from stream */ + apr_bucket_alloc_t *bucket_alloc; struct apr_thread_cond_t *output_drained; /* block on writing */ - struct h2_response *response;/* submittable response created */ int files_handles_owned; }; @@ -58,6 +66,16 @@ h2_io *h2_io_create(int id, apr_pool_t *pool, apr_bucket_alloc_t *bucket_alloc); */ void h2_io_destroy(h2_io *io); +/** + * Set the response of this stream. + */ +void h2_io_set_response(h2_io *io, struct h2_response *response); + +/** + * Reset the stream with the given error code. + */ +void h2_io_rst(h2_io *io, int error); + /** * The input data is completely queued. Blocked reads will return immediately * and give either data or EOF. @@ -89,6 +107,12 @@ apr_status_t h2_io_in_write(h2_io *io, apr_bucket_brigade *bb); */ apr_status_t h2_io_in_close(h2_io *io); +/** + * Shuts all input down. Will close input and mark any data buffered + * as consumed. + */ +apr_status_t h2_io_in_shutdown(h2_io *io); + /******************************************************************************* * Output handling of streams. ******************************************************************************/ @@ -105,22 +129,27 @@ apr_status_t h2_io_in_close(h2_io *io); */ apr_status_t h2_io_out_readx(h2_io *io, h2_io_data_cb *cb, void *ctx, - apr_size_t *plen, int *peos); + apr_off_t *plen, int *peos); + +apr_status_t h2_io_out_read_to(h2_io *io, + apr_bucket_brigade *bb, + apr_off_t *plen, int *peos); apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, - apr_size_t maxlen, int *pfile_buckets_allowed); + apr_size_t maxlen, apr_table_t *trailers, + int *pfile_buckets_allowed); /** * Closes the input. After existing data has been read, APR_EOF will * be returned. */ -apr_status_t h2_io_out_close(h2_io *io); +apr_status_t h2_io_out_close(h2_io *io, apr_table_t *trailers); /** * Gives the overall length of the data that is currently queued for * output. */ -apr_size_t h2_io_out_length(h2_io *io); +apr_off_t h2_io_out_length(h2_io *io); #endif /* defined(__mod_h2__h2_io__) */ diff --git a/modules/http2/h2_io_set.c b/modules/http2/h2_io_set.c index 91afde8f..2bb6e694 100644 --- a/modules/http2/h2_io_set.c +++ b/modules/http2/h2_io_set.c @@ -78,19 +78,6 @@ h2_io *h2_io_set_get(h2_io_set *sp, int stream_id) return ps? *ps : NULL; } -h2_io *h2_io_set_get_highest_prio(h2_io_set *set) -{ - h2_io *highest = NULL; - int i; - for (i = 0; i < set->list->nelts; ++i) { - h2_io *io = h2_io_IDX(set->list, i); - if (!highest /*|| io-prio even higher */ ) { - highest = io; - } - } - return highest; -} - static void h2_io_set_sort(h2_io_set *sp) { qsort(sp->list->elts, sp->list->nelts, sp->list->elt_size, @@ -118,41 +105,44 @@ apr_status_t h2_io_set_add(h2_io_set *sp, h2_io *io) return APR_SUCCESS; } +static void remove_idx(h2_io_set *sp, int idx) +{ + int n; + --sp->list->nelts; + n = sp->list->nelts - idx; + if (n > 0) { + /* Close the hole in the array by moving the upper + * parts down one step. + */ + h2_io **selts = (h2_io**)sp->list->elts; + memmove(selts + idx, selts + idx + 1, n * sizeof(h2_io*)); + } +} + h2_io *h2_io_set_remove(h2_io_set *sp, h2_io *io) { int i; for (i = 0; i < sp->list->nelts; ++i) { h2_io *e = h2_io_IDX(sp->list, i); if (e == io) { - int n; - --sp->list->nelts; - n = sp->list->nelts - i; - if (n > 0) { - /* Close the hole in the array by moving the upper - * parts down one step. - */ - h2_io **selts = (h2_io**)sp->list->elts; - memmove(selts+i, selts+i+1, n * sizeof(h2_io*)); - } + remove_idx(sp, i); return e; } } return NULL; } -void h2_io_set_destroy_all(h2_io_set *sp) +h2_io *h2_io_set_pop_highest_prio(h2_io_set *set) { - int i; - for (i = 0; i < sp->list->nelts; ++i) { - h2_io *io = h2_io_IDX(sp->list, i); - h2_io_destroy(io); + /* For now, this just removes the first element in the set. + * the name is misleading... + */ + if (set->list->nelts > 0) { + h2_io *io = h2_io_IDX(set->list, 0); + remove_idx(set, 0); + return io; } - sp->list->nelts = 0; -} - -void h2_io_set_remove_all(h2_io_set *sp) -{ - sp->list->nelts = 0; + return NULL; } int h2_io_set_is_empty(h2_io_set *sp) @@ -161,16 +151,17 @@ int h2_io_set_is_empty(h2_io_set *sp) return sp->list->nelts == 0; } -void h2_io_set_iter(h2_io_set *sp, +int h2_io_set_iter(h2_io_set *sp, h2_io_set_iter_fn *iter, void *ctx) { int i; for (i = 0; i < sp->list->nelts; ++i) { h2_io *s = h2_io_IDX(sp->list, i); if (!iter(ctx, s)) { - break; + return 0; } } + return 1; } apr_size_t h2_io_set_size(h2_io_set *sp) diff --git a/modules/http2/h2_io_set.h b/modules/http2/h2_io_set.h index a9c6546c..04ff8702 100644 --- a/modules/http2/h2_io_set.h +++ b/modules/http2/h2_io_set.h @@ -30,18 +30,27 @@ void h2_io_set_destroy(h2_io_set *set); apr_status_t h2_io_set_add(h2_io_set *set, struct h2_io *io); h2_io *h2_io_set_get(h2_io_set *set, int stream_id); -h2_io *h2_io_set_get_highest_prio(h2_io_set *set); h2_io *h2_io_set_remove(h2_io_set *set, struct h2_io *io); -void h2_io_set_remove_all(h2_io_set *set); -void h2_io_set_destroy_all(h2_io_set *set); int h2_io_set_is_empty(h2_io_set *set); apr_size_t h2_io_set_size(h2_io_set *set); typedef int h2_io_set_iter_fn(void *ctx, struct h2_io *io); -void h2_io_set_iter(h2_io_set *set, - h2_io_set_iter_fn *iter, void *ctx); +/** + * Iterator over all h2_io* in the set or until a + * callback returns 0. It is not safe to add or remove + * set members during iteration. + * + * @param set the set of h2_io to iterate over + * @param iter the function to call for each io + * @param ctx user data for the callback + * @return 1 iff iteration completed for all members + */ +int h2_io_set_iter(h2_io_set *set, + h2_io_set_iter_fn *iter, void *ctx); + +h2_io *h2_io_set_pop_highest_prio(h2_io_set *set); #endif /* defined(__mod_h2__h2_io_set__) */ diff --git a/modules/http2/h2_mplx.c b/modules/http2/h2_mplx.c index 2d07b1eb..333a9b5c 100644 --- a/modules/http2/h2_mplx.c +++ b/modules/http2/h2_mplx.c @@ -29,6 +29,7 @@ #include "h2_private.h" #include "h2_config.h" #include "h2_conn.h" +#include "h2_h2.h" #include "h2_io.h" #include "h2_io_set.h" #include "h2_response.h" @@ -40,7 +41,22 @@ #include "h2_task_input.h" #include "h2_task_output.h" #include "h2_task_queue.h" +#include "h2_worker.h" #include "h2_workers.h" +#include "h2_util.h" + + +#define H2_MPLX_IO_OUT(lvl,m,io,msg) \ + do { \ + if (APLOG_C_IS_LEVEL((m)->c,lvl)) \ + h2_util_bb_log((m)->c,(io)->id,lvl,msg,(io)->bbout); \ + } while(0) + +#define H2_MPLX_IO_IN(lvl,m,io,msg) \ + do { \ + if (APLOG_C_IS_LEVEL((m)->c,lvl)) \ + h2_util_bb_log((m)->c,(io)->id,lvl,msg,(io)->bbin); \ + } while(0) static int is_aborted(h2_mplx *m, apr_status_t *pstatus) { @@ -57,11 +73,10 @@ static void have_out_data_for(h2_mplx *m, int stream_id); static void h2_mplx_destroy(h2_mplx *m) { AP_DEBUG_ASSERT(m); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, + "h2_mplx(%ld): destroy, refs=%d", + m->id, m->refs); m->aborted = 1; - if (m->q) { - h2_tq_destroy(m->q); - m->q = NULL; - } if (m->ready_ios) { h2_io_set_destroy(m->ready_ios); m->ready_ios = NULL; @@ -71,11 +86,6 @@ static void h2_mplx_destroy(h2_mplx *m) m->stream_ios = NULL; } - if (m->lock) { - apr_thread_mutex_destroy(m->lock); - m->lock = NULL; - } - if (m->pool) { apr_pool_destroy(m->pool); } @@ -92,10 +102,11 @@ static void h2_mplx_destroy(h2_mplx *m) * their HTTP/1 cousins, the separate allocator seems to work better * than protecting a shared h2_session one with an own lock. */ -h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *parent, h2_workers *workers) +h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *parent, + const h2_config *conf, + h2_workers *workers) { apr_status_t status = APR_SUCCESS; - h2_config *conf = h2_config_get(c); apr_allocator_t *allocator = NULL; h2_mplx *m; AP_DEBUG_ASSERT(conf); @@ -109,7 +120,7 @@ h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *parent, h2_workers *workers) if (m) { m->id = c->id; APR_RING_ELEM_INIT(m, link); - apr_atomic_set32(&m->refs, 1); + m->refs = 1; m->c = c; apr_pool_create_ex(&m->pool, parent, NULL, allocator); if (!m->pool) { @@ -126,10 +137,9 @@ h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *parent, h2_workers *workers) m->bucket_alloc = apr_bucket_alloc_create(m->pool); - m->q = h2_tq_create(m->id, m->pool); + m->q = h2_tq_create(m->pool, h2_config_geti(conf, H2_CONF_MAX_STREAMS)); m->stream_ios = h2_io_set_create(m->pool); m->ready_ios = h2_io_set_create(m->pool); - m->closed = h2_stream_set_create(m->pool); m->stream_max_mem = h2_config_geti(conf, H2_CONF_STREAM_MAX_MEM); m->workers = workers; @@ -138,27 +148,31 @@ h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *parent, h2_workers *workers) return m; } -static void reference(h2_mplx *m) -{ - apr_atomic_inc32(&m->refs); -} - -static void release(h2_mplx *m) +static void release(h2_mplx *m, int lock) { - if (!apr_atomic_dec32(&m->refs)) { + if (lock) { + apr_thread_mutex_lock(m->lock); + --m->refs; if (m->join_wait) { apr_thread_cond_signal(m->join_wait); } + apr_thread_mutex_unlock(m->lock); + } + else { + --m->refs; } } void h2_mplx_reference(h2_mplx *m) { - reference(m); + apr_thread_mutex_lock(m->lock); + ++m->refs; + apr_thread_mutex_unlock(m->lock); } + void h2_mplx_release(h2_mplx *m) { - release(m); + release(m, 1); } static void workers_register(h2_mplx *m) { @@ -180,38 +194,94 @@ static void workers_unregister(h2_mplx *m) { h2_workers_unregister(m->workers, m); } +static int io_process_events(h2_mplx *m, h2_io *io) { + if (io->input_consumed && m->input_consumed) { + m->input_consumed(m->input_consumed_ctx, + io->id, io->input_consumed); + io->input_consumed = 0; + return 1; + } + return 0; +} + + +static void io_destroy(h2_mplx *m, h2_io *io, int events) +{ + apr_pool_t *pool = io->pool; + + /* cleanup any buffered input */ + h2_io_in_shutdown(io); + if (events) { + /* Process outstanding events before destruction */ + io_process_events(m, io); + } + + io->pool = NULL; + /* The pool is cleared/destroyed which also closes all + * allocated file handles. Give this count back to our + * file handle pool. */ + m->file_handles_allowed += io->files_handles_owned; + h2_io_set_remove(m->stream_ios, io); + h2_io_set_remove(m->ready_ios, io); + h2_io_destroy(io); + + if (pool) { + apr_pool_clear(pool); + if (m->spare_pool) { + apr_pool_destroy(m->spare_pool); + } + m->spare_pool = pool; + } +} + +static int io_stream_done(h2_mplx *m, h2_io *io, int rst_error) +{ + /* Remove io from ready set, we will never submit it */ + h2_io_set_remove(m->ready_ios, io); + if (io->task_done || h2_tq_remove(m->q, io->id)) { + /* already finished or not even started yet */ + io_destroy(m, io, 1); + return 0; + } + else { + /* cleanup once task is done */ + io->orphaned = 1; + if (rst_error) { + h2_io_rst(io, rst_error); + } + return 1; + } +} + +static int stream_done_iter(void *ctx, h2_io *io) { + return io_stream_done((h2_mplx*)ctx, io, 0); +} + apr_status_t h2_mplx_release_and_join(h2_mplx *m, apr_thread_cond_t *wait) { apr_status_t status; + workers_unregister(m); - status = apr_thread_mutex_lock(m->lock); if (APR_SUCCESS == status) { - int attempts = 0; - - release(m); - while (apr_atomic_read32(&m->refs) > 0) { + while (!h2_io_set_iter(m->stream_ios, stream_done_iter, m)) { + /* iterator until all h2_io have been orphaned or destroyed */ + } + + release(m, 0); + while (m->refs > 0) { m->join_wait = wait; - ap_log_cerror(APLOG_MARK, (attempts? APLOG_INFO : APLOG_DEBUG), - 0, m->c, + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, "h2_mplx(%ld): release_join, refs=%d, waiting...", m->id, m->refs); - apr_thread_cond_timedwait(wait, m->lock, apr_time_from_sec(10)); - if (++attempts >= 6) { - ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, m->c, - APLOGNO(02952) - "h2_mplx(%ld): join attempts exhausted, refs=%d", - m->id, m->refs); - break; - } + apr_thread_cond_wait(wait, m->lock); } - if (m->join_wait) { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, - "h2_mplx(%ld): release_join -> destroy", m->id); - } - m->join_wait = NULL; - apr_thread_mutex_unlock(m->lock); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, + "h2_mplx(%ld): release_join -> destroy, (#ios=%ld)", + m->id, (long)h2_io_set_size(m->stream_ios)); h2_mplx_destroy(m); + /* all gone */ + /*apr_thread_mutex_unlock(m->lock);*/ } return status; } @@ -223,84 +293,26 @@ void h2_mplx_abort(h2_mplx *m) status = apr_thread_mutex_lock(m->lock); if (APR_SUCCESS == status) { m->aborted = 1; - h2_io_set_destroy_all(m->stream_ios); apr_thread_mutex_unlock(m->lock); } - workers_unregister(m); } - -h2_stream *h2_mplx_open_io(h2_mplx *m, int stream_id) -{ - h2_stream *stream = NULL; - apr_status_t status; - h2_io *io; - - if (m->aborted) { - return NULL; - } - status = apr_thread_mutex_lock(m->lock); - if (APR_SUCCESS == status) { - apr_pool_t *stream_pool = m->spare_pool; - - if (!stream_pool) { - apr_pool_create(&stream_pool, m->pool); - } - else { - m->spare_pool = NULL; - } - - stream = h2_stream_create(stream_id, stream_pool, m); - stream->state = H2_STREAM_ST_OPEN; - - io = h2_io_set_get(m->stream_ios, stream_id); - if (!io) { - io = h2_io_create(stream_id, stream_pool, m->bucket_alloc); - h2_io_set_add(m->stream_ios, io); - } - status = io? APR_SUCCESS : APR_ENOMEM; - apr_thread_mutex_unlock(m->lock); - } - return stream; -} - -static void stream_destroy(h2_mplx *m, h2_stream *stream, h2_io *io) -{ - apr_pool_t *pool = h2_stream_detach_pool(stream); - if (pool) { - apr_pool_clear(pool); - if (m->spare_pool) { - apr_pool_destroy(m->spare_pool); - } - m->spare_pool = pool; - } - h2_stream_destroy(stream); - if (io) { - /* The pool is cleared/destroyed which also closes all - * allocated file handles. Give this count back to our - * file handle pool. */ - m->file_handles_allowed += io->files_handles_owned; - h2_io_set_remove(m->stream_ios, io); - h2_io_set_remove(m->ready_ios, io); - h2_io_destroy(io); - } -} - -apr_status_t h2_mplx_cleanup_stream(h2_mplx *m, h2_stream *stream) +apr_status_t h2_mplx_stream_done(h2_mplx *m, int stream_id, int rst_error) { apr_status_t status; + AP_DEBUG_ASSERT(m); status = apr_thread_mutex_lock(m->lock); if (APR_SUCCESS == status) { - h2_io *io = h2_io_set_get(m->stream_ios, stream->id); - if (!io || io->task_done) { - /* No more io or task already done -> cleanup immediately */ - stream_destroy(m, stream, io); - } - else { - /* Add stream to closed set for cleanup when task is done */ - h2_stream_set_add(m->closed, stream); + h2_io *io = h2_io_set_get(m->stream_ios, stream_id); + + /* there should be an h2_io, once the stream has been scheduled + * for processing, e.g. when we received all HEADERs. But when + * a stream is cancelled very early, it will not exist. */ + if (io) { + io_stream_done(m, io, rst_error); } + apr_thread_mutex_unlock(m->lock); } return status; @@ -310,21 +322,17 @@ void h2_mplx_task_done(h2_mplx *m, int stream_id) { apr_status_t status = apr_thread_mutex_lock(m->lock); if (APR_SUCCESS == status) { - h2_stream *stream = h2_stream_set_get(m->closed, stream_id); h2_io *io = h2_io_set_get(m->stream_ios, stream_id); ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, "h2_mplx(%ld): task(%d) done", m->id, stream_id); - if (stream) { - /* stream was already closed by main connection and is in - * zombie state. Now that the task is done with it, we - * can free its resources. */ - h2_stream_set_remove(m->closed, stream); - stream_destroy(m, stream, io); - } - else if (io) { - /* main connection has not finished stream. Mark task as done - * so that eventual cleanup can start immediately. */ + if (io) { io->task_done = 1; + if (io->orphaned) { + io_destroy(m, io, 0); + } + else { + /* hang around until the stream deregisteres */ + } } apr_thread_mutex_unlock(m->lock); } @@ -342,15 +350,17 @@ apr_status_t h2_mplx_in_read(h2_mplx *m, apr_read_type_e block, status = apr_thread_mutex_lock(m->lock); if (APR_SUCCESS == status) { h2_io *io = h2_io_set_get(m->stream_ios, stream_id); - if (io) { + if (io && !io->orphaned) { io->input_arrived = iowait; - status = h2_io_in_read(io, bb, 0); - while (status == APR_EAGAIN + H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_read_pre"); + status = h2_io_in_read(io, bb, -1); + while (APR_STATUS_IS_EAGAIN(status) && !is_aborted(m, &status) && block == APR_BLOCK_READ) { apr_thread_cond_wait(io->input_arrived, m->lock); - status = h2_io_in_read(io, bb, 0); + status = h2_io_in_read(io, bb, -1); } + H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_read_post"); io->input_arrived = NULL; } else { @@ -372,11 +382,14 @@ apr_status_t h2_mplx_in_write(h2_mplx *m, int stream_id, status = apr_thread_mutex_lock(m->lock); if (APR_SUCCESS == status) { h2_io *io = h2_io_set_get(m->stream_ios, stream_id); - if (io) { + if (io && !io->orphaned) { + H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_write_pre"); status = h2_io_in_write(io, bb); + H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_write_post"); if (io->input_arrived) { apr_thread_cond_signal(io->input_arrived); } + io_process_events(m, io); } else { status = APR_EOF; @@ -396,11 +409,13 @@ apr_status_t h2_mplx_in_close(h2_mplx *m, int stream_id) status = apr_thread_mutex_lock(m->lock); if (APR_SUCCESS == status) { h2_io *io = h2_io_set_get(m->stream_ios, stream_id); - if (io) { + if (io && !io->orphaned) { status = h2_io_in_close(io); + H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_close"); if (io->input_arrived) { apr_thread_cond_signal(io->input_arrived); } + io_process_events(m, io); } else { status = APR_ECONNABORTED; @@ -411,24 +426,26 @@ apr_status_t h2_mplx_in_close(h2_mplx *m, int stream_id) } typedef struct { - h2_mplx_consumed_cb *cb; - void *cb_ctx; + h2_mplx * m; int streams_updated; } update_ctx; static int update_window(void *ctx, h2_io *io) { - if (io->input_consumed) { - update_ctx *uctx = (update_ctx*)ctx; - uctx->cb(uctx->cb_ctx, io->id, io->input_consumed); - io->input_consumed = 0; + update_ctx *uctx = (update_ctx*)ctx; + if (io_process_events(uctx->m, io)) { ++uctx->streams_updated; } return 1; } -apr_status_t h2_mplx_in_update_windows(h2_mplx *m, - h2_mplx_consumed_cb *cb, void *cb_ctx) +void h2_mplx_set_consumed_cb(h2_mplx *m, h2_mplx_consumed_cb *cb, void *ctx) +{ + m->input_consumed = cb; + m->input_consumed_ctx = ctx; +} + +apr_status_t h2_mplx_in_update_windows(h2_mplx *m) { apr_status_t status; AP_DEBUG_ASSERT(m); @@ -439,8 +456,7 @@ apr_status_t h2_mplx_in_update_windows(h2_mplx *m, if (APR_SUCCESS == status) { update_ctx ctx; - ctx.cb = cb; - ctx.cb_ctx = cb_ctx; + ctx.m = m; ctx.streams_updated = 0; status = APR_EAGAIN; @@ -456,7 +472,8 @@ apr_status_t h2_mplx_in_update_windows(h2_mplx *m, apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id, h2_io_data_cb *cb, void *ctx, - apr_size_t *plen, int *peos) + apr_off_t *plen, int *peos, + apr_table_t **ptrailers) { apr_status_t status; AP_DEBUG_ASSERT(m); @@ -466,8 +483,44 @@ apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id, status = apr_thread_mutex_lock(m->lock); if (APR_SUCCESS == status) { h2_io *io = h2_io_set_get(m->stream_ios, stream_id); - if (io) { + if (io && !io->orphaned) { + H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_readx_pre"); + status = h2_io_out_readx(io, cb, ctx, plen, peos); + H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_readx_post"); + if (status == APR_SUCCESS && cb && io->output_drained) { + apr_thread_cond_signal(io->output_drained); + } + } + else { + status = APR_ECONNABORTED; + } + + *ptrailers = (*peos && io->response)? io->response->trailers : NULL; + apr_thread_mutex_unlock(m->lock); + } + return status; +} + +apr_status_t h2_mplx_out_read_to(h2_mplx *m, int stream_id, + apr_bucket_brigade *bb, + apr_off_t *plen, int *peos, + apr_table_t **ptrailers) +{ + apr_status_t status; + AP_DEBUG_ASSERT(m); + if (m->aborted) { + return APR_ECONNABORTED; + } + status = apr_thread_mutex_lock(m->lock); + if (APR_SUCCESS == status) { + h2_io *io = h2_io_set_get(m->stream_ios, stream_id); + if (io && !io->orphaned) { + H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_read_to_pre"); + + status = h2_io_out_read_to(io, bb, plen, peos); + + H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_read_to_post"); if (status == APR_SUCCESS && io->output_drained) { apr_thread_cond_signal(io->output_drained); } @@ -475,6 +528,7 @@ apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id, else { status = APR_ECONNABORTED; } + *ptrailers = (*peos && io->response)? io->response->trailers : NULL; apr_thread_mutex_unlock(m->lock); } return status; @@ -490,24 +544,46 @@ h2_stream *h2_mplx_next_submit(h2_mplx *m, h2_stream_set *streams) } status = apr_thread_mutex_lock(m->lock); if (APR_SUCCESS == status) { - h2_io *io = h2_io_set_get_highest_prio(m->ready_ios); + h2_io *io = h2_io_set_pop_highest_prio(m->ready_ios); if (io) { - h2_response *response = io->response; - - AP_DEBUG_ASSERT(response); - h2_io_set_remove(m->ready_ios, io); - - stream = h2_stream_set_get(streams, response->stream_id); + stream = h2_stream_set_get(streams, io->id); if (stream) { - h2_stream_set_response(stream, response, io->bbout); - if (io->output_drained) { - apr_thread_cond_signal(io->output_drained); + if (io->rst_error) { + h2_stream_rst(stream, io->rst_error); } + else { + AP_DEBUG_ASSERT(io->response); + H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_next_submit_pre"); + h2_stream_set_response(stream, io->response, io->bbout); + H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_next_submit_post"); + } + } else { - ap_log_cerror(APLOG_MARK, APLOG_WARNING, APR_NOTFOUND, m->c, - APLOGNO(02953) "h2_mplx(%ld): stream for response %d", - m->id, response->stream_id); + /* We have the io ready, but the stream has gone away, maybe + * reset by the client. Should no longer happen since such + * streams should clear io's from the ready queue. + */ + ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, m->c, APLOGNO(02953) + "h2_mplx(%ld): stream for response %d closed, " + "resetting io to close request processing", + m->id, io->id); + io->orphaned = 1; + if (io->task_done) { + io_destroy(m, io, 1); + } + else { + /* hang around until the h2_task is done, but + * shutdown input and send out any events (e.g. window + * updates) asap. */ + h2_io_in_shutdown(io); + h2_io_rst(io, H2_ERR_STREAM_CLOSED); + io_process_events(m, io); + } + } + + if (io->output_drained) { + apr_thread_cond_signal(io->output_drained); } } apr_thread_mutex_unlock(m->lock); @@ -517,6 +593,7 @@ h2_stream *h2_mplx_next_submit(h2_mplx *m, h2_stream_set *streams) static apr_status_t out_write(h2_mplx *m, h2_io *io, ap_filter_t* f, apr_bucket_brigade *bb, + apr_table_t *trailers, struct apr_thread_cond_t *iowait) { apr_status_t status = APR_SUCCESS; @@ -529,15 +606,15 @@ static apr_status_t out_write(h2_mplx *m, h2_io *io, && (status == APR_SUCCESS) && !is_aborted(m, &status)) { - status = h2_io_out_write(io, bb, m->stream_max_mem, + status = h2_io_out_write(io, bb, m->stream_max_mem, trailers, &m->file_handles_allowed); - /* Wait for data to drain until there is room again */ while (!APR_BRIGADE_EMPTY(bb) && iowait && status == APR_SUCCESS && (m->stream_max_mem <= h2_io_out_length(io)) && !is_aborted(m, &status)) { + trailers = NULL; io->output_drained = iowait; if (f) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, @@ -549,6 +626,7 @@ static apr_status_t out_write(h2_mplx *m, h2_io *io, } } apr_brigade_cleanup(bb); + return status; } @@ -559,18 +637,18 @@ static apr_status_t out_open(h2_mplx *m, int stream_id, h2_response *response, apr_status_t status = APR_SUCCESS; h2_io *io = h2_io_set_get(m->stream_ios, stream_id); - if (io) { + if (io && !io->orphaned) { if (f) { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, - "h2_mplx(%ld-%d): open response: %s", - m->id, stream_id, response->status); + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, + "h2_mplx(%ld-%d): open response: %d, rst=%d", + m->id, stream_id, response->http_status, + response->rst_error); } - io->response = h2_response_copy(io->pool, response); - AP_DEBUG_ASSERT(io->response); + h2_io_set_response(io, response); h2_io_set_add(m->ready_ios, io); if (bb) { - status = out_write(m, io, f, bb, iowait); + status = out_write(m, io, f, bb, response->trailers, iowait); } have_out_data_for(m, stream_id); } @@ -592,6 +670,9 @@ apr_status_t h2_mplx_out_open(h2_mplx *m, int stream_id, h2_response *response, status = apr_thread_mutex_lock(m->lock); if (APR_SUCCESS == status) { status = out_open(m, stream_id, response, f, bb, iowait); + if (APLOGctrace1(m->c)) { + h2_util_bb_log(m->c, stream_id, APLOG_TRACE1, "h2_mplx_out_open", bb); + } if (m->aborted) { return APR_ECONNABORTED; } @@ -603,6 +684,7 @@ apr_status_t h2_mplx_out_open(h2_mplx *m, int stream_id, h2_response *response, apr_status_t h2_mplx_out_write(h2_mplx *m, int stream_id, ap_filter_t* f, apr_bucket_brigade *bb, + apr_table_t *trailers, struct apr_thread_cond_t *iowait) { apr_status_t status; @@ -614,8 +696,13 @@ apr_status_t h2_mplx_out_write(h2_mplx *m, int stream_id, if (APR_SUCCESS == status) { if (!m->aborted) { h2_io *io = h2_io_set_get(m->stream_ios, stream_id); - if (io) { - status = out_write(m, io, f, bb, iowait); + if (io && !io->orphaned) { + status = out_write(m, io, f, bb, trailers, iowait); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, m->c, + "h2_mplx(%ld-%d): write with trailers=%s", + m->id, io->id, trailers? "yes" : "no"); + H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_write"); + have_out_data_for(m, stream_id); if (m->aborted) { return APR_ECONNABORTED; @@ -633,7 +720,7 @@ apr_status_t h2_mplx_out_write(h2_mplx *m, int stream_id, return status; } -apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id) +apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id, apr_table_t *trailers) { apr_status_t status; AP_DEBUG_ASSERT(m); @@ -644,17 +731,25 @@ apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id) if (APR_SUCCESS == status) { if (!m->aborted) { h2_io *io = h2_io_set_get(m->stream_ios, stream_id); - if (io) { - if (!io->response || !io->response->ngheader) { + if (io && !io->orphaned) { + if (!io->response && !io->rst_error) { /* In case a close comes before a response was created, * insert an error one so that our streams can properly * reset. */ - h2_response *r = h2_response_create(stream_id, - "500", NULL, m->pool); + h2_response *r = h2_response_die(stream_id, APR_EGENERAL, + io->request, m->pool); status = out_open(m, stream_id, r, NULL, NULL, NULL); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, m->c, + "h2_mplx(%ld-%d): close, no response, no rst", + m->id, io->id); } - status = h2_io_out_close(io); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, m->c, + "h2_mplx(%ld-%d): close with trailers=%s", + m->id, io->id, trailers? "yes" : "no"); + status = h2_io_out_close(io, trailers); + H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_close"); + have_out_data_for(m, stream_id); if (m->aborted) { /* if we were the last output, the whole session might @@ -672,6 +767,38 @@ apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id) return status; } +apr_status_t h2_mplx_out_rst(h2_mplx *m, int stream_id, int error) +{ + apr_status_t status; + AP_DEBUG_ASSERT(m); + if (m->aborted) { + return APR_ECONNABORTED; + } + status = apr_thread_mutex_lock(m->lock); + if (APR_SUCCESS == status) { + if (!m->aborted) { + h2_io *io = h2_io_set_get(m->stream_ios, stream_id); + if (io && !io->rst_error && !io->orphaned) { + h2_io_rst(io, error); + if (!io->response) { + h2_io_set_add(m->ready_ios, io); + } + H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_rst"); + + have_out_data_for(m, stream_id); + if (io->output_drained) { + apr_thread_cond_signal(io->output_drained); + } + } + else { + status = APR_ECONNABORTED; + } + } + apr_thread_mutex_unlock(m->lock); + } + return status; +} + int h2_mplx_in_has_eos_for(h2_mplx *m, int stream_id) { int has_eos = 0; @@ -684,7 +811,7 @@ int h2_mplx_in_has_eos_for(h2_mplx *m, int stream_id) if (APR_SUCCESS == status) { h2_io *io = h2_io_set_get(m->stream_ios, stream_id); if (io) { - has_eos = h2_io_in_has_eos_for(io); + has_eos = io->orphaned || h2_io_in_has_eos_for(io); } apr_thread_mutex_unlock(m->lock); } @@ -742,61 +869,104 @@ static void have_out_data_for(h2_mplx *m, int stream_id) } } -apr_status_t h2_mplx_do_task(h2_mplx *m, struct h2_task *task) +apr_status_t h2_mplx_reprioritize(h2_mplx *m, h2_stream_pri_cmp *cmp, void *ctx) { apr_status_t status; + AP_DEBUG_ASSERT(m); if (m->aborted) { return APR_ECONNABORTED; } status = apr_thread_mutex_lock(m->lock); if (APR_SUCCESS == status) { - /* TODO: needs to sort queue by priority */ + h2_tq_sort(m->q, cmp, ctx); + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c, - "h2_mplx: do task(%s)", task->id); - h2_tq_append(m->q, task); + "h2_mplx(%ld): reprioritize tasks", m->id); apr_thread_mutex_unlock(m->lock); } workers_register(m); return status; } -h2_task *h2_mplx_pop_task(h2_mplx *m, int *has_more) +static h2_io *open_io(h2_mplx *m, int stream_id) +{ + apr_pool_t *io_pool = m->spare_pool; + h2_io *io; + + if (!io_pool) { + apr_pool_create(&io_pool, m->pool); + } + else { + m->spare_pool = NULL; + } + + io = h2_io_create(stream_id, io_pool, m->bucket_alloc); + h2_io_set_add(m->stream_ios, io); + + return io; +} + + +apr_status_t h2_mplx_process(h2_mplx *m, int stream_id, + const h2_request *req, int eos, + h2_stream_pri_cmp *cmp, void *ctx) { - h2_task *task = NULL; apr_status_t status; + AP_DEBUG_ASSERT(m); if (m->aborted) { - *has_more = 0; - return NULL; + return APR_ECONNABORTED; } status = apr_thread_mutex_lock(m->lock); if (APR_SUCCESS == status) { - task = h2_tq_pop_first(m->q); - if (task) { - h2_task_set_started(task); + h2_io *io = open_io(m, stream_id); + io->request = req; + io->request_body = !eos; + + if (eos) { + status = h2_io_in_close(io); } - *has_more = !h2_tq_empty(m->q); + + h2_tq_add(m->q, io->id, cmp, ctx); + + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, m->c, + "h2_mplx(%ld-%d): process", m->c->id, stream_id); + H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_process"); apr_thread_mutex_unlock(m->lock); } - return task; + + if (status == APR_SUCCESS) { + workers_register(m); + } + return status; } -apr_status_t h2_mplx_create_task(h2_mplx *m, struct h2_stream *stream) +h2_task *h2_mplx_pop_task(h2_mplx *m, h2_worker *w, int *has_more) { + h2_task *task = NULL; apr_status_t status; + AP_DEBUG_ASSERT(m); if (m->aborted) { - return APR_ECONNABORTED; + *has_more = 0; + return NULL; } status = apr_thread_mutex_lock(m->lock); if (APR_SUCCESS == status) { - conn_rec *c = h2_conn_create(m->c, stream->pool); - stream->task = h2_task_create(m->id, stream->id, - stream->pool, m, c); - + int sid; + while (!task && (sid = h2_tq_shift(m->q)) > 0) { + /* Anything not already setup correctly in the task + * needs to be so now, as task will be executed right about + * when this method returns. */ + h2_io *io = h2_io_set_get(m->stream_ios, sid); + if (io) { + task = h2_worker_create_task(w, m, io->request, !io->request_body); + } + } + *has_more = !h2_tq_empty(m->q); apr_thread_mutex_unlock(m->lock); } - return status; + return task; } diff --git a/modules/http2/h2_mplx.h b/modules/http2/h2_mplx.h index 62977d61..f2805be3 100644 --- a/modules/http2/h2_mplx.h +++ b/modules/http2/h2_mplx.h @@ -41,8 +41,10 @@ struct h2_config; struct h2_response; struct h2_task; struct h2_stream; +struct h2_request; struct h2_io_set; struct apr_thread_cond_t; +struct h2_worker; struct h2_workers; struct h2_stream_set; struct h2_task_queue; @@ -51,10 +53,16 @@ struct h2_task_queue; typedef struct h2_mplx h2_mplx; +/** + * Callback invoked for every stream that had input data read since + * the last invocation. + */ +typedef void h2_mplx_consumed_cb(void *ctx, int stream_id, apr_off_t consumed); + struct h2_mplx { long id; APR_RING_ENTRY(h2_mplx) link; - volatile apr_uint32_t refs; + volatile int refs; conn_rec *c; apr_pool_t *pool; apr_bucket_alloc_t *bucket_alloc; @@ -70,12 +78,16 @@ struct h2_mplx { int aborted; apr_size_t stream_max_mem; - apr_pool_t *spare_pool; /* spare pool, ready for next stream */ - struct h2_stream_set *closed; /* streams closed, but task ongoing */ + apr_pool_t *spare_pool; /* spare pool, ready for next io */ struct h2_workers *workers; int file_handles_allowed; + + h2_mplx_consumed_cb *input_consumed; + void *input_consumed_ctx; }; + + /******************************************************************************* * Object lifecycle and information. ******************************************************************************/ @@ -85,6 +97,7 @@ struct h2_mplx { * Implicitly has reference count 1. */ h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *master, + const struct h2_config *conf, struct h2_workers *workers); /** @@ -96,6 +109,7 @@ void h2_mplx_reference(h2_mplx *m); * Decreases the reference counter of this mplx. */ void h2_mplx_release(h2_mplx *m); + /** * Decreases the reference counter of this mplx and waits for it * to reached 0, destroy the mplx afterwards. @@ -117,15 +131,16 @@ void h2_mplx_task_done(h2_mplx *m, int stream_id); /******************************************************************************* * IO lifetime of streams. ******************************************************************************/ -/** - * Prepares the multiplexer to handle in-/output on the given stream id. - */ -struct h2_stream *h2_mplx_open_io(h2_mplx *mplx, int stream_id); /** - * Ends cleanup of a stream in sync with execution thread. + * Notifies mplx that a stream has finished processing. + * + * @param m the mplx itself + * @param stream_id the id of the stream being done + * @param rst_error if != 0, the stream was reset with the error given + * */ -apr_status_t h2_mplx_cleanup_stream(h2_mplx *m, struct h2_stream *stream); +apr_status_t h2_mplx_stream_done(h2_mplx *m, int stream_id, int rst_error); /* Return != 0 iff the multiplexer has data for the given stream. */ @@ -143,13 +158,40 @@ apr_status_t h2_mplx_out_trywait(h2_mplx *m, apr_interval_time_t timeout, ******************************************************************************/ /** - * Perform the task on the given stream. + * Process a stream request. + * + * @param m the multiplexer + * @param stream_id the identifier of the stream + * @param r the request to be processed + * @param eos if input is complete + * @param cmp the stream priority compare function + * @param ctx context data for the compare function + */ +apr_status_t h2_mplx_process(h2_mplx *m, int stream_id, + const struct h2_request *r, int eos, + h2_stream_pri_cmp *cmp, void *ctx); + +/** + * Stream priorities have changed, reschedule pending tasks. + * + * @param m the multiplexer + * @param cmp the stream priority compare function + * @param ctx context data for the compare function */ -apr_status_t h2_mplx_do_task(h2_mplx *mplx, struct h2_task *task); +apr_status_t h2_mplx_reprioritize(h2_mplx *m, h2_stream_pri_cmp *cmp, void *ctx); -struct h2_task *h2_mplx_pop_task(h2_mplx *mplx, int *has_more); +struct h2_task *h2_mplx_pop_task(h2_mplx *mplx, struct h2_worker *w, int *has_more); -apr_status_t h2_mplx_create_task(h2_mplx *mplx, struct h2_stream *stream); +/** + * Register a callback for the amount of input data consumed per stream. The + * will only ever be invoked from the thread creating this h2_mplx, e.g. when + * calls from that thread into this h2_mplx are made. + * + * @param m the multiplexer to register the callback at + * @param cb the function to invoke + * @param ctx user supplied argument to invocation. + */ +void h2_mplx_set_consumed_cb(h2_mplx *m, h2_mplx_consumed_cb *cb, void *ctx); /******************************************************************************* * Input handling of streams. @@ -185,20 +227,15 @@ apr_status_t h2_mplx_in_close(h2_mplx *m, int stream_id); int h2_mplx_in_has_eos_for(h2_mplx *m, int stream_id); /** - * Callback invoked for every stream that had input data read since - * the last invocation. - */ -typedef void h2_mplx_consumed_cb(void *ctx, int stream_id, apr_size_t consumed); - -/** - * Invoke the callback for all streams that had bytes read since the last - * call to this function. If no stream had input data consumed, the callback - * is not invoked. + * Invoke the consumed callback for all streams that had bytes read since the + * last call to this function. If no stream had input data consumed, the + * callback is not invoked. + * The consumed callback may also be invoked at other times whenever + * the need arises. * Returns APR_SUCCESS when an update happened, APR_EAGAIN if no update * happened. */ -apr_status_t h2_mplx_in_update_windows(h2_mplx *m, - h2_mplx_consumed_cb *cb, void *ctx); +apr_status_t h2_mplx_in_update_windows(h2_mplx *m); /******************************************************************************* * Output handling of streams. @@ -219,7 +256,17 @@ struct h2_stream *h2_mplx_next_submit(h2_mplx *m, */ apr_status_t h2_mplx_out_readx(h2_mplx *mplx, int stream_id, h2_io_data_cb *cb, void *ctx, - apr_size_t *plen, int *peos); + apr_off_t *plen, int *peos, + apr_table_t **ptrailers); + +/** + * Reads output data into the given brigade. Will never block, but + * return APR_EAGAIN until data arrives or the stream is closed. + */ +apr_status_t h2_mplx_out_read_to(h2_mplx *mplx, int stream_id, + apr_bucket_brigade *bb, + apr_off_t *plen, int *peos, + apr_table_t **ptrailers); /** * Opens the output for the given stream with the specified response. @@ -235,17 +282,21 @@ apr_status_t h2_mplx_out_open(h2_mplx *mplx, int stream_id, * @param stream_id the stream identifier * @param filter the apache filter context of the data * @param bb the bucket brigade to append + * @param trailers optional trailers for response, maybe NULL * @param iowait a conditional used for block/signalling in h2_mplx */ apr_status_t h2_mplx_out_write(h2_mplx *mplx, int stream_id, ap_filter_t* filter, apr_bucket_brigade *bb, + apr_table_t *trailers, struct apr_thread_cond_t *iowait); /** - * Closes the output stream. Readers of this stream will get all pending - * data and then only APR_EOF as result. + * Closes the output for stream stream_id. Optionally forwards trailers + * fromt the processed stream. */ -apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id); +apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id, apr_table_t *trailers); + +apr_status_t h2_mplx_out_rst(h2_mplx *m, int stream_id, int error); /******************************************************************************* * h2_mplx list Manipulation. diff --git a/modules/http2/h2_push.c b/modules/http2/h2_push.c new file mode 100644 index 00000000..b80ffb90 --- /dev/null +++ b/modules/http2/h2_push.c @@ -0,0 +1,395 @@ +/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include +#include +#include + +#include "h2_private.h" +#include "h2_h2.h" +#include "h2_util.h" +#include "h2_push.h" +#include "h2_request.h" +#include "h2_response.h" + + +typedef struct { + const h2_request *req; + apr_pool_t *pool; + apr_array_header_t *pushes; + const char *s; + size_t slen; + size_t i; + + const char *link; + apr_table_t *params; + char b[4096]; +} link_ctx; + +static int attr_char(char c) +{ + switch (c) { + case '!': + case '#': + case '$': + case '&': + case '+': + case '-': + case '.': + case '^': + case '_': + case '`': + case '|': + case '~': + return 1; + default: + return apr_isalnum(c); + } +} + +static int ptoken_char(char c) +{ + switch (c) { + case '!': + case '#': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case '-': + case '.': + case '/': + case ':': + case '<': + case '=': + case '>': + case '?': + case '@': + case '[': + case ']': + case '^': + case '_': + case '`': + case '{': + case '|': + case '}': + case '~': + return 1; + default: + return apr_isalnum(c); + } +} + +static int skip_ws(link_ctx *ctx) +{ + char c; + while (ctx->i < ctx->slen + && (((c = ctx->s[ctx->i]) == ' ') || (c == '\t'))) { + ++ctx->i; + } + return (ctx->i < ctx->slen); +} + +static int find_chr(link_ctx *ctx, char c, size_t *pidx) +{ + size_t j; + for (j = ctx->i; j < ctx->slen; ++j) { + if (ctx->s[j] == c) { + *pidx = j; + return 1; + } + } + return 0; +} + +static int read_chr(link_ctx *ctx, char c) +{ + if (ctx->i < ctx->slen && ctx->s[ctx->i] == c) { + ++ctx->i; + return 1; + } + return 0; +} + +static char *mk_str(link_ctx *ctx, size_t end) +{ + if (ctx->i < end) { + return apr_pstrndup(ctx->pool, ctx->s + ctx->i, end - ctx->i); + } + return ""; +} + +static int read_qstring(link_ctx *ctx, char **ps) +{ + if (skip_ws(ctx) && read_chr(ctx, '\"')) { + size_t end; + if (find_chr(ctx, '\"', &end)) { + *ps = mk_str(ctx, end); + ctx->i = end + 1; + return 1; + } + } + return 0; +} + +static int read_ptoken(link_ctx *ctx, char **ps) +{ + if (skip_ws(ctx)) { + size_t i; + for (i = ctx->i; i < ctx->slen && ptoken_char(ctx->s[i]); ++i) { + /* nop */ + } + if (i > ctx->i) { + *ps = mk_str(ctx, i); + ctx->i = i; + return 1; + } + } + return 0; +} + + +static int read_link(link_ctx *ctx) +{ + if (skip_ws(ctx) && read_chr(ctx, '<')) { + size_t end; + if (find_chr(ctx, '>', &end)) { + ctx->link = mk_str(ctx, end); + ctx->i = end + 1; + return 1; + } + } + return 0; +} + +static int read_pname(link_ctx *ctx, char **pname) +{ + if (skip_ws(ctx)) { + size_t i; + for (i = ctx->i; i < ctx->slen && attr_char(ctx->s[i]); ++i) { + /* nop */ + } + if (i > ctx->i) { + *pname = mk_str(ctx, i); + ctx->i = i; + return 1; + } + } + return 0; +} + +static int read_pvalue(link_ctx *ctx, char **pvalue) +{ + if (skip_ws(ctx) && read_chr(ctx, '=')) { + if (read_qstring(ctx, pvalue) || read_ptoken(ctx, pvalue)) { + return 1; + } + } + return 0; +} + +static int read_param(link_ctx *ctx) +{ + if (skip_ws(ctx) && read_chr(ctx, ';')) { + char *name, *value = ""; + if (read_pname(ctx, &name)) { + read_pvalue(ctx, &value); /* value is optional */ + apr_table_setn(ctx->params, name, value); + return 1; + } + } + return 0; +} + +static int read_sep(link_ctx *ctx) +{ + if (skip_ws(ctx) && read_chr(ctx, ',')) { + return 1; + } + return 0; +} + +static void init_params(link_ctx *ctx) +{ + if (!ctx->params) { + ctx->params = apr_table_make(ctx->pool, 5); + } + else { + apr_table_clear(ctx->params); + } +} + +static int same_authority(const h2_request *req, const apr_uri_t *uri) +{ + if (uri->scheme != NULL && strcmp(uri->scheme, req->scheme)) { + return 0; + } + if (uri->hostinfo != NULL && strcmp(uri->hostinfo, req->authority)) { + return 0; + } + return 1; +} + +static int set_header(void *ctx, const char *key, const char *value) +{ + apr_table_setn(ctx, key, value); + return 1; +} + + +static int add_push(link_ctx *ctx) +{ + /* so, we have read a Link header and need to decide + * if we transform it into a push. + */ + const char *rel = apr_table_get(ctx->params, "rel"); + if (rel && !strcmp("preload", rel)) { + apr_uri_t uri; + if (apr_uri_parse(ctx->pool, ctx->link, &uri) == APR_SUCCESS) { + if (uri.path && same_authority(ctx->req, &uri)) { + char *path; + apr_table_t *headers; + h2_request *req; + h2_push *push; + + /* We only want to generate pushes for resources in the + * same authority than the original request. + * icing: i think that is wise, otherwise we really need to + * check that the vhost/server is available and uses the same + * TLS (if any) parameters. + */ + path = apr_uri_unparse(ctx->pool, &uri, APR_URI_UNP_OMITSITEPART); + + push = apr_pcalloc(ctx->pool, sizeof(*push)); + + headers = apr_table_make(ctx->pool, 5); + apr_table_do(set_header, headers, ctx->req->headers, + "User-Agent", + "Cache-Control", + "Accept-Language", + NULL); + req = h2_request_createn(0, ctx->pool, ctx->req->config, + "GET", ctx->req->scheme, + ctx->req->authority, + path, headers); + h2_request_end_headers(req, ctx->pool, 1); + push->req = req; + + if (!ctx->pushes) { + ctx->pushes = apr_array_make(ctx->pool, 5, sizeof(h2_push*)); + } + APR_ARRAY_PUSH(ctx->pushes, h2_push*) = push; + } + } + } + return 0; +} + +static void inspect_link(link_ctx *ctx, const char *s, size_t slen) +{ + /* RFC 5988 + Link = "Link" ":" #link-value + link-value = "<" URI-Reference ">" *( ";" link-param ) + link-param = ( ( "rel" "=" relation-types ) + | ( "anchor" "=" <"> URI-Reference <"> ) + | ( "rev" "=" relation-types ) + | ( "hreflang" "=" Language-Tag ) + | ( "media" "=" ( MediaDesc | ( <"> MediaDesc <"> ) ) ) + | ( "title" "=" quoted-string ) + | ( "title*" "=" ext-value ) + | ( "type" "=" ( media-type | quoted-mt ) ) + | ( link-extension ) ) + link-extension = ( parmname [ "=" ( ptoken | quoted-string ) ] ) + | ( ext-name-star "=" ext-value ) + ext-name-star = parmname "*" ; reserved for RFC2231-profiled + ; extensions. Whitespace NOT + ; allowed in between. + ptoken = 1*ptokenchar + ptokenchar = "!" | "#" | "$" | "%" | "&" | "'" | "(" + | ")" | "*" | "+" | "-" | "." | "/" | DIGIT + | ":" | "<" | "=" | ">" | "?" | "@" | ALPHA + | "[" | "]" | "^" | "_" | "`" | "{" | "|" + | "}" | "~" + media-type = type-name "/" subtype-name + quoted-mt = <"> media-type <"> + relation-types = relation-type + | <"> relation-type *( 1*SP relation-type ) <"> + relation-type = reg-rel-type | ext-rel-type + reg-rel-type = LOALPHA *( LOALPHA | DIGIT | "." | "-" ) + ext-rel-type = URI + + and from + parmname = 1*attr-char + attr-char = ALPHA / DIGIT + / "!" / "#" / "$" / "&" / "+" / "-" / "." + / "^" / "_" / "`" / "|" / "~" + */ + + ctx->s = s; + ctx->slen = slen; + ctx->i = 0; + + while (read_link(ctx)) { + init_params(ctx); + while (read_param(ctx)) { + /* nop */ + } + add_push(ctx); + if (!read_sep(ctx)) { + break; + } + } +} + +static int head_iter(void *ctx, const char *key, const char *value) +{ + if (!apr_strnatcasecmp("link", key)) { + inspect_link(ctx, value, strlen(value)); + } + return 1; +} + +apr_array_header_t *h2_push_collect(apr_pool_t *p, const h2_request *req, + const h2_response *res) +{ + /* Collect push candidates from the request/response pair. + * + * One source for pushes are "rel=preload" link headers + * in the response. + * + * TODO: This may be extended in the future by hooks or callbacks + * where other modules can provide push information directly. + */ + if (res->headers) { + link_ctx ctx; + + memset(&ctx, 0, sizeof(ctx)); + ctx.req = req; + ctx.pool = p; + + apr_table_do(head_iter, &ctx, res->headers, NULL); + return ctx.pushes; + } + return NULL; +} diff --git a/modules/http2/h2_push.h b/modules/http2/h2_push.h new file mode 100644 index 00000000..871548ce --- /dev/null +++ b/modules/http2/h2_push.h @@ -0,0 +1,31 @@ +/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __mod_h2__h2_push__ +#define __mod_h2__h2_push__ + +struct h2_request; +struct h2_response; +struct h2_ngheader; + +typedef struct h2_push { + const struct h2_request *req; +} h2_push; + + +apr_array_header_t *h2_push_collect(apr_pool_t *p, + const struct h2_request *req, + const struct h2_response *res); + +#endif /* defined(__mod_h2__h2_push__) */ diff --git a/modules/http2/h2_request.c b/modules/http2/h2_request.c index ca9a362c..bf2d0c2e 100644 --- a/modules/http2/h2_request.c +++ b/modules/http2/h2_request.c @@ -19,68 +19,163 @@ #include #include -#include +#include +#include +#include #include +#include +#include +#include +#include +#include #include "h2_private.h" +#include "h2_config.h" #include "h2_mplx.h" -#include "h2_to_h1.h" #include "h2_request.h" #include "h2_task.h" #include "h2_util.h" -h2_request *h2_request_create(int id, apr_pool_t *pool, - apr_bucket_alloc_t *bucket_alloc) +h2_request *h2_request_create(int id, apr_pool_t *pool, + const struct h2_config *config) +{ + return h2_request_createn(id, pool, config, + NULL, NULL, NULL, NULL, NULL); +} + +h2_request *h2_request_createn(int id, apr_pool_t *pool, + const struct h2_config *config, + const char *method, const char *scheme, + const char *authority, const char *path, + apr_table_t *header) { h2_request *req = apr_pcalloc(pool, sizeof(h2_request)); - if (req) { - req->id = id; - req->pool = pool; - req->bucket_alloc = bucket_alloc; - } + + req->id = id; + req->config = config; + req->method = method; + req->scheme = scheme; + req->authority = authority; + req->path = path; + req->headers = header? header : apr_table_make(pool, 10); + req->request_time = apr_time_now(); + return req; } void h2_request_destroy(h2_request *req) { - if (req->to_h1) { - h2_to_h1_destroy(req->to_h1); - req->to_h1 = NULL; +} + +static apr_status_t inspect_clen(h2_request *req, const char *s) +{ + char *end; + req->content_length = apr_strtoi64(s, &end, 10); + return (s == end)? APR_EINVAL : APR_SUCCESS; +} + +static apr_status_t add_h1_header(h2_request *req, apr_pool_t *pool, + const char *name, size_t nlen, + const char *value, size_t vlen) +{ + char *hname, *hvalue; + + if (h2_req_ignore_header(name, nlen)) { + return APR_SUCCESS; + } + else if (H2_HD_MATCH_LIT("cookie", name, nlen)) { + const char *existing = apr_table_get(req->headers, "cookie"); + if (existing) { + char *nval; + + /* Cookie header come separately in HTTP/2, but need + * to be merged by "; " (instead of default ", ") + */ + hvalue = apr_pstrndup(pool, value, vlen); + nval = apr_psprintf(pool, "%s; %s", existing, hvalue); + apr_table_setn(req->headers, "Cookie", nval); + return APR_SUCCESS; + } + } + else if (H2_HD_MATCH_LIT("host", name, nlen)) { + if (apr_table_get(req->headers, "Host")) { + return APR_SUCCESS; /* ignore duplicate */ + } } + + hname = apr_pstrndup(pool, name, nlen); + hvalue = apr_pstrndup(pool, value, vlen); + h2_util_camel_case_header(hname, nlen); + apr_table_mergen(req->headers, hname, hvalue); + + return APR_SUCCESS; } -static apr_status_t insert_request_line(h2_request *req, h2_mplx *m); +typedef struct { + h2_request *req; + apr_pool_t *pool; +} h1_ctx; -apr_status_t h2_request_rwrite(h2_request *req, request_rec *r, h2_mplx *m) +static int set_h1_header(void *ctx, const char *key, const char *value) +{ + h1_ctx *x = ctx; + size_t klen = strlen(key); + if (!h2_req_ignore_header(key, klen)) { + add_h1_header(x->req, x->pool, key, klen, value, strlen(value)); + } + return 1; +} + +static apr_status_t add_all_h1_header(h2_request *req, apr_pool_t *pool, + apr_table_t *header) +{ + h1_ctx x; + x.req = req; + x.pool = pool; + apr_table_do(set_h1_header, &x, header, NULL); + return APR_SUCCESS; +} + + +apr_status_t h2_request_rwrite(h2_request *req, request_rec *r) { apr_status_t status; - req->method = r->method; + + req->config = h2_config_rget(r); + req->method = r->method; + req->scheme = (r->parsed_uri.scheme? r->parsed_uri.scheme + : ap_http_scheme(r)); req->authority = r->hostname; - req->path = r->uri; - if (!ap_strchr_c(req->authority, ':') && r->parsed_uri.port_str) { - req->authority = apr_psprintf(req->pool, "%s:%s", req->authority, - r->parsed_uri.port_str); + req->path = apr_uri_unparse(r->pool, &r->parsed_uri, + APR_URI_UNP_OMITSITEPART); + + if (!ap_strchr_c(req->authority, ':') && r->server && r->server->port) { + apr_port_t defport = apr_uri_port_of_scheme(req->scheme); + if (defport != r->server->port) { + /* port info missing and port is not default for scheme: append */ + req->authority = apr_psprintf(r->pool, "%s:%d", req->authority, + (int)r->server->port); + } } - req->scheme = NULL; - - status = insert_request_line(req, m); - if (status == APR_SUCCESS) { - status = h2_to_h1_add_headers(req->to_h1, r->headers_in); - } + AP_DEBUG_ASSERT(req->scheme); + AP_DEBUG_ASSERT(req->authority); + AP_DEBUG_ASSERT(req->path); + AP_DEBUG_ASSERT(req->method); + + status = add_all_h1_header(req, r->pool, r->headers_in); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, - "h2_request(%d): written request %s %s, host=%s", - req->id, req->method, req->path, req->authority); - + "h2_request(%d): rwrite %s host=%s://%s%s", + req->id, req->method, req->scheme, req->authority, req->path); + return status; } -apr_status_t h2_request_write_header(h2_request *req, - const char *name, size_t nlen, - const char *value, size_t vlen, - h2_mplx *m) +apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool, + const char *name, size_t nlen, + const char *value, size_t vlen) { apr_status_t status = APR_SUCCESS; @@ -90,8 +185,8 @@ apr_status_t h2_request_write_header(h2_request *req, if (name[0] == ':') { /* pseudo header, see ch. 8.1.2.3, always should come first */ - if (req->to_h1) { - ap_log_perror(APLOG_MARK, APLOG_ERR, 0, req->pool, + if (!apr_is_empty_table(req->headers)) { + ap_log_perror(APLOG_MARK, APLOG_ERR, 0, pool, APLOGNO(02917) "h2_request(%d): pseudo header after request start", req->id); @@ -100,25 +195,25 @@ apr_status_t h2_request_write_header(h2_request *req, if (H2_HEADER_METHOD_LEN == nlen && !strncmp(H2_HEADER_METHOD, name, nlen)) { - req->method = apr_pstrndup(req->pool, value, vlen); + req->method = apr_pstrndup(pool, value, vlen); } else if (H2_HEADER_SCHEME_LEN == nlen && !strncmp(H2_HEADER_SCHEME, name, nlen)) { - req->scheme = apr_pstrndup(req->pool, value, vlen); + req->scheme = apr_pstrndup(pool, value, vlen); } else if (H2_HEADER_PATH_LEN == nlen && !strncmp(H2_HEADER_PATH, name, nlen)) { - req->path = apr_pstrndup(req->pool, value, vlen); + req->path = apr_pstrndup(pool, value, vlen); } else if (H2_HEADER_AUTH_LEN == nlen && !strncmp(H2_HEADER_AUTH, name, nlen)) { - req->authority = apr_pstrndup(req->pool, value, vlen); + req->authority = apr_pstrndup(pool, value, vlen); } else { char buffer[32]; memset(buffer, 0, 32); strncpy(buffer, name, (nlen > 31)? 31 : nlen); - ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, req->pool, + ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, pool, APLOGNO(02954) "h2_request(%d): ignoring unknown pseudo header %s", req->id, buffer); @@ -126,57 +221,235 @@ apr_status_t h2_request_write_header(h2_request *req, } else { /* non-pseudo header, append to work bucket of stream */ - if (!req->to_h1) { - status = insert_request_line(req, m); - if (status != APR_SUCCESS) { - return status; - } - } - - if (status == APR_SUCCESS) { - status = h2_to_h1_add_header(req->to_h1, - name, nlen, value, vlen); - } + status = add_h1_header(req, pool, name, nlen, value, vlen); } return status; } -apr_status_t h2_request_write_data(h2_request *req, - const char *data, size_t len) +apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos) { - return h2_to_h1_add_data(req->to_h1, data, len); + const char *s; + + if (req->eoh) { + return APR_EINVAL; + } + + /* Always set the "Host" header from :authority, see rfc7540, ch. 8.1.2.3 */ + if (!req->authority) { + return APR_BADARG; + } + apr_table_setn(req->headers, "Host", req->authority); + + s = apr_table_get(req->headers, "Content-Length"); + if (s) { + if (inspect_clen(req, s) != APR_SUCCESS) { + ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, pool, + APLOGNO(02959) + "h2_request(%d): content-length value not parsed: %s", + req->id, s); + return APR_EINVAL; + } + } + else { + /* no content-length given */ + req->content_length = -1; + if (!eos) { + /* We have not seen a content-length and have no eos, + * simulate a chunked encoding for our HTTP/1.1 infrastructure, + * in case we have "H2SerializeHeaders on" here + */ + req->chunked = 1; + apr_table_mergen(req->headers, "Transfer-Encoding", "chunked"); + } + else if (apr_table_get(req->headers, "Content-Type")) { + /* If we have a content-type, but already see eos, no more + * data will come. Signal a zero content length explicitly. + */ + apr_table_setn(req->headers, "Content-Length", "0"); + } + } + + req->eoh = 1; + + /* In the presence of trailers, force behaviour of chunked encoding */ + s = apr_table_get(req->headers, "Trailer"); + if (s && s[0]) { + req->trailers = apr_table_make(pool, 5); + if (!req->chunked) { + req->chunked = 1; + apr_table_mergen(req->headers, "Transfer-Encoding", "chunked"); + } + } + + return APR_SUCCESS; } -apr_status_t h2_request_end_headers(h2_request *req, struct h2_mplx *m, - h2_task *task, int eos) +static apr_status_t add_h1_trailer(h2_request *req, apr_pool_t *pool, + const char *name, size_t nlen, + const char *value, size_t vlen) { - if (!req->to_h1) { - apr_status_t status = insert_request_line(req, m); - if (status != APR_SUCCESS) { - return status; - } + char *hname, *hvalue; + + if (h2_req_ignore_trailer(name, nlen)) { + return APR_SUCCESS; } - return h2_to_h1_end_headers(req->to_h1, task, eos); + + hname = apr_pstrndup(pool, name, nlen); + hvalue = apr_pstrndup(pool, value, vlen); + h2_util_camel_case_header(hname, nlen); + + apr_table_mergen(req->trailers, hname, hvalue); + + return APR_SUCCESS; } -apr_status_t h2_request_close(h2_request *req) + +apr_status_t h2_request_add_trailer(h2_request *req, apr_pool_t *pool, + const char *name, size_t nlen, + const char *value, size_t vlen) { - return h2_to_h1_close(req->to_h1); + if (!req->trailers) { + ap_log_perror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, pool, + "h2_request(%d): unanounced trailers", + req->id); + return APR_EINVAL; + } + if (nlen == 0 || name[0] == ':') { + ap_log_perror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, pool, + "h2_request(%d): pseudo header in trailer", + req->id); + return APR_EINVAL; + } + return add_h1_trailer(req, pool, name, nlen, value, vlen); } -static apr_status_t insert_request_line(h2_request *req, h2_mplx *m) +#define OPT_COPY(p, s) ((s)? apr_pstrdup(p, s) : NULL) + +void h2_request_copy(apr_pool_t *p, h2_request *dst, const h2_request *src) { - req->to_h1 = h2_to_h1_create(req->id, req->pool, req->bucket_alloc, - req->method, - req->scheme, - req->authority, - req->path, m); - return req->to_h1? APR_SUCCESS : APR_ENOMEM; + /* keep the dst id */ + dst->method = OPT_COPY(p, src->method); + dst->scheme = OPT_COPY(p, src->scheme); + dst->authority = OPT_COPY(p, src->authority); + dst->path = OPT_COPY(p, src->path); + dst->headers = apr_table_clone(p, src->headers); + dst->content_length = src->content_length; + dst->chunked = src->chunked; + dst->eoh = src->eoh; } -apr_status_t h2_request_flush(h2_request *req) +request_rec *h2_request_create_rec(const h2_request *req, conn_rec *conn) { - return h2_to_h1_flush(req->to_h1); + request_rec *r; + apr_pool_t *p; + int access_status = HTTP_OK; + + apr_pool_create(&p, conn->pool); + apr_pool_tag(p, "request"); + r = apr_pcalloc(p, sizeof(request_rec)); + AP_READ_REQUEST_ENTRY((intptr_t)r, (uintptr_t)conn); + r->pool = p; + r->connection = conn; + r->server = conn->base_server; + + r->user = NULL; + r->ap_auth_type = NULL; + + r->allowed_methods = ap_make_method_list(p, 2); + + r->headers_in = apr_table_copy(r->pool, req->headers); + r->trailers_in = apr_table_make(r->pool, 5); + r->subprocess_env = apr_table_make(r->pool, 25); + r->headers_out = apr_table_make(r->pool, 12); + r->err_headers_out = apr_table_make(r->pool, 5); + r->trailers_out = apr_table_make(r->pool, 5); + r->notes = apr_table_make(r->pool, 5); + + r->request_config = ap_create_request_config(r->pool); + /* Must be set before we run create request hook */ + + r->proto_output_filters = conn->output_filters; + r->output_filters = r->proto_output_filters; + r->proto_input_filters = conn->input_filters; + r->input_filters = r->proto_input_filters; + ap_run_create_request(r); + r->per_dir_config = r->server->lookup_defaults; + + r->sent_bodyct = 0; /* bytect isn't for body */ + + r->read_length = 0; + r->read_body = REQUEST_NO_BODY; + + r->status = HTTP_OK; /* Until further notice */ + r->header_only = 0; + r->the_request = NULL; + + /* Begin by presuming any module can make its own path_info assumptions, + * until some module interjects and changes the value. + */ + r->used_path_info = AP_REQ_DEFAULT_PATH_INFO; + + r->useragent_addr = conn->client_addr; + r->useragent_ip = conn->client_ip; + + ap_run_pre_read_request(r, conn); + + /* Time to populate r with the data we have. */ + r->request_time = req->request_time; + r->method = req->method; + /* Provide quick information about the request method as soon as known */ + r->method_number = ap_method_number_of(r->method); + if (r->method_number == M_GET && r->method[0] == 'H') { + r->header_only = 1; + } + + ap_parse_uri(r, req->path); + r->protocol = (char*)"HTTP/2"; + r->proto_num = HTTP_VERSION(2, 0); + + r->the_request = apr_psprintf(r->pool, "%s %s %s", + r->method, req->path, r->protocol); + + /* update what we think the virtual host is based on the headers we've + * now read. may update status. + * Leave r->hostname empty, vhost will parse if form our Host: header, + * otherwise we get complains about port numbers. + */ + r->hostname = NULL; + ap_update_vhost_from_headers(r); + + /* we may have switched to another server */ + r->per_dir_config = r->server->lookup_defaults; + + /* + * Add the HTTP_IN filter here to ensure that ap_discard_request_body + * called by ap_die and by ap_send_error_response works correctly on + * status codes that do not cause the connection to be dropped and + * in situations where the connection should be kept alive. + */ + ap_add_input_filter_handle(ap_http_input_filter_handle, + NULL, r, r->connection); + + if (access_status != HTTP_OK + || (access_status = ap_run_post_read_request(r))) { + /* Request check post hooks failed. An example of this would be a + * request for a vhost where h2 is disabled --> 421. + */ + ap_die(access_status, r); + ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); + ap_run_log_transaction(r); + r = NULL; + goto traceout; + } + + AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, + (char *)r->uri, (char *)r->server->defn_name, + r->status); + return r; +traceout: + AP_READ_REQUEST_FAILURE((uintptr_t)r); + return r; } + diff --git a/modules/http2/h2_request.h b/modules/http2/h2_request.h index aa5e0bc3..9e74492c 100644 --- a/modules/http2/h2_request.h +++ b/modules/http2/h2_request.h @@ -19,10 +19,8 @@ /* h2_request is the transformer of HTTP2 streams into HTTP/1.1 internal * format that will be fed to various httpd input filters to finally * become a request_rec to be handled by soemone. - * - * Ideally, we would make a request_rec without serializing the headers - * we have only to make someone else parse them back. */ +struct h2_config; struct h2_to_h1; struct h2_mplx; struct h2_task; @@ -30,38 +28,59 @@ struct h2_task; typedef struct h2_request h2_request; struct h2_request { - int id; /* http2 stream id */ - apr_pool_t *pool; - apr_bucket_alloc_t *bucket_alloc; - struct h2_to_h1 *to_h1; /* Converter to HTTP/1.1 format*/ - + int id; /* stream id */ + /* pseudo header values, see ch. 8.1.2.3 */ const char *method; const char *scheme; const char *authority; const char *path; + + apr_table_t *headers; + apr_table_t *trailers; + + apr_time_t request_time; + apr_off_t content_length; + int chunked; + int eoh; + + const struct h2_config *config; }; h2_request *h2_request_create(int id, apr_pool_t *pool, - apr_bucket_alloc_t *bucket_alloc); + const struct h2_config *config); + +h2_request *h2_request_createn(int id, apr_pool_t *pool, + const struct h2_config *config, + const char *method, const char *scheme, + const char *authority, const char *path, + apr_table_t *headers); + void h2_request_destroy(h2_request *req); -apr_status_t h2_request_flush(h2_request *req); +apr_status_t h2_request_rwrite(h2_request *req, request_rec *r); + +apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool, + const char *name, size_t nlen, + const char *value, size_t vlen); -apr_status_t h2_request_write_header(h2_request *req, - const char *name, size_t nlen, - const char *value, size_t vlen, - struct h2_mplx *m); +apr_status_t h2_request_add_trailer(h2_request *req, apr_pool_t *pool, + const char *name, size_t nlen, + const char *value, size_t vlen); -apr_status_t h2_request_write_data(h2_request *request, - const char *data, size_t len); +apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos); -apr_status_t h2_request_end_headers(h2_request *req, struct h2_mplx *m, - struct h2_task *task, int eos); +void h2_request_copy(apr_pool_t *p, h2_request *dst, const h2_request *src); -apr_status_t h2_request_close(h2_request *req); +/** + * Create a request_rec representing the h2_request to be + * processed on the given connection. + * + * @param req the h2 request to process + * @param conn the connection to process the request on + * @return the request_rec representing the request + */ +request_rec *h2_request_create_rec(const h2_request *req, conn_rec *conn); -apr_status_t h2_request_rwrite(h2_request *req, request_rec *r, - struct h2_mplx *m); #endif /* defined(__mod_h2__h2_request__) */ diff --git a/modules/http2/h2_response.c b/modules/http2/h2_response.c index 9cedd855..d16fee29 100644 --- a/modules/http2/h2_response.c +++ b/modules/http2/h2_response.c @@ -21,50 +21,30 @@ #include #include #include +#include #include #include "h2_private.h" +#include "h2_h2.h" #include "h2_util.h" +#include "h2_request.h" #include "h2_response.h" -static h2_ngheader *make_ngheader(apr_pool_t *pool, const char *status, - apr_table_t *header); -static int ignore_header(const char *name) +static apr_table_t *parse_headers(apr_array_header_t *hlines, apr_pool_t *pool) { - return (H2_HD_MATCH_LIT_CS("connection", name) - || H2_HD_MATCH_LIT_CS("proxy-connection", name) - || H2_HD_MATCH_LIT_CS("upgrade", name) - || H2_HD_MATCH_LIT_CS("keep-alive", name) - || H2_HD_MATCH_LIT_CS("transfer-encoding", name)); -} - -h2_response *h2_response_create(int stream_id, - const char *http_status, - apr_array_header_t *hlines, - apr_pool_t *pool) -{ - apr_table_t *header; - h2_response *response = apr_pcalloc(pool, sizeof(h2_response)); - int i; - if (response == NULL) { - return NULL; - } - - response->stream_id = stream_id; - response->status = http_status; - response->content_length = -1; - if (hlines) { - header = apr_table_make(pool, hlines->nelts); + apr_table_t *headers = apr_table_make(pool, hlines->nelts); + int i; + for (i = 0; i < hlines->nelts; ++i) { char *hline = ((char **)hlines->elts)[i]; char *sep = ap_strchr(hline, ':'); if (!sep) { ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, pool, - APLOGNO(02955) "h2_response(%d): invalid header[%d] '%s'", - response->stream_id, i, (char*)hline); + APLOGNO(02955) "h2_response: invalid header[%d] '%s'", + i, (char*)hline); /* not valid format, abort */ return NULL; } @@ -72,161 +52,129 @@ h2_response *h2_response_create(int stream_id, while (*sep == ' ' || *sep == '\t') { ++sep; } - if (ignore_header(hline)) { - /* never forward, ch. 8.1.2.2 */ - } - else { - apr_table_merge(header, hline, sep); - if (*sep && H2_HD_MATCH_LIT_CS("content-length", hline)) { - char *end; - response->content_length = apr_strtoi64(sep, &end, 10); - if (sep == end) { - ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, - pool, APLOGNO(02956) - "h2_response(%d): content-length" - " value not parsed: %s", - response->stream_id, sep); - response->content_length = -1; - } - } + + if (!h2_util_ignore_header(hline)) { + apr_table_merge(headers, hline, sep); } } + return headers; } else { - header = apr_table_make(pool, 0); + return apr_table_make(pool, 0); } - - response->rheader = header; - return response; } -h2_response *h2_response_rcreate(int stream_id, request_rec *r, - apr_table_t *header, apr_pool_t *pool) +static h2_response *h2_response_create_int(int stream_id, + int rst_error, + int http_status, + apr_table_t *headers, + apr_pool_t *pool) { - h2_response *response = apr_pcalloc(pool, sizeof(h2_response)); + h2_response *response; + const char *s; + + if (!headers) { + return NULL; + } + + response = apr_pcalloc(pool, sizeof(h2_response)); if (response == NULL) { return NULL; } response->stream_id = stream_id; - response->status = apr_psprintf(pool, "%d", r->status); + response->rst_error = rst_error; + response->http_status = http_status? http_status : 500; response->content_length = -1; - response->rheader = header; + response->headers = headers; + s = apr_table_get(headers, "Content-Length"); + if (s) { + char *end; + + response->content_length = apr_strtoi64(s, &end, 10); + if (s == end) { + ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, + pool, APLOGNO(02956) + "h2_response: content-length" + " value not parsed: %s", s); + response->content_length = -1; + } + } return response; } -void h2_response_destroy(h2_response *response) -{ - (void)response; -} -h2_response *h2_response_copy(apr_pool_t *pool, h2_response *from) +h2_response *h2_response_create(int stream_id, + int rst_error, + int http_status, + apr_array_header_t *hlines, + apr_pool_t *pool) { - h2_response *to = apr_pcalloc(pool, sizeof(h2_response)); - to->stream_id = from->stream_id; - to->status = apr_pstrdup(pool, from->status); - to->content_length = from->content_length; - if (from->rheader) { - to->ngheader = make_ngheader(pool, to->status, from->rheader); - } - return to; + return h2_response_create_int(stream_id, rst_error, http_status, + parse_headers(hlines, pool), pool); } -typedef struct { - nghttp2_nv *nv; - size_t nvlen; - size_t nvstrlen; - size_t offset; - char *strbuf; - apr_pool_t *pool; -} nvctx_t; - -static int count_header(void *ctx, const char *key, const char *value) +h2_response *h2_response_rcreate(int stream_id, request_rec *r, + apr_table_t *header, apr_pool_t *pool) { - if (!ignore_header(key)) { - nvctx_t *nvctx = (nvctx_t*)ctx; - nvctx->nvlen++; - nvctx->nvstrlen += strlen(key) + strlen(value) + 2; + h2_response *response = apr_pcalloc(pool, sizeof(h2_response)); + if (response == NULL) { + return NULL; } - return 1; -} - -#define NV_ADD_LIT_CS(nv, k, v) addnv_lit_cs(nv, k, sizeof(k) - 1, v, strlen(v)) -#define NV_ADD_CS_CS(nv, k, v) addnv_cs_cs(nv, k, strlen(k), v, strlen(v)) -#define NV_BUF_ADD(nv, s, len) memcpy(nv->strbuf, s, len); \ -s = nv->strbuf; \ -nv->strbuf += len + 1 - -static void addnv_cs_cs(nvctx_t *ctx, const char *key, size_t key_len, - const char *value, size_t val_len) -{ - nghttp2_nv *nv = &ctx->nv[ctx->offset]; - NV_BUF_ADD(ctx, key, key_len); - NV_BUF_ADD(ctx, value, val_len); - - nv->name = (uint8_t*)key; - nv->namelen = key_len; - nv->value = (uint8_t*)value; - nv->valuelen = val_len; + response->stream_id = stream_id; + response->http_status = r->status; + response->content_length = -1; + response->headers = header; + + if (response->http_status == HTTP_FORBIDDEN) { + const char *cause = apr_table_get(r->notes, "ssl-renegotiate-forbidden"); + if (cause) { + /* This request triggered a TLS renegotiation that is now allowed + * in HTTP/2. Tell the client that it should use HTTP/1.1 for this. + */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, response->http_status, r, + "h2_response(%ld-%d): renegotiate forbidden, cause: %s", + (long)r->connection->id, stream_id, cause); + response->rst_error = H2_ERR_HTTP_1_1_REQUIRED; + } + } - ctx->offset++; + return response; } -static void addnv_lit_cs(nvctx_t *ctx, const char *key, size_t key_len, - const char *value, size_t val_len) +h2_response *h2_response_die(int stream_id, apr_status_t type, + const struct h2_request *req, apr_pool_t *pool) { - nghttp2_nv *nv = &ctx->nv[ctx->offset]; + apr_table_t *headers = apr_table_make(pool, 5); + char *date = NULL; - NV_BUF_ADD(ctx, value, val_len); + date = apr_palloc(pool, APR_RFC822_DATE_LEN); + ap_recent_rfc822_date(date, req->request_time); + apr_table_setn(headers, "Date", date); + apr_table_setn(headers, "Server", ap_get_server_banner()); - nv->name = (uint8_t*)key; - nv->namelen = key_len; - nv->value = (uint8_t*)value; - nv->valuelen = val_len; - - ctx->offset++; + return h2_response_create_int(stream_id, 0, 500, headers, pool); } -static int add_header(void *ctx, const char *key, const char *value) +h2_response *h2_response_clone(apr_pool_t *pool, h2_response *from) { - if (!ignore_header(key)) { - nvctx_t *nvctx = (nvctx_t*)ctx; - NV_ADD_CS_CS(nvctx, key, value); + h2_response *to = apr_pcalloc(pool, sizeof(h2_response)); + to->stream_id = from->stream_id; + to->http_status = from->http_status; + to->content_length = from->content_length; + if (from->headers) { + to->headers = apr_table_clone(pool, from->headers); } - return 1; + if (from->trailers) { + to->trailers = apr_table_clone(pool, from->trailers); + } + return to; } -static h2_ngheader *make_ngheader(apr_pool_t *pool, const char *status, - apr_table_t *header) +void h2_response_set_trailers(h2_response *response, apr_table_t *trailers) { - size_t n; - h2_ngheader *h; - nvctx_t ctx; - - ctx.nv = NULL; - ctx.nvlen = 1; - ctx.nvstrlen = strlen(status) + 1; - ctx.offset = 0; - ctx.strbuf = NULL; - ctx.pool = pool; - - apr_table_do(count_header, &ctx, header, NULL); - - n = (sizeof(h2_ngheader) - + (ctx.nvlen * sizeof(nghttp2_nv)) + ctx.nvstrlen); - h = apr_pcalloc(pool, n); - if (h) { - ctx.nv = (nghttp2_nv*)(h + 1); - ctx.strbuf = (char*)&ctx.nv[ctx.nvlen]; - - NV_ADD_LIT_CS(&ctx, ":status", status); - apr_table_do(add_header, &ctx, header, NULL); - - h->nv = ctx.nv; - h->nvlen = ctx.nvlen; - } - return h; + response->trailers = trailers; } diff --git a/modules/http2/h2_response.h b/modules/http2/h2_response.h index 456d2226..426eeead 100644 --- a/modules/http2/h2_response.h +++ b/modules/http2/h2_response.h @@ -16,32 +16,67 @@ #ifndef __mod_h2__h2_response__ #define __mod_h2__h2_response__ -/* h2_response is just the data belonging the the head of a HTTP response, - * suitable prepared to be fed to nghttp2 for response submit. - */ -typedef struct h2_ngheader { - nghttp2_nv *nv; - apr_size_t nvlen; -} h2_ngheader; +struct h2_request; +struct h2_push; typedef struct h2_response { int stream_id; - const char *status; + int rst_error; + int http_status; apr_off_t content_length; - apr_table_t *rheader; - h2_ngheader *ngheader; + apr_table_t *headers; + apr_table_t *trailers; } h2_response; +/** + * Create the response from the status and parsed header lines. + * @param stream_id id of the stream to create the response for + * @param rst_error error for reset or 0 + * @param http_status http status code of response + * @param hlines the text lines of the response header + * @param pool the memory pool to use + */ h2_response *h2_response_create(int stream_id, - const char *http_status, - apr_array_header_t *hlines, - apr_pool_t *pool); + int rst_error, + int http_status, + apr_array_header_t *hlines, + apr_pool_t *pool); +/** + * Create the response from the given request_rec. + * @param stream_id id of the stream to create the response for + * @param r the request record which was processed + * @param header the headers of the response + * @param pool the memory pool to use + */ h2_response *h2_response_rcreate(int stream_id, request_rec *r, apr_table_t *header, apr_pool_t *pool); -void h2_response_destroy(h2_response *response); +/** + * Create the response for the given error. + * @param stream_id id of the stream to create the response for + * @param type the error code + * @param req the original h2_request + * @param pool the memory pool to use + */ +h2_response *h2_response_die(int stream_id, apr_status_t type, + const struct h2_request *req, apr_pool_t *pool); + +/** + * Deep copies the response into a new pool. + * @param pool the pool to use for the clone + * @param from the response to clone + * @return the cloned response + */ +h2_response *h2_response_clone(apr_pool_t *pool, h2_response *from); -h2_response *h2_response_copy(apr_pool_t *pool, h2_response *from); +/** + * Set the trailers in the reponse. Will replace any existing trailers. Will + * *not* clone the table. + * + * @param response the repsone to set the trailers for + * @param trailers the trailers to set + */ +void h2_response_set_trailers(h2_response *response, apr_table_t *trailers); #endif /* defined(__mod_h2__h2_response__) */ diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c index c3456a06..1d79364e 100644 --- a/modules/http2/h2_session.c +++ b/modules/http2/h2_session.c @@ -24,9 +24,12 @@ #include #include "h2_private.h" +#include "h2_bucket_eos.h" #include "h2_config.h" #include "h2_h2.h" #include "h2_mplx.h" +#include "h2_push.h" +#include "h2_request.h" #include "h2_response.h" #include "h2_stream.h" #include "h2_stream_set.h" @@ -41,56 +44,130 @@ static int frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen); static int h2_session_status_from_apr_status(apr_status_t rv) { - switch (rv) { - case APR_SUCCESS: - return NGHTTP2_NO_ERROR; - case APR_EAGAIN: - case APR_TIMEUP: - return NGHTTP2_ERR_WOULDBLOCK; - case APR_EOF: + if (rv == APR_SUCCESS) { + return NGHTTP2_NO_ERROR; + } + else if (APR_STATUS_IS_EAGAIN(rv)) { + return NGHTTP2_ERR_WOULDBLOCK; + } + else if (APR_STATUS_IS_EOF(rv)) { return NGHTTP2_ERR_EOF; - default: - return NGHTTP2_ERR_PROTO; } + return NGHTTP2_ERR_PROTO; +} + +static void update_window(void *ctx, int stream_id, apr_off_t bytes_read) +{ + h2_session *session = (h2_session*)ctx; + nghttp2_session_consume(session->ngh2, stream_id, bytes_read); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_session(%ld-%d): consumed %ld bytes", + session->id, stream_id, (long)bytes_read); } -static int stream_open(h2_session *session, int stream_id) + +h2_stream *h2_session_open_stream(h2_session *session, int stream_id) { h2_stream * stream; + apr_pool_t *stream_pool; if (session->aborted) { - return NGHTTP2_ERR_CALLBACK_FAILURE; + return NULL; } - stream = h2_mplx_open_io(session->mplx, stream_id); - if (stream) { - h2_stream_set_add(session->streams, stream); - if (stream->id > session->max_stream_received) { - session->max_stream_received = stream->id; - } - - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, - "h2_session: stream(%ld-%d): opened", - session->id, stream_id); + if (session->spare) { + stream_pool = session->spare; + session->spare = NULL; + } + else { + apr_pool_create(&stream_pool, session->pool); + } + + stream = h2_stream_open(stream_id, stream_pool, session); + + h2_stream_set_add(session->streams, stream); + if (H2_STREAM_CLIENT_INITIATED(stream_id) + && stream_id > session->max_stream_received) { + session->max_stream_received = stream->id; + } + + return stream; +} + +#ifdef H2_NG2_STREAM_API + +/** + * Determine the importance of streams when scheduling tasks. + * - if both stream depend on the same one, compare weights + * - if one stream is closer to the root, prioritize that one + * - if both are on the same level, use the weight of their root + * level ancestors + */ +static int spri_cmp(int sid1, nghttp2_stream *s1, + int sid2, nghttp2_stream *s2, h2_session *session) +{ + nghttp2_stream *p1, *p2; + + p1 = nghttp2_stream_get_parent(s1); + p2 = nghttp2_stream_get_parent(s2); + + if (p1 == p2) { + int32_t w1, w2; - return 0; + w1 = nghttp2_stream_get_weight(s1); + w2 = nghttp2_stream_get_weight(s2); + return w2 - w1; } + else if (!p1) { + /* stream 1 closer to root */ + return -1; + } + else if (!p2) { + /* stream 2 closer to root */ + return 1; + } + return spri_cmp(sid1, p1, sid2, p2, session); +} + +static int stream_pri_cmp(int sid1, int sid2, void *ctx) +{ + h2_session *session = ctx; + nghttp2_stream *s1, *s2; - ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, session->c, - APLOGNO(02918) - "h2_session: stream(%ld-%d): unable to create", - session->id, stream_id); - return NGHTTP2_ERR_INVALID_STREAM_ID; + s1 = nghttp2_session_find_stream(session->ngh2, sid1); + s2 = nghttp2_session_find_stream(session->ngh2, sid2); + + if (s1 == s2) { + return 0; + } + else if (!s1) { + return 1; + } + else if (!s2) { + return -1; + } + return spri_cmp(sid1, s1, sid2, s2, session); } -static apr_status_t stream_end_headers(h2_session *session, - h2_stream *stream, int eos) +#else /* ifdef H2_NG2_STREAM_API */ + +/* In absence of nghttp2_stream API, which gives information about + * priorities since nghttp2 1.3.x, we just sort the streams by + * their identifier, aka. order of arrival. + */ +static int stream_pri_cmp(int sid1, int sid2, void *ctx) { - (void)session; - return h2_stream_write_eoh(stream, eos); + (void)ctx; + return sid1 - sid2; } -static apr_status_t send_data(h2_session *session, const char *data, - apr_size_t length); +#endif /* (ifdef else) H2_NG2_STREAM_API */ + +static apr_status_t stream_schedule(h2_session *session, + h2_stream *stream, int eos) +{ + (void)session; + return h2_stream_schedule(stream, eos, stream_pri_cmp, session); +} /* * Callback when nghttp2 wants to send bytes back to the client. @@ -100,14 +177,15 @@ static ssize_t send_cb(nghttp2_session *ngh2, int flags, void *userp) { h2_session *session = (h2_session *)userp; - apr_status_t status = send_data(session, (const char *)data, length); + apr_status_t status; (void)ngh2; (void)flags; + status = h2_conn_io_write(&session->io, (const char *)data, length); if (status == APR_SUCCESS) { return length; } - if (status == APR_EAGAIN || status == APR_TIMEUP) { + if (APR_STATUS_IS_EAGAIN(status)) { return NGHTTP2_ERR_WOULDBLOCK; } ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, @@ -140,19 +218,19 @@ static int on_data_chunk_recv_cb(nghttp2_session *ngh2, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *userp) { - int rv; h2_session *session = (h2_session *)userp; + apr_status_t status = APR_SUCCESS; h2_stream * stream; - apr_status_t status; + int rv; (void)flags; if (session->aborted) { return NGHTTP2_ERR_CALLBACK_FAILURE; } - stream = h2_stream_set_get(session->streams, stream_id); + + stream = h2_session_get_stream(session, stream_id); if (!stream) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c, - APLOGNO(02919) + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, "h2_session: stream(%ld-%d): on_data_chunk for unknown stream", session->id, (int)stream_id); rv = nghttp2_submit_rst_stream(ngh2, NGHTTP2_FLAG_NONE, stream_id, @@ -165,11 +243,12 @@ static int on_data_chunk_recv_cb(nghttp2_session *ngh2, uint8_t flags, status = h2_stream_write_data(stream, (const char *)data, len); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, session->c, - "h2_stream(%ld-%d): written DATA, length %d", - session->id, stream_id, (int)len); + "h2_stream(%ld-%d): data_chunk_recv, written %ld bytes", + session->id, stream_id, (long)len); if (status != APR_SUCCESS) { + update_window(session, stream_id, len); rv = nghttp2_submit_rst_stream(ngh2, NGHTTP2_FLAG_NONE, stream_id, - NGHTTP2_INTERNAL_ERROR); + H2_STREAM_RST(stream, H2_ERR_INTERNAL_ERROR)); if (nghttp2_is_fatal(rv)) { return NGHTTP2_ERR_CALLBACK_FAILURE; } @@ -177,56 +256,7 @@ static int on_data_chunk_recv_cb(nghttp2_session *ngh2, uint8_t flags, return 0; } -static int before_frame_send_cb(nghttp2_session *ngh2, - const nghttp2_frame *frame, - void *userp) -{ - h2_session *session = (h2_session *)userp; - (void)ngh2; - - if (session->aborted) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - if (APLOGctrace2(session->c)) { - char buffer[256]; - frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0])); - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, - "h2_session(%ld): before_frame_send %s", - session->id, buffer); - } - return 0; -} - -static int on_frame_send_cb(nghttp2_session *ngh2, - const nghttp2_frame *frame, - void *userp) -{ - h2_session *session = (h2_session *)userp; - (void)ngh2; (void)frame; - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c, - "h2_session(%ld): on_frame_send", session->id); - return 0; -} - -static int on_frame_not_send_cb(nghttp2_session *ngh2, - const nghttp2_frame *frame, - int lib_error_code, void *userp) -{ - h2_session *session = (h2_session *)userp; - (void)ngh2; - - if (APLOGctrace2(session->c)) { - char buffer[256]; - - frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0])); - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, - "h2_session: callback on_frame_not_send error=%d %s", - lib_error_code, buffer); - } - return 0; -} - -static apr_status_t stream_destroy(h2_session *session, +static apr_status_t stream_release(h2_session *session, h2_stream *stream, uint32_t error_code) { @@ -242,11 +272,13 @@ static apr_status_t stream_destroy(h2_session *session, ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, "h2_stream(%ld-%d): closing with err=%d %s", session->id, (int)stream->id, (int)error_code, - nghttp2_strerror(error_code)); + h2_h2_err_description(error_code)); + h2_stream_rst(stream, error_code); } - h2_stream_set_remove(session->streams, stream); - return h2_mplx_cleanup_stream(session->mplx, stream); + return h2_conn_io_writeb(&session->io, + h2_bucket_eos_create(session->c->bucket_alloc, + stream)); } static int on_stream_close_cb(nghttp2_session *ngh2, int32_t stream_id, @@ -259,33 +291,30 @@ static int on_stream_close_cb(nghttp2_session *ngh2, int32_t stream_id, if (session->aborted) { return NGHTTP2_ERR_CALLBACK_FAILURE; } - stream = h2_stream_set_get(session->streams, stream_id); + stream = h2_session_get_stream(session, stream_id); if (stream) { - stream_destroy(session, stream, error_code); - } - - if (error_code) { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, - "h2_stream(%ld-%d): close error %d", - session->id, (int)stream_id, error_code); + stream_release(session, stream, error_code); } - return 0; } static int on_begin_headers_cb(nghttp2_session *ngh2, const nghttp2_frame *frame, void *userp) { - /* This starts a new stream. */ - int rv; + h2_session *session = (h2_session *)userp; + h2_stream *s; + + /* We may see HEADERs at the start of a stream or after all DATA + * streams to carry trailers. */ (void)ngh2; - rv = stream_open((h2_session *)userp, frame->hd.stream_id); - if (rv != NGHTTP2_ERR_CALLBACK_FAILURE) { - /* on_header_cb or on_frame_recv_cb will dectect that stream - does not exist and submit RST_STREAM. */ - return 0; + s = h2_session_get_stream(session, frame->hd.stream_id); + if (s) { + /* nop */ } - return NGHTTP2_ERR_CALLBACK_FAILURE; + else { + s = h2_session_open_stream((h2_session *)userp, frame->hd.stream_id); + } + return s? 0 : NGHTTP2_ERR_CALLBACK_FAILURE; } static int on_header_cb(nghttp2_session *ngh2, const nghttp2_frame *frame, @@ -303,8 +332,8 @@ static int on_header_cb(nghttp2_session *ngh2, const nghttp2_frame *frame, if (session->aborted) { return NGHTTP2_ERR_CALLBACK_FAILURE; } - stream = h2_stream_set_get(session->streams, - frame->hd.stream_id); + + stream = h2_session_get_stream(session, frame->hd.stream_id); if (!stream) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c, APLOGNO(02920) @@ -313,9 +342,9 @@ static int on_header_cb(nghttp2_session *ngh2, const nghttp2_frame *frame, return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } - status = h2_stream_write_header(stream, - (const char *)name, namelen, - (const char *)value, valuelen); + status = h2_stream_add_header(stream, (const char *)name, namelen, + (const char *)value, valuelen); + if (status != APR_SUCCESS) { return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; } @@ -331,63 +360,67 @@ static int on_frame_recv_cb(nghttp2_session *ng2s, const nghttp2_frame *frame, void *userp) { - int rv; h2_session *session = (h2_session *)userp; apr_status_t status = APR_SUCCESS; + h2_stream *stream; + if (session->aborted) { return NGHTTP2_ERR_CALLBACK_FAILURE; } - ++session->frames_received; - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, - "h2_session(%ld): on_frame_rcv #%ld, type=%d", session->id, + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c, + "h2_stream(%ld-%d): on_frame_rcv #%ld, type=%d", + session->id, frame->hd.stream_id, (long)session->frames_received, frame->hd.type); + + ++session->frames_received; switch (frame->hd.type) { - case NGHTTP2_HEADERS: { - int eos; - h2_stream * stream = h2_stream_set_get(session->streams, - frame->hd.stream_id); - if (stream == NULL) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c, - APLOGNO(02921) - "h2_session: stream(%ld-%d): HEADERS frame " - "for unknown stream", session->id, - (int)frame->hd.stream_id); - rv = nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE, - frame->hd.stream_id, - NGHTTP2_INTERNAL_ERROR); - if (nghttp2_is_fatal(rv)) { - return NGHTTP2_ERR_CALLBACK_FAILURE; + case NGHTTP2_HEADERS: + /* This can be HEADERS for a new stream, defining the request, + * or HEADER may come after DATA at the end of a stream as in + * trailers */ + stream = h2_session_get_stream(session, frame->hd.stream_id); + if (stream) { + int eos = (frame->hd.flags & NGHTTP2_FLAG_END_STREAM); + + if (h2_stream_is_scheduled(stream)) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, + "h2_stream(%ld-%d): TRAILER, eos=%d", + session->id, frame->hd.stream_id, eos); + if (eos) { + status = h2_stream_close_input(stream); + } + } + else { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, + "h2_stream(%ld-%d): HEADER, eos=%d", + session->id, frame->hd.stream_id, eos); + status = stream_schedule(session, stream, eos); } - return 0; } - - eos = (frame->hd.flags & NGHTTP2_FLAG_END_STREAM); - status = stream_end_headers(session, stream, eos); - + else { + status = APR_EINVAL; + } break; - } - case NGHTTP2_DATA: { - h2_stream * stream = h2_stream_set_get(session->streams, - frame->hd.stream_id); - if (stream == NULL) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c, - APLOGNO(02922) - "h2_session: stream(%ld-%d): DATA frame " - "for unknown stream", session->id, - (int)frame->hd.stream_id); - rv = nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE, - frame->hd.stream_id, - NGHTTP2_INTERNAL_ERROR); - if (nghttp2_is_fatal(rv)) { - return NGHTTP2_ERR_CALLBACK_FAILURE; + case NGHTTP2_DATA: + stream = h2_session_get_stream(session, frame->hd.stream_id); + if (stream) { + int eos = (frame->hd.flags & NGHTTP2_FLAG_END_STREAM); + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, + "h2_stream(%ld-%d): DATA, len=%ld, eos=%d", + session->id, frame->hd.stream_id, + (long)frame->hd.length, eos); + if (eos) { + status = h2_stream_close_input(stream); } - return 0; + } + else { + status = APR_EINVAL; } break; - } - case NGHTTP2_PRIORITY: { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, + case NGHTTP2_PRIORITY: + session->reprioritize = 1; + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c, "h2_session: stream(%ld-%d): PRIORITY frame " " weight=%d, dependsOn=%d, exclusive=%d", session->id, (int)frame->hd.stream_id, @@ -395,7 +428,13 @@ static int on_frame_recv_cb(nghttp2_session *ng2s, frame->priority.pri_spec.stream_id, frame->priority.pri_spec.exclusive); break; - } + case NGHTTP2_WINDOW_UPDATE: + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c, + "h2_session: stream(%ld-%d): WINDOW_UPDATE " + "incr=%d", + session->id, (int)frame->hd.stream_id, + frame->window_update.window_size_increment); + break; default: if (APLOGctrace2(session->c)) { char buffer[256]; @@ -408,23 +447,10 @@ static int on_frame_recv_cb(nghttp2_session *ng2s, break; } - /* only DATA and HEADERS frame can bear END_STREAM flag. Other - frame types may have other flag which has the same value, so we - have to check the frame type first. */ - if ((frame->hd.type == NGHTTP2_DATA || frame->hd.type == NGHTTP2_HEADERS) && - frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - h2_stream * stream = h2_stream_set_get(session->streams, - frame->hd.stream_id); - if (stream != NULL) { - status = h2_stream_write_eos(stream); - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, - "h2_stream(%ld-%d): input closed", - session->id, (int)frame->hd.stream_id); - } - } - if (status != APR_SUCCESS) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c, + int rv; + + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, APLOGNO(02923) "h2_session: stream(%ld-%d): error handling frame", session->id, (int)frame->hd.stream_id); @@ -434,24 +460,20 @@ static int on_frame_recv_cb(nghttp2_session *ng2s, if (nghttp2_is_fatal(rv)) { return NGHTTP2_ERR_CALLBACK_FAILURE; } - return 0; } return 0; } -static apr_status_t send_data(h2_session *session, const char *data, - apr_size_t length) -{ - return h2_conn_io_write(&session->io, data, length); -} - static apr_status_t pass_data(void *ctx, - const char *data, apr_size_t length) + const char *data, apr_off_t length) { - return send_data((h2_session*)ctx, data, length); + return h2_conn_io_write(&((h2_session*)ctx)->io, data, length); } + +static char immortal_zeros[H2_MAX_PADLEN]; + static int on_send_data_cb(nghttp2_session *ngh2, nghttp2_frame *frame, const uint8_t *framehd, @@ -462,7 +484,7 @@ static int on_send_data_cb(nghttp2_session *ngh2, apr_status_t status = APR_SUCCESS; h2_session *session = (h2_session *)userp; int stream_id = (int)frame->hd.stream_id; - const unsigned char padlen = frame->data.padlen; + unsigned char padlen; int eos; h2_stream *stream; @@ -472,7 +494,12 @@ static int on_send_data_cb(nghttp2_session *ngh2, return NGHTTP2_ERR_CALLBACK_FAILURE; } - stream = h2_stream_set_get(session->streams, stream_id); + if (frame->data.padlen > H2_MAX_PADLEN) { + return NGHTTP2_ERR_PROTO; + } + padlen = (unsigned char)frame->data.padlen; + + stream = h2_session_get_stream(session, stream_id); if (!stream) { ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_NOTFOUND, session->c, APLOGNO(02924) @@ -481,44 +508,89 @@ static int on_send_data_cb(nghttp2_session *ngh2, return NGHTTP2_ERR_CALLBACK_FAILURE; } - status = send_data(session, (const char *)framehd, 9); - if (status == APR_SUCCESS) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c, + "h2_stream(%ld-%d): send_data_cb for %ld bytes", + session->id, (int)stream_id, (long)length); + + if (h2_conn_io_is_buffered(&session->io)) { + status = h2_conn_io_write(&session->io, (const char *)framehd, 9); + if (status == APR_SUCCESS) { + if (padlen) { + status = h2_conn_io_write(&session->io, (const char *)&padlen, 1); + } + + if (status == APR_SUCCESS) { + apr_off_t len = length; + status = h2_stream_readx(stream, pass_data, session, &len, &eos); + if (status == APR_SUCCESS && len != length) { + status = APR_EINVAL; + } + } + + if (status == APR_SUCCESS && padlen) { + if (padlen) { + status = h2_conn_io_write(&session->io, immortal_zeros, padlen); + } + } + } + } + else { + apr_bucket *b; + char *header = apr_pcalloc(stream->pool, 10); + memcpy(header, (const char *)framehd, 9); if (padlen) { - status = send_data(session, (const char *)&padlen, 1); + header[9] = (char)padlen; } - + b = apr_bucket_pool_create(header, padlen? 10 : 9, + stream->pool, session->c->bucket_alloc); + status = h2_conn_io_writeb(&session->io, b); + if (status == APR_SUCCESS) { - apr_size_t len = length; - status = h2_stream_readx(stream, pass_data, session, - &len, &eos); + apr_off_t len = length; + status = h2_stream_read_to(stream, session->io.output, &len, &eos); if (status == APR_SUCCESS && len != length) { status = APR_EINVAL; } } - + if (status == APR_SUCCESS && padlen) { - if (padlen) { - char pad[256]; - memset(pad, 0, padlen); - status = send_data(session, pad, padlen); - } + b = apr_bucket_immortal_create(immortal_zeros, padlen, + session->c->bucket_alloc); + status = h2_conn_io_writeb(&session->io, b); } } + if (status == APR_SUCCESS) { + stream->data_frames_sent++; + h2_conn_io_consider_flush(&session->io); return 0; } - else if (status != APR_EOF) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c, + else { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, APLOGNO(02925) "h2_stream(%ld-%d): failed send_data_cb", session->id, (int)stream_id); - return NGHTTP2_ERR_CALLBACK_FAILURE; } return h2_session_status_from_apr_status(status); } +static int on_frame_send_cb(nghttp2_session *ngh2, + const nghttp2_frame *frame, + void *user_data) +{ + h2_session *session = user_data; + if (APLOGcdebug(session->c)) { + char buffer[256]; + + frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0])); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_session(%ld): frame_send %s", + session->id, buffer); + } + return 0; +} #define NGH2_SET_CALLBACK(callbacks, name, fn)\ nghttp2_session_callbacks_set_##name##_callback(callbacks, fn) @@ -537,20 +609,66 @@ static apr_status_t init_callbacks(conn_rec *c, nghttp2_session_callbacks **pcb) NGH2_SET_CALLBACK(*pcb, on_frame_recv, on_frame_recv_cb); NGH2_SET_CALLBACK(*pcb, on_invalid_frame_recv, on_invalid_frame_recv_cb); NGH2_SET_CALLBACK(*pcb, on_data_chunk_recv, on_data_chunk_recv_cb); - NGH2_SET_CALLBACK(*pcb, before_frame_send, before_frame_send_cb); - NGH2_SET_CALLBACK(*pcb, on_frame_send, on_frame_send_cb); - NGH2_SET_CALLBACK(*pcb, on_frame_not_send, on_frame_not_send_cb); NGH2_SET_CALLBACK(*pcb, on_stream_close, on_stream_close_cb); NGH2_SET_CALLBACK(*pcb, on_begin_headers, on_begin_headers_cb); NGH2_SET_CALLBACK(*pcb, on_header, on_header_cb); NGH2_SET_CALLBACK(*pcb, send_data, on_send_data_cb); + NGH2_SET_CALLBACK(*pcb, on_frame_send, on_frame_send_cb); + + return APR_SUCCESS; +} + +static apr_status_t session_pool_cleanup(void *data) +{ + h2_session *session = data; + /* keep us from destroying the pool, since that is already ongoing. */ + session->pool = NULL; + h2_session_destroy(session); return APR_SUCCESS; } +static void *session_malloc(size_t size, void *ctx) +{ + h2_session *session = ctx; + ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, session->c, + "h2_session(%ld): malloc(%ld)", + session->id, (long)size); + return malloc(size); +} + +static void session_free(void *p, void *ctx) +{ + h2_session *session = ctx; + + ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, session->c, + "h2_session(%ld): free()", + session->id); + free(p); +} + +static void *session_calloc(size_t n, size_t size, void *ctx) +{ + h2_session *session = ctx; + + ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, session->c, + "h2_session(%ld): calloc(%ld, %ld)", + session->id, (long)n, (long)size); + return calloc(n, size); +} + +static void *session_realloc(void *p, size_t size, void *ctx) +{ + h2_session *session = ctx; + ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, session->c, + "h2_session(%ld): realloc(%ld)", + session->id, (long)size); + return realloc(p, size); +} + static h2_session *h2_session_create_int(conn_rec *c, request_rec *r, - h2_config *config, + const h2_config *config, h2_workers *workers) { nghttp2_session_callbacks *callbacks = NULL; @@ -566,26 +684,32 @@ static h2_session *h2_session_create_int(conn_rec *c, session = apr_pcalloc(pool, sizeof(h2_session)); if (session) { int rv; + nghttp2_mem *mem; + session->id = c->id; session->c = c; session->r = r; + session->config = config; + + session->pool = pool; + apr_pool_pre_cleanup_register(pool, session, session_pool_cleanup); session->max_stream_count = h2_config_geti(config, H2_CONF_MAX_STREAMS); session->max_stream_mem = h2_config_geti(config, H2_CONF_STREAM_MAX_MEM); - session->pool = pool; - status = apr_thread_cond_create(&session->iowait, session->pool); if (status != APR_SUCCESS) { return NULL; } - session->streams = h2_stream_set_create(session->pool); + session->streams = h2_stream_set_create(session->pool, session->max_stream_count); session->workers = workers; - session->mplx = h2_mplx_create(c, session->pool, workers); + session->mplx = h2_mplx_create(c, session->pool, config, workers); + + h2_mplx_set_consumed_cb(session->mplx, update_window, session); - h2_conn_io_init(&session->io, c); + h2_conn_io_init(&session->io, c, config, session->pool); session->bbtmp = apr_brigade_create(session->pool, c->bucket_alloc); status = init_callbacks(c, &callbacks); @@ -604,16 +728,27 @@ static h2_session *h2_session_create_int(conn_rec *c, h2_session_destroy(session); return NULL; } - nghttp2_option_set_peer_max_concurrent_streams(options, (uint32_t)session->max_stream_count); - /* We need to handle window updates ourself, otherwise we * get flooded by nghttp2. */ nghttp2_option_set_no_auto_window_update(options, 1); - rv = nghttp2_session_server_new2(&session->ngh2, callbacks, - session, options); + if (APLOGctrace6(c)) { + mem = apr_pcalloc(session->pool, sizeof(nghttp2_mem)); + mem->mem_user_data = session; + mem->malloc = session_malloc; + mem->free = session_free; + mem->calloc = session_calloc; + mem->realloc = session_realloc; + + rv = nghttp2_session_server_new3(&session->ngh2, callbacks, + session, options, mem); + } + else { + rv = nghttp2_session_server_new2(&session->ngh2, callbacks, + session, options); + } nghttp2_session_callbacks_del(callbacks); nghttp2_option_del(options); @@ -629,27 +764,49 @@ static h2_session *h2_session_create_int(conn_rec *c, return session; } -h2_session *h2_session_create(conn_rec *c, h2_config *config, +h2_session *h2_session_create(conn_rec *c, const h2_config *config, h2_workers *workers) { return h2_session_create_int(c, NULL, config, workers); } -h2_session *h2_session_rcreate(request_rec *r, h2_config *config, +h2_session *h2_session_rcreate(request_rec *r, const h2_config *config, h2_workers *workers) { return h2_session_create_int(r->connection, r, config, workers); } +static void h2_session_cleanup(h2_session *session) +{ + AP_DEBUG_ASSERT(session); + /* This is an early cleanup of the session that may + * discard what is no longer necessary for *new* streams + * and general HTTP/2 processing. + * At this point, all frames are in transit or somehwere in + * our buffers or passed down output filters. + * h2 streams might still being written out. + */ + if (session->ngh2) { + nghttp2_session_del(session->ngh2); + session->ngh2 = NULL; + } + if (session->spare) { + apr_pool_destroy(session->spare); + session->spare = NULL; + } +} + void h2_session_destroy(h2_session *session) { AP_DEBUG_ASSERT(session); + h2_session_cleanup(session); + if (session->mplx) { h2_mplx_release_and_join(session->mplx, session->iowait); session->mplx = NULL; } if (session->streams) { - if (h2_stream_set_size(session->streams)) { + if (!h2_stream_set_is_empty(session->streams)) { ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c, "h2_session(%ld): destroy, %d streams open", session->id, (int)h2_stream_set_size(session->streams)); @@ -657,35 +814,37 @@ void h2_session_destroy(h2_session *session) h2_stream_set_destroy(session->streams); session->streams = NULL; } - if (session->ngh2) { - nghttp2_session_del(session->ngh2); - session->ngh2 = NULL; - } - h2_conn_io_destroy(&session->io); - - if (session->iowait) { - apr_thread_cond_destroy(session->iowait); - session->iowait = NULL; - } - if (session->pool) { apr_pool_destroy(session->pool); } } + +void h2_session_eoc_callback(h2_session *session) +{ + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, + "session(%ld): cleanup and destroy", session->id); + apr_pool_cleanup_kill(session->pool, session, session_pool_cleanup); + h2_session_destroy(session); +} + static apr_status_t h2_session_abort_int(h2_session *session, int reason) { AP_DEBUG_ASSERT(session); if (!session->aborted) { session->aborted = 1; - if (session->ngh2) { - - if (!reason) { + + if (session->ngh2) { + if (NGHTTP2_ERR_EOF == reason) { + /* This is our way of indication that the connection is + * gone. No use to send any GOAWAY frames. */ + nghttp2_session_terminate_session(session->ngh2, reason); + } + else if (!reason) { nghttp2_submit_goaway(session->ngh2, NGHTTP2_FLAG_NONE, session->max_stream_received, reason, NULL, 0); nghttp2_session_send(session->ngh2); - h2_conn_io_flush(&session->io); } else { const char *err = nghttp2_strerror(reason); @@ -694,21 +853,13 @@ static apr_status_t h2_session_abort_int(h2_session *session, int reason) "session(%ld): aborting session, reason=%d %s", session->id, reason, err); - if (NGHTTP2_ERR_EOF == reason) { - /* This is our way of indication that the connection is - * gone. No use to send any GOAWAY frames. */ - nghttp2_session_terminate_session(session->ngh2, reason); - } - else { - /* The connection might still be there and we shut down - * with GOAWAY and reason information. */ - nghttp2_submit_goaway(session->ngh2, NGHTTP2_FLAG_NONE, - session->max_stream_received, - reason, (const uint8_t *)err, - strlen(err)); - nghttp2_session_send(session->ngh2); - h2_conn_io_flush(&session->io); - } + /* The connection might still be there and we shut down + * with GOAWAY and reason information. */ + nghttp2_submit_goaway(session->ngh2, NGHTTP2_FLAG_NONE, + session->max_stream_received, + reason, (const uint8_t *)err, + strlen(err)); + nghttp2_session_send(session->ngh2); } } h2_mplx_abort(session->mplx); @@ -731,10 +882,12 @@ apr_status_t h2_session_abort(h2_session *session, apr_status_t reason, int rv) rv = 0; /* ...gracefully shut down */ break; case APR_EBADF: /* connection unusable, terminate silently */ - case APR_ECONNABORTED: - rv = NGHTTP2_ERR_EOF; - break; default: + if (APR_STATUS_IS_ECONNABORTED(reason) + || APR_STATUS_IS_ECONNRESET(reason) + || APR_STATUS_IS_EBADF(reason)) { + rv = NGHTTP2_ERR_EOF; + } break; } } @@ -744,21 +897,18 @@ apr_status_t h2_session_abort(h2_session *session, apr_status_t reason, int rv) apr_status_t h2_session_start(h2_session *session, int *rv) { apr_status_t status = APR_SUCCESS; - h2_config *config; nghttp2_settings_entry settings[3]; + size_t slen; + int win_size; AP_DEBUG_ASSERT(session); /* Start the conversation by submitting our SETTINGS frame */ *rv = 0; - config = h2_config_get(session->c); if (session->r) { const char *s, *cs; apr_size_t dlen; h2_stream * stream; - /* better for vhost matching */ - config = h2_config_rget(session->r); - /* 'h2c' mode: we should have a 'HTTP2-Settings' header with * base64 encoded client settings. */ s = apr_table_get(session->r->headers_in, "HTTP2-Settings"); @@ -789,8 +939,8 @@ apr_status_t h2_session_start(h2_session *session, int *rv) } /* Now we need to auto-open stream 1 for the request we got. */ - *rv = stream_open(session, 1); - if (*rv != 0) { + stream = h2_session_open_stream(session, 1); + if (!stream) { status = APR_EGENERAL; ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r, APLOGNO(02933) "open stream 1: %s", @@ -798,49 +948,57 @@ apr_status_t h2_session_start(h2_session *session, int *rv) return status; } - stream = h2_stream_set_get(session->streams, 1); - if (stream == NULL) { - status = APR_EGENERAL; - ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r, - APLOGNO(02934) "lookup of stream 1"); - return status; - } - - status = h2_stream_rwrite(stream, session->r); + status = h2_stream_set_request(stream, session->r); if (status != APR_SUCCESS) { return status; } - status = stream_end_headers(session, stream, 1); + status = stream_schedule(session, stream, 1); if (status != APR_SUCCESS) { return status; } } - settings[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - settings[0].value = (uint32_t)session->max_stream_count; - settings[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - settings[1].value = h2_config_geti(config, H2_CONF_WIN_SIZE); - settings[2].settings_id = NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE; - settings[2].value = 64*1024; + slen = 0; + settings[slen].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + settings[slen].value = (uint32_t)session->max_stream_count; + ++slen; + win_size = h2_config_geti(session->config, H2_CONF_WIN_SIZE); + if (win_size != H2_INITIAL_WINDOW_SIZE) { + settings[slen].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + settings[slen].value = win_size; + ++slen; + } *rv = nghttp2_submit_settings(session->ngh2, NGHTTP2_FLAG_NONE, - settings, - sizeof(settings)/sizeof(settings[0])); + settings, slen); if (*rv != 0) { status = APR_EGENERAL; ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c, APLOGNO(02935) "nghttp2_submit_settings: %s", nghttp2_strerror(*rv)); } - + else { + /* use maximum possible value for connection window size. We are only + * interested in per stream flow control. which have the initial window + * size configured above. + * Therefore, for our use, the connection window can only get in the + * way. Example: if we allow 100 streams with a 32KB window each, we + * buffer up to 3.2 MB of data. Unless we do separate connection window + * interim updates, any smaller connection window will lead to blocking + * in DATA flow. + */ + *rv = nghttp2_submit_window_update(session->ngh2, NGHTTP2_FLAG_NONE, + 0, NGHTTP2_MAX_WINDOW_SIZE - win_size); + if (*rv != 0) { + status = APR_EGENERAL; + ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c, + APLOGNO(02970) "nghttp2_submit_window_update: %s", + nghttp2_strerror(*rv)); + } + } return status; } -static int h2_session_want_write(h2_session *session) -{ - return nghttp2_session_want_write(session->ngh2); -} - typedef struct { h2_session *session; int resume_count; @@ -853,7 +1011,7 @@ static int resume_on_data(void *ctx, h2_stream *stream) { AP_DEBUG_ASSERT(stream); if (h2_stream_is_suspended(stream)) { - if (h2_mplx_out_has_data_for(stream->m, stream->id)) { + if (h2_mplx_out_has_data_for(stream->session->mplx, stream->id)) { int rv; h2_stream_set_suspended(stream, 0); ++rctx->resume_count; @@ -886,98 +1044,12 @@ static int h2_session_resume_streams_with_data(h2_session *session) { return 0; } -static void update_window(void *ctx, int stream_id, apr_size_t bytes_read) -{ - h2_session *session = (h2_session*)ctx; - nghttp2_session_consume(session->ngh2, stream_id, bytes_read); -} - -static apr_status_t h2_session_update_windows(h2_session *session) -{ - return h2_mplx_in_update_windows(session->mplx, update_window, session); -} - -apr_status_t h2_session_write(h2_session *session, apr_interval_time_t timeout) -{ - apr_status_t status = APR_EAGAIN; - h2_stream *stream = NULL; - int flush_output = 0; - - AP_DEBUG_ASSERT(session); - - /* Check that any pending window updates are sent. */ - status = h2_session_update_windows(session); - if (status == APR_SUCCESS) { - flush_output = 1; - } - else if (status != APR_EAGAIN) { - return status; - } - - if (h2_session_want_write(session)) { - int rv; - status = APR_SUCCESS; - rv = nghttp2_session_send(session->ngh2); - if (rv != 0) { - ap_log_cerror( APLOG_MARK, APLOG_DEBUG, 0, session->c, - "h2_session: send: %s", nghttp2_strerror(rv)); - if (nghttp2_is_fatal(rv)) { - h2_session_abort_int(session, rv); - status = APR_ECONNABORTED; - } - } - flush_output = 1; - } - - /* If we have responses ready, submit them now. */ - while ((stream = h2_mplx_next_submit(session->mplx, - session->streams)) != NULL) { - status = h2_session_handle_response(session, stream); - flush_output = 1; - } - - if (h2_session_resume_streams_with_data(session) > 0) { - flush_output = 1; - } - - if (!flush_output && timeout > 0 && !h2_session_want_write(session)) { - status = h2_mplx_out_trywait(session->mplx, timeout, session->iowait); - - if (status != APR_TIMEUP - && h2_session_resume_streams_with_data(session) > 0) { - flush_output = 1; - } - else { - /* nothing happened to ongoing streams, do some house-keeping */ - } - } - - if (h2_session_want_write(session)) { - int rv; - status = APR_SUCCESS; - rv = nghttp2_session_send(session->ngh2); - if (rv != 0) { - ap_log_cerror( APLOG_MARK, APLOG_DEBUG, 0, session->c, - "h2_session: send2: %s", nghttp2_strerror(rv)); - if (nghttp2_is_fatal(rv)) { - h2_session_abort_int(session, rv); - status = APR_ECONNABORTED; - } - } - flush_output = 1; - } - - if (flush_output) { - h2_conn_io_flush(&session->io); - } - - return status; -} - h2_stream *h2_session_get_stream(h2_session *session, int stream_id) { - AP_DEBUG_ASSERT(session); - return h2_stream_set_get(session->streams, stream_id); + if (!session->last_stream || stream_id != session->last_stream->id) { + session->last_stream = h2_stream_set_get(session->streams, stream_id); + } + return session->last_stream; } /* h2_io_on_read_cb implementation that offers the data read @@ -1010,20 +1082,19 @@ static apr_status_t session_receive(const char *data, apr_size_t len, return APR_SUCCESS; } -apr_status_t h2_session_read(h2_session *session, apr_read_type_e block) -{ - AP_DEBUG_ASSERT(session); - return h2_conn_io_read(&session->io, block, session_receive, session); -} - apr_status_t h2_session_close(h2_session *session) { AP_DEBUG_ASSERT(session); - return session->aborted? APR_SUCCESS : h2_conn_io_flush(&session->io); + if (!session->aborted) { + h2_session_abort_int(session, 0); + } + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0,session->c, + "h2_session: closing, writing eoc"); + + h2_session_cleanup(session); + return h2_conn_io_close(&session->io, session); } -/* The session wants to send more DATA for the given stream. - */ static ssize_t stream_data_cb(nghttp2_session *ng2s, int32_t stream_id, uint8_t *buf, @@ -1033,18 +1104,27 @@ static ssize_t stream_data_cb(nghttp2_session *ng2s, void *puser) { h2_session *session = (h2_session *)puser; - apr_size_t nread = length; + apr_off_t nread = length; int eos = 0; apr_status_t status; h2_stream *stream; AP_DEBUG_ASSERT(session); + /* The session wants to send more DATA for the stream. We need + * to find out how much of the requested length we can send without + * blocking. + * Indicate EOS when we encounter it or DEFERRED if the stream + * should be suspended. + * TODO: for handling of TRAILERS, the EOF indication needs + * to be aware of that. + */ + (void)ng2s; (void)buf; (void)source; - stream = h2_stream_set_get(session->streams, stream_id); + stream = h2_session_get_stream(session, stream_id); if (!stream) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_NOTFOUND, session->c, + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c, APLOGNO(02937) "h2_stream(%ld-%d): data requested but stream not found", session->id, (int)stream_id); @@ -1062,6 +1142,10 @@ static ssize_t stream_data_cb(nghttp2_session *ng2s, case APR_SUCCESS: break; + case APR_ECONNRESET: + return nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE, + stream->id, stream->rst_error); + case APR_EAGAIN: /* If there is no data available, our session will automatically * suspend this stream and not ask for more data until we resume @@ -1088,6 +1172,22 @@ static ssize_t stream_data_cb(nghttp2_session *ng2s, } if (eos) { + apr_table_t *trailers = h2_stream_get_trailers(stream); + if (trailers && !apr_is_empty_table(trailers)) { + h2_ngheader *nh; + int rv; + + nh = h2_util_ngheader_make(stream->pool, trailers); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_stream(%ld-%d): submit %d trailers", + session->id, (int)stream_id,(int) nh->nvlen); + rv = nghttp2_submit_trailer(ng2s, stream->id, nh->nv, nh->nvlen); + if (rv < 0) { + nread = rv; + } + *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; + } + *data_flags |= NGHTTP2_DATA_FLAG_EOF; } @@ -1100,57 +1200,84 @@ typedef struct { size_t offset; } nvctx_t; -static int submit_response(h2_session *session, h2_response *response) -{ - nghttp2_data_provider provider; - int rv; - - memset(&provider, 0, sizeof(provider)); - provider.source.fd = response->stream_id; - provider.read_callback = stream_data_cb; - - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, - "h2_stream(%ld-%d): submitting response %s", - session->id, response->stream_id, response->status); - - rv = nghttp2_submit_response(session->ngh2, response->stream_id, - response->ngheader->nv, - response->ngheader->nvlen, &provider); - - if (rv != 0) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c, - APLOGNO(02939) "h2_stream(%ld-%d): submit_response: %s", - session->id, response->stream_id, nghttp2_strerror(rv)); - } - else { - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, - "h2_stream(%ld-%d): submitted response %s, rv=%d", - session->id, response->stream_id, - response->status, rv); - } - return rv; -} - -/* Start submitting the response to a stream request. This is possible +/** + * Start submitting the response to a stream request. This is possible * once we have all the response headers. The response body will be * read by the session using the callback we supply. */ -apr_status_t h2_session_handle_response(h2_session *session, h2_stream *stream) +static apr_status_t submit_response(h2_session *session, h2_stream *stream) { apr_status_t status = APR_SUCCESS; int rv = 0; AP_DEBUG_ASSERT(session); AP_DEBUG_ASSERT(stream); - AP_DEBUG_ASSERT(stream->response); + AP_DEBUG_ASSERT(stream->response || stream->rst_error); - if (stream->response->ngheader) { - rv = submit_response(session, stream->response); + if (stream->submitted) { + rv = NGHTTP2_PROTOCOL_ERROR; + } + else if (stream->response && stream->response->headers) { + nghttp2_data_provider provider; + h2_response *response = stream->response; + h2_ngheader *ngh; + const h2_priority *prio; + + memset(&provider, 0, sizeof(provider)); + provider.source.fd = stream->id; + provider.read_callback = stream_data_cb; + + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_stream(%ld-%d): submit response %d", + session->id, stream->id, response->http_status); + + /* If this stream is not a pushed one itself, + * and HTTP/2 server push is enabled here, + * and the response is in the range 200-299 *), + * and the remote side has pushing enabled, + * -> find and perform any pushes on this stream + * *before* we submit the stream response itself. + * This helps clients avoid opening new streams on Link + * headers that get pushed right afterwards. + * + * *) the response code is relevant, as we do not want to + * make pushes on 401 or 403 codes, neiterh on 301/302 + * and friends. And if we see a 304, we do not push either + * as the client, having this resource in its cache, might + * also have the pushed ones as well. + */ + if (!stream->initiated_on + && h2_config_geti(session->config, H2_CONF_PUSH) + && H2_HTTP_2XX(response->http_status) + && h2_session_push_enabled(session)) { + + h2_stream_submit_pushes(stream); + } + + prio = h2_stream_get_priority(stream); + if (prio) { + h2_session_set_prio(session, stream, prio); + /* no showstopper if that fails for some reason */ + } + + ngh = h2_util_ngheader_make_res(stream->pool, response->http_status, + response->headers); + rv = nghttp2_submit_response(session->ngh2, response->stream_id, + ngh->nv, ngh->nvlen, &provider); + } else { + int err = H2_STREAM_RST(stream, H2_ERR_PROTOCOL_ERROR); + + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_stream(%ld-%d): RST_STREAM, err=%d", + session->id, stream->id, err); + rv = nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE, - stream->id, NGHTTP2_PROTOCOL_ERROR); + stream->id, err); } + stream->submitted = 1; + if (nghttp2_is_fatal(rv)) { status = APR_EGENERAL; h2_session_abort_int(session, rv); @@ -1158,37 +1285,191 @@ apr_status_t h2_session_handle_response(h2_session *session, h2_stream *stream) APLOGNO(02940) "submit_response: %s", nghttp2_strerror(rv)); } + return status; } -int h2_session_is_done(h2_session *session) +struct h2_stream *h2_session_push(h2_session *session, h2_stream *is, + h2_push *push) { - AP_DEBUG_ASSERT(session); - return (session->aborted - || !session->ngh2 - || (!nghttp2_session_want_read(session->ngh2) - && !nghttp2_session_want_write(session->ngh2))); + apr_status_t status; + h2_stream *stream; + h2_ngheader *ngh; + int nid; + + ngh = h2_util_ngheader_make_req(is->pool, push->req); + nid = nghttp2_submit_push_promise(session->ngh2, 0, is->id, + ngh->nv, ngh->nvlen, NULL); + + if (nid <= 0) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_stream(%ld-%d): submitting push promise fail: %s", + session->id, is->id, nghttp2_strerror(nid)); + return NULL; + } + + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_stream(%ld-%d): promised new stream %d for %s %s on %d", + session->id, is->id, nid, + push->req->method, push->req->path, is->id); + + stream = h2_session_open_stream(session, nid); + if (stream) { + h2_stream_set_h2_request(stream, is->id, push->req); + status = stream_schedule(session, stream, 1); + if (status != APR_SUCCESS) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, + "h2_stream(%ld-%d): scheduling push stream", + session->id, stream->id); + h2_stream_cleanup(stream); + stream = NULL; + } + ++session->unsent_promises; + } + else { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_stream(%ld-%d): failed to create stream obj %d", + session->id, is->id, nid); + } + + if (!stream) { + /* try to tell the client that it should not wait. */ + nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE, nid, + NGHTTP2_INTERNAL_ERROR); + } + + return stream; } -static int log_stream(void *ctx, h2_stream *stream) +static int valid_weight(float f) { - h2_session *session = (h2_session *)ctx; - AP_DEBUG_ASSERT(session); - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, - "h2_stream(%ld-%d): in set, suspended=%d, aborted=%d, " - "has_data=%d", - session->id, stream->id, stream->suspended, stream->aborted, - h2_mplx_out_has_data_for(session->mplx, stream->id)); - return 1; + int w = (int)f; + return (w < NGHTTP2_MIN_WEIGHT? NGHTTP2_MIN_WEIGHT : + (w > NGHTTP2_MAX_WEIGHT)? NGHTTP2_MAX_WEIGHT : w); } -void h2_session_log_stats(h2_session *session) +apr_status_t h2_session_set_prio(h2_session *session, h2_stream *stream, + const h2_priority *prio) { - AP_DEBUG_ASSERT(session); - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, - "h2_session(%ld): %d open streams", - session->id, (int)h2_stream_set_size(session->streams)); - h2_stream_set_iter(session->streams, log_stream, session); + apr_status_t status = APR_SUCCESS; +#ifdef H2_NG2_CHANGE_PRIO + nghttp2_stream *s_grandpa, *s_parent, *s; + + s = nghttp2_session_find_stream(session->ngh2, stream->id); + if (!s) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_stream(%ld-%d): lookup of nghttp2_stream failed", + session->id, stream->id); + return APR_EINVAL; + } + + s_parent = nghttp2_stream_get_parent(s); + if (s_parent) { + nghttp2_priority_spec ps; + int id_parent, id_grandpa, w_parent, w, rv = 0; + char *ptype = "AFTER"; + h2_dependency dep = prio->dependency; + + id_parent = nghttp2_stream_get_stream_id(s_parent); + s_grandpa = nghttp2_stream_get_parent(s_parent); + if (s_grandpa) { + id_grandpa = nghttp2_stream_get_stream_id(s_grandpa); + } + else { + /* parent of parent does not exist, + * only possible if parent == root */ + dep = H2_DEPENDANT_AFTER; + } + + switch (dep) { + case H2_DEPENDANT_INTERLEAVED: + /* PUSHed stream is to be interleaved with initiating stream. + * It is made a sibling of the initiating stream and gets a + * proportional weight [1, MAX_WEIGHT] of the initiaing + * stream weight. + */ + ptype = "INTERLEAVED"; + w_parent = nghttp2_stream_get_weight(s_parent); + w = valid_weight(w_parent * ((float)prio->weight / NGHTTP2_MAX_WEIGHT)); + nghttp2_priority_spec_init(&ps, id_grandpa, w, 0); + break; + + case H2_DEPENDANT_BEFORE: + /* PUSHed stream os to be sent BEFORE the initiating stream. + * It gets the same weight as the initiating stream, replaces + * that stream in the dependency tree and has the initiating + * stream as child. + */ + ptype = "BEFORE"; + w = w_parent = nghttp2_stream_get_weight(s_parent); + nghttp2_priority_spec_init(&ps, stream->id, w_parent, 0); + id_grandpa = nghttp2_stream_get_stream_id(s_grandpa); + rv = nghttp2_session_change_stream_priority(session->ngh2, id_parent, &ps); + if (rv < 0) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_stream(%ld-%d): PUSH BEFORE2, weight=%d, " + "depends=%d, returned=%d", + session->id, id_parent, ps.weight, ps.stream_id, rv); + return APR_EGENERAL; + } + nghttp2_priority_spec_init(&ps, id_grandpa, w, 0); + break; + + case H2_DEPENDANT_AFTER: + /* The PUSHed stream is to be sent after the initiating stream. + * Give if the specified weight and let it depend on the intiating + * stream. + */ + /* fall through, it's the default */ + default: + nghttp2_priority_spec_init(&ps, id_parent, valid_weight(prio->weight), 0); + break; + } + + + rv = nghttp2_session_change_stream_priority(session->ngh2, stream->id, &ps); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_stream(%ld-%d): PUSH %s, weight=%d, " + "depends=%d, returned=%d", + session->id, stream->id, ptype, + ps.weight, ps.stream_id, rv); + status = (rv < 0)? APR_EGENERAL : APR_SUCCESS; + } +#else + (void)session; + (void)stream; + (void)prio; + (void)valid_weight; +#endif + return status; +} + +apr_status_t h2_session_stream_destroy(h2_session *session, h2_stream *stream) +{ + apr_pool_t *pool = h2_stream_detach_pool(stream); + + /* this may be called while the session has already freed + * some internal structures. */ + if (session->mplx) { + h2_mplx_stream_done(session->mplx, stream->id, stream->rst_error); + if (session->last_stream == stream) { + session->last_stream = NULL; + } + } + + if (session->streams) { + h2_stream_set_remove(session->streams, stream->id); + } + h2_stream_destroy(stream); + + if (pool) { + apr_pool_clear(pool); + if (session->spare) { + apr_pool_destroy(session->spare); + } + session->spare = pool; + } + return APR_SUCCESS; } static int frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen) @@ -1268,3 +1549,218 @@ static int frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen) } } +int h2_session_push_enabled(h2_session *session) +{ + return nghttp2_session_get_remote_settings(session->ngh2, + NGHTTP2_SETTINGS_ENABLE_PUSH); +} + + +apr_status_t h2_session_process(h2_session *session) +{ + apr_status_t status = APR_SUCCESS; + apr_interval_time_t wait_micros = 0; + static const int MAX_WAIT_MICROS = 200 * 1000; + int got_streams = 0; + h2_stream *stream; + + while (!session->aborted && (nghttp2_session_want_read(session->ngh2) + || nghttp2_session_want_write(session->ngh2))) { + int have_written = 0; + int have_read = 0; + + got_streams = !h2_stream_set_is_empty(session->streams); + if (got_streams) { + h2_session_resume_streams_with_data(session); + + if (h2_stream_set_has_unsubmitted(session->streams)) { + int unsent_submits = 0; + + /* If we have responses ready, submit them now. */ + while ((stream = h2_mplx_next_submit(session->mplx, session->streams))) { + status = submit_response(session, stream); + ++unsent_submits; + + /* Unsent push promises are written immediately, as nghttp2 + * 1.5.0 realizes internal stream data structures only on + * send and we might need them for other submits. + * Also, to conserve memory, we send at least every 10 submits + * so that nghttp2 does not buffer all outbound items too + * long. + */ + if (status == APR_SUCCESS + && (session->unsent_promises || unsent_submits > 10)) { + int rv = nghttp2_session_send(session->ngh2); + if (rv != 0) { + ap_log_cerror( APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_session: send: %s", nghttp2_strerror(rv)); + if (nghttp2_is_fatal(rv)) { + h2_session_abort(session, status, rv); + goto end_process; + } + } + else { + have_written = 1; + wait_micros = 0; + session->unsent_promises = 0; + unsent_submits = 0; + } + } + } + } + } + + /* Send data as long as we have it and window sizes allow. We are + * a server after all. + */ + if (nghttp2_session_want_write(session->ngh2)) { + int rv; + + rv = nghttp2_session_send(session->ngh2); + if (rv != 0) { + ap_log_cerror( APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_session: send: %s", nghttp2_strerror(rv)); + if (nghttp2_is_fatal(rv)) { + h2_session_abort(session, status, rv); + goto end_process; + } + } + else { + have_written = 1; + wait_micros = 0; + session->unsent_promises = 0; + } + } + + if (wait_micros > 0) { + if (APLOGcdebug(session->c)) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_session: wait for data, %ld micros", + (long)wait_micros); + } + nghttp2_session_send(session->ngh2); + h2_conn_io_flush(&session->io); + status = h2_mplx_out_trywait(session->mplx, wait_micros, session->iowait); + + if (status == APR_TIMEUP) { + if (wait_micros < MAX_WAIT_MICROS) { + wait_micros *= 2; + } + } + } + + if (nghttp2_session_want_read(session->ngh2)) + { + /* When we + * - and have no streams at all + * - or have streams, but none is suspended or needs submit and + * have nothing written on the last try + * + * or, the other way around + * - have only streams where data can be sent, but could + * not send anything + * + * then we are waiting on frames from the client (for + * example WINDOW_UPDATE or HEADER) and without new frames + * from the client, we cannot make any progress, + * + * and *then* we can safely do a blocking read. + */ + int may_block = (session->frames_received <= 1); + if (!may_block) { + if (got_streams) { + may_block = (!have_written + && !h2_stream_set_has_unsubmitted(session->streams) + && !h2_stream_set_has_suspended(session->streams)); + } + else { + may_block = 1; + } + } + + if (may_block) { + h2_conn_io_flush(&session->io); + if (session->c->cs) { + session->c->cs->state = (got_streams? CONN_STATE_HANDLER + : CONN_STATE_WRITE_COMPLETION); + } + status = h2_conn_io_read(&session->io, APR_BLOCK_READ, + session_receive, session); + } + else { + if (session->c->cs) { + session->c->cs->state = CONN_STATE_HANDLER; + } + status = h2_conn_io_read(&session->io, APR_NONBLOCK_READ, + session_receive, session); + } + + switch (status) { + case APR_SUCCESS: /* successful read, reset our idle timers */ + have_read = 1; + wait_micros = 0; + break; + case APR_EAGAIN: /* non-blocking read, nothing there */ + break; + default: + if (APR_STATUS_IS_ETIMEDOUT(status) + || APR_STATUS_IS_ECONNABORTED(status) + || APR_STATUS_IS_ECONNRESET(status) + || APR_STATUS_IS_EOF(status) + || APR_STATUS_IS_EBADF(status)) { + /* common status for a client that has left */ + ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c, + "h2_session(%ld): terminating", + session->id); + /* Stolen from mod_reqtimeout to speed up lingering when + * a read timeout happened. + */ + apr_table_setn(session->c->notes, "short-lingering-close", "1"); + } + else { + /* uncommon status, log on INFO so that we see this */ + ap_log_cerror( APLOG_MARK, APLOG_INFO, status, session->c, + APLOGNO(02950) + "h2_session(%ld): error reading, terminating", + session->id); + } + h2_session_abort(session, status, 0); + goto end_process; + } + } + + got_streams = !h2_stream_set_is_empty(session->streams); + if (got_streams) { + if (session->reprioritize) { + h2_mplx_reprioritize(session->mplx, stream_pri_cmp, session); + session->reprioritize = 0; + } + + if (!have_read && !have_written) { + /* Nothing read or written. That means no data yet ready to + * be send out. Slowly back off... + */ + if (wait_micros == 0) { + wait_micros = 10; + } + } + + /* Check that any pending window updates are sent. */ + status = h2_mplx_in_update_windows(session->mplx); + if (APR_STATUS_IS_EAGAIN(status)) { + status = APR_SUCCESS; + } + else if (status == APR_SUCCESS) { + /* need to flush window updates onto the connection asap */ + h2_conn_io_flush(&session->io); + } + } + + if (have_written) { + h2_conn_io_flush(&session->io); + } + } + +end_process: + return status; +} diff --git a/modules/http2/h2_session.h b/modules/http2/h2_session.h index 12768a90..cc260808 100644 --- a/modules/http2/h2_session.h +++ b/modules/http2/h2_session.h @@ -41,6 +41,8 @@ struct apr_thread_mutext_t; struct apr_thread_cond_t; struct h2_config; struct h2_mplx; +struct h2_priority; +struct h2_push; struct h2_response; struct h2_session; struct h2_stream; @@ -57,7 +59,12 @@ struct h2_session { conn_rec *c; /* the connection this session serves */ request_rec *r; /* the request that started this in case * of 'h2c', NULL otherwise */ + const struct h2_config *config; /* Relevant config for this session */ int aborted; /* this session is being aborted */ + int reprioritize; /* scheduled streams priority needs to + * be re-evaluated */ + int unsent_promises; /* number of submitted, but not yet sent + * push promised */ apr_size_t frames_received; /* number of http/2 frames received */ apr_size_t max_stream_count; /* max number of open streams */ apr_size_t max_stream_mem; /* max buffer memory for a single stream */ @@ -69,11 +76,14 @@ struct h2_session { h2_conn_io io; /* io on httpd conn filters */ struct h2_mplx *mplx; /* multiplexer for stream data */ + struct h2_stream *last_stream; /* last stream worked with */ struct h2_stream_set *streams; /* streams handled by this session */ int max_stream_received; /* highest stream id created */ int max_stream_handled; /* highest stream id handled successfully */ + apr_pool_t *spare; /* spare stream pool */ + struct nghttp2_session *ngh2; /* the nghttp2 session (internal use) */ struct h2_workers *workers; /* for executing stream tasks */ }; @@ -87,7 +97,7 @@ struct h2_session { * @param workers the worker pool to use * @return the created session */ -h2_session *h2_session_create(conn_rec *c, struct h2_config *cfg, +h2_session *h2_session_create(conn_rec *c, const struct h2_config *cfg, struct h2_workers *workers); /** @@ -98,9 +108,17 @@ h2_session *h2_session_create(conn_rec *c, struct h2_config *cfg, * @param workers the worker pool to use * @return the created session */ -h2_session *h2_session_rcreate(request_rec *r, struct h2_config *cfg, +h2_session *h2_session_rcreate(request_rec *r, const struct h2_config *cfg, struct h2_workers *workers); +/** + * Process the given HTTP/2 session until it is ended or a fatal + * error occured. + * + * @param session the sessionm to process + */ +apr_status_t h2_session_process(h2_session *session); + /** * Destroy the session and all objects it still contains. This will not * destroy h2_task instances that have not finished yet. @@ -108,6 +126,13 @@ h2_session *h2_session_rcreate(request_rec *r, struct h2_config *cfg, */ void h2_session_destroy(h2_session *session); +/** + * Cleanup the session and all objects it still contains. This will not + * destroy h2_task instances that have not finished yet. + * @param session the session to destroy + */ +void h2_session_eoc_callback(h2_session *session); + /** * Called once at start of session. * Sets up the session and sends the initial SETTINGS frame. @@ -117,12 +142,6 @@ void h2_session_destroy(h2_session *session); */ apr_status_t h2_session_start(h2_session *session, int *rv); -/** - * Determine if session is finished. - * @return != 0 iff session is finished and connection can be closed. - */ -int h2_session_is_done(h2_session *session); - /** * Called when an error occured and the session needs to shut down. * @param session the session to shut down @@ -137,19 +156,6 @@ apr_status_t h2_session_abort(h2_session *session, apr_status_t reason, int rv); */ apr_status_t h2_session_close(h2_session *session); -/* Read more data from the client connection. Used normally with blocking - * APR_NONBLOCK_READ, which will return APR_EAGAIN when no data is available. - * Use with APR_BLOCK_READ only when certain that no data needs to be written - * while waiting. */ -apr_status_t h2_session_read(h2_session *session, apr_read_type_e block); - -/* Write data out to the client, if there is any. Otherwise, wait for - * a maximum of timeout micro-seconds and return to the caller. If timeout - * occurred, APR_TIMEUP will be returned. - */ -apr_status_t h2_session_write(h2_session *session, - apr_interval_time_t timeout); - /* Start submitting the response to a stream request. This is possible * once we have all the response headers. */ apr_status_t h2_session_handle_response(h2_session *session, @@ -158,6 +164,44 @@ apr_status_t h2_session_handle_response(h2_session *session, /* Get the h2_stream for the given stream idenrtifier. */ struct h2_stream *h2_session_get_stream(h2_session *session, int stream_id); -void h2_session_log_stats(h2_session *session); +/** + * Create and register a new stream under the given id. + * + * @param session the session to register in + * @param stream_id the new stream identifier + * @return the new stream + */ +struct h2_stream *h2_session_open_stream(h2_session *session, int stream_id); + +/** + * Returns if client settings have push enabled. + * @param != 0 iff push is enabled in client settings + */ +int h2_session_push_enabled(h2_session *session); + +/** + * Destroy the stream and release it everywhere. Reclaim all resources. + * @param session the session to which the stream belongs + * @param stream the stream to destroy + */ +apr_status_t h2_session_stream_destroy(h2_session *session, + struct h2_stream *stream); + +/** + * Submit a push promise on the stream and schedule the new steam for + * processing.. + * + * @param session the session to work in + * @param is the stream initiating the push + * @param push the push to promise + * @return the new promised stream or NULL + */ +struct h2_stream *h2_session_push(h2_session *session, + struct h2_stream *is, struct h2_push *push); + +apr_status_t h2_session_set_prio(h2_session *session, + struct h2_stream *stream, + const struct h2_priority *prio); + #endif /* defined(__mod_h2__h2_session__) */ diff --git a/modules/http2/h2_stream.c b/modules/http2/h2_stream.c index 52781d84..58f722bc 100644 --- a/modules/http2/h2_stream.c +++ b/modules/http2/h2_stream.c @@ -16,8 +16,6 @@ #include #include -#define APR_POOL_DEBUG 7 - #include #include #include @@ -27,9 +25,13 @@ #include "h2_private.h" #include "h2_conn.h" +#include "h2_config.h" +#include "h2_h2.h" #include "h2_mplx.h" +#include "h2_push.h" #include "h2_request.h" #include "h2_response.h" +#include "h2_session.h" #include "h2_stream.h" #include "h2_task.h" #include "h2_ctx.h" @@ -38,57 +40,153 @@ #include "h2_util.h" -static void set_state(h2_stream *stream, h2_stream_state_t state) +#define H2_STREAM_OUT(lvl,s,msg) \ + do { \ + if (APLOG_C_IS_LEVEL((s)->session->c,lvl)) \ + h2_util_bb_log((s)->session->c,(s)->id,lvl,msg,(s)->bbout); \ + } while(0) +#define H2_STREAM_IN(lvl,s,msg) \ + do { \ + if (APLOG_C_IS_LEVEL((s)->session->c,lvl)) \ + h2_util_bb_log((s)->session->c,(s)->id,lvl,msg,(s)->bbin); \ + } while(0) + + +static int state_transition[][7] = { + /* ID OP RL RR CI CO CL */ +/*ID*/{ 1, 0, 0, 0, 0, 0, 0 }, +/*OP*/{ 1, 1, 0, 0, 0, 0, 0 }, +/*RL*/{ 0, 0, 1, 0, 0, 0, 0 }, +/*RR*/{ 0, 0, 0, 1, 0, 0, 0 }, +/*CI*/{ 1, 1, 0, 0, 1, 0, 0 }, +/*CO*/{ 1, 1, 0, 0, 0, 1, 0 }, +/*CL*/{ 1, 1, 0, 0, 1, 1, 1 }, +}; + +static int set_state(h2_stream *stream, h2_stream_state_t state) { - AP_DEBUG_ASSERT(stream); - if (stream->state != state) { + int allowed = state_transition[state][stream->state]; + if (allowed) { stream->state = state; + return 1; } + + ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, stream->session->c, + "h2_stream(%ld-%d): invalid state transition from %d to %d", + stream->session->id, stream->id, stream->state, state); + return 0; } -h2_stream *h2_stream_create(int id, apr_pool_t *pool, struct h2_mplx *m) +static int close_input(h2_stream *stream) { - h2_stream *stream = apr_pcalloc(pool, sizeof(h2_stream)); - if (stream != NULL) { - stream->id = id; - stream->state = H2_STREAM_ST_IDLE; - stream->pool = pool; - stream->m = m; - stream->request = h2_request_create(id, pool, m->c->bucket_alloc); - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, - "h2_stream(%ld-%d): created", m->id, stream->id); + switch (stream->state) { + case H2_STREAM_ST_CLOSED_INPUT: + case H2_STREAM_ST_CLOSED: + return 0; /* ignore, idempotent */ + case H2_STREAM_ST_CLOSED_OUTPUT: + /* both closed now */ + set_state(stream, H2_STREAM_ST_CLOSED); + break; + default: + /* everything else we jump to here */ + set_state(stream, H2_STREAM_ST_CLOSED_INPUT); + break; } - return stream; + return 1; } -static void h2_stream_cleanup(h2_stream *stream) +static int input_closed(h2_stream *stream) { - if (stream->request) { - h2_request_destroy(stream->request); - stream->request = NULL; + switch (stream->state) { + case H2_STREAM_ST_OPEN: + case H2_STREAM_ST_CLOSED_OUTPUT: + return 0; + default: + return 1; } } +static int close_output(h2_stream *stream) +{ + switch (stream->state) { + case H2_STREAM_ST_CLOSED_OUTPUT: + case H2_STREAM_ST_CLOSED: + return 0; /* ignore, idempotent */ + case H2_STREAM_ST_CLOSED_INPUT: + /* both closed now */ + set_state(stream, H2_STREAM_ST_CLOSED); + break; + default: + /* everything else we jump to here */ + set_state(stream, H2_STREAM_ST_CLOSED_OUTPUT); + break; + } + return 1; +} + +static int input_open(h2_stream *stream) +{ + switch (stream->state) { + case H2_STREAM_ST_OPEN: + case H2_STREAM_ST_CLOSED_OUTPUT: + return 1; + default: + return 0; + } +} + +static int output_open(h2_stream *stream) +{ + switch (stream->state) { + case H2_STREAM_ST_OPEN: + case H2_STREAM_ST_CLOSED_INPUT: + return 1; + default: + return 0; + } +} + +h2_stream *h2_stream_create(int id, apr_pool_t *pool, h2_session *session) +{ + h2_stream *stream = apr_pcalloc(pool, sizeof(h2_stream)); + stream->id = id; + stream->state = H2_STREAM_ST_IDLE; + stream->pool = pool; + stream->session = session; + return stream; +} + +h2_stream *h2_stream_open(int id, apr_pool_t *pool, h2_session *session) +{ + h2_stream *stream = h2_stream_create(id, pool, session); + set_state(stream, H2_STREAM_ST_OPEN); + stream->request = h2_request_create(id, pool, session->config); + stream->bbout = apr_brigade_create(stream->pool, + stream->session->c->bucket_alloc); + + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, + "h2_stream(%ld-%d): opened", session->id, stream->id); + return stream; +} + apr_status_t h2_stream_destroy(h2_stream *stream) { AP_DEBUG_ASSERT(stream); - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->m->c, - "h2_stream(%ld-%d): destroy", stream->m->id, stream->id); - h2_stream_cleanup(stream); - - if (stream->task) { - h2_task_destroy(stream->task); - stream->task = NULL; + if (stream->request) { + h2_request_destroy(stream->request); + stream->request = NULL; } + if (stream->pool) { apr_pool_destroy(stream->pool); } return APR_SUCCESS; } -void h2_stream_attach_pool(h2_stream *stream, apr_pool_t *pool) +void h2_stream_cleanup(h2_stream *stream) { - stream->pool = pool; + h2_session_stream_destroy(stream->session, stream); + /* stream is gone */ } apr_pool_t *h2_stream_detach_pool(h2_stream *stream) @@ -98,171 +196,426 @@ apr_pool_t *h2_stream_detach_pool(h2_stream *stream) return pool; } -void h2_stream_abort(h2_stream *stream) +void h2_stream_rst(h2_stream *stream, int error_code) { - AP_DEBUG_ASSERT(stream); - stream->aborted = 1; + stream->rst_error = error_code; + close_input(stream); + close_output(stream); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->session->c, + "h2_stream(%ld-%d): reset, error=%d", + stream->session->id, stream->id, error_code); } apr_status_t h2_stream_set_response(h2_stream *stream, h2_response *response, apr_bucket_brigade *bb) { + apr_status_t status = APR_SUCCESS; + if (!output_open(stream)) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->session->c, + "h2_stream(%ld-%d): output closed", + stream->session->id, stream->id); + return APR_ECONNRESET; + } + stream->response = response; if (bb && !APR_BRIGADE_EMPTY(bb)) { - if (!stream->bbout) { - stream->bbout = apr_brigade_create(stream->pool, - stream->m->c->bucket_alloc); - } - return h2_util_move(stream->bbout, bb, 16 * 1024, NULL, - "h2_stream_set_response"); + int move_all = INT_MAX; + /* we can move file handles from h2_mplx into this h2_stream as many + * as we want, since the lifetimes are the same and we are not freeing + * the ones in h2_mplx->io before this stream is done. */ + H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream set_response_pre"); + status = h2_util_move(stream->bbout, bb, 16 * 1024, &move_all, + "h2_stream_set_response"); + H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream set_response_post"); } - return APR_SUCCESS; + + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, stream->session->c, + "h2_stream(%ld-%d): set_response(%d)", + stream->session->id, stream->id, response->http_status); + return status; } -static int set_closed(h2_stream *stream) +apr_status_t h2_stream_set_request(h2_stream *stream, request_rec *r) { - switch (stream->state) { - case H2_STREAM_ST_CLOSED_INPUT: - case H2_STREAM_ST_CLOSED: - return 0; /* ignore, idempotent */ - case H2_STREAM_ST_CLOSED_OUTPUT: - /* both closed now */ - set_state(stream, H2_STREAM_ST_CLOSED); - break; - default: - /* everything else we jump to here */ - set_state(stream, H2_STREAM_ST_CLOSED_INPUT); - break; + apr_status_t status; + AP_DEBUG_ASSERT(stream); + if (stream->rst_error) { + return APR_ECONNRESET; } - return 1; + set_state(stream, H2_STREAM_ST_OPEN); + status = h2_request_rwrite(stream->request, r); + return status; } -apr_status_t h2_stream_rwrite(h2_stream *stream, request_rec *r) +void h2_stream_set_h2_request(h2_stream *stream, int initiated_on, + const h2_request *req) +{ + h2_request_copy(stream->pool, stream->request, req); + stream->initiated_on = initiated_on; + stream->request->eoh = 0; +} + +apr_status_t h2_stream_add_header(h2_stream *stream, + const char *name, size_t nlen, + const char *value, size_t vlen) { - apr_status_t status; AP_DEBUG_ASSERT(stream); - set_state(stream, H2_STREAM_ST_OPEN); - status = h2_request_rwrite(stream->request, r, stream->m); - return status; + if (h2_stream_is_scheduled(stream)) { + return h2_request_add_trailer(stream->request, stream->pool, + name, nlen, value, vlen); + } + else { + if (!input_open(stream)) { + return APR_ECONNRESET; + } + return h2_request_add_header(stream->request, stream->pool, + name, nlen, value, vlen); + } } -apr_status_t h2_stream_write_eoh(h2_stream *stream, int eos) +apr_status_t h2_stream_schedule(h2_stream *stream, int eos, + h2_stream_pri_cmp *cmp, void *ctx) { apr_status_t status; AP_DEBUG_ASSERT(stream); + AP_DEBUG_ASSERT(stream->session); + AP_DEBUG_ASSERT(stream->session->mplx); + + if (!output_open(stream)) { + return APR_ECONNRESET; + } + if (stream->scheduled) { + return APR_EINVAL; + } + if (eos) { + close_input(stream); + } /* Seeing the end-of-headers, we have everything we need to * start processing it. */ - status = h2_mplx_create_task(stream->m, stream); + status = h2_request_end_headers(stream->request, stream->pool, eos); if (status == APR_SUCCESS) { - status = h2_request_end_headers(stream->request, - stream->m, stream->task, eos); - if (status == APR_SUCCESS) { - status = h2_mplx_do_task(stream->m, stream->task); + if (!eos) { + stream->bbin = apr_brigade_create(stream->pool, + stream->session->c->bucket_alloc); } - if (eos) { - status = h2_stream_write_eos(stream); - } - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, stream->m->c, - "h2_mplx(%ld-%d): start stream, task %s %s (%s)", - stream->m->id, stream->id, - stream->request->method, stream->request->path, - stream->request->authority); + stream->input_remaining = stream->request->content_length; + + status = h2_mplx_process(stream->session->mplx, stream->id, + stream->request, eos, cmp, ctx); + stream->scheduled = 1; + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->session->c, + "h2_stream(%ld-%d): scheduled %s %s://%s%s", + stream->session->id, stream->id, + stream->request->method, stream->request->scheme, + stream->request->authority, stream->request->path); } + else { + h2_stream_rst(stream, H2_ERR_INTERNAL_ERROR); + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->session->c, + "h2_stream(%ld-%d): RST=2 (internal err) %s %s://%s%s", + stream->session->id, stream->id, + stream->request->method, stream->request->scheme, + stream->request->authority, stream->request->path); + } + return status; } -apr_status_t h2_stream_write_eos(h2_stream *stream) +int h2_stream_is_scheduled(h2_stream *stream) { - AP_DEBUG_ASSERT(stream); - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->m->c, - "h2_stream(%ld-%d): closing input", - stream->m->id, stream->id); - if (set_closed(stream)) { - return h2_request_close(stream->request); + return stream->scheduled; +} + +static apr_status_t h2_stream_input_flush(h2_stream *stream) +{ + apr_status_t status = APR_SUCCESS; + if (stream->bbin && !APR_BRIGADE_EMPTY(stream->bbin)) { + + status = h2_mplx_in_write(stream->session->mplx, stream->id, stream->bbin); + if (status != APR_SUCCESS) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, stream->session->mplx->c, + "h2_stream(%ld-%d): flushing input data", + stream->session->id, stream->id); + } } - return APR_SUCCESS; + return status; } -apr_status_t h2_stream_write_header(h2_stream *stream, - const char *name, size_t nlen, - const char *value, size_t vlen) +static apr_status_t input_flush(apr_bucket_brigade *bb, void *ctx) { + (void)bb; + return h2_stream_input_flush(ctx); +} + +static apr_status_t input_add_data(h2_stream *stream, + const char *data, size_t len, int chunked) +{ + apr_status_t status = APR_SUCCESS; + + if (chunked) { + status = apr_brigade_printf(stream->bbin, input_flush, stream, + "%lx\r\n", (unsigned long)len); + if (status == APR_SUCCESS) { + status = apr_brigade_write(stream->bbin, input_flush, stream, data, len); + if (status == APR_SUCCESS) { + status = apr_brigade_puts(stream->bbin, input_flush, stream, "\r\n"); + } + } + } + else { + status = apr_brigade_write(stream->bbin, input_flush, stream, data, len); + } + return status; +} + +static int input_add_header(void *str, const char *key, const char *value) +{ + h2_stream *stream = str; + apr_status_t status = input_add_data(stream, key, strlen(key), 0); + if (status == APR_SUCCESS) { + status = input_add_data(stream, ": ", 2, 0); + if (status == APR_SUCCESS) { + status = input_add_data(stream, value, strlen(value), 0); + if (status == APR_SUCCESS) { + status = input_add_data(stream, "\r\n", 2, 0); + } + } + } + return (status == APR_SUCCESS); +} + +apr_status_t h2_stream_close_input(h2_stream *stream) +{ + apr_status_t status = APR_SUCCESS; + AP_DEBUG_ASSERT(stream); - switch (stream->state) { - case H2_STREAM_ST_IDLE: - set_state(stream, H2_STREAM_ST_OPEN); - break; - case H2_STREAM_ST_OPEN: - break; - default: - return APR_EINVAL; + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->session->c, + "h2_stream(%ld-%d): closing input", + stream->session->id, stream->id); + + if (stream->rst_error) { + return APR_ECONNRESET; } - return h2_request_write_header(stream->request, name, nlen, - value, vlen, stream->m); + + H2_STREAM_IN(APLOG_TRACE2, stream, "close_pre"); + if (close_input(stream) && stream->bbin) { + if (stream->request->chunked) { + apr_table_t *trailers = stream->request->trailers; + if (trailers && !apr_is_empty_table(trailers)) { + status = input_add_data(stream, "0\r\n", 3, 0); + apr_table_do(input_add_header, stream, trailers, NULL); + status = input_add_data(stream, "\r\n", 2, 0); + } + else { + status = input_add_data(stream, "0\r\n\r\n", 5, 0); + } + } + + if (status == APR_SUCCESS) { + status = h2_stream_input_flush(stream); + } + if (status == APR_SUCCESS) { + status = h2_mplx_in_close(stream->session->mplx, stream->id); + } + } + H2_STREAM_IN(APLOG_TRACE2, stream, "close_post"); + return status; } apr_status_t h2_stream_write_data(h2_stream *stream, const char *data, size_t len) { + apr_status_t status = APR_SUCCESS; + AP_DEBUG_ASSERT(stream); - AP_DEBUG_ASSERT(stream); - switch (stream->state) { - case H2_STREAM_ST_OPEN: - break; - default: - return APR_EINVAL; + if (input_closed(stream) || !stream->request->eoh || !stream->bbin) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->session->c, + "h2_stream(%ld-%d): writing denied, closed=%d, eoh=%d, bbin=%d", + stream->session->id, stream->id, input_closed(stream), + stream->request->eoh, !!stream->bbin); + return APR_EINVAL; + } + + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->session->c, + "h2_stream(%ld-%d): add %ld input bytes", + stream->session->id, stream->id, (long)len); + + H2_STREAM_IN(APLOG_TRACE2, stream, "write_data_pre"); + if (stream->request->chunked) { + /* if input may have a body and we have not seen any + * content-length header, we need to chunk the input data. + */ + status = input_add_data(stream, data, len, 1); } - return h2_request_write_data(stream->request, data, len); + else { + stream->input_remaining -= len; + if (stream->input_remaining < 0) { + ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, stream->session->c, + APLOGNO(02961) + "h2_stream(%ld-%d): got %ld more content bytes than announced " + "in content-length header: %ld", + stream->session->id, stream->id, + (long)stream->request->content_length, + -(long)stream->input_remaining); + h2_stream_rst(stream, H2_ERR_PROTOCOL_ERROR); + return APR_ECONNABORTED; + } + status = input_add_data(stream, data, len, 0); + } + if (status == APR_SUCCESS) { + status = h2_stream_input_flush(stream); + } + H2_STREAM_IN(APLOG_TRACE2, stream, "write_data_post"); + return status; } apr_status_t h2_stream_prep_read(h2_stream *stream, - apr_size_t *plen, int *peos) + apr_off_t *plen, int *peos) { apr_status_t status = APR_SUCCESS; const char *src; + apr_table_t *trailers = NULL; + int test_read = (*plen == 0); - if (stream->bbout && !APR_BRIGADE_EMPTY(stream->bbout)) { + if (stream->rst_error) { + return APR_ECONNRESET; + } + + H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream prep_read_pre"); + if (!APR_BRIGADE_EMPTY(stream->bbout)) { src = "stream"; status = h2_util_bb_avail(stream->bbout, plen, peos); - if (status == APR_SUCCESS && !*peos && !*plen) { + if (!test_read && status == APR_SUCCESS && !*peos && !*plen) { apr_brigade_cleanup(stream->bbout); return h2_stream_prep_read(stream, plen, peos); } + trailers = stream->response? stream->response->trailers : NULL; } else { src = "mplx"; - status = h2_mplx_out_readx(stream->m, stream->id, - NULL, NULL, plen, peos); + status = h2_mplx_out_readx(stream->session->mplx, stream->id, + NULL, NULL, plen, peos, &trailers); + if (trailers && stream->response) { + h2_response_set_trailers(stream->response, trailers); + } } - if (status == APR_SUCCESS && !*peos && !*plen) { + + if (!test_read && status == APR_SUCCESS && !*peos && !*plen) { status = APR_EAGAIN; } - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->m->c, - "h2_stream(%ld-%d): prep_read %s, len=%ld eos=%d", - stream->m->id, stream->id, - src, (long)*plen, *peos); + + H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream prep_read_post"); + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c, + "h2_stream(%ld-%d): prep_read %s, len=%ld eos=%d, trailers=%s", + stream->session->id, stream->id, src, (long)*plen, *peos, + trailers? "yes" : "no"); return status; } apr_status_t h2_stream_readx(h2_stream *stream, h2_io_data_cb *cb, void *ctx, - apr_size_t *plen, int *peos) + apr_off_t *plen, int *peos) { - if (stream->bbout && !APR_BRIGADE_EMPTY(stream->bbout)) { - return h2_util_bb_readx(stream->bbout, cb, ctx, plen, peos); + apr_status_t status = APR_SUCCESS; + apr_table_t *trailers = NULL; + const char *src; + + H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream readx_pre"); + if (stream->rst_error) { + return APR_ECONNRESET; } - return h2_mplx_out_readx(stream->m, stream->id, - cb, ctx, plen, peos); + *peos = 0; + if (!APR_BRIGADE_EMPTY(stream->bbout)) { + apr_off_t origlen = *plen; + + src = "stream"; + status = h2_util_bb_readx(stream->bbout, cb, ctx, plen, peos); + if (status == APR_SUCCESS && !*peos && !*plen) { + apr_brigade_cleanup(stream->bbout); + *plen = origlen; + return h2_stream_readx(stream, cb, ctx, plen, peos); + } + } + else { + src = "mplx"; + status = h2_mplx_out_readx(stream->session->mplx, stream->id, + cb, ctx, plen, peos, &trailers); + } + + if (trailers && stream->response) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c, + "h2_stream(%ld-%d): readx, saving trailers", + stream->session->id, stream->id); + h2_response_set_trailers(stream->response, trailers); + } + + if (status == APR_SUCCESS && !*peos && !*plen) { + status = APR_EAGAIN; + } + + H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream readx_post"); + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c, + "h2_stream(%ld-%d): readx %s, len=%ld eos=%d", + stream->session->id, stream->id, src, (long)*plen, *peos); + H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream readx_post"); + + return status; } +apr_status_t h2_stream_read_to(h2_stream *stream, apr_bucket_brigade *bb, + apr_off_t *plen, int *peos) +{ + apr_status_t status = APR_SUCCESS; + apr_table_t *trailers = NULL; + + H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream read_to_pre"); + if (stream->rst_error) { + return APR_ECONNRESET; + } + + if (APR_BRIGADE_EMPTY(stream->bbout)) { + apr_off_t tlen = *plen; + int eos; + status = h2_mplx_out_read_to(stream->session->mplx, stream->id, + stream->bbout, &tlen, &eos, &trailers); + } + + if (status == APR_SUCCESS && !APR_BRIGADE_EMPTY(stream->bbout)) { + status = h2_transfer_brigade(bb, stream->bbout, stream->pool, + plen, peos); + } + else { + *plen = 0; + *peos = 0; + } + + if (trailers && stream->response) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c, + "h2_stream(%ld-%d): read_to, saving trailers", + stream->session->id, stream->id); + h2_response_set_trailers(stream->response, trailers); + } + + if (status == APR_SUCCESS && !*peos && !*plen) { + status = APR_EAGAIN; + } + H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream read_to_post"); + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c, + "h2_stream(%ld-%d): read_to, len=%ld eos=%d", + stream->session->id, stream->id, (long)*plen, *peos); + return status; +} void h2_stream_set_suspended(h2_stream *stream, int suspended) { AP_DEBUG_ASSERT(stream); stream->suspended = !!suspended; + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->session->c, + "h2_stream(%ld-%d): suspended=%d", + stream->session->id, stream->id, stream->suspended); } int h2_stream_is_suspended(h2_stream *stream) @@ -271,3 +624,61 @@ int h2_stream_is_suspended(h2_stream *stream) return stream->suspended; } +int h2_stream_input_is_open(h2_stream *stream) +{ + return input_open(stream); +} + +int h2_stream_needs_submit(h2_stream *stream) +{ + switch (stream->state) { + case H2_STREAM_ST_OPEN: + case H2_STREAM_ST_CLOSED_INPUT: + case H2_STREAM_ST_CLOSED_OUTPUT: + case H2_STREAM_ST_CLOSED: + return !stream->submitted; + default: + return 0; + } +} + +apr_status_t h2_stream_submit_pushes(h2_stream *stream) +{ + apr_status_t status = APR_SUCCESS; + apr_array_header_t *pushes; + int i; + + pushes = h2_push_collect(stream->pool, stream->request, stream->response); + if (pushes && !apr_is_empty_array(pushes)) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c, + "h2_stream(%ld-%d): found %d push candidates", + stream->session->id, stream->id, pushes->nelts); + for (i = 0; i < pushes->nelts; ++i) { + h2_push *push = APR_ARRAY_IDX(pushes, i, h2_push*); + h2_stream *s = h2_session_push(stream->session, stream, push); + if (!s) { + status = APR_ECONNRESET; + break; + } + } + } + return status; +} + +apr_table_t *h2_stream_get_trailers(h2_stream *stream) +{ + return stream->response? stream->response->trailers : NULL; +} + +const h2_priority *h2_stream_get_priority(h2_stream *stream) +{ + if (stream->initiated_on && stream->response) { + const char *ctype = apr_table_get(stream->response->headers, "content-type"); + if (ctype) { + /* FIXME: Not good enough, config needs to come from request->server */ + return h2_config_get_priority(stream->session->config, ctype); + } + } + return NULL; +} + diff --git a/modules/http2/h2_stream.h b/modules/http2/h2_stream.h index 0608f2f3..7b3eb3e7 100644 --- a/modules/http2/h2_stream.h +++ b/modules/http2/h2_stream.h @@ -19,19 +19,14 @@ /** * A HTTP/2 stream, e.g. a client request+response in HTTP/1.1 terms. * - * Ok, not quite, but close enough, since we do not implement server - * pushes yet. - * * A stream always belongs to a h2_session, the one managing the * connection to the client. The h2_session writes to the h2_stream, * adding HEADERS and DATA and finally an EOS. When headers are done, - * h2_stream can create a h2_task that can be scheduled to fullfill the - * request. + * h2_stream is scheduled for handling, which is expected to produce + * a h2_response. * - * This response headers are added directly to the h2_mplx of the session, - * but the response DATA can be read via h2_stream. Reading data will - * never block but return APR_EAGAIN when there currently is no data (and - * no eos) in the multiplexer for this stream. + * The h2_response gives the HEADER frames to sent to the client, followed + * by DATA frames read from the h2_stream until EOS is reached. */ #include "h2_io.h" @@ -46,62 +41,269 @@ typedef enum { } h2_stream_state_t; struct h2_mplx; +struct h2_priority; struct h2_request; struct h2_response; +struct h2_session; struct h2_task; typedef struct h2_stream h2_stream; struct h2_stream { int id; /* http2 stream id */ + int initiated_on; /* http2 stream id this was initiated on or 0 */ h2_stream_state_t state; /* http/2 state of this stream */ - struct h2_mplx *m; /* the multiplexer to work with */ + struct h2_session *session; /* the session this stream belongs to */ + + apr_pool_t *pool; /* the memory pool for this stream */ + struct h2_request *request; /* the request made in this stream */ + struct h2_response *response; /* the response, once ready */ int aborted; /* was aborted */ int suspended; /* DATA sending has been suspended */ + int rst_error; /* stream error for RST_STREAM */ + int scheduled; /* stream has been scheduled */ + int submitted; /* response HEADER has been sent */ - apr_pool_t *pool; /* the memory pool for this stream */ - struct h2_request *request; /* the request made in this stream */ + apr_off_t input_remaining; /* remaining bytes on input as advertised via content-length */ + apr_bucket_brigade *bbin; /* input DATA */ - struct h2_task *task; /* task created for this stream */ - struct h2_response *response; /* the response, once ready */ apr_bucket_brigade *bbout; /* output DATA */ + apr_off_t data_frames_sent; /* # of DATA frames sent out for this stream */ }; -h2_stream *h2_stream_create(int id, apr_pool_t *pool, struct h2_mplx *m); +#define H2_STREAM_RST(s, def) (s->rst_error? s->rst_error : (def)) + +/** + * Create a stream in IDLE state. + * @param id the stream identifier + * @param pool the memory pool to use for this stream + * @param session the session this stream belongs to + * @return the newly created IDLE stream + */ +h2_stream *h2_stream_create(int id, apr_pool_t *pool, struct h2_session *session); +/** + * Create a stream in OPEN state. + * @param id the stream identifier + * @param pool the memory pool to use for this stream + * @param session the session this stream belongs to + * @return the newly opened stream + */ +h2_stream *h2_stream_open(int id, apr_pool_t *pool, struct h2_session *session); + +/** + * Destroy any resources held by this stream. Will destroy memory pool + * if still owned by the stream. + * + * @param stream the stream to destroy + */ apr_status_t h2_stream_destroy(h2_stream *stream); +/** + * Removes stream from h2_session and destroys it. + * + * @param stream the stream to cleanup + */ +void h2_stream_cleanup(h2_stream *stream); + +/** + * Detach the memory pool from the stream. Will prevent stream + * destruction to take the pool with it. + * + * @param stream the stream to detach the pool from + * @param the detached memmory pool or NULL if stream no longer has one + */ apr_pool_t *h2_stream_detach_pool(h2_stream *stream); -void h2_stream_attach_pool(h2_stream *stream, apr_pool_t *pool); -void h2_stream_abort(h2_stream *stream); -apr_status_t h2_stream_rwrite(h2_stream *stream, request_rec *r); +/** + * Initialize stream->request with the given request_rec. + * + * @param stream stream to write request to + * @param r the request with all the meta data + */ +apr_status_t h2_stream_set_request(h2_stream *stream, request_rec *r); -apr_status_t h2_stream_write_eos(h2_stream *stream); +/** + * Initialize stream->request with the given h2_request. + * + * @param stream the stream to init the request for + * @param req the request for initializing, will be copied + */ +void h2_stream_set_h2_request(h2_stream *stream, int initiated_on, + const struct h2_request *req); -apr_status_t h2_stream_write_header(h2_stream *stream, - const char *name, size_t nlen, - const char *value, size_t vlen); +/* + * Add a HTTP/2 header (including pseudo headers) or trailer + * to the given stream, depending on stream state. + * + * @param stream stream to write the header to + * @param name the name of the HTTP/2 header + * @param nlen the number of characters in name + * @param value the header value + * @param vlen the number of characters in value + */ +apr_status_t h2_stream_add_header(h2_stream *stream, + const char *name, size_t nlen, + const char *value, size_t vlen); -apr_status_t h2_stream_write_eoh(h2_stream *stream, int eos); +/** + * Closes the stream's input. + * + * @param stream stream to close intput of + */ +apr_status_t h2_stream_close_input(h2_stream *stream); +/* + * Write a chunk of DATA to the stream. + * + * @param stream stream to write the data to + * @param data the beginning of the bytes to write + * @param len the number of bytes to write + */ apr_status_t h2_stream_write_data(h2_stream *stream, const char *data, size_t len); +/** + * Reset the stream. Stream write/reads will return errors afterwards. + * + * @param stream the stream to reset + * @param error_code the HTTP/2 error code + */ +void h2_stream_rst(h2_stream *streamm, int error_code); + +/** + * Schedule the stream for execution. All header information must be + * present. Use the given priority comparision callback to determine + * order in queued streams. + * + * @param stream the stream to schedule + * @param eos != 0 iff no more input will arrive + * @param cmp priority comparision + * @param ctx context for comparision + */ +apr_status_t h2_stream_schedule(h2_stream *stream, int eos, + h2_stream_pri_cmp *cmp, void *ctx); + +/** + * Determine if stream has been scheduled already. + * @param stream the stream to check on + * @return != 0 iff stream has been scheduled + */ +int h2_stream_is_scheduled(h2_stream *stream); + +/** + * Set the response for this stream. Invoked when all meta data for + * the stream response has been collected. + * + * @param stream the stream to set the response for + * @param resonse the response data for the stream + * @param bb bucket brigade with output data for the stream. Optional, + * may be incomplete. + */ apr_status_t h2_stream_set_response(h2_stream *stream, struct h2_response *response, apr_bucket_brigade *bb); +/** + * Do a speculative read on the stream output to determine the + * amount of data that can be read. + * + * @param stream the stream to speculatively read from + * @param plen (in-/out) number of bytes requested and on return amount of bytes that + * may be read without blocking + * @param peos (out) != 0 iff end of stream will be reached when reading plen + * bytes (out value). + * @return APR_SUCCESS if out information was computed successfully. + * APR_EAGAIN if not data is available and end of stream has not been + * reached yet. + */ apr_status_t h2_stream_prep_read(h2_stream *stream, - apr_size_t *plen, int *peos); + apr_off_t *plen, int *peos); +/** + * Read data from the stream output. + * + * @param stream the stream to read from + * @param cb callback to invoke for byte chunks read. Might be invoked + * multiple times (with different values) for one read operation. + * @param ctx context data for callback + * @param plen (in-/out) max. number of bytes to read and on return actual + * number of bytes read + * @param peos (out) != 0 iff end of stream has been reached while reading + * @return APR_SUCCESS if out information was computed successfully. + * APR_EAGAIN if not data is available and end of stream has not been + * reached yet. + */ apr_status_t h2_stream_readx(h2_stream *stream, h2_io_data_cb *cb, - void *ctx, apr_size_t *plen, int *peos); + void *ctx, apr_off_t *plen, int *peos); + +/** + * Read a maximum number of bytes into the bucket brigade. + * + * @param stream the stream to read from + * @param bb the brigade to append output to + * @param plen (in-/out) max. number of bytes to append and on return actual + * number of bytes appended to brigade + * @param peos (out) != 0 iff end of stream has been reached while reading + * @return APR_SUCCESS if out information was computed successfully. + * APR_EAGAIN if not data is available and end of stream has not been + * reached yet. + */ +apr_status_t h2_stream_read_to(h2_stream *stream, apr_bucket_brigade *bb, + apr_off_t *plen, int *peos); +/** + * Set the suspended state of the stream. + * @param stream the stream to change state on + * @param suspended boolean value if stream is suspended + */ void h2_stream_set_suspended(h2_stream *stream, int suspended); + +/** + * Check if the stream has been suspended. + * @param stream the stream to check + * @return != 0 iff stream is suspended. + */ int h2_stream_is_suspended(h2_stream *stream); +/** + * Check if the stream has open input. + * @param stream the stream to check + * @return != 0 iff stream has open input. + */ +int h2_stream_input_is_open(h2_stream *stream); + +/** + * Check if the stream has not submitted a response or RST yet. + * @param stream the stream to check + * @return != 0 iff stream has not submitted a response or RST. + */ +int h2_stream_needs_submit(h2_stream *stream); + +/** + * Submit any server push promises on this stream and schedule + * the tasks connection with these. + * + * @param stream the stream for which to submit + */ +apr_status_t h2_stream_submit_pushes(h2_stream *stream); + +/** + * Get optional trailers for this stream, may be NULL. Meaningful + * results can only be expected when the end of the response body has + * been reached. + * + * @param stream to ask for trailers + * @return trailers for NULL + */ +apr_table_t *h2_stream_get_trailers(h2_stream *stream); + +/** + * Get priority information set for this stream. + */ +const struct h2_priority *h2_stream_get_priority(h2_stream *stream); + #endif /* defined(__mod_h2__h2_stream__) */ diff --git a/modules/http2/h2_stream_set.c b/modules/http2/h2_stream_set.c index dddd2e39..aa0f8c65 100644 --- a/modules/http2/h2_stream_set.c +++ b/modules/http2/h2_stream_set.c @@ -16,34 +16,31 @@ #include #include +#include #include #include -#include -#include #include #include "h2_private.h" -#include "h2_session.h" #include "h2_stream.h" -#include "h2_task.h" #include "h2_stream_set.h" -#define H2_STREAM_IDX(list, i) ((h2_stream**)(list)->elts)[i] struct h2_stream_set { - apr_array_header_t *list; + apr_hash_t *hash; }; -h2_stream_set *h2_stream_set_create(apr_pool_t *pool) +static unsigned int stream_hash(const char *key, apr_ssize_t *klen) +{ + return (unsigned int)(*((int*)key)); +} + +h2_stream_set *h2_stream_set_create(apr_pool_t *pool, int max) { h2_stream_set *sp = apr_pcalloc(pool, sizeof(h2_stream_set)); - if (sp) { - sp->list = apr_array_make(pool, 100, sizeof(h2_stream*)); - if (!sp->list) { - return NULL; - } - } + sp->hash = apr_hash_make_custom(pool, stream_hash); + return sp; } @@ -52,113 +49,97 @@ void h2_stream_set_destroy(h2_stream_set *sp) (void)sp; } -static int h2_stream_id_cmp(const void *s1, const void *s2) +h2_stream *h2_stream_set_get(h2_stream_set *sp, int stream_id) { - h2_stream **pstream1 = (h2_stream **)s1; - h2_stream **pstream2 = (h2_stream **)s2; - return (*pstream1)->id - (*pstream2)->id; + return apr_hash_get(sp->hash, &stream_id, sizeof(stream_id)); } -h2_stream *h2_stream_set_get(h2_stream_set *sp, int stream_id) +void h2_stream_set_add(h2_stream_set *sp, h2_stream *stream) { - /* we keep the array sorted by id, so lookup can be done - * by bsearch. - */ - h2_stream key; - h2_stream *pkey, **ps; - memset(&key, 0, sizeof(key)); - key.id = stream_id; - pkey = &key; - ps = bsearch(&pkey, sp->list->elts, sp->list->nelts, - sp->list->elt_size, h2_stream_id_cmp); - return ps? *ps : NULL; -} - -static void h2_stream_set_sort(h2_stream_set *sp) -{ - qsort(sp->list->elts, sp->list->nelts, sp->list->elt_size, - h2_stream_id_cmp); -} - -apr_status_t h2_stream_set_add(h2_stream_set *sp, h2_stream *stream) -{ - h2_stream *existing = h2_stream_set_get(sp, stream->id); - if (!existing) { - int last; - APR_ARRAY_PUSH(sp->list, h2_stream*) = stream; - /* Normally, streams get added in ascending order if id. We - * keep the array sorted, so we just need to check of the newly - * appended stream has a lower id than the last one. if not, - * sorting is not necessary. - */ - last = sp->list->nelts - 1; - if (last > 0 - && (H2_STREAM_IDX(sp->list, last)->id - < H2_STREAM_IDX(sp->list, last-1)->id)) { - h2_stream_set_sort(sp); - } - } - return APR_SUCCESS; -} - -h2_stream *h2_stream_set_remove(h2_stream_set *sp, h2_stream *stream) -{ - int i; - for (i = 0; i < sp->list->nelts; ++i) { - h2_stream *s = H2_STREAM_IDX(sp->list, i); - if (s == stream) { - int n; - --sp->list->nelts; - n = sp->list->nelts - i; - if (n > 0) { - /* Close the hole in the array by moving the upper - * parts down one step. - */ - h2_stream **selts = (h2_stream**)sp->list->elts; - memmove(selts+i, selts+i+1, n * sizeof(h2_stream*)); - } - return s; - } - } - return NULL; + apr_hash_set(sp->hash, &stream->id, sizeof(stream->id), stream); } -void h2_stream_set_remove_all(h2_stream_set *sp) +void h2_stream_set_remove(h2_stream_set *sp, int stream_id) { - sp->list->nelts = 0; + apr_hash_set(sp->hash, &stream_id, sizeof(stream_id), NULL); } int h2_stream_set_is_empty(h2_stream_set *sp) { - AP_DEBUG_ASSERT(sp); - return sp->list->nelts == 0; + return apr_hash_count(sp->hash) == 0; } -h2_stream *h2_stream_set_find(h2_stream_set *sp, - h2_stream_set_match_fn *match, void *ctx) +apr_size_t h2_stream_set_size(h2_stream_set *sp) { - h2_stream *s = NULL; - int i; - for (i = 0; !s && i < sp->list->nelts; ++i) { - s = match(ctx, H2_STREAM_IDX(sp->list, i)); - } - return s; + return apr_hash_count(sp->hash); +} + +typedef struct { + h2_stream_set_iter_fn *iter; + void *ctx; +} iter_ctx; + +static int hash_iter(void *ctx, const void *key, apr_ssize_t klen, + const void *val) +{ + iter_ctx *ictx = ctx; + return ictx->iter(ictx->ctx, (h2_stream*)val); } void h2_stream_set_iter(h2_stream_set *sp, h2_stream_set_iter_fn *iter, void *ctx) { - int i; - for (i = 0; i < sp->list->nelts; ++i) { - h2_stream *s = H2_STREAM_IDX(sp->list, i); - if (!iter(ctx, s)) { - break; - } + iter_ctx ictx; + ictx.iter = iter; + ictx.ctx = ctx; + apr_hash_do(hash_iter, &ictx, sp->hash); +} + +static int unsubmitted_iter(void *ctx, h2_stream *stream) +{ + if (h2_stream_needs_submit(stream)) { + *((int *)ctx) = 1; + return 0; } + return 1; } -apr_size_t h2_stream_set_size(h2_stream_set *sp) +int h2_stream_set_has_unsubmitted(h2_stream_set *sp) +{ + int has_unsubmitted = 0; + h2_stream_set_iter(sp, unsubmitted_iter, &has_unsubmitted); + return has_unsubmitted; +} + +static int input_open_iter(void *ctx, h2_stream *stream) +{ + if (h2_stream_input_is_open(stream)) { + *((int *)ctx) = 1; + return 0; + } + return 1; +} + +int h2_stream_set_has_open_input(h2_stream_set *sp) +{ + int has_input_open = 0; + h2_stream_set_iter(sp, input_open_iter, &has_input_open); + return has_input_open; +} + +static int suspended_iter(void *ctx, h2_stream *stream) +{ + if (h2_stream_is_suspended(stream)) { + *((int *)ctx) = 1; + return 0; + } + return 1; +} + +int h2_stream_set_has_suspended(h2_stream_set *sp) { - return sp->list->nelts; + int has_suspended = 0; + h2_stream_set_iter(sp, suspended_iter, &has_suspended); + return has_suspended; } diff --git a/modules/http2/h2_stream_set.h b/modules/http2/h2_stream_set.h index 56075834..d0041c48 100644 --- a/modules/http2/h2_stream_set.h +++ b/modules/http2/h2_stream_set.h @@ -27,26 +27,25 @@ typedef int h2_stream_set_iter_fn(void *ctx, h2_stream *stream); typedef struct h2_stream_set h2_stream_set; -h2_stream_set *h2_stream_set_create(apr_pool_t *pool); +h2_stream_set *h2_stream_set_create(apr_pool_t *pool, int max); void h2_stream_set_destroy(h2_stream_set *sp); -apr_status_t h2_stream_set_add(h2_stream_set *sp, h2_stream *stream); +void h2_stream_set_add(h2_stream_set *sp, h2_stream *stream); h2_stream *h2_stream_set_get(h2_stream_set *sp, int stream_id); -h2_stream *h2_stream_set_remove(h2_stream_set *sp,h2_stream *stream); +void h2_stream_set_remove(h2_stream_set *sp, int stream_id); -void h2_stream_set_remove_all(h2_stream_set *sp); +void h2_stream_set_iter(h2_stream_set *sp, + h2_stream_set_iter_fn *iter, void *ctx); int h2_stream_set_is_empty(h2_stream_set *sp); apr_size_t h2_stream_set_size(h2_stream_set *sp); -h2_stream *h2_stream_set_find(h2_stream_set *sp, - h2_stream_set_match_fn *match, void *ctx); - -void h2_stream_set_iter(h2_stream_set *sp, - h2_stream_set_iter_fn *iter, void *ctx); +int h2_stream_set_has_unsubmitted(h2_stream_set *sp); +int h2_stream_set_has_open_input(h2_stream_set *sp); +int h2_stream_set_has_suspended(h2_stream_set *sp); #endif /* defined(__mod_h2__h2_stream_set__) */ diff --git a/modules/http2/h2_switch.c b/modules/http2/h2_switch.c index 23c34490..ab8b90f7 100644 --- a/modules/http2/h2_switch.c +++ b/modules/http2/h2_switch.c @@ -34,17 +34,6 @@ #include "h2_h2.h" #include "h2_switch.h" -/******************************************************************************* - * SSL var lookup - */ -APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup, - (apr_pool_t *, server_rec *, - conn_rec *, request_rec *, - char *)); -static char *(*opt_ssl_var_lookup)(apr_pool_t *, server_rec *, - conn_rec *, request_rec *, - char *); - /******************************************************************************* * Once per lifetime init, retrieve optional functions */ @@ -52,7 +41,6 @@ apr_status_t h2_switch_init(apr_pool_t *pool, server_rec *s) { (void)pool; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "h2_switch init"); - opt_ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup); return APR_SUCCESS; } @@ -63,7 +51,8 @@ static int h2_protocol_propose(conn_rec *c, request_rec *r, apr_array_header_t *proposals) { int proposed = 0; - const char **protos = h2_h2_is_tls(c)? h2_tls_protos : h2_clear_protos; + int is_tls = h2_h2_is_tls(c); + const char **protos = is_tls? h2_tls_protos : h2_clear_protos; (void)s; if (strcmp(AP_PROTOCOL_HTTP1, ap_get_protocol(c))) { @@ -74,12 +63,23 @@ static int h2_protocol_propose(conn_rec *c, request_rec *r, return DECLINED; } + if (!h2_is_acceptable_connection(c, 0)) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + "protocol propose: connection requirements not met"); + return DECLINED; + } + if (r) { - const char *p; /* So far, this indicates an HTTP/1 Upgrade header initiated * protocol switch. For that, the HTTP2-Settings header needs * to be present and valid for the connection. */ + const char *p; + + if (!h2_allows_h2_upgrade(c)) { + return DECLINED; + } + p = apr_table_get(r->headers_in, "HTTP2-Settings"); if (!p) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, @@ -152,9 +152,10 @@ static int h2_protocol_switch(conn_rec *c, request_rec *r, server_rec *s, */ ap_remove_input_filter_byhandle(r->input_filters, "http_in"); ap_remove_input_filter_byhandle(r->input_filters, "reqtimeout"); + ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER"); /* Ok, start an h2_conn on this one. */ - status = h2_conn_rprocess(r); + status = h2_conn_process(r->connection, r, r->server); if (status != DONE) { /* Nothing really to do about this. */ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, diff --git a/modules/http2/h2_task.c b/modules/http2/h2_task.c index bbea7b20..55bcec72 100644 --- a/modules/http2/h2_task.c +++ b/modules/http2/h2_task.c @@ -38,6 +38,7 @@ #include "h2_from_h1.h" #include "h2_h2.h" #include "h2_mplx.h" +#include "h2_request.h" #include "h2_session.h" #include "h2_stream.h" #include "h2_task_input.h" @@ -52,33 +53,33 @@ static apr_status_t h2_filter_stream_input(ap_filter_t* filter, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes) { - h2_task_env *env = filter->ctx; - AP_DEBUG_ASSERT(env); - if (!env->input) { + h2_task *task = filter->ctx; + AP_DEBUG_ASSERT(task); + if (!task->input) { return APR_ECONNABORTED; } - return h2_task_input_read(env->input, filter, brigade, + return h2_task_input_read(task->input, filter, brigade, mode, block, readbytes); } static apr_status_t h2_filter_stream_output(ap_filter_t* filter, apr_bucket_brigade* brigade) { - h2_task_env *env = filter->ctx; - AP_DEBUG_ASSERT(env); - if (!env->output) { + h2_task *task = filter->ctx; + AP_DEBUG_ASSERT(task); + if (!task->output) { return APR_ECONNABORTED; } - return h2_task_output_write(env->output, filter, brigade); + return h2_task_output_write(task->output, filter, brigade); } static apr_status_t h2_filter_read_response(ap_filter_t* f, apr_bucket_brigade* bb) { - h2_task_env *env = f->ctx; - AP_DEBUG_ASSERT(env); - if (!env->output || !env->output->from_h1) { + h2_task *task = f->ctx; + AP_DEBUG_ASSERT(task); + if (!task->output || !task->output->from_h1) { return APR_ECONNABORTED; } - return h2_from_h1_read_response(env->output->from_h1, f, bb); + return h2_from_h1_read_response(task->output->from_h1, f, bb); } /******************************************************************************* @@ -110,6 +111,8 @@ void h2_task_register_hooks(void) NULL, AP_FTYPE_NETWORK); ap_register_output_filter("H1_TO_H2_RESP", h2_filter_read_response, NULL, AP_FTYPE_PROTOCOL); + ap_register_output_filter("H2_TRAILERS", h2_response_trailers_filter, + NULL, AP_FTYPE_PROTOCOL); } static int h2_task_pre_conn(conn_rec* c, void *arg) @@ -119,85 +122,43 @@ static int h2_task_pre_conn(conn_rec* c, void *arg) (void)arg; if (h2_ctx_is_task(ctx)) { - h2_task_env *env = h2_ctx_get_task(ctx); - - /* This connection is a pseudo-connection used for a h2_task. - * Since we read/write directly from it ourselves, we need - * to disable a possible ssl connection filter. - */ - h2_tls_disable(c); + h2_task *task = h2_ctx_get_task(ctx); ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, "h2_h2, pre_connection, found stream task"); /* Add our own, network level in- and output filters. */ - ap_add_input_filter("H2_TO_H1", env, NULL, c); - ap_add_output_filter("H1_TO_H2", env, NULL, c); + ap_add_input_filter("H2_TO_H1", task, NULL, c); + ap_add_output_filter("H1_TO_H2", task, NULL, c); } return OK; } -static int h2_task_process_conn(conn_rec* c) -{ - h2_ctx *ctx = h2_ctx_get(c); - - if (h2_ctx_is_task(ctx)) { - if (!ctx->task_env->serialize_headers) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, - "h2_h2, processing request directly"); - h2_task_process_request(ctx->task_env); - return DONE; - } - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, - "h2_task(%s), serialized handling", ctx->task_env->id); - } - return DECLINED; -} - - -h2_task *h2_task_create(long session_id, - int stream_id, - apr_pool_t *stream_pool, - h2_mplx *mplx, conn_rec *c) +h2_task *h2_task_create(long session_id, const h2_request *req, + apr_pool_t *pool, h2_mplx *mplx, int eos) { - h2_task *task = apr_pcalloc(stream_pool, sizeof(h2_task)); + h2_task *task = apr_pcalloc(pool, sizeof(h2_task)); if (task == NULL) { - ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, stream_pool, + ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, pool, APLOGNO(02941) "h2_task(%ld-%d): create stream task", - session_id, stream_id); - h2_mplx_out_close(mplx, stream_id); + session_id, req->id); + h2_mplx_out_close(mplx, req->id, NULL); return NULL; } - APR_RING_ELEM_INIT(task, link); + task->id = apr_psprintf(pool, "%ld-%d", session_id, req->id); + task->stream_id = req->id; + task->pool = pool; + task->mplx = mplx; + task->c = h2_conn_create(mplx->c, task->pool); - task->id = apr_psprintf(stream_pool, "%ld-%d", session_id, stream_id); - task->stream_id = stream_id; - task->mplx = mplx; - - task->c = c; + task->request = req; + task->input_eos = eos; - ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, stream_pool, - "h2_task(%s): created", task->id); return task; } -void h2_task_set_request(h2_task *task, - const char *method, - const char *scheme, - const char *authority, - const char *path, - apr_table_t *headers, int eos) -{ - task->method = method; - task->scheme = scheme; - task->authority = authority; - task->path = path; - task->headers = headers; - task->input_eos = eos; -} - apr_status_t h2_task_destroy(h2_task *task) { (void)task; @@ -207,240 +168,59 @@ apr_status_t h2_task_destroy(h2_task *task) apr_status_t h2_task_do(h2_task *task, h2_worker *worker) { apr_status_t status = APR_SUCCESS; - h2_config *cfg = h2_config_get(task->mplx->c); - h2_task_env env; AP_DEBUG_ASSERT(task); - memset(&env, 0, sizeof(env)); - - env.id = task->id; - env.stream_id = task->stream_id; - env.mplx = task->mplx; - task->mplx = NULL; - - env.input_eos = task->input_eos; - env.serialize_headers = h2_config_geti(cfg, H2_CONF_SER_HEADERS); - - /* Create a subpool from the worker one to be used for all things - * with life-time of this task_env execution. - */ - apr_pool_create(&env.pool, h2_worker_get_pool(worker)); + task->serialize_headers = h2_config_geti(task->request->config, H2_CONF_SER_HEADERS); - /* Link the env to the worker which provides useful things such - * as mutex, a socket etc. */ - env.io = h2_worker_get_cond(worker); - - /* Clone fields, so that lifetimes become (more) independent. */ - env.method = apr_pstrdup(env.pool, task->method); - env.scheme = apr_pstrdup(env.pool, task->scheme); - env.authority = apr_pstrdup(env.pool, task->authority); - env.path = apr_pstrdup(env.pool, task->path); - env.headers = apr_table_clone(env.pool, task->headers); + status = h2_worker_setup_task(worker, task); - /* Setup the pseudo connection to use our own pool and bucket_alloc */ - env.c = *task->c; - task->c = NULL; - status = h2_conn_setup(&env, worker); - - /* save in connection that this one is a pseudo connection, prevents - * other hooks from messing with it. */ - h2_ctx_create_for(&env.c, &env); + /* save in connection that this one is a pseudo connection */ + h2_ctx_create_for(task->c, task); if (status == APR_SUCCESS) { - env.input = h2_task_input_create(&env, env.pool, - env.c.bucket_alloc); - env.output = h2_task_output_create(&env, env.pool, - env.c.bucket_alloc); - status = h2_conn_process(&env.c, h2_worker_get_socket(worker)); - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, &env.c, - "h2_task(%s): processing done", env.id); + task->input = h2_task_input_create(task, task->pool, + task->c->bucket_alloc); + task->output = h2_task_output_create(task, task->pool); + + ap_process_connection(task->c, h2_worker_get_socket(worker)); + + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c, + "h2_task(%s): processing done", task->id); } else { - ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, &env.c, - APLOGNO(02957) "h2_task(%s): error setting up h2_task_env", - env.id); + ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, task->c, + APLOGNO(02957) "h2_task(%s): error setting up h2_task", + task->id); } - if (env.input) { - h2_task_input_destroy(env.input); - env.input = NULL; + if (task->input) { + h2_task_input_destroy(task->input); + task->input = NULL; } - if (env.output) { - h2_task_output_close(env.output); - h2_task_output_destroy(env.output); - env.output = NULL; + if (task->output) { + h2_task_output_close(task->output); + h2_task_output_destroy(task->output); + task->output = NULL; } - h2_task_set_finished(task); - if (env.io) { - apr_thread_cond_signal(env.io); + if (task->io) { + apr_thread_cond_signal(task->io); } - if (env.pool) { - apr_pool_destroy(env.pool); - env.pool = NULL; - } - - if (env.c.id) { - h2_conn_post(&env.c, worker); - } - - h2_mplx_task_done(env.mplx, env.stream_id); + h2_worker_release_task(worker, task); + h2_mplx_task_done(task->mplx, task->stream_id); return status; } -int h2_task_has_started(h2_task *task) -{ - AP_DEBUG_ASSERT(task); - return apr_atomic_read32(&task->has_started); -} - -void h2_task_set_started(h2_task *task) -{ - AP_DEBUG_ASSERT(task); - apr_atomic_set32(&task->has_started, 1); -} - -int h2_task_has_finished(h2_task *task) -{ - return apr_atomic_read32(&task->has_finished); -} - -void h2_task_set_finished(h2_task *task) -{ - apr_atomic_set32(&task->has_finished, 1); -} - -void h2_task_die(h2_task_env *env, int status, request_rec *r) +static apr_status_t h2_task_process_request(const h2_request *req, conn_rec *c) { - (void)env; - ap_die(status, r); -} - -static request_rec *h2_task_create_request(h2_task_env *env) -{ - conn_rec *conn = &env->c; - request_rec *r; - apr_pool_t *p; - int access_status = HTTP_OK; - - apr_pool_create(&p, conn->pool); - apr_pool_tag(p, "request"); - r = apr_pcalloc(p, sizeof(request_rec)); - AP_READ_REQUEST_ENTRY((intptr_t)r, (uintptr_t)conn); - r->pool = p; - r->connection = conn; - r->server = conn->base_server; - - r->user = NULL; - r->ap_auth_type = NULL; - - r->allowed_methods = ap_make_method_list(p, 2); - - r->headers_in = apr_table_copy(r->pool, env->headers); - r->trailers_in = apr_table_make(r->pool, 5); - r->subprocess_env = apr_table_make(r->pool, 25); - r->headers_out = apr_table_make(r->pool, 12); - r->err_headers_out = apr_table_make(r->pool, 5); - r->trailers_out = apr_table_make(r->pool, 5); - r->notes = apr_table_make(r->pool, 5); - - r->request_config = ap_create_request_config(r->pool); - /* Must be set before we run create request hook */ - - r->proto_output_filters = conn->output_filters; - r->output_filters = r->proto_output_filters; - r->proto_input_filters = conn->input_filters; - r->input_filters = r->proto_input_filters; - ap_run_create_request(r); - r->per_dir_config = r->server->lookup_defaults; - - r->sent_bodyct = 0; /* bytect isn't for body */ - - r->read_length = 0; - r->read_body = REQUEST_NO_BODY; - - r->status = HTTP_OK; /* Until further notice */ - r->header_only = 0; - r->the_request = NULL; - - /* Begin by presuming any module can make its own path_info assumptions, - * until some module interjects and changes the value. - */ - r->used_path_info = AP_REQ_DEFAULT_PATH_INFO; - - r->useragent_addr = conn->client_addr; - r->useragent_ip = conn->client_ip; - - ap_run_pre_read_request(r, conn); - - /* Time to populate r with the data we have. */ - r->request_time = apr_time_now(); - r->the_request = apr_psprintf(r->pool, "%s %s HTTP/1.1", - env->method, env->path); - r->method = env->method; - /* Provide quick information about the request method as soon as known */ - r->method_number = ap_method_number_of(r->method); - if (r->method_number == M_GET && r->method[0] == 'H') { - r->header_only = 1; - } - - ap_parse_uri(r, env->path); - r->protocol = (char*)"HTTP/2"; - r->proto_num = HTTP_VERSION(2, 0); - - /* update what we think the virtual host is based on the headers we've - * now read. may update status. - * Leave r->hostname empty, vhost will parse if form our Host: header, - * otherwise we get complains about port numbers. - */ - r->hostname = NULL; - ap_update_vhost_from_headers(r); - - /* we may have switched to another server */ - r->per_dir_config = r->server->lookup_defaults; - - /* - * Add the HTTP_IN filter here to ensure that ap_discard_request_body - * called by ap_die and by ap_send_error_response works correctly on - * status codes that do not cause the connection to be dropped and - * in situations where the connection should be kept alive. - */ - ap_add_input_filter_handle(ap_http_input_filter_handle, - NULL, r, r->connection); - - if (access_status != HTTP_OK - || (access_status = ap_run_post_read_request(r))) { - /* Request check post hooks failed. An example of this would be a - * request for a vhost where h2 is disabled --> 421. - */ - h2_task_die(env, access_status, r); - ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); - ap_run_log_transaction(r); - r = NULL; - goto traceout; - } - - AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, - (char *)r->uri, (char *)r->server->defn_name, - r->status); - return r; -traceout: - AP_READ_REQUEST_FAILURE((uintptr_t)r); - return r; -} - - -apr_status_t h2_task_process_request(h2_task_env *env) -{ - conn_rec *c = &env->c; request_rec *r; conn_state_t *cs = c->cs; - r = h2_task_create_request(env); + r = h2_request_create_rec(req, c); if (r && (r->status == HTTP_OK)) { ap_update_child_status(c->sbh, SERVER_BUSY_READ, r); @@ -454,6 +234,8 @@ apr_status_t h2_task_process_request(h2_task_env *env) * will result in a segfault immediately instead * of nondeterministic failures later. */ + if (cs) + cs->state = CONN_STATE_WRITE_COMPLETION; r = NULL; } ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, NULL); @@ -462,6 +244,24 @@ apr_status_t h2_task_process_request(h2_task_env *env) return APR_SUCCESS; } +static int h2_task_process_conn(conn_rec* c) +{ + h2_ctx *ctx = h2_ctx_get(c); + + if (h2_ctx_is_task(ctx)) { + if (!ctx->task->serialize_headers) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, + "h2_h2, processing request directly"); + h2_task_process_request(ctx->task->request, c); + return DONE; + } + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, + "h2_task(%s), serialized handling", ctx->task->id); + } + return DECLINED; +} + + diff --git a/modules/http2/h2_task.h b/modules/http2/h2_task.h index b66ce38c..1cc32d64 100644 --- a/modules/http2/h2_task.h +++ b/modules/http2/h2_task.h @@ -39,149 +39,40 @@ struct apr_thread_cond_t; struct h2_conn; struct h2_mplx; struct h2_task; +struct h2_request; struct h2_resp_head; struct h2_worker; typedef struct h2_task h2_task; struct h2_task { - /** Links to the rest of the tasks */ - APR_RING_ENTRY(h2_task) link; - const char *id; int stream_id; struct h2_mplx *mplx; - volatile apr_uint32_t has_started; - volatile apr_uint32_t has_finished; - - const char *method; - const char *scheme; - const char *authority; - const char *path; - apr_table_t *headers; + const struct h2_request *request; int input_eos; - struct conn_rec *c; -}; + int serialize_headers; -typedef struct h2_task_env h2_task_env; + struct conn_rec *c; -struct h2_task_env { - const char *id; - int stream_id; - struct h2_mplx *mplx; - apr_pool_t *pool; /* pool for task lifetime things */ apr_bucket_alloc_t *bucket_alloc; - - const char *method; - const char *scheme; - const char *authority; - const char *path; - apr_table_t *headers; - int input_eos; - - int serialize_headers; - - struct conn_rec c; struct h2_task_input *input; struct h2_task_output *output; struct apr_thread_cond_t *io; /* used to wait for events on */ }; -/** - * The magic pointer value that indicates the head of a h2_task list - * @param b The task list - * @return The magic pointer value - */ -#define H2_TASK_LIST_SENTINEL(b) APR_RING_SENTINEL((b), h2_task, link) - -/** - * Determine if the task list is empty - * @param b The list to check - * @return true or false - */ -#define H2_TASK_LIST_EMPTY(b) APR_RING_EMPTY((b), h2_task, link) - -/** - * Return the first task in a list - * @param b The list to query - * @return The first task in the list - */ -#define H2_TASK_LIST_FIRST(b) APR_RING_FIRST(b) - -/** - * Return the last task in a list - * @param b The list to query - * @return The last task int he list - */ -#define H2_TASK_LIST_LAST(b) APR_RING_LAST(b) - -/** - * Insert a single task at the front of a list - * @param b The list to add to - * @param e The task to insert - */ -#define H2_TASK_LIST_INSERT_HEAD(b, e) do { \ - h2_task *ap__b = (e); \ - APR_RING_INSERT_HEAD((b), ap__b, h2_task, link); \ -} while (0) - -/** - * Insert a single task at the end of a list - * @param b The list to add to - * @param e The task to insert - */ -#define H2_TASK_LIST_INSERT_TAIL(b, e) do { \ - h2_task *ap__b = (e); \ - APR_RING_INSERT_TAIL((b), ap__b, h2_task, link); \ -} while (0) - -/** - * Get the next task in the list - * @param e The current task - * @return The next task - */ -#define H2_TASK_NEXT(e) APR_RING_NEXT((e), link) -/** - * Get the previous task in the list - * @param e The current task - * @return The previous task - */ -#define H2_TASK_PREV(e) APR_RING_PREV((e), link) - -/** - * Remove a task from its list - * @param e The task to remove - */ -#define H2_TASK_REMOVE(e) APR_RING_REMOVE((e), link) - - -h2_task *h2_task_create(long session_id, int stream_id, - apr_pool_t *pool, struct h2_mplx *mplx, - conn_rec *c); +h2_task *h2_task_create(long session_id, const struct h2_request *req, + apr_pool_t *pool, struct h2_mplx *mplx, + int eos); apr_status_t h2_task_destroy(h2_task *task); -void h2_task_set_request(h2_task *task, - const char *method, - const char *scheme, - const char *authority, - const char *path, - apr_table_t *headers, int eos); - - apr_status_t h2_task_do(h2_task *task, struct h2_worker *worker); -apr_status_t h2_task_process_request(h2_task_env *env); - -int h2_task_has_started(h2_task *task); -void h2_task_set_started(h2_task *task); -int h2_task_has_finished(h2_task *task); -void h2_task_set_finished(h2_task *task); void h2_task_register_hooks(void); -void h2_task_die(h2_task_env *env, int status, request_rec *r); #endif /* defined(__mod_h2__h2_task__) */ diff --git a/modules/http2/h2_task_input.c b/modules/http2/h2_task_input.c index 487f7e60..49be7cfd 100644 --- a/modules/http2/h2_task_input.c +++ b/modules/http2/h2_task_input.c @@ -23,6 +23,7 @@ #include "h2_private.h" #include "h2_conn.h" #include "h2_mplx.h" +#include "h2_request.h" #include "h2_session.h" #include "h2_stream.h" #include "h2_task_input.h" @@ -42,49 +43,34 @@ static int ser_header(void *ctx, const char *name, const char *value) return 1; } -h2_task_input *h2_task_input_create(h2_task_env *env, apr_pool_t *pool, +h2_task_input *h2_task_input_create(h2_task *task, apr_pool_t *pool, apr_bucket_alloc_t *bucket_alloc) { h2_task_input *input = apr_pcalloc(pool, sizeof(h2_task_input)); if (input) { - input->env = env; + input->task = task; input->bb = NULL; - if (env->serialize_headers) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, &env->c, + if (task->serialize_headers) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c, "h2_task_input(%s): serialize request %s %s", - env->id, env->method, env->path); + task->id, task->request->method, task->request->path); input->bb = apr_brigade_create(pool, bucket_alloc); apr_brigade_printf(input->bb, NULL, NULL, "%s %s HTTP/1.1\r\n", - env->method, env->path); - apr_table_do(ser_header, input, env->headers, NULL); + task->request->method, task->request->path); + apr_table_do(ser_header, input, task->request->headers, NULL); apr_brigade_puts(input->bb, NULL, NULL, "\r\n"); - if (input->env->input_eos) { + if (input->task->input_eos) { APR_BRIGADE_INSERT_TAIL(input->bb, apr_bucket_eos_create(bucket_alloc)); } } - else if (!input->env->input_eos) { + else if (!input->task->input_eos) { input->bb = apr_brigade_create(pool, bucket_alloc); } else { /* We do not serialize and have eos already, no need to * create a bucket brigade. */ } - - if (APLOGcdebug(&env->c)) { - char buffer[1024]; - apr_size_t len = sizeof(buffer)-1; - if (input->bb) { - apr_brigade_flatten(input->bb, buffer, &len); - } - else { - len = 0; - } - buffer[len] = 0; - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, &env->c, - "h2_task_input(%s): request is: %s", - env->id, buffer); - } } return input; } @@ -104,32 +90,31 @@ apr_status_t h2_task_input_read(h2_task_input *input, apr_status_t status = APR_SUCCESS; apr_off_t bblen = 0; - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, "h2_task_input(%s): read, block=%d, mode=%d, readbytes=%ld", - input->env->id, block, mode, (long)readbytes); + input->task->id, block, mode, (long)readbytes); + + if (mode == AP_MODE_INIT) { + return ap_get_brigade(f->c->input_filters, bb, mode, block, readbytes); + } if (is_aborted(f)) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, - "h2_task_input(%s): is aborted", - input->env->id); + "h2_task_input(%s): is aborted", input->task->id); return APR_ECONNABORTED; } - if (mode == AP_MODE_INIT) { - return APR_SUCCESS; - } - if (input->bb) { status = apr_brigade_length(input->bb, 1, &bblen); if (status != APR_SUCCESS) { ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, f->c, APLOGNO(02958) "h2_task_input(%s): brigade length fail", - input->env->id); + input->task->id); return status; } } - if ((bblen == 0) && input->env->input_eos) { + if ((bblen == 0) && input->task->input_eos) { return APR_EOF; } @@ -139,19 +124,19 @@ apr_status_t h2_task_input_read(h2_task_input *input, ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "h2_task_input(%s): get more data from mplx, block=%d, " "readbytes=%ld, queued=%ld", - input->env->id, block, + input->task->id, block, (long)readbytes, (long)bblen); /* Although we sometimes get called with APR_NONBLOCK_READs, we seem to fill our buffer blocking. Otherwise we get EAGAIN, return that to our caller and everyone throws up their hands, never calling us again. */ - status = h2_mplx_in_read(input->env->mplx, APR_BLOCK_READ, - input->env->stream_id, input->bb, - input->env->io); + status = h2_mplx_in_read(input->task->mplx, APR_BLOCK_READ, + input->task->stream_id, input->bb, + input->task->io); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "h2_task_input(%s): mplx in read returned", - input->env->id); + input->task->id); if (status != APR_SUCCESS) { return status; } @@ -160,27 +145,27 @@ apr_status_t h2_task_input_read(h2_task_input *input, return status; } if ((bblen == 0) && (block == APR_NONBLOCK_READ)) { - return h2_util_has_eos(input->bb, 0)? APR_EOF : APR_EAGAIN; + return h2_util_has_eos(input->bb, -1)? APR_EOF : APR_EAGAIN; } ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "h2_task_input(%s): mplx in read, %ld bytes in brigade", - input->env->id, (long)bblen); + input->task->id, (long)bblen); } ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "h2_task_input(%s): read, mode=%d, block=%d, " "readbytes=%ld, queued=%ld", - input->env->id, mode, block, + input->task->id, mode, block, (long)readbytes, (long)bblen); if (!APR_BRIGADE_EMPTY(input->bb)) { if (mode == AP_MODE_EXHAUSTIVE) { /* return all we have */ - return h2_util_move(bb, input->bb, readbytes, 0, + return h2_util_move(bb, input->bb, readbytes, NULL, "task_input_read(exhaustive)"); } else if (mode == AP_MODE_READBYTES) { - return h2_util_move(bb, input->bb, readbytes, 0, + return h2_util_move(bb, input->bb, readbytes, NULL, "task_input_read(readbytes)"); } else if (mode == AP_MODE_SPECULATIVE) { @@ -199,7 +184,7 @@ apr_status_t h2_task_input_read(h2_task_input *input, buffer[len] = 0; ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "h2_task_input(%s): getline: %s", - input->env->id, buffer); + input->task->id, buffer); } return status; } diff --git a/modules/http2/h2_task_input.h b/modules/http2/h2_task_input.h index 32adc177..ed0a99fa 100644 --- a/modules/http2/h2_task_input.h +++ b/modules/http2/h2_task_input.h @@ -22,16 +22,16 @@ */ struct apr_thread_cond_t; struct h2_mplx; -struct h2_task_env; +struct h2_task; typedef struct h2_task_input h2_task_input; struct h2_task_input { - struct h2_task_env *env; + struct h2_task *task; apr_bucket_brigade *bb; }; -h2_task_input *h2_task_input_create(struct h2_task_env *env, apr_pool_t *pool, +h2_task_input *h2_task_input_create(struct h2_task *task, apr_pool_t *pool, apr_bucket_alloc_t *bucket_alloc); void h2_task_input_destroy(h2_task_input *input); diff --git a/modules/http2/h2_task_output.c b/modules/http2/h2_task_output.c index 879cb5fa..1d097ab3 100644 --- a/modules/http2/h2_task_output.c +++ b/modules/http2/h2_task_output.c @@ -24,6 +24,7 @@ #include "h2_private.h" #include "h2_conn.h" #include "h2_mplx.h" +#include "h2_request.h" #include "h2_session.h" #include "h2_stream.h" #include "h2_from_h1.h" @@ -33,16 +34,14 @@ #include "h2_util.h" -h2_task_output *h2_task_output_create(h2_task_env *env, apr_pool_t *pool, - apr_bucket_alloc_t *bucket_alloc) +h2_task_output *h2_task_output_create(h2_task *task, apr_pool_t *pool) { h2_task_output *output = apr_pcalloc(pool, sizeof(h2_task_output)); - (void)bucket_alloc; if (output) { - output->env = env; + output->task = task; output->state = H2_TASK_OUT_INIT; - output->from_h1 = h2_from_h1_create(env->stream_id, pool); + output->from_h1 = h2_from_h1_create(task->stream_id, pool); if (!output->from_h1) { return NULL; } @@ -73,27 +72,42 @@ static apr_status_t open_if_needed(h2_task_output *output, ap_filter_t *f, ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, "h2_task_output(%s): write without response " "for %s %s %s", - output->env->id, output->env->method, - output->env->authority, output->env->path); + output->task->id, output->task->request->method, + output->task->request->authority, + output->task->request->path); f->c->aborted = 1; } - if (output->env->io) { - apr_thread_cond_broadcast(output->env->io); + if (output->task->io) { + apr_thread_cond_broadcast(output->task->io); } return APR_ECONNABORTED; } - return h2_mplx_out_open(output->env->mplx, output->env->stream_id, - response, f, bb, output->env->io); + output->trailers_passed = !!response->trailers; + return h2_mplx_out_open(output->task->mplx, output->task->stream_id, + response, f, bb, output->task->io); } return APR_EOF; } +static apr_table_t *get_trailers(h2_task_output *output) +{ + if (!output->trailers_passed) { + h2_response *response = h2_from_h1_get_response(output->from_h1); + if (response && response->trailers) { + output->trailers_passed = 1; + return response->trailers; + } + } + return NULL; +} + void h2_task_output_close(h2_task_output *output) { open_if_needed(output, NULL, NULL); if (output->state != H2_TASK_OUT_DONE) { - h2_mplx_out_close(output->env->mplx, output->env->stream_id); + h2_mplx_out_close(output->task->mplx, output->task->stream_id, + get_trailers(output)); output->state = H2_TASK_OUT_DONE; } } @@ -111,9 +125,10 @@ apr_status_t h2_task_output_write(h2_task_output *output, ap_filter_t* f, apr_bucket_brigade* bb) { apr_status_t status; + if (APR_BRIGADE_EMPTY(bb)) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, - "h2_task_output(%s): empty write", output->env->id); + "h2_task_output(%s): empty write", output->task->id); return APR_SUCCESS; } @@ -121,12 +136,13 @@ apr_status_t h2_task_output_write(h2_task_output *output, if (status != APR_EOF) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "h2_task_output(%s): opened and passed brigade", - output->env->id); + output->task->id); return status; } + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, - "h2_task_output(%s): write brigade", output->env->id); - return h2_mplx_out_write(output->env->mplx, output->env->stream_id, - f, bb, output->env->io); + "h2_task_output(%s): write brigade", output->task->id); + return h2_mplx_out_write(output->task->mplx, output->task->stream_id, + f, bb, get_trailers(output), output->task->io); } diff --git a/modules/http2/h2_task_output.h b/modules/http2/h2_task_output.h index 86571a1e..de03890e 100644 --- a/modules/http2/h2_task_output.h +++ b/modules/http2/h2_task_output.h @@ -23,7 +23,7 @@ */ struct apr_thread_cond_t; struct h2_mplx; -struct h2_task_env; +struct h2_task; struct h2_from_h1; typedef enum { @@ -35,13 +35,13 @@ typedef enum { typedef struct h2_task_output h2_task_output; struct h2_task_output { - struct h2_task_env *env; + struct h2_task *task; h2_task_output_state_t state; struct h2_from_h1 *from_h1; + int trailers_passed; }; -h2_task_output *h2_task_output_create(struct h2_task_env *env, apr_pool_t *pool, - apr_bucket_alloc_t *bucket_alloc); +h2_task_output *h2_task_output_create(struct h2_task *task, apr_pool_t *pool); void h2_task_output_destroy(h2_task_output *output); diff --git a/modules/http2/h2_task_queue.c b/modules/http2/h2_task_queue.c index a81cc100..23bad194 100644 --- a/modules/http2/h2_task_queue.c +++ b/modules/http2/h2_task_queue.c @@ -23,65 +23,158 @@ #include "h2_task_queue.h" -h2_task_queue *h2_tq_create(long id, apr_pool_t *pool) +static void tq_grow(h2_task_queue *q, int nlen); +static void tq_swap(h2_task_queue *q, int i, int j); +static int tq_bubble_up(h2_task_queue *q, int i, int top, + h2_tq_cmp *cmp, void *ctx); +static int tq_bubble_down(h2_task_queue *q, int i, int bottom, + h2_tq_cmp *cmp, void *ctx); + +h2_task_queue *h2_tq_create(apr_pool_t *pool, int capacity) { h2_task_queue *q = apr_pcalloc(pool, sizeof(h2_task_queue)); if (q) { - q->id = id; - APR_RING_ELEM_INIT(q, link); - APR_RING_INIT(&q->tasks, h2_task, link); + q->pool = pool; + tq_grow(q, capacity); + q->nelts = 0; } return q; } -void h2_tq_destroy(h2_task_queue *q) +int h2_tq_empty(h2_task_queue *q) +{ + return q->nelts == 0; +} + +void h2_tq_add(h2_task_queue *q, int sid, h2_tq_cmp *cmp, void *ctx) { - while (!H2_TASK_LIST_EMPTY(&q->tasks)) { - h2_task *task = H2_TASK_LIST_FIRST(&q->tasks); - H2_TASK_REMOVE(task); + int i; + + if (q->nelts >= q->nalloc) { + tq_grow(q, q->nalloc * 2); } + + i = (q->head + q->nelts) % q->nalloc; + q->elts[i] = sid; + ++q->nelts; + + /* bubble it to the front of the queue */ + tq_bubble_up(q, i, q->head, cmp, ctx); } -static int in_list(h2_task_queue *q, h2_task *task) +int h2_tq_remove(h2_task_queue *q, int sid) { - h2_task *e; - for (e = H2_TASK_LIST_FIRST(&q->tasks); - e != H2_TASK_LIST_SENTINEL(&q->tasks); - e = H2_TASK_NEXT(e)) { - if (e == task) { - return 1; + int i; + for (i = 0; i < q->nelts; ++i) { + if (sid == q->elts[(q->head + i) % q->nalloc]) { + break; } } + + if (i < q->nelts) { + ++i; + for (; i < q->nelts; ++i) { + q->elts[(q->head+i-1)%q->nalloc] = q->elts[(q->head+i)%q->nalloc]; + } + --q->nelts; + return 1; + } return 0; } -int h2_tq_empty(h2_task_queue *q) +void h2_tq_sort(h2_task_queue *q, h2_tq_cmp *cmp, void *ctx) { - return H2_TASK_LIST_EMPTY(&q->tasks); + /* Assume that changes in ordering are minimal. This needs, + * best case, q->nelts - 1 comparisions to check that nothing + * changed. + */ + if (q->nelts > 0) { + int i, ni, prev, last; + + /* Start at the end of the queue and create a tail of sorted + * entries. Make that tail one element longer in each iteration. + */ + last = i = (q->head + q->nelts - 1) % q->nalloc; + while (i != q->head) { + prev = (q->nalloc + i - 1) % q->nalloc; + + ni = tq_bubble_up(q, i, prev, cmp, ctx); + if (ni == prev) { + /* i bubbled one up, bubble the new i down, which + * keeps all tasks below i sorted. */ + tq_bubble_down(q, i, last, cmp, ctx); + } + i = prev; + }; + } +} + + +int h2_tq_shift(h2_task_queue *q) +{ + int sid; + + if (q->nelts <= 0) { + return 0; + } + + sid = q->elts[q->head]; + q->head = (q->head + 1) % q->nalloc; + q->nelts--; + + return sid; +} + +static void tq_grow(h2_task_queue *q, int nlen) +{ + AP_DEBUG_ASSERT(q->nalloc <= nlen); + if (nlen > q->nalloc) { + int *nq = apr_pcalloc(q->pool, sizeof(h2_task *) * nlen); + if (q->nelts > 0) { + int l = ((q->head + q->nelts) % q->nalloc) - q->head; + + memmove(nq, q->elts + q->head, sizeof(int) * l); + if (l < q->nelts) { + /* elts wrapped, append elts in [0, remain] to nq */ + int remain = q->nelts - l; + memmove(nq + l, q->elts, sizeof(int) * remain); + } + } + q->elts = nq; + q->nalloc = nlen; + q->head = 0; + } } -void h2_tq_append(h2_task_queue *q, struct h2_task *task) +static void tq_swap(h2_task_queue *q, int i, int j) { - H2_TASK_LIST_INSERT_TAIL(&q->tasks, task); + int x = q->elts[i]; + q->elts[i] = q->elts[j]; + q->elts[j] = x; } -apr_status_t h2_tq_remove(h2_task_queue *q, struct h2_task *task) +static int tq_bubble_up(h2_task_queue *q, int i, int top, + h2_tq_cmp *cmp, void *ctx) { - if (in_list(q, task)) { - H2_TASK_REMOVE(task); - return APR_SUCCESS; + int prev; + while (((prev = (q->nalloc + i - 1) % q->nalloc), i != top) + && (*cmp)(q->elts[i], q->elts[prev], ctx) < 0) { + tq_swap(q, prev, i); + i = prev; } - return APR_NOTFOUND; + return i; } -h2_task *h2_tq_pop_first(h2_task_queue *q) +static int tq_bubble_down(h2_task_queue *q, int i, int bottom, + h2_tq_cmp *cmp, void *ctx) { - if (!H2_TASK_LIST_EMPTY(&q->tasks)) { - h2_task *task = H2_TASK_LIST_FIRST(&q->tasks); - H2_TASK_REMOVE(task); - return task; + int next; + while (((next = (q->nalloc + i + 1) % q->nalloc), i != bottom) + && (*cmp)(q->elts[i], q->elts[next], ctx) > 0) { + tq_swap(q, next, i); + i = next; } - return NULL; + return i; } diff --git a/modules/http2/h2_task_queue.h b/modules/http2/h2_task_queue.h index d93d74ac..dcc46d03 100644 --- a/modules/http2/h2_task_queue.h +++ b/modules/http2/h2_task_queue.h @@ -19,29 +19,38 @@ struct h2_task; /** - * A simple ring of rings that keeps a list of h2_tasks and can - * be ringed itself, using the APR RING macros. + * h2_task_queue keeps a list of sorted h2_task* in ascending order. */ typedef struct h2_task_queue h2_task_queue; struct h2_task_queue { - APR_RING_ENTRY(h2_task_queue) link; - APR_RING_HEAD(h2_tasks, h2_task) tasks; - long id; + int *elts; + int head; + int nelts; + int nalloc; + apr_pool_t *pool; }; /** - * Allocate a new queue from the pool and initialize. - * @param id the identifier of the queue - * @param pool the memory pool + * Comparator for two task to determine their order. + * + * @param s1 stream id to compare + * @param s2 stream id to compare + * @param ctx provided user data + * @return value is the same as for strcmp() and has the effect: + * == 0: s1 and s2 are treated equal in ordering + * < 0: s1 should be sorted before s2 + * > 0: s2 should be sorted before s1 */ -h2_task_queue *h2_tq_create(long id, apr_pool_t *pool); +typedef int h2_tq_cmp(int s1, int s2, void *ctx); + /** - * Release all queue tasks. - * @param q the queue to destroy + * Allocate a new queue from the pool and initialize. + * @param id the identifier of the queue + * @param pool the memory pool */ -void h2_tq_destroy(h2_task_queue *q); +h2_task_queue *h2_tq_create(apr_pool_t *pool, int capacity); /** * Return != 0 iff there are no tasks in the queue. @@ -50,99 +59,41 @@ void h2_tq_destroy(h2_task_queue *q); int h2_tq_empty(h2_task_queue *q); /** - * Append the task to the end of the queue. + * Add a stream idto the queue. + * * @param q the queue to append the task to - * @param task the task to append - */ -void h2_tq_append(h2_task_queue *q, struct h2_task *task); - -/** - * Remove a task from the queue. Return APR_SUCCESS if the task - * was indeed queued, APR_NOTFOUND otherwise. - * @param q the queue to remove from - * @param task the task to remove - */ -apr_status_t h2_tq_remove(h2_task_queue *q, struct h2_task *task); - -/** - * Get the first task from the queue or NULL if the queue is empty. The - * task will be removed. - * @param q the queue to pop the first task from - */ -h2_task *h2_tq_pop_first(h2_task_queue *q); - -/******************************************************************************* - * Queue Manipulation. - ******************************************************************************/ - -/** - * The magic pointer value that indicates the head of a h2_task_queue list - * @param b The queue list - * @return The magic pointer value + * @param sid the stream id to add + * @param cmp the comparator for sorting + * @param ctx user data for comparator */ -#define H2_TQ_LIST_SENTINEL(b) APR_RING_SENTINEL((b), h2_task_queue, link) +void h2_tq_add(h2_task_queue *q, int sid, h2_tq_cmp *cmp, void *ctx); /** - * Determine if the queue list is empty - * @param b The list to check - * @return true or false + * Remove the stream id from the queue. Return != 0 iff task + * was found in queue. + * @param q the task queue + * @param sid the stream id to remove + * @return != 0 iff task was found in queue */ -#define H2_TQ_LIST_EMPTY(b) APR_RING_EMPTY((b), h2_task_queue, link) +int h2_tq_remove(h2_task_queue *q, int sid); /** - * Return the first queue in a list - * @param b The list to query - * @return The first queue in the list - */ -#define H2_TQ_LIST_FIRST(b) APR_RING_FIRST(b) - -/** - * Return the last queue in a list - * @param b The list to query - * @return The last queue int he list - */ -#define H2_TQ_LIST_LAST(b) APR_RING_LAST(b) - -/** - * Insert a single queue at the front of a list - * @param b The list to add to - * @param e The queue to insert - */ -#define H2_TQ_LIST_INSERT_HEAD(b, e) do { \ -h2_task_queue *ap__b = (e); \ -APR_RING_INSERT_HEAD((b), ap__b, h2_task_queue, link); \ -} while (0) - -/** - * Insert a single queue at the end of a list - * @param b The list to add to - * @param e The queue to insert - */ -#define H2_TQ_LIST_INSERT_TAIL(b, e) do { \ -h2_task_queue *ap__b = (e); \ -APR_RING_INSERT_TAIL((b), ap__b, h2_task_queue, link); \ -} while (0) - -/** - * Get the next queue in the list - * @param e The current queue - * @return The next queue - */ -#define H2_TQ_NEXT(e) APR_RING_NEXT((e), link) -/** - * Get the previous queue in the list - * @param e The current queue - * @return The previous queue + * Sort the stream idqueue again. Call if the task ordering + * has changed. + * + * @param q the queue to sort + * @param cmp the comparator for sorting + * @param ctx user data for the comparator */ -#define H2_TQ_PREV(e) APR_RING_PREV((e), link) +void h2_tq_sort(h2_task_queue *q, h2_tq_cmp *cmp, void *ctx); /** - * Remove a queue from its list - * @param e The queue to remove + * Get the first stream id from the queue or NULL if the queue is empty. + * The task will be removed. + * + * @param q the queue to get the first task from + * @return the first stream id of the queue, 0 if empty */ -#define H2_TQ_REMOVE(e) APR_RING_REMOVE((e), link) - - -#define H2_TQ_EMPTY(e) H2_TASK_LIST_EMPTY(&(e)->tasks) +int h2_tq_shift(h2_task_queue *q); #endif /* defined(__mod_h2__h2_task_queue__) */ diff --git a/modules/http2/h2_to_h1.c b/modules/http2/h2_to_h1.c deleted file mode 100644 index 8dacfe80..00000000 --- a/modules/http2/h2_to_h1.c +++ /dev/null @@ -1,305 +0,0 @@ -/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include - -#include -#include -#include -#include - -#include "h2_private.h" -#include "h2_mplx.h" -#include "h2_response.h" -#include "h2_task.h" -#include "h2_to_h1.h" -#include "h2_util.h" - - -h2_to_h1 *h2_to_h1_create(int stream_id, apr_pool_t *pool, - apr_bucket_alloc_t *bucket_alloc, - const char *method, - const char *scheme, - const char *authority, - const char *path, - struct h2_mplx *m) -{ - h2_to_h1 *to_h1; - if (!method) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, m->c, - APLOGNO(02943) - "h2_to_h1: header start but :method missing"); - return NULL; - } - if (!path) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, m->c, - APLOGNO(02944) - "h2_to_h1: header start but :path missing"); - return NULL; - } - - to_h1 = apr_pcalloc(pool, sizeof(h2_to_h1)); - if (to_h1) { - to_h1->stream_id = stream_id; - to_h1->pool = pool; - to_h1->method = method; - to_h1->scheme = scheme; - to_h1->authority = authority; - to_h1->path = path; - to_h1->m = m; - to_h1->headers = apr_table_make(to_h1->pool, 10); - to_h1->bb = apr_brigade_create(pool, bucket_alloc); - to_h1->chunked = 0; /* until we see a content-type and no length */ - to_h1->content_len = -1; - } - return to_h1; -} - -void h2_to_h1_destroy(h2_to_h1 *to_h1) -{ - to_h1->bb = NULL; -} - -apr_status_t h2_to_h1_add_header(h2_to_h1 *to_h1, - const char *name, size_t nlen, - const char *value, size_t vlen) -{ - char *hname, *hvalue; - if (H2_HD_MATCH_LIT("transfer-encoding", name, nlen)) { - if (!apr_strnatcasecmp("chunked", value)) { - /* This should never arrive here in a HTTP/2 request */ - ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_BADARG, to_h1->m->c, - APLOGNO(02945) - "h2_to_h1: 'transfer-encoding: chunked' received"); - return APR_BADARG; - } - } - else if (H2_HD_MATCH_LIT("content-length", name, nlen)) { - char *end; - to_h1->content_len = apr_strtoi64(value, &end, 10); - if (value == end) { - ap_log_cerror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, to_h1->m->c, - APLOGNO(02959) - "h2_request(%d): content-length value not parsed: %s", - to_h1->stream_id, value); - return APR_EINVAL; - } - to_h1->remain_len = to_h1->content_len; - to_h1->chunked = 0; - } - else if (H2_HD_MATCH_LIT("content-type", name, nlen)) { - /* If we see a content-type and have no length (yet), - * we need to chunk. */ - to_h1->chunked = (to_h1->content_len == -1); - } - else if ((to_h1->seen_host && H2_HD_MATCH_LIT("host", name, nlen)) - || H2_HD_MATCH_LIT("expect", name, nlen) - || H2_HD_MATCH_LIT("upgrade", name, nlen) - || H2_HD_MATCH_LIT("connection", name, nlen) - || H2_HD_MATCH_LIT("proxy-connection", name, nlen) - || H2_HD_MATCH_LIT("keep-alive", name, nlen) - || H2_HD_MATCH_LIT("http2-settings", name, nlen)) { - /* ignore these. */ - return APR_SUCCESS; - } - else if (H2_HD_MATCH_LIT("cookie", name, nlen)) { - const char *existing = apr_table_get(to_h1->headers, "cookie"); - if (existing) { - char *nval; - - /* Cookie headers come separately in HTTP/2, but need - * to be merged by "; " (instead of default ", ") - */ - hvalue = apr_pstrndup(to_h1->pool, value, vlen); - nval = apr_psprintf(to_h1->pool, "%s; %s", existing, hvalue); - apr_table_setn(to_h1->headers, "Cookie", nval); - return APR_SUCCESS; - } - } - else if (H2_HD_MATCH_LIT("host", name, nlen)) { - to_h1->seen_host = 1; - } - - hname = apr_pstrndup(to_h1->pool, name, nlen); - hvalue = apr_pstrndup(to_h1->pool, value, vlen); - h2_util_camel_case_header(hname, nlen); - apr_table_mergen(to_h1->headers, hname, hvalue); - - return APR_SUCCESS; -} - -static int set_header(void *ctx, const char *key, const char *value) -{ - h2_to_h1 *to_h1 = (h2_to_h1*)ctx; - h2_to_h1_add_header(to_h1, key, strlen(key), value, strlen(value)); - return 1; -} - -apr_status_t h2_to_h1_add_headers(h2_to_h1 *to_h1, apr_table_t *headers) -{ - apr_table_do(set_header, to_h1, headers, NULL); - return APR_SUCCESS; -} - -apr_status_t h2_to_h1_end_headers(h2_to_h1 *to_h1, h2_task *task, int eos) -{ - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, to_h1->m->c, - "h2_to_h1(%ld-%d): end headers", - to_h1->m->id, to_h1->stream_id); - - if (to_h1->eoh) { - return APR_EINVAL; - } - - if (!to_h1->seen_host) { - /* Need to add a "Host" header if not already there to - * make virtual hosts work correctly. */ - if (!to_h1->authority) { - return APR_BADARG; - } - apr_table_set(to_h1->headers, "Host", to_h1->authority); - } - - if (eos && to_h1->chunked) { - /* We had chunking figured out, but the EOS is already there. - * unmark chunking and set a definitive content-length. - */ - to_h1->chunked = 0; - apr_table_setn(to_h1->headers, "Content-Length", "0"); - } - else if (to_h1->chunked) { - /* We have not seen a content-length. We therefore must - * pass any request content in chunked form. - */ - apr_table_mergen(to_h1->headers, "Transfer-Encoding", "chunked"); - } - - h2_task_set_request(task, to_h1->method, - to_h1->scheme, - to_h1->authority, - to_h1->path, - to_h1->headers, eos); - to_h1->eoh = 1; - - if (eos) { - apr_status_t status = h2_to_h1_close(to_h1); - if (status != APR_SUCCESS) { - ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, to_h1->m->c, - APLOGNO(02960) - "h2_to_h1(%ld-%d): end headers, eos=%d", - to_h1->m->id, to_h1->stream_id, eos); - } - return status; - } - return APR_SUCCESS; -} - -static apr_status_t flush(apr_bucket_brigade *bb, void *ctx) -{ - (void)bb; - return h2_to_h1_flush((h2_to_h1*)ctx); -} - -static apr_status_t h2_to_h1_add_data_raw(h2_to_h1 *to_h1, - const char *data, size_t len) -{ - apr_status_t status = APR_SUCCESS; - - if (to_h1->eos || !to_h1->eoh) { - return APR_EINVAL; - } - - status = apr_brigade_write(to_h1->bb, flush, to_h1, data, len); - if (status == APR_SUCCESS) { - status = h2_to_h1_flush(to_h1); - } - return status; -} - - -apr_status_t h2_to_h1_add_data(h2_to_h1 *to_h1, - const char *data, size_t len) -{ - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, to_h1->m->c, - "h2_to_h1(%ld-%d): add %ld data bytes", - to_h1->m->id, to_h1->stream_id, (long)len); - - if (to_h1->chunked) { - /* if input may have a body and we have not seen any - * content-length header, we need to chunk the input data. - */ - apr_status_t status = apr_brigade_printf(to_h1->bb, NULL, NULL, - "%lx\r\n", (unsigned long)len); - if (status == APR_SUCCESS) { - status = h2_to_h1_add_data_raw(to_h1, data, len); - if (status == APR_SUCCESS) { - status = apr_brigade_puts(to_h1->bb, NULL, NULL, "\r\n"); - } - } - return status; - } - else { - to_h1->remain_len -= len; - if (to_h1->remain_len < 0) { - ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, to_h1->m->c, - APLOGNO(02961) - "h2_to_h1(%ld-%d): got %ld more content bytes than announced " - "in content-length header: %ld", - to_h1->m->id, to_h1->stream_id, - (long)to_h1->content_len, -(long)to_h1->remain_len); - } - return h2_to_h1_add_data_raw(to_h1, data, len); - } -} - -apr_status_t h2_to_h1_flush(h2_to_h1 *to_h1) -{ - apr_status_t status = APR_SUCCESS; - if (!APR_BRIGADE_EMPTY(to_h1->bb)) { - ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, to_h1->m->c, - "h2_to_h1(%ld-%d): flush request bytes", - to_h1->m->id, to_h1->stream_id); - - status = h2_mplx_in_write(to_h1->m, to_h1->stream_id, to_h1->bb); - if (status != APR_SUCCESS) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, status, to_h1->m->c, - APLOGNO(02946) "h2_request(%d): pushing request data", - to_h1->stream_id); - } - } - return status; -} - -apr_status_t h2_to_h1_close(h2_to_h1 *to_h1) -{ - apr_status_t status = APR_SUCCESS; - if (!to_h1->eos) { - if (to_h1->chunked) { - status = h2_to_h1_add_data_raw(to_h1, "0\r\n\r\n", 5); - } - to_h1->eos = 1; - status = h2_to_h1_flush(to_h1); - ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, to_h1->m->c, - "h2_to_h1(%d): close", to_h1->stream_id); - - status = h2_mplx_in_close(to_h1->m, to_h1->stream_id); - } - return status; -} - - diff --git a/modules/http2/h2_to_h1.h b/modules/http2/h2_to_h1.h deleted file mode 100644 index 74586e2b..00000000 --- a/modules/http2/h2_to_h1.h +++ /dev/null @@ -1,87 +0,0 @@ -/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __mod_h2__h2_to_h1__ -#define __mod_h2__h2_to_h1__ - -struct h2_mplx; -struct h2_task; -typedef struct h2_to_h1 h2_to_h1; - -struct h2_to_h1 { - int stream_id; - apr_pool_t *pool; - h2_mplx *m; - - const char *method; - const char *scheme; - const char *authority; - const char *path; - - int chunked; - int eoh; - int eos; - int flushed; - int seen_host; - - apr_off_t content_len; - apr_off_t remain_len; - apr_table_t *headers; - apr_bucket_brigade *bb; -}; - -/* Create a converter from a HTTP/2 request to a serialzation in - * HTTP/1.1 format. The serialized data will be written onto the - * given h2_mplx instance. - */ -h2_to_h1 *h2_to_h1_create(int stream_id, apr_pool_t *pool, - apr_bucket_alloc_t *bucket_alloc, - const char *method, - const char *scheme, - const char *authority, - const char *path, - struct h2_mplx *m); - -/* Destroy the converter and free resources. */ -void h2_to_h1_destroy(h2_to_h1 *to_h1); - -/* Add a header to the serialization. Only valid to call after start - * and before end_headers. - */ -apr_status_t h2_to_h1_add_header(h2_to_h1 *to_h1, - const char *name, size_t nlen, - const char *value, size_t vlen); - -apr_status_t h2_to_h1_add_headers(h2_to_h1 *to_h1, apr_table_t *headers); - -/** End the request headers. - */ -apr_status_t h2_to_h1_end_headers(h2_to_h1 *to_h1, - struct h2_task *task, int eos); - -/* Add request body data. - */ -apr_status_t h2_to_h1_add_data(h2_to_h1 *to_h1, - const char *data, size_t len); - -/* Flush the converted data onto the h2_mplx instance. - */ -apr_status_t h2_to_h1_flush(h2_to_h1 *to_h1); - -/* Close the request, flushed automatically. - */ -apr_status_t h2_to_h1_close(h2_to_h1 *to_h1); - -#endif /* defined(__mod_h2__h2_to_h1__) */ diff --git a/modules/http2/h2_util.c b/modules/http2/h2_util.c index 9d141be9..76ecc276 100644 --- a/modules/http2/h2_util.c +++ b/modules/http2/h2_util.c @@ -20,10 +20,12 @@ #include #include #include +#include #include #include "h2_private.h" +#include "h2_request.h" #include "h2_util.h" size_t h2_util_hex_dump(char *buffer, size_t maxlen, @@ -204,6 +206,10 @@ const char *h2_util_first_token_match(apr_pool_t *pool, const char *s, return NULL; } +/******************************************************************************* + * h2_util for bucket brigades + ******************************************************************************/ + /* DEEP_COPY==0 crashes under load. I think the setaside is fine, * however buckets moved to another thread will still be * free'd against the old bucket_alloc. *And* if the old @@ -214,7 +220,7 @@ static const int DEEP_COPY = 1; static const int FILE_MOVE = 1; static apr_status_t last_not_included(apr_bucket_brigade *bb, - apr_size_t maxlen, + apr_off_t maxlen, int same_alloc, int *pfile_buckets_allowed, apr_bucket **pend) @@ -223,7 +229,7 @@ static apr_status_t last_not_included(apr_bucket_brigade *bb, apr_status_t status = APR_SUCCESS; int files_allowed = pfile_buckets_allowed? *pfile_buckets_allowed : 0; - if (maxlen > 0) { + if (maxlen >= 0) { /* Find the bucket, up to which we reach maxlen/mem bytes */ for (b = APR_BRIGADE_FIRST(bb); (b != APR_BRIGADE_SENTINEL(bb)); @@ -274,7 +280,7 @@ static apr_status_t last_not_included(apr_bucket_brigade *bb, #define LOG_LEVEL APLOG_INFO apr_status_t h2_util_move(apr_bucket_brigade *to, apr_bucket_brigade *from, - apr_size_t maxlen, int *pfile_handles_allowed, + apr_off_t maxlen, int *pfile_handles_allowed, const char *msg) { apr_status_t status = APR_SUCCESS; @@ -406,7 +412,7 @@ apr_status_t h2_util_move(apr_bucket_brigade *to, apr_bucket_brigade *from, } apr_status_t h2_util_copy(apr_bucket_brigade *to, apr_bucket_brigade *from, - apr_size_t maxlen, const char *msg) + apr_off_t maxlen, const char *msg) { apr_status_t status = APR_SUCCESS; int same_alloc; @@ -482,7 +488,7 @@ int h2_util_has_flush_or_eos(apr_bucket_brigade *bb) { return 0; } -int h2_util_has_eos(apr_bucket_brigade *bb, apr_size_t len) +int h2_util_has_eos(apr_bucket_brigade *bb, apr_off_t len) { apr_bucket *b, *end; @@ -536,7 +542,7 @@ int h2_util_bb_has_data_or_eos(apr_bucket_brigade *bb) } apr_status_t h2_util_bb_avail(apr_bucket_brigade *bb, - apr_size_t *plen, int *peos) + apr_off_t *plen, int *peos) { apr_status_t status; apr_off_t blen = 0; @@ -549,36 +555,29 @@ apr_status_t h2_util_bb_avail(apr_bucket_brigade *bb, else if (blen == 0) { /* empty brigade, does it have an EOS bucket somwhere? */ *plen = 0; - *peos = h2_util_has_eos(bb, 0); + *peos = h2_util_has_eos(bb, -1); } - else if (blen > 0) { + else { /* data in the brigade, limit the length returned. Check for EOS * bucket only if we indicate data. This is required since plen == 0 * means "the whole brigade" for h2_util_hash_eos() */ - if (blen < (apr_off_t)*plen) { + if (blen < *plen || *plen < 0) { *plen = blen; } - *peos = (*plen > 0)? h2_util_has_eos(bb, *plen) : 0; - } - else if (blen < 0) { - /* famous SHOULD NOT HAPPEN, sinc we told apr_brigade_length to readall - */ - *plen = 0; - *peos = h2_util_has_eos(bb, 0); - return APR_EINVAL; + *peos = h2_util_has_eos(bb, *plen); } return APR_SUCCESS; } apr_status_t h2_util_bb_readx(apr_bucket_brigade *bb, h2_util_pass_cb *cb, void *ctx, - apr_size_t *plen, int *peos) + apr_off_t *plen, int *peos) { apr_status_t status = APR_SUCCESS; int consume = (cb != NULL); - apr_size_t written = 0; - apr_size_t avail = *plen; + apr_off_t written = 0; + apr_off_t avail = *plen; apr_bucket *next, *b; /* Pass data in our brigade through the callback until the length @@ -606,8 +605,7 @@ apr_status_t h2_util_bb_readx(apr_bucket_brigade *bb, if (b->length == ((apr_size_t)-1)) { /* read to determine length */ - status = apr_bucket_read(b, &data, &data_len, - APR_NONBLOCK_READ); + status = apr_bucket_read(b, &data, &data_len, APR_NONBLOCK_READ); } else { data_len = b->length; @@ -647,3 +645,343 @@ apr_status_t h2_util_bb_readx(apr_bucket_brigade *bb, return status; } +void h2_util_bb_log(conn_rec *c, int stream_id, int level, + const char *tag, apr_bucket_brigade *bb) +{ + char buffer[16 * 1024]; + const char *line = "(null)"; + apr_size_t bmax = sizeof(buffer)/sizeof(buffer[0]); + int off = 0; + apr_bucket *b; + + if (bb) { + memset(buffer, 0, bmax--); + for (b = APR_BRIGADE_FIRST(bb); + bmax && (b != APR_BRIGADE_SENTINEL(bb)); + b = APR_BUCKET_NEXT(b)) { + + if (APR_BUCKET_IS_METADATA(b)) { + if (APR_BUCKET_IS_EOS(b)) { + off += apr_snprintf(buffer+off, bmax-off, "eos "); + } + else if (APR_BUCKET_IS_FLUSH(b)) { + off += apr_snprintf(buffer+off, bmax-off, "flush "); + } + else if (AP_BUCKET_IS_EOR(b)) { + off += apr_snprintf(buffer+off, bmax-off, "eor "); + } + else { + off += apr_snprintf(buffer+off, bmax-off, "meta(unknown) "); + } + } + else { + const char *btype = "data"; + if (APR_BUCKET_IS_FILE(b)) { + btype = "file"; + } + else if (APR_BUCKET_IS_PIPE(b)) { + btype = "pipe"; + } + else if (APR_BUCKET_IS_SOCKET(b)) { + btype = "socket"; + } + else if (APR_BUCKET_IS_HEAP(b)) { + btype = "heap"; + } + else if (APR_BUCKET_IS_TRANSIENT(b)) { + btype = "transient"; + } + else if (APR_BUCKET_IS_IMMORTAL(b)) { + btype = "immortal"; + } +#if APR_HAS_MMAP + else if (APR_BUCKET_IS_MMAP(b)) { + btype = "mmap"; + } +#endif + else if (APR_BUCKET_IS_POOL(b)) { + btype = "pool"; + } + + off += apr_snprintf(buffer+off, bmax-off, "%s[%ld] ", + btype, + (long)(b->length == ((apr_size_t)-1)? + -1 : b->length)); + } + } + line = *buffer? buffer : "(empty)"; + } + ap_log_cerror(APLOG_MARK, level, 0, c, "bb_dump(%ld-%d)-%s: %s", + c->id, stream_id, tag, line); + +} + +apr_status_t h2_transfer_brigade(apr_bucket_brigade *to, + apr_bucket_brigade *from, + apr_pool_t *p, + apr_off_t *plen, + int *peos) +{ + apr_bucket *e; + apr_off_t len = 0, remain = *plen; + apr_status_t rv; + + *peos = 0; + + while (!APR_BRIGADE_EMPTY(from)) { + e = APR_BRIGADE_FIRST(from); + + if (APR_BUCKET_IS_METADATA(e)) { + if (APR_BUCKET_IS_EOS(e)) { + *peos = 1; + } + } + else { + if (remain > 0 && e->length == ((apr_size_t)-1)) { + const char *ign; + apr_size_t ilen; + rv = apr_bucket_read(e, &ign, &ilen, APR_BLOCK_READ); + if (rv != APR_SUCCESS) { + return rv; + } + } + + if (remain < e->length) { + if (remain <= 0) { + return APR_SUCCESS; + } + apr_bucket_split(e, remain); + } + } + + rv = apr_bucket_setaside(e, p); + + /* If the bucket type does not implement setaside, then + * (hopefully) morph it into a bucket type which does, and set + * *that* aside... */ + if (rv == APR_ENOTIMPL) { + const char *s; + apr_size_t n; + + rv = apr_bucket_read(e, &s, &n, APR_BLOCK_READ); + if (rv == APR_SUCCESS) { + rv = apr_bucket_setaside(e, p); + } + } + + if (rv != APR_SUCCESS) { + /* Return an error but still save the brigade if + * ->setaside() is really not implemented. */ + if (rv != APR_ENOTIMPL) { + return rv; + } + } + + APR_BUCKET_REMOVE(e); + APR_BRIGADE_INSERT_TAIL(to, e); + len += e->length; + remain -= e->length; + } + + *plen = len; + return APR_SUCCESS; +} + +/******************************************************************************* + * h2_ngheader + ******************************************************************************/ + +int h2_util_ignore_header(const char *name) +{ + /* never forward, ch. 8.1.2.2 */ + return (H2_HD_MATCH_LIT_CS("connection", name) + || H2_HD_MATCH_LIT_CS("proxy-connection", name) + || H2_HD_MATCH_LIT_CS("upgrade", name) + || H2_HD_MATCH_LIT_CS("keep-alive", name) + || H2_HD_MATCH_LIT_CS("transfer-encoding", name)); +} + +static int count_header(void *ctx, const char *key, const char *value) +{ + if (!h2_util_ignore_header(key)) { + (*((size_t*)ctx))++; + } + return 1; +} + +#define NV_ADD_LIT_CS(nv, k, v) add_header(nv, k, sizeof(k) - 1, v, strlen(v)) +#define NV_ADD_CS_CS(nv, k, v) add_header(nv, k, strlen(k), v, strlen(v)) + +static int add_header(h2_ngheader *ngh, + const char *key, size_t key_len, + const char *value, size_t val_len) +{ + nghttp2_nv *nv = &ngh->nv[ngh->nvlen++]; + + nv->name = (uint8_t*)key; + nv->namelen = key_len; + nv->value = (uint8_t*)value; + nv->valuelen = val_len; + return 1; +} + +static int add_table_header(void *ctx, const char *key, const char *value) +{ + if (!h2_util_ignore_header(key)) { + add_header(ctx, key, strlen(key), value, strlen(value)); + } + return 1; +} + + +h2_ngheader *h2_util_ngheader_make(apr_pool_t *p, apr_table_t *header) +{ + h2_ngheader *ngh; + size_t n; + + n = 0; + apr_table_do(count_header, &n, header, NULL); + + ngh = apr_pcalloc(p, sizeof(h2_ngheader)); + ngh->nv = apr_pcalloc(p, n * sizeof(nghttp2_nv)); + apr_table_do(add_table_header, ngh, header, NULL); + + return ngh; +} + +h2_ngheader *h2_util_ngheader_make_res(apr_pool_t *p, + int http_status, + apr_table_t *header) +{ + h2_ngheader *ngh; + size_t n; + + n = 1; + apr_table_do(count_header, &n, header, NULL); + + ngh = apr_pcalloc(p, sizeof(h2_ngheader)); + ngh->nv = apr_pcalloc(p, n * sizeof(nghttp2_nv)); + NV_ADD_LIT_CS(ngh, ":status", apr_psprintf(p, "%d", http_status)); + apr_table_do(add_table_header, ngh, header, NULL); + + return ngh; +} + +h2_ngheader *h2_util_ngheader_make_req(apr_pool_t *p, + const struct h2_request *req) +{ + + h2_ngheader *ngh; + size_t n; + + AP_DEBUG_ASSERT(req); + AP_DEBUG_ASSERT(req->scheme); + AP_DEBUG_ASSERT(req->authority); + AP_DEBUG_ASSERT(req->path); + AP_DEBUG_ASSERT(req->method); + + n = 4; + apr_table_do(count_header, &n, req->headers, NULL); + + ngh = apr_pcalloc(p, sizeof(h2_ngheader)); + ngh->nv = apr_pcalloc(p, n * sizeof(nghttp2_nv)); + NV_ADD_LIT_CS(ngh, ":scheme", req->scheme); + NV_ADD_LIT_CS(ngh, ":authority", req->authority); + NV_ADD_LIT_CS(ngh, ":path", req->path); + NV_ADD_LIT_CS(ngh, ":method", req->method); + apr_table_do(add_table_header, ngh, req->headers, NULL); + + return ngh; +} + +/******************************************************************************* + * header HTTP/1 <-> HTTP/2 conversions + ******************************************************************************/ + + +typedef struct { + const char *name; + size_t len; +} literal; + +#define H2_DEF_LITERAL(n) { (n), (sizeof(n)-1) } +#define H2_ALEN(a) (sizeof(a)/sizeof((a)[0])) +#define H2_LIT_ARGS(a) (a),H2_ALEN(a) + +static literal IgnoredRequestHeaders[] = { + H2_DEF_LITERAL("host"), + H2_DEF_LITERAL("expect"), + H2_DEF_LITERAL("upgrade"), + H2_DEF_LITERAL("connection"), + H2_DEF_LITERAL("keep-alive"), + H2_DEF_LITERAL("http2-settings"), + H2_DEF_LITERAL("proxy-connection"), + H2_DEF_LITERAL("transfer-encoding"), +}; +static literal IgnoredRequestTrailers[] = { /* Ignore, see rfc7230, ch. 4.1.2 */ + H2_DEF_LITERAL("te"), + H2_DEF_LITERAL("host"), + H2_DEF_LITERAL("range"), + H2_DEF_LITERAL("cookie"), + H2_DEF_LITERAL("expect"), + H2_DEF_LITERAL("pragma"), + H2_DEF_LITERAL("max-forwards"), + H2_DEF_LITERAL("cache-control"), + H2_DEF_LITERAL("authorization"), + H2_DEF_LITERAL("content-length"), + H2_DEF_LITERAL("proxy-authorization"), +}; +static literal IgnoredResponseTrailers[] = { + H2_DEF_LITERAL("age"), + H2_DEF_LITERAL("date"), + H2_DEF_LITERAL("vary"), + H2_DEF_LITERAL("cookie"), + H2_DEF_LITERAL("expires"), + H2_DEF_LITERAL("warning"), + H2_DEF_LITERAL("location"), + H2_DEF_LITERAL("retry-after"), + H2_DEF_LITERAL("cache-control"), + H2_DEF_LITERAL("www-authenticate"), + H2_DEF_LITERAL("proxy-authenticate"), +}; + +static int ignore_header(const literal *lits, size_t llen, + const char *name, size_t nlen) +{ + const literal *lit; + int i; + + for (i = 0; i < llen; ++i) { + lit = &lits[i]; + if (lit->len == nlen && !apr_strnatcasecmp(lit->name, name)) { + return 1; + } + } + return 0; +} + +int h2_req_ignore_header(const char *name, size_t len) +{ + return ignore_header(H2_LIT_ARGS(IgnoredRequestHeaders), name, len); +} + +int h2_req_ignore_trailer(const char *name, size_t len) +{ + return (h2_req_ignore_header(name, len) + || ignore_header(H2_LIT_ARGS(IgnoredRequestTrailers), name, len)); +} + +int h2_res_ignore_trailer(const char *name, size_t len) +{ + return ignore_header(H2_LIT_ARGS(IgnoredResponseTrailers), name, len); +} + +void h2_req_strip_ignored_header(apr_table_t *headers) +{ + int i; + for (i = 0; i < H2_ALEN(IgnoredRequestHeaders); ++i) { + apr_table_unset(headers, IgnoredRequestHeaders[i].name); + } +} + + diff --git a/modules/http2/h2_util.h b/modules/http2/h2_util.h index 9a1b5c6d..8f8be299 100644 --- a/modules/http2/h2_util.h +++ b/modules/http2/h2_util.h @@ -16,6 +16,7 @@ #ifndef __mod_h2__h2_util__ #define __mod_h2__h2_util__ +struct h2_request; struct nghttp2_frame; size_t h2_util_hex_dump(char *buffer, size_t maxlen, @@ -29,6 +30,11 @@ char *h2_strlwr(char *s); void h2_util_camel_case_header(char *s, size_t len); +int h2_req_ignore_header(const char *name, size_t len); +int h2_req_ignore_trailer(const char *name, size_t len); +void h2_req_strip_ignored_header(apr_table_t *headers); +int h2_res_ignore_trailer(const char *name, size_t len); + /** * Return != 0 iff the string s contains the token, as specified in * HTTP header syntax, rfc7230. @@ -67,19 +73,33 @@ apr_size_t h2_util_base64url_decode(const char **decoded, nv->value = (uint8_t *)VALUE; \ nv->valuelen = strlen(VALUE) +int h2_util_ignore_header(const char *name); + +typedef struct h2_ngheader { + nghttp2_nv *nv; + apr_size_t nvlen; +} h2_ngheader; + +h2_ngheader *h2_util_ngheader_make(apr_pool_t *p, apr_table_t *header); +h2_ngheader *h2_util_ngheader_make_res(apr_pool_t *p, + int http_status, + apr_table_t *header); +h2_ngheader *h2_util_ngheader_make_req(apr_pool_t *p, + const struct h2_request *req); + /** * Moves data from one brigade into another. If maxlen > 0, it only * moves up to maxlen bytes into the target brigade, making bucket splits * if needed. * @param to the brigade to move the data to * @param from the brigade to get the data from - * @param maxlen of bytes to move, 0 for all + * @param maxlen of bytes to move, <= 0 for all * @param pfile_buckets_allowed how many file buckets may be moved, * may be 0 or NULL * @param msg message for use in logging */ apr_status_t h2_util_move(apr_bucket_brigade *to, apr_bucket_brigade *from, - apr_size_t maxlen, int *pfile_buckets_allowed, + apr_off_t maxlen, int *pfile_buckets_allowed, const char *msg); /** @@ -88,11 +108,11 @@ apr_status_t h2_util_move(apr_bucket_brigade *to, apr_bucket_brigade *from, * if needed. * @param to the brigade to copy the data to * @param from the brigade to get the data from - * @param maxlen of bytes to copy, 0 for all + * @param maxlen of bytes to copy, <= 0 for all * @param msg message for use in logging */ apr_status_t h2_util_copy(apr_bucket_brigade *to, apr_bucket_brigade *from, - apr_size_t maxlen, const char *msg); + apr_off_t maxlen, const char *msg); /** * Return != 0 iff there is a FLUSH or EOS bucket in the brigade. @@ -100,7 +120,7 @@ apr_status_t h2_util_copy(apr_bucket_brigade *to, apr_bucket_brigade *from, * @return != 0 iff brigade holds FLUSH or EOS bucket (or both) */ int h2_util_has_flush_or_eos(apr_bucket_brigade *bb); -int h2_util_has_eos(apr_bucket_brigade *bb, apr_size_t len); +int h2_util_has_eos(apr_bucket_brigade *bb, apr_off_t len); int h2_util_bb_has_data(apr_bucket_brigade *bb); int h2_util_bb_has_data_or_eos(apr_bucket_brigade *bb); @@ -112,13 +132,52 @@ int h2_util_bb_has_data_or_eos(apr_bucket_brigade *bb); * @param on return, if eos has been reached */ apr_status_t h2_util_bb_avail(apr_bucket_brigade *bb, - apr_size_t *plen, int *peos); + apr_off_t *plen, int *peos); typedef apr_status_t h2_util_pass_cb(void *ctx, - const char *data, apr_size_t len); + const char *data, apr_off_t len); +/** + * Read at most *plen bytes from the brigade and pass them into the + * given callback. If cb is NULL, just return the amount of data that + * could have been read. + * If an EOS was/would be encountered, set *peos != 0. + * @param bb the brigade to read from + * @param cb the callback to invoke for the read data + * @param ctx optional data passed to callback + * @param plen inout, as input gives the maximum number of bytes to read, + * on return specifies the actual/would be number of bytes + * @param peos != 0 iff an EOS bucket was/would be encountered. + */ apr_status_t h2_util_bb_readx(apr_bucket_brigade *bb, h2_util_pass_cb *cb, void *ctx, - apr_size_t *plen, int *peos); + apr_off_t *plen, int *peos); + +/** + * Logs the bucket brigade (which bucket types with what length) + * to the log at the given level. + * @param c the connection to log for + * @param stream_id the stream identifier this brigade belongs to + * @param level the log level (as in APLOG_*) + * @param tag a short message text about the context + * @param bb the brigade to log + */ +void h2_util_bb_log(conn_rec *c, int stream_id, int level, + const char *tag, apr_bucket_brigade *bb); + +/** + * Transfer buckets from one brigade to another with a limit on the + * maximum amount of bytes transfered. + * @param to brigade to transfer buckets to + * @param from brigades to remove buckets from + * @param p pool that buckets should be setaside to + * @param plen maximum bytes to transfer, actual bytes transferred + * @param peos if an EOS bucket was transferred + */ +apr_status_t h2_transfer_brigade(apr_bucket_brigade *to, + apr_bucket_brigade *from, + apr_pool_t *p, + apr_off_t *plen, + int *peos); #endif /* defined(__mod_h2__h2_util__) */ diff --git a/modules/http2/h2_version.h b/modules/http2/h2_version.h index 7a03865c..07d73519 100644 --- a/modules/http2/h2_version.h +++ b/modules/http2/h2_version.h @@ -20,7 +20,7 @@ * @macro * Version number of the h2 module as c string */ -#define MOD_HTTP2_VERSION "1.0.0" +#define MOD_HTTP2_VERSION "1.0.11" /** * @macro @@ -28,7 +28,7 @@ * release. This is a 24 bit number with 8 bits for major number, 8 bits * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. */ -#define MOD_HTTP2_VERSION_NUM 0x010000 +#define MOD_HTTP2_VERSION_NUM 0x01000b #endif /* mod_h2_h2_version_h */ diff --git a/modules/http2/h2_worker.c b/modules/http2/h2_worker.c index 8145b7aa..3119cb08 100644 --- a/modules/http2/h2_worker.c +++ b/modules/http2/h2_worker.c @@ -22,7 +22,9 @@ #include #include "h2_private.h" +#include "h2_conn.h" #include "h2_mplx.h" +#include "h2_request.h" #include "h2_task.h" #include "h2_worker.h" @@ -55,7 +57,7 @@ static void* APR_THREAD_FUNC execute(apr_thread_t *thread, void *wctx) if (worker->task) { h2_task_do(worker->task, worker); worker->task = NULL; - apr_thread_cond_signal(h2_worker_get_cond(worker)); + apr_thread_cond_signal(worker->io); } } @@ -71,6 +73,19 @@ static void* APR_THREAD_FUNC execute(apr_thread_t *thread, void *wctx) return NULL; } +static apr_status_t cleanup_join_thread(void *ctx) +{ + h2_worker *w = ctx; + /* do the join only when the worker is aborted. Otherwise, + * we are probably in a process shutdown. + */ + if (w->thread && w->aborted) { + apr_status_t rv; + apr_thread_join(&rv, w->thread); + } + return APR_SUCCESS; +} + h2_worker *h2_worker_create(int id, apr_pool_t *parent_pool, apr_threadattr_t *attr, @@ -81,8 +96,9 @@ h2_worker *h2_worker_create(int id, apr_allocator_t *allocator = NULL; apr_pool_t *pool = NULL; h2_worker *w; + apr_status_t status; - apr_status_t status = apr_allocator_create(&allocator); + status = apr_allocator_create(&allocator); if (status != APR_SUCCESS) { return NULL; } @@ -99,7 +115,6 @@ h2_worker *h2_worker_create(int id, w->id = id; w->pool = pool; - w->bucket_alloc = apr_bucket_alloc_create(pool); w->get_next = get_next; w->worker_done = worker_done; @@ -110,7 +125,8 @@ h2_worker *h2_worker_create(int id, return NULL; } - apr_thread_create(&w->thread, attr, execute, w, pool); + apr_pool_pre_cleanup_register(w->pool, w, cleanup_join_thread); + apr_thread_create(&w->thread, attr, execute, w, w->pool); } return w; } @@ -143,28 +159,46 @@ int h2_worker_is_aborted(h2_worker *worker) return worker->aborted; } -apr_thread_t *h2_worker_get_thread(h2_worker *worker) +h2_task *h2_worker_create_task(h2_worker *worker, h2_mplx *m, + const h2_request *req, int eos) { - return worker->thread; + h2_task *task; + + /* Create a subpool from the worker one to be used for all things + * with life-time of this task execution. + */ + if (!worker->task_pool) { + apr_pool_create(&worker->task_pool, worker->pool); + } + task = h2_task_create(m->id, req, worker->task_pool, m, eos); + + /* Link the task to the worker which provides useful things such + * as mutex, a socket etc. */ + task->io = worker->io; + + return task; } -apr_thread_cond_t *h2_worker_get_cond(h2_worker *worker) -{ - return worker->io; +apr_status_t h2_worker_setup_task(h2_worker *worker, h2_task *task) { + apr_status_t status; + + + status = h2_conn_setup(task, apr_bucket_alloc_create(task->pool), + worker->thread, worker->socket); + + return status; } -apr_socket_t *h2_worker_get_socket(h2_worker *worker) +void h2_worker_release_task(h2_worker *worker, struct h2_task *task) { - return worker->socket; + task->io = NULL; + task->pool = NULL; + apr_pool_clear(worker->task_pool); } -apr_pool_t *h2_worker_get_pool(h2_worker *worker) +apr_socket_t *h2_worker_get_socket(h2_worker *worker) { - return worker->pool; + return worker->socket; } -apr_bucket_alloc_t *h2_worker_get_bucket_alloc(h2_worker *worker) -{ - return worker->bucket_alloc; -} diff --git a/modules/http2/h2_worker.h b/modules/http2/h2_worker.h index 9c69e6b5..035448e5 100644 --- a/modules/http2/h2_worker.h +++ b/modules/http2/h2_worker.h @@ -18,6 +18,7 @@ struct apr_thread_cond_t; struct h2_mplx; +struct h2_request; struct h2_task; /* h2_worker is a basically a apr_thread_t that reads fromt he h2_workers @@ -44,7 +45,7 @@ struct h2_worker { int id; apr_thread_t *thread; apr_pool_t *pool; - apr_bucket_alloc_t *bucket_alloc; + apr_pool_t *task_pool; struct apr_thread_cond_t *io; apr_socket_t *socket; @@ -142,14 +143,11 @@ int h2_worker_get_id(h2_worker *worker); int h2_worker_is_aborted(h2_worker *worker); -apr_pool_t *h2_worker_get_pool(h2_worker *worker); - -apr_bucket_alloc_t *h2_worker_get_bucket_alloc(h2_worker *worker); +struct h2_task *h2_worker_create_task(h2_worker *worker, struct h2_mplx *m, + const struct h2_request *req, int eos); +apr_status_t h2_worker_setup_task(h2_worker *worker, struct h2_task *task); +void h2_worker_release_task(h2_worker *worker, struct h2_task *task); apr_socket_t *h2_worker_get_socket(h2_worker *worker); -apr_thread_t *h2_worker_get_thread(h2_worker *worker); - -struct apr_thread_cond_t *h2_worker_get_cond(h2_worker *worker); - #endif /* defined(__mod_h2__h2_worker__) */ diff --git a/modules/http2/h2_workers.c b/modules/http2/h2_workers.c index cf300958..3c08ff35 100644 --- a/modules/http2/h2_workers.c +++ b/modules/http2/h2_workers.c @@ -42,6 +42,22 @@ static int in_list(h2_workers *workers, h2_mplx *m) return 0; } +static void cleanup_zombies(h2_workers *workers, int lock) { + if (lock) { + apr_thread_mutex_lock(workers->lock); + } + while (!H2_WORKER_LIST_EMPTY(&workers->zombies)) { + h2_worker *zombie = H2_WORKER_LIST_FIRST(&workers->zombies); + H2_WORKER_REMOVE(zombie); + ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s, + "h2_workers: cleanup zombie %d", zombie->id); + h2_worker_destroy(zombie); + } + if (lock) { + apr_thread_mutex_unlock(workers->lock); + } +} + /** * Get the next task for the given worker. Will block until a task arrives @@ -63,7 +79,7 @@ static apr_status_t get_mplx_next(h2_worker *worker, h2_mplx **pm, if (*pm && ptask != NULL) { /* We have a h2_mplx instance and the worker wants the next task. * Try to get one from the given mplx. */ - *ptask = h2_mplx_pop_task(*pm, &has_more); + *ptask = h2_mplx_pop_task(*pm, worker, &has_more); if (*ptask) { return APR_SUCCESS; } @@ -108,7 +124,7 @@ static apr_status_t get_mplx_next(h2_worker *worker, h2_mplx **pm, m = H2_MPLX_LIST_FIRST(&workers->mplxs); H2_MPLX_REMOVE(m); - task = h2_mplx_pop_task(m, &has_more); + task = h2_mplx_pop_task(m, worker, &has_more); if (task) { if (has_more) { H2_MPLX_LIST_INSERT_TAIL(&workers->mplxs, m); @@ -123,23 +139,12 @@ static apr_status_t get_mplx_next(h2_worker *worker, h2_mplx **pm, if (!task) { /* Need to wait for either a new mplx to arrive. */ + cleanup_zombies(workers, 0); + if (workers->worker_count > workers->min_size) { apr_time_t now = apr_time_now(); if (now >= (start_wait + max_wait)) { /* waited long enough without getting a task. */ - status = APR_TIMEUP; - } - else { - ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s, - "h2_worker(%d): waiting signal, " - "worker_count=%d", worker->id, - (int)workers->worker_count); - status = apr_thread_cond_timedwait(workers->mplx_added, - workers->lock, max_wait); - } - - if (status == APR_TIMEUP) { - /* waited long enough */ if (workers->worker_count > workers->min_size) { ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s, @@ -148,6 +153,12 @@ static apr_status_t get_mplx_next(h2_worker *worker, h2_mplx **pm, break; } } + ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s, + "h2_worker(%d): waiting signal, " + "worker_count=%d", worker->id, + (int)workers->worker_count); + apr_thread_cond_timedwait(workers->mplx_added, + workers->lock, max_wait); } else { ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s, @@ -163,7 +174,7 @@ static apr_status_t get_mplx_next(h2_worker *worker, h2_mplx **pm, * needed to give up with more than enough workers. */ if (task) { - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s, + ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s, "h2_worker(%d): start task(%s)", h2_worker_get_id(worker), task->id); /* Since we hand out a reference to the worker, we increase @@ -194,11 +205,11 @@ static void worker_done(h2_worker *worker, void *ctx) h2_workers *workers = (h2_workers *)ctx; apr_status_t status = apr_thread_mutex_lock(workers->lock); if (status == APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s, + ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s, "h2_worker(%d): done", h2_worker_get_id(worker)); H2_WORKER_REMOVE(worker); --workers->worker_count; - h2_worker_destroy(worker); + H2_WORKER_LIST_INSERT_TAIL(&workers->zombies, worker); apr_thread_mutex_unlock(workers->lock); } @@ -213,7 +224,7 @@ static apr_status_t add_worker(h2_workers *workers) if (!w) { return APR_ENOMEM; } - ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, workers->s, + ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s, "h2_workers: adding worker(%d)", h2_worker_get_id(w)); ++workers->worker_count; H2_WORKER_LIST_INSERT_TAIL(&workers->workers, w); @@ -235,15 +246,22 @@ static apr_status_t h2_workers_start(h2_workers *workers) { return status; } -h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pool, +h2_workers *h2_workers_create(server_rec *s, apr_pool_t *server_pool, int min_size, int max_size) { apr_status_t status; h2_workers *workers; + apr_pool_t *pool; + AP_DEBUG_ASSERT(s); - AP_DEBUG_ASSERT(pool); - status = APR_SUCCESS; + AP_DEBUG_ASSERT(server_pool); + /* let's have our own pool that will be parent to all h2_worker + * instances we create. This happens in various threads, but always + * guarded by our lock. Without this pool, all subpool creations would + * happen on the pool handed to us, which we do not guard. + */ + apr_pool_create(&pool, server_pool); workers = apr_pcalloc(pool, sizeof(h2_workers)); if (workers) { workers->s = s; @@ -255,6 +273,7 @@ h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pool, apr_threadattr_create(&workers->thread_attr, workers->pool); APR_RING_INIT(&workers->workers, h2_worker, link); + APR_RING_INIT(&workers->zombies, h2_worker, link); APR_RING_INIT(&workers->mplxs, h2_mplx, link); status = apr_thread_mutex_create(&workers->lock, @@ -278,6 +297,9 @@ h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pool, void h2_workers_destroy(h2_workers *workers) { + /* before we go, cleanup any zombie workers that may have accumulated */ + cleanup_zombies(workers, 1); + if (workers->mplx_added) { apr_thread_cond_destroy(workers->mplx_added); workers->mplx_added = NULL; @@ -294,13 +316,17 @@ void h2_workers_destroy(h2_workers *workers) h2_worker *w = H2_WORKER_LIST_FIRST(&workers->workers); H2_WORKER_REMOVE(w); } + if (workers->pool) { + apr_pool_destroy(workers->pool); + /* workers is gone */ + } } apr_status_t h2_workers_register(h2_workers *workers, struct h2_mplx *m) { apr_status_t status = apr_thread_mutex_lock(workers->lock); if (status == APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_DEBUG, status, workers->s, + ap_log_error(APLOG_MARK, APLOG_TRACE2, status, workers->s, "h2_workers: register mplx(%ld)", m->id); if (in_list(workers, m)) { status = APR_EAGAIN; @@ -320,6 +346,9 @@ apr_status_t h2_workers_register(h2_workers *workers, struct h2_mplx *m) add_worker(workers); } + /* cleanup any zombie workers that may have accumulated */ + cleanup_zombies(workers, 0); + apr_thread_mutex_unlock(workers->lock); } return status; @@ -334,6 +363,9 @@ apr_status_t h2_workers_unregister(h2_workers *workers, struct h2_mplx *m) H2_MPLX_REMOVE(m); status = APR_SUCCESS; } + /* cleanup any zombie workers that may have accumulated */ + cleanup_zombies(workers, 0); + apr_thread_mutex_unlock(workers->lock); } return status; diff --git a/modules/http2/h2_workers.h b/modules/http2/h2_workers.h index 50fd6b8a..99aa1f4d 100644 --- a/modules/http2/h2_workers.h +++ b/modules/http2/h2_workers.h @@ -42,6 +42,7 @@ struct h2_workers { apr_threadattr_t *thread_attr; APR_RING_HEAD(h2_worker_list, h2_worker) workers; + APR_RING_HEAD(h2_worker_zombies, h2_worker) zombies; APR_RING_HEAD(h2_mplx_list, h2_mplx) mplxs; int worker_count; diff --git a/modules/http2/mod_http2.dsp b/modules/http2/mod_http2.dsp index c3e139e9..12a3abfe 100644 --- a/modules/http2/mod_http2.dsp +++ b/modules/http2/mod_http2.dsp @@ -105,6 +105,14 @@ SOURCE=./h2_alt_svc.c # End Source File # Begin Source File +SOURCE=./h2_bucket_eoc.c +# End Source File +# Begin Source File + +SOURCE=./h2_bucket_eos.c +# End Source File +# Begin Source File + SOURCE=./h2_config.c # End Source File # Begin Source File @@ -141,6 +149,10 @@ SOURCE=./h2_mplx.c # End Source File # Begin Source File +SOURCE=./h2_push.c +# End Source File +# Begin Source File + SOURCE=./h2_request.c # End Source File # Begin Source File @@ -181,10 +193,6 @@ SOURCE=./h2_task_queue.c # End Source File # Begin Source File -SOURCE=./h2_to_h1.c -# End Source File -# Begin Source File - SOURCE=./h2_util.c # End Source File # Begin Source File diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c index 0f896bdf..05f471b6 100644 --- a/modules/proxy/mod_proxy.c +++ b/modules/proxy/mod_proxy.c @@ -65,7 +65,7 @@ static const char *set_worker_param(apr_pool_t *p, apr_interval_time_t timeout; if (!strcasecmp(key, "loadfactor")) { - /* Normalized load factor. Used with BalancerMamber, + /* Normalized load factor. Used with BalancerMember, * it is a number between 1 and 100. */ worker->s->lbfactor = atoi(val); diff --git a/modules/proxy/mod_proxy_fdpass.c b/modules/proxy/mod_proxy_fdpass.c index 63cf4696..195b0fdb 100644 --- a/modules/proxy/mod_proxy_fdpass.c +++ b/modules/proxy/mod_proxy_fdpass.c @@ -57,7 +57,6 @@ static apr_status_t get_socket_from_path(apr_pool_t *p, *out_sock = NULL; rv = apr_socket_create(&s, AF_UNIX, SOCK_STREAM, 0, p); - if (rv != APR_SUCCESS) { return rv; } @@ -72,7 +71,6 @@ static apr_status_t get_socket_from_path(apr_pool_t *p, return APR_SUCCESS; } - static apr_status_t send_socket(apr_pool_t *p, apr_socket_t *s, apr_socket_t *outbound) @@ -119,7 +117,6 @@ static apr_status_t send_socket(apr_pool_t *p, return errno; } - return APR_SUCCESS; } @@ -149,7 +146,7 @@ static int proxy_fdpass_handler(request_rec *r, proxy_worker *worker, { int status; - const char *flush_method = worker->s->flusher ? worker->s->flusher : "flush"; + const char *flush_method = *worker->s->flusher ? worker->s->flusher : "flush"; proxy_fdpass_flush *flush = ap_lookup_provider(PROXY_FDPASS_FLUSHER, flush_method, "0"); @@ -191,7 +188,6 @@ static int proxy_fdpass_handler(request_rec *r, proxy_worker *worker, ap_set_core_module_config(r->connection->conn_config, dummy); } - return OK; } diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c index 41d7f9f7..717a694b 100644 --- a/modules/ssl/mod_ssl.c +++ b/modules/ssl/mod_ssl.c @@ -377,6 +377,7 @@ static int ssl_hook_pre_config(apr_pool_t *pconf, static SSLConnRec *ssl_init_connection_ctx(conn_rec *c) { SSLConnRec *sslconn = myConnConfig(c); + SSLSrvConfigRec *sc; if (sslconn) { return sslconn; @@ -386,6 +387,8 @@ static SSLConnRec *ssl_init_connection_ctx(conn_rec *c) sslconn->server = c->base_server; sslconn->verify_depth = UNSET; + sc = mySrvConfig(c->base_server); + sslconn->cipher_suite = sc->server->auth.cipher_suite; myConnConfigSet(c, sslconn); @@ -525,6 +528,7 @@ static apr_port_t ssl_hook_default_port(const request_rec *r) static int ssl_hook_pre_connection(conn_rec *c, void *csd) { + SSLSrvConfigRec *sc; SSLConnRec *sslconn = myConnConfig(c); @@ -537,8 +541,8 @@ static int ssl_hook_pre_connection(conn_rec *c, void *csd) /* * Immediately stop processing if SSL is disabled for this connection */ - if (!(sc && (sc->enabled == SSL_ENABLED_TRUE || - (sslconn && sslconn->is_proxy)))) + if (c->master || !(sc && (sc->enabled == SSL_ENABLED_TRUE || + (sslconn && sslconn->is_proxy)))) { return DECLINED; } @@ -566,6 +570,26 @@ static int ssl_hook_pre_connection(conn_rec *c, void *csd) return ssl_init_ssl_connection(c, NULL); } +static int ssl_hook_process_connection(conn_rec* c) +{ + SSLConnRec *sslconn = myConnConfig(c); + + if (sslconn && !sslconn->disabled) { + /* On an active SSL connection, let the input filters initialize + * themselves which triggers the handshake, which again triggers + * all kinds of useful things such as SNI and ALPN. + */ + apr_bucket_brigade* temp; + + temp = apr_brigade_create(c->pool, c->bucket_alloc); + ap_get_brigade(c->input_filters, temp, + AP_MODE_INIT, APR_BLOCK_READ, 0); + apr_brigade_destroy(temp); + } + + return DECLINED; +} + /* * the module registration phase */ @@ -579,6 +603,8 @@ static void ssl_register_hooks(apr_pool_t *p) ssl_io_filter_register(p); ap_hook_pre_connection(ssl_hook_pre_connection,NULL,NULL, APR_HOOK_MIDDLE); + ap_hook_process_connection(ssl_hook_process_connection, + NULL, NULL, APR_HOOK_MIDDLE); ap_hook_test_config (ssl_hook_ConfigTest, NULL,NULL, APR_HOOK_MIDDLE); ap_hook_post_config (ssl_init_Module, NULL,NULL, APR_HOOK_MIDDLE); ap_hook_http_scheme (ssl_hook_http_scheme, NULL,NULL, APR_HOOK_MIDDLE); diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c index e5c70f39..e68e6d23 100644 --- a/modules/ssl/ssl_engine_config.c +++ b/modules/ssl/ssl_engine_config.c @@ -832,8 +832,7 @@ const char *ssl_cmd_SSLCertificateFile(cmd_parms *cmd, return err; } - *(const char **)apr_array_push(sc->server->pks->cert_files) = - apr_pstrdup(cmd->pool, arg); + *(const char **)apr_array_push(sc->server->pks->cert_files) = arg; return NULL; } @@ -849,8 +848,7 @@ const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *cmd, return err; } - *(const char **)apr_array_push(sc->server->pks->key_files) = - apr_pstrdup(cmd->pool, arg); + *(const char **)apr_array_push(sc->server->pks->key_files) = arg; return NULL; } diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c index 1e6c6184..424f4e5e 100644 --- a/modules/ssl/ssl_engine_io.c +++ b/modules/ssl/ssl_engine_io.c @@ -187,6 +187,7 @@ static int bio_filter_out_write(BIO *bio, const char *in, int inl) { bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)(bio->ptr); apr_bucket *e; + int need_flush; /* Abort early if the client has initiated a renegotiation. */ if (outctx->filter_ctx->config->reneg_state == RENEG_ABORT) { @@ -205,6 +206,26 @@ static int bio_filter_out_write(BIO *bio, const char *in, int inl) e = apr_bucket_transient_create(in, inl, outctx->bb->bucket_alloc); APR_BRIGADE_INSERT_TAIL(outctx->bb, e); + /* In theory, OpenSSL should flush as necessary, but it is known + * not to do so correctly in some cases (< 0.9.8m; see PR 46952), + * or on the proxy/client side (after ssl23_client_hello(), e.g. + * ssl/proxy.t test suite). + * + * Historically, this flush call was performed only for an SSLv2 + * connection or for a proxy connection. Calling _out_flush can + * be expensive in cases where requests/reponses are pipelined, + * so limit the performance impact to handshake time. + */ +#if OPENSSL_VERSION_NUMBER < 0x0009080df + need_flush = !SSL_is_init_finished(outctx->filter_ctx->pssl) +#else + need_flush = SSL_in_connect_init(outctx->filter_ctx->pssl); +#endif + if (need_flush) { + e = apr_bucket_flush_create(outctx->bb->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(outctx->bb, e); + } + if (bio_filter_out_pass(outctx) < 0) { return -1; } @@ -298,9 +319,6 @@ typedef struct { apr_pool_t *pool; char buffer[AP_IOBUFSIZE]; ssl_filter_ctx_t *filter_ctx; -#ifdef HAVE_TLS_ALPN - int alpn_finished; /* 1 if ALPN has finished, 0 otherwise */ -#endif } bio_filter_in_ctx_t; /* @@ -448,21 +466,6 @@ static int bio_filter_in_read(BIO *bio, char *in, int inlen) return -1; } - /* In theory, OpenSSL should flush as necessary, but it is known - * not to do so correctly in some cases; see PR 46952. - * - * Historically, this flush call was performed only for an SSLv2 - * connection or for a proxy connection. Calling _out_flush - * should be very cheap in cases where it is unnecessary (and no - * output is buffered) so the performance impact of doing it - * unconditionally should be minimal. - */ - if (bio_filter_out_flush(inctx->bio_out) < 0) { - bio_filter_out_ctx_t *outctx = inctx->bio_out->ptr; - inctx->rc = outctx->rc; - return -1; - } - BIO_clear_retry_flags(bio); if (!inctx->bb) { @@ -860,7 +863,8 @@ static void ssl_io_filter_disable(SSLConnRec *sslconn, ap_filter_t *f) static apr_status_t ssl_io_filter_error(ap_filter_t *f, apr_bucket_brigade *bb, - apr_status_t status) + apr_status_t status, + int is_init) { SSLConnRec *sslconn = myConnConfig(f->c); apr_bucket *bucket; @@ -874,8 +878,13 @@ static apr_status_t ssl_io_filter_error(ap_filter_t *f, "trying to send HTML error page"); ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, sslconn->server); - sslconn->non_ssl_request = NON_SSL_SEND_HDR_SEP; ssl_io_filter_disable(sslconn, f); + f->c->keepalive = AP_CONN_CLOSE; + if (is_init) { + sslconn->non_ssl_request = NON_SSL_SEND_REQLINE; + return APR_EGENERAL; + } + sslconn->non_ssl_request = NON_SSL_SEND_HDR_SEP; /* fake the request line */ bucket = HTTP_ON_HTTPS_PORT_BUCKET(f->c->bucket_alloc); @@ -1329,11 +1338,22 @@ static apr_status_t ssl_io_filter_input(ap_filter_t *f, } if (!inctx->ssl) { + apr_bucket *bucket; SSLConnRec *sslconn = myConnConfig(f->c); + if (sslconn->non_ssl_request == NON_SSL_SEND_REQLINE) { + bucket = HTTP_ON_HTTPS_PORT_BUCKET(f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, bucket); + if (mode != AP_MODE_SPECULATIVE) { + sslconn->non_ssl_request = NON_SSL_SEND_HDR_SEP; + } + return APR_SUCCESS; + } if (sslconn->non_ssl_request == NON_SSL_SEND_HDR_SEP) { - apr_bucket *bucket = apr_bucket_immortal_create(CRLF, 2, f->c->bucket_alloc); + bucket = apr_bucket_immortal_create(CRLF, 2, f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, bucket); - sslconn->non_ssl_request = NON_SSL_SET_ERROR_MSG; + if (mode != AP_MODE_SPECULATIVE) { + sslconn->non_ssl_request = NON_SSL_SET_ERROR_MSG; + } return APR_SUCCESS; } return ap_get_brigade(f->next, bb, mode, block, readbytes); @@ -1354,7 +1374,7 @@ static apr_status_t ssl_io_filter_input(ap_filter_t *f, * rather than have SSLEngine On configured. */ if ((status = ssl_io_filter_handshake(inctx->filter_ctx)) != APR_SUCCESS) { - return ssl_io_filter_error(f, bb, status); + return ssl_io_filter_error(f, bb, status, is_init); } if (is_init) { @@ -1408,7 +1428,7 @@ static apr_status_t ssl_io_filter_input(ap_filter_t *f, /* Handle custom errors. */ if (status != APR_SUCCESS) { - return ssl_io_filter_error(f, bb, status); + return ssl_io_filter_error(f, bb, status, 0); } /* Create a transient bucket out of the decrypted data. */ @@ -1418,41 +1438,6 @@ static apr_status_t ssl_io_filter_input(ap_filter_t *f, APR_BRIGADE_INSERT_TAIL(bb, bucket); } -#ifdef HAVE_TLS_ALPN - /* By this point, Application-Layer Protocol Negotiation (ALPN) should be - * completed (if our version of OpenSSL supports it). If we haven't already, - * find out which protocol was decided upon and inform other modules - * by calling alpn_proto_negotiated_hook. - */ - if (!inctx->alpn_finished) { - SSLConnRec *sslconn = myConnConfig(f->c); - const unsigned char *next_proto = NULL; - unsigned next_proto_len = 0; - const char *protocol; - - SSL_get0_alpn_selected(inctx->ssl, &next_proto, &next_proto_len); - if (next_proto && next_proto_len) { - protocol = apr_pstrmemdup(f->c->pool, (const char *)next_proto, - next_proto_len); - ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, f->c, - APLOGNO(02836) "ALPN selected protocol: '%s'", - protocol); - - if (strcmp(protocol, ap_get_protocol(f->c))) { - status = ap_switch_protocol(f->c, NULL, sslconn->server, - protocol); - if (status != APR_SUCCESS) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, status, f->c, - APLOGNO(02908) "protocol switch to '%s' failed", - protocol); - return status; - } - } - } - inctx->alpn_finished = 1; - } -#endif - return APR_SUCCESS; } @@ -1629,52 +1614,33 @@ static apr_status_t ssl_io_filter_output(ap_filter_t *f, inctx->block = APR_BLOCK_READ; if ((status = ssl_io_filter_handshake(filter_ctx)) != APR_SUCCESS) { - return ssl_io_filter_error(f, bb, status); + return ssl_io_filter_error(f, bb, status, 0); } - while (!APR_BRIGADE_EMPTY(bb)) { + while (!APR_BRIGADE_EMPTY(bb) && status == APR_SUCCESS) { apr_bucket *bucket = APR_BRIGADE_FIRST(bb); - /* If it is a flush or EOS, we need to pass this down. - * These types do not require translation by OpenSSL. - */ - if (APR_BUCKET_IS_EOS(bucket) || APR_BUCKET_IS_FLUSH(bucket)) { - if (bio_filter_out_flush(filter_ctx->pbioWrite) < 0) { - status = outctx->rc; - break; + if (APR_BUCKET_IS_METADATA(bucket)) { + /* Pass through metadata buckets untouched. EOC is + * special; terminate the SSL layer first. */ + if (AP_BUCKET_IS_EOC(bucket)) { + ssl_filter_io_shutdown(filter_ctx, f->c, 0); } - - if (APR_BUCKET_IS_EOS(bucket)) { - /* - * By definition, nothing can come after EOS. - * which also means we can pass the rest of this brigade - * without creating a new one since it only contains the - * EOS bucket. - */ - - if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { - return status; - } - break; - } - else { - /* bio_filter_out_flush() already passed down a flush bucket - * if there was any data to be flushed. - */ - apr_bucket_delete(bucket); - } - } - else if (AP_BUCKET_IS_EOC(bucket)) { - /* The EOC bucket indicates connection closure, so SSL - * shutdown must now be performed. */ - ssl_filter_io_shutdown(filter_ctx, f->c, 0); - if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { - return status; - } - break; + AP_DEBUG_ASSERT(APR_BRIGADE_EMPTY(outctx->bb)); + + /* Metadata buckets are passed one per brigade; it might + * be more efficient (but also more complex) to use + * outctx->bb as a true buffer and interleave these with + * data buckets. */ + APR_BUCKET_REMOVE(bucket); + APR_BRIGADE_INSERT_HEAD(outctx->bb, bucket); + status = ap_pass_brigade(f->next, outctx->bb); + if (status == APR_SUCCESS && f->c->aborted) + status = APR_ECONNRESET; + apr_brigade_cleanup(outctx->bb); } else { - /* filter output */ + /* Filter a data bucket. */ const char *data; apr_size_t len; @@ -1687,7 +1653,9 @@ static apr_status_t ssl_io_filter_output(ap_filter_t *f, break; } rblock = APR_BLOCK_READ; - continue; /* and try again with a blocking read. */ + /* and try again with a blocking read. */ + status = APR_SUCCESS; + continue; } rblock = APR_NONBLOCK_READ; @@ -1698,11 +1666,8 @@ static apr_status_t ssl_io_filter_output(ap_filter_t *f, status = ssl_filter_write(f, data, len); apr_bucket_delete(bucket); - - if (status != APR_SUCCESS) { - break; - } } + } return status; @@ -1934,9 +1899,6 @@ static void ssl_io_input_add_filter(ssl_filter_ctx_t *filter_ctx, conn_rec *c, inctx->block = APR_BLOCK_READ; inctx->pool = c->pool; inctx->filter_ctx = filter_ctx; -#ifdef HAVE_TLS_ALPN - inctx->alpn_finished = 0; -#endif } /* The request_rec pointer is passed in here only to ensure that the diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c index cd07b1bc..f3699773 100644 --- a/modules/ssl/ssl_engine_kernel.c +++ b/modules/ssl/ssl_engine_kernel.c @@ -113,6 +113,108 @@ static int has_buffered_data(request_rec *r) return result; } +static int ap_array_same_str_set(apr_array_header_t *s1, apr_array_header_t *s2) +{ + int i; + const char *c; + + if (s1 == s2) { + return 1; + } + else if (!s1 || !s2 || (s1->nelts != s2->nelts)) { + return 0; + } + + for (i = 0; i < s1->nelts; i++) { + c = APR_ARRAY_IDX(s1, i, const char *); + if (!c || !ap_array_str_contains(s2, c)) { + return 0; + } + } + return 1; +} + +static int ssl_pk_server_compatible(modssl_pk_server_t *pks1, + modssl_pk_server_t *pks2) +{ + if (!pks1 || !pks2) { + return 0; + } + /* both have the same certificates? */ + if ((pks1->ca_name_path != pks2->ca_name_path) + && (!pks1->ca_name_path || !pks2->ca_name_path + || strcmp(pks1->ca_name_path, pks2->ca_name_path))) { + return 0; + } + if ((pks1->ca_name_file != pks2->ca_name_file) + && (!pks1->ca_name_file || !pks2->ca_name_file + || strcmp(pks1->ca_name_file, pks2->ca_name_file))) { + return 0; + } + if (!ap_array_same_str_set(pks1->cert_files, pks2->cert_files) + || !ap_array_same_str_set(pks1->key_files, pks2->key_files)) { + return 0; + } + return 1; +} + +static int ssl_auth_compatible(modssl_auth_ctx_t *a1, + modssl_auth_ctx_t *a2) +{ + if (!a1 || !a2) { + return 0; + } + /* both have the same verification */ + if ((a1->verify_depth != a2->verify_depth) + || (a1->verify_mode != a2->verify_mode)) { + return 0; + } + /* both have the same ca path/file */ + if ((a1->ca_cert_path != a2->ca_cert_path) + && (!a1->ca_cert_path || !a2->ca_cert_path + || strcmp(a1->ca_cert_path, a2->ca_cert_path))) { + return 0; + } + if ((a1->ca_cert_file != a2->ca_cert_file) + && (!a1->ca_cert_file || !a2->ca_cert_file + || strcmp(a1->ca_cert_file, a2->ca_cert_file))) { + return 0; + } + /* both have the same ca cipher suite string */ + if ((a1->cipher_suite != a2->cipher_suite) + && (!a1->cipher_suite || !a2->cipher_suite + || strcmp(a1->cipher_suite, a2->cipher_suite))) { + return 0; + } + return 1; +} + +static int ssl_ctx_compatible(modssl_ctx_t *ctx1, + modssl_ctx_t *ctx2) +{ + if (!ctx1 || !ctx2 + || (ctx1->protocol != ctx2->protocol) + || !ssl_auth_compatible(&ctx1->auth, &ctx2->auth) + || !ssl_pk_server_compatible(ctx1->pks, ctx2->pks)) { + return 0; + } + return 1; +} + +static int ssl_server_compatible(server_rec *s1, server_rec *s2) +{ + SSLSrvConfigRec *sc1 = s1? mySrvConfig(s1) : NULL; + SSLSrvConfigRec *sc2 = s2? mySrvConfig(s2) : NULL; + + /* both use the same TLS protocol? */ + if (!sc1 || !sc2 + || !ssl_ctx_compatible(sc1->server, sc2->server)) { + return 0; + } + + return 1; +} + /* * Post Read Request Handler */ @@ -137,7 +239,13 @@ int ssl_hook_ReadReq(request_rec *r) } } + /* If we are on a slave connection, we do not expect to have an SSLConnRec, + * but our master connection might. */ sslconn = myConnConfig(r->connection); + if (!(sslconn && sslconn->ssl) && r->connection->master) { + sslconn = myConnConfig(r->connection->master); + } + if (!sslconn) { return DECLINED; } @@ -195,15 +303,16 @@ int ssl_hook_ReadReq(request_rec *r) " provided in HTTP request", servername); return HTTP_BAD_REQUEST; } - if (r->server != handshakeserver) { + if (r->server != handshakeserver + && !ssl_server_compatible(sslconn->server, r->server)) { /* - * We are really not in Kansas anymore... * The request does not select the virtual host that was - * selected by the SNI. + * selected by the SNI and its SSL parameters are different */ + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(02032) "Hostname %s provided via SNI and hostname %s provided" - " via HTTP select a different server", + " via HTTP have no compatible SSL setup", servername, r->hostname); return HTTP_MISDIRECTED_REQUEST; } @@ -302,6 +411,7 @@ int ssl_hook_Access(request_rec *r) SSLConnRec *sslconn = myConnConfig(r->connection); SSL *ssl = sslconn ? sslconn->ssl : NULL; server_rec *handshakeserver = sslconn ? sslconn->server : NULL; + SSLSrvConfigRec *hssc = handshakeserver? mySrvConfig(handshakeserver) : NULL; SSL_CTX *ctx = NULL; apr_array_header_t *requires; ssl_require_t *ssl_requires; @@ -313,8 +423,19 @@ int ssl_hook_Access(request_rec *r) X509_STORE_CTX cert_store_ctx; STACK_OF(SSL_CIPHER) *cipher_list_old = NULL, *cipher_list = NULL; const SSL_CIPHER *cipher = NULL; - int depth, verify_old, verify, n; - + int depth, verify_old, verify, n, is_slave = 0; + const char *ncipher_suite; + + /* On a slave connection, we do not expect to have an SSLConnRec, but + * our master connection might have one. */ + if (!(sslconn && ssl) && r->connection->master) { + sslconn = myConnConfig(r->connection->master); + ssl = sslconn ? sslconn->ssl : NULL; + handshakeserver = sslconn ? sslconn->server : NULL; + hssc = handshakeserver? mySrvConfig(handshakeserver) : NULL; + is_slave = 1; + } + if (ssl) { /* * We should have handshaken here (on handshakeserver), @@ -333,7 +454,7 @@ int ssl_hook_Access(request_rec *r) * Support for SSLRequireSSL directive */ if (dc->bSSLRequired && !ssl) { - if (sc->enabled == SSL_ENABLED_OPTIONAL) { + if ((sc->enabled == SSL_ENABLED_OPTIONAL) && !is_slave) { /* This vhost was configured for optional SSL, just tell the * client that we need to upgrade. */ @@ -416,8 +537,13 @@ int ssl_hook_Access(request_rec *r) * new cipher suite. This approach is fine because the user explicitly * has to enable this via ``SSLOptions +OptRenegotiate''. So we do no * implicit optimizations. - */ - if (dc->szCipherSuite || (r->server != handshakeserver)) { + */ + ncipher_suite = (dc->szCipherSuite? + dc->szCipherSuite : (r->server != handshakeserver)? + sc->server->auth.cipher_suite : NULL); + + if (ncipher_suite && (!sslconn->cipher_suite + || strcmp(ncipher_suite, sslconn->cipher_suite))) { /* remember old state */ if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) { @@ -432,10 +558,18 @@ int ssl_hook_Access(request_rec *r) } /* configure new state */ - if ((dc->szCipherSuite || sc->server->auth.cipher_suite) && - !SSL_set_cipher_list(ssl, dc->szCipherSuite ? - dc->szCipherSuite : - sc->server->auth.cipher_suite)) { + if (is_slave) { + /* TODO: this categorically fails changed cipher suite settings + * on slave connections. We could do better by + * - create a new SSL* from our SSL_CTX and set cipher suite there, + * and retrieve ciphers, free afterwards + * Modifying the SSL on a slave connection is no good. + */ + apr_table_setn(r->notes, "ssl-renegotiate-forbidden", "cipher-suite"); + return HTTP_FORBIDDEN; + } + + if (!SSL_set_cipher_list(ssl, ncipher_suite)) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02253) "Unable to reconfigure (per-directory) " "permitted SSL ciphers"); @@ -502,6 +636,15 @@ int ssl_hook_Access(request_rec *r) } if (renegotiate) { + if (is_slave) { + /* The request causes renegotiation on a slave connection. + * This is not allowed since we might have concurrent requests + * on this connection. + */ + apr_table_setn(r->notes, "ssl-renegotiate-forbidden", "cipher-suite"); + return HTTP_FORBIDDEN; + } + #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE if (sc->cipher_server_pref == TRUE) { SSL_set_options(ssl, SSL_OP_CIPHER_SERVER_PREFERENCE); @@ -554,6 +697,7 @@ int ssl_hook_Access(request_rec *r) */ if ((dc->nVerifyClient != SSL_CVERIFY_UNSET) || (sc->server->auth.verify_mode != SSL_CVERIFY_UNSET)) { + /* remember old state */ verify_old = SSL_get_verify_mode(ssl); /* configure new state */ @@ -572,6 +716,9 @@ int ssl_hook_Access(request_rec *r) verify |= SSL_VERIFY_PEER; } + /* TODO: this seems premature since we do not know if there + * are any changes required. + */ SSL_set_verify(ssl, verify, ssl_callback_SSLVerify); SSL_set_verify_result(ssl, X509_V_OK); @@ -587,6 +734,14 @@ int ssl_hook_Access(request_rec *r) (verify & SSL_VERIFY_FAIL_IF_NO_PEER_CERT))) { renegotiate = TRUE; + if (is_slave) { + /* The request causes renegotiation on a slave connection. + * This is not allowed since we might have concurrent requests + * on this connection. + */ + apr_table_setn(r->notes, "ssl-renegotiate-forbidden", "verify-client"); + return HTTP_FORBIDDEN; + } /* optimization */ if ((dc->nOptions & SSL_OPT_OPTRENEGOTIATE) && @@ -907,6 +1062,10 @@ int ssl_hook_Access(request_rec *r) return HTTP_FORBIDDEN; } } + /* remember any new cipher suite used in renegotiation */ + if (ncipher_suite) { + sslconn->cipher_suite = ncipher_suite; + } } /* If we're trying to have the user name set from a client @@ -1170,6 +1329,10 @@ int ssl_hook_Fixup(request_rec *r) apr_table_mergen(r->headers_out, "Connection", "upgrade"); } + if (!(sslconn && sslconn->ssl) && r->connection->master) { + sslconn = myConnConfig(r->connection->master); + } + /* * Check to see if SSL is on */ @@ -1192,8 +1355,8 @@ int ssl_hook_Fixup(request_rec *r) /* standard SSL environment variables */ if (dc->nOptions & SSL_OPT_STDENVVARS) { - modssl_var_extract_dns(env, sslconn->ssl, r->pool); - modssl_var_extract_san_entries(env, sslconn->ssl, r->pool); + modssl_var_extract_dns(env, ssl, r->pool); + modssl_var_extract_san_entries(env, ssl, r->pool); for (i = 0; ssl_hook_Fixup_vars[i]; i++) { var = (char *)ssl_hook_Fixup_vars[i]; @@ -2037,7 +2200,8 @@ static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s) * retrieval */ sslcon->server = s; - + sslcon->cipher_suite = sc->server->auth.cipher_suite; + /* * There is one special filter callback, which is set * very early depending on the base_server's log level. @@ -2194,14 +2358,30 @@ int ssl_callback_alpn_select(SSL *ssl, init_vhost(c, ssl); proposed = ap_select_protocol(c, NULL, sslconn->server, client_protos); - *out = (const unsigned char *)(proposed? proposed : ap_get_protocol(c)); - len = strlen((const char*)*out); + if (!proposed) { + proposed = ap_get_protocol(c); + } + + len = strlen(proposed); if (len > 255) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02840) "ALPN negotiated protocol name too long"); return SSL_TLSEXT_ERR_ALERT_FATAL; } + *out = (const unsigned char *)proposed; *outlen = (unsigned char)len; + + if (strcmp(proposed, ap_get_protocol(c))) { + apr_status_t status; + + status = ap_switch_protocol(c, NULL, sslconn->server, proposed); + if (status != APR_SUCCESS) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, status, c, + APLOGNO(02908) "protocol switch to '%s' failed", + proposed); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + } return SSL_TLSEXT_ERR_OK; } diff --git a/modules/ssl/ssl_engine_vars.c b/modules/ssl/ssl_engine_vars.c index 0530eea4..a6b0d0da 100644 --- a/modules/ssl/ssl_engine_vars.c +++ b/modules/ssl/ssl_engine_vars.c @@ -39,7 +39,7 @@ ** _________________________________________________________________ */ -static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, request_rec *r, char *var); +static char *ssl_var_lookup_ssl(apr_pool_t *p, SSLConnRec *sslconn, request_rec *r, char *var); static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs, char *var); static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var); static char *ssl_var_lookup_ssl_cert_san(apr_pool_t *p, X509 *xs, char *var); @@ -49,15 +49,25 @@ static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs); static char *ssl_var_lookup_ssl_cert_chain(apr_pool_t *p, STACK_OF(X509) *sk, char *var); static char *ssl_var_lookup_ssl_cert_rfc4523_cea(apr_pool_t *p, SSL *ssl); static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs); -static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c); -static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var); +static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, SSLConnRec *sslconn); +static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, SSLConnRec *sslconn, char *var); static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize); static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var); static char *ssl_var_lookup_ssl_compress_meth(SSL *ssl); -static int ssl_is_https(conn_rec *c) +static SSLConnRec *ssl_get_effective_config(conn_rec *c) { SSLConnRec *sslconn = myConnConfig(c); + if (!(sslconn && sslconn->ssl) && c->master) { + /* use master connection if no SSL defined here */ + sslconn = myConnConfig(c->master); + } + return sslconn; +} + +static int ssl_is_https(conn_rec *c) +{ + SSLConnRec *sslconn = ssl_get_effective_config(c); return sslconn && sslconn->ssl; } @@ -75,9 +85,17 @@ static apr_array_header_t *expr_peer_ext_list_fn(ap_expr_eval_ctx_t *ctx, static const char *expr_var_fn(ap_expr_eval_ctx_t *ctx, const void *data) { char *var = (char *)data; - SSLConnRec *sslconn = myConnConfig(ctx->c); + SSLConnRec *sslconn = ssl_get_effective_config(ctx->c); + + return sslconn ? ssl_var_lookup_ssl(ctx->p, sslconn, ctx->r, var) : NULL; +} + +static const char *expr_func_fn(ap_expr_eval_ctx_t *ctx, const void *data, + const char *arg) +{ + char *var = (char *)arg; - return sslconn ? ssl_var_lookup_ssl(ctx->p, ctx->c, ctx->r, var) : NULL; + return var ? ssl_var_lookup(ctx->p, ctx->s, ctx->c, ctx->r, var) : NULL; } static int ssl_expr_lookup(ap_expr_lookup_parms *parms) @@ -94,6 +112,15 @@ static int ssl_expr_lookup(ap_expr_lookup_parms *parms) return OK; } break; + case AP_EXPR_FUNC_STRING: + /* Function SSL() is implemented by us. + */ + if (strcEQ(parms->name, "SSL")) { + *parms->func = expr_func_fn; + *parms->data = NULL; + return OK; + } + break; case AP_EXPR_FUNC_LIST: if (strcEQ(parms->name, "PeerExtList")) { *parms->func = expr_peer_ext_list_fn; @@ -244,10 +271,10 @@ char *ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, * Connection stuff */ if (result == NULL && c != NULL) { - SSLConnRec *sslconn = myConnConfig(c); + SSLConnRec *sslconn = ssl_get_effective_config(c); if (strlen(var) > 4 && strcEQn(var, "SSL_", 4) && sslconn && sslconn->ssl) - result = ssl_var_lookup_ssl(p, c, r, var+4); + result = ssl_var_lookup_ssl(p, sslconn, r, var+4); else if (strcEQ(var, "HTTPS")) { if (sslconn && sslconn->ssl) result = "on"; @@ -317,10 +344,9 @@ char *ssl_var_lookup(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, return (char *)result; } -static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, request_rec *r, - char *var) +static char *ssl_var_lookup_ssl(apr_pool_t *p, SSLConnRec *sslconn, + request_rec *r, char *var) { - SSLConnRec *sslconn = myConnConfig(c); char *result; X509 *xs; STACK_OF(X509) *sk; @@ -360,7 +386,7 @@ static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, request_rec *r, result = "Initial"; } else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) { - result = ssl_var_lookup_ssl_cipher(p, c, var+6); + result = ssl_var_lookup_ssl_cipher(p, sslconn, var+6); } else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) { sk = SSL_get_peer_cert_chain(ssl); @@ -370,7 +396,7 @@ static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, request_rec *r, result = ssl_var_lookup_ssl_cert_rfc4523_cea(p, ssl); } else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) { - result = ssl_var_lookup_ssl_cert_verify(p, c); + result = ssl_var_lookup_ssl_cert_verify(p, sslconn); } else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) { if ((xs = SSL_get_peer_certificate(ssl)) != NULL) { @@ -779,9 +805,8 @@ static char *ssl_var_lookup_ssl_cert_PEM(apr_pool_t *p, X509 *xs) return result; } -static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c) +static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, SSLConnRec *sslconn) { - SSLConnRec *sslconn = myConnConfig(c); char *result; long vrc; const char *verr; @@ -815,9 +840,8 @@ static char *ssl_var_lookup_ssl_cert_verify(apr_pool_t *p, conn_rec *c) return result; } -static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, conn_rec *c, char *var) +static char *ssl_var_lookup_ssl_cipher(apr_pool_t *p, SSLConnRec *sslconn, char *var) { - SSLConnRec *sslconn = myConnConfig(c); char *result; BOOL resdup; int usekeysize, algkeysize; @@ -1030,7 +1054,7 @@ static int dump_extn_value(BIO *bio, ASN1_OCTET_STRING *str) apr_array_header_t *ssl_ext_list(apr_pool_t *p, conn_rec *c, int peer, const char *extension) { - SSLConnRec *sslconn = myConnConfig(c); + SSLConnRec *sslconn = ssl_get_effective_config(c); SSL *ssl = NULL; apr_array_header_t *array = NULL; X509 *xs = NULL; @@ -1174,7 +1198,7 @@ void ssl_var_log_config_register(apr_pool_t *p) */ static const char *ssl_var_log_handler_c(request_rec *r, char *a) { - SSLConnRec *sslconn = myConnConfig(r->connection); + SSLConnRec *sslconn = ssl_get_effective_config(r->connection); char *result; if (sslconn == NULL || sslconn->ssl == NULL) diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h index dc4e61a6..a70208aa 100644 --- a/modules/ssl/ssl_private.h +++ b/modules/ssl/ssl_private.h @@ -442,6 +442,7 @@ typedef struct { int disabled; enum { NON_SSL_OK = 0, /* is SSL request, or error handling completed */ + NON_SSL_SEND_REQLINE, /* Need to send the fake request line */ NON_SSL_SEND_HDR_SEP, /* Need to send the header separator */ NON_SSL_SET_ERROR_MSG /* Need to set the error message */ } non_ssl_request; @@ -460,6 +461,8 @@ typedef struct { } reneg_state; server_rec *server; + + const char *cipher_suite; /* cipher suite used in last reneg */ } SSLConnRec; /* BIG FAT WARNING: SSLModConfigRec has unusual memory lifetime: it is diff --git a/modules/ssl/ssl_util_stapling.c b/modules/ssl/ssl_util_stapling.c index 7d9df5fd..67caade3 100644 --- a/modules/ssl/ssl_util_stapling.c +++ b/modules/ssl/ssl_util_stapling.c @@ -332,10 +332,12 @@ static int stapling_check_response(server_rec *s, modssl_ctx_t *mctx, certinfo *cinf, OCSP_RESPONSE *rsp, BOOL *pok) { - int status, reason; + int status = V_OCSP_CERTSTATUS_UNKNOWN; + int reason = OCSP_REVOKED_STATUS_NOSTATUS; OCSP_BASICRESP *bs = NULL; ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; int response_status = OCSP_response_status(rsp); + int rv = SSL_TLSEXT_ERR_OK; if (pok) *pok = FALSE; @@ -360,9 +362,11 @@ static int stapling_check_response(server_rec *s, modssl_ctx_t *mctx, if (!OCSP_resp_find_status(bs, cinf->cid, &status, &reason, &rev, &thisupd, &nextupd)) { - /* If ID not present just pass back to client */ + /* If ID not present pass back to client (if configured so) */ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01935) "stapling_check_response: certificate ID not present in response!"); + if (mctx->stapling_return_errors == FALSE) + rv = SSL_TLSEXT_ERR_NOACK; } else { if (OCSP_check_validity(thisupd, nextupd, @@ -385,19 +389,45 @@ static int stapling_check_response(server_rec *s, modssl_ctx_t *mctx, "stapling_check_response: cached response expired"); } - OCSP_BASICRESP_free(bs); - return SSL_TLSEXT_ERR_NOACK; + rv = SSL_TLSEXT_ERR_NOACK; + } + + if (status != V_OCSP_CERTSTATUS_GOOD) { + char snum[MAX_STRING_LEN] = { '\0' }; + BIO *bio = BIO_new(BIO_s_mem()); + + if (bio) { + int n; + if ((i2a_ASN1_INTEGER(bio, cinf->cid->serialNumber) != -1) && + ((n = BIO_read(bio, snum, sizeof snum - 1)) > 0)) + snum[n] = '\0'; + BIO_free(bio); + } + + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02969) + "stapling_check_response: response has certificate " + "status %s (reason: %s) for serial number %s", + OCSP_cert_status_str(status), + (reason != OCSP_REVOKED_STATUS_NOSTATUS) ? + OCSP_crl_reason_str(reason) : "n/a", + snum[0] ? snum : "[n/a]"); + + if (mctx->stapling_return_errors == FALSE) { + if (pok) + *pok = FALSE; + rv = SSL_TLSEXT_ERR_NOACK; + } } } OCSP_BASICRESP_free(bs); - return SSL_TLSEXT_ERR_OK; + return rv; } static BOOL stapling_renew_response(server_rec *s, modssl_ctx_t *mctx, SSL *ssl, certinfo *cinf, OCSP_RESPONSE **prsp, - apr_pool_t *pool) + BOOL *pok, apr_pool_t *pool) { conn_rec *conn = (conn_rec *)SSL_get_app_data(ssl); apr_pool_t *vpool; @@ -405,7 +435,6 @@ static BOOL stapling_renew_response(server_rec *s, modssl_ctx_t *mctx, SSL *ssl, OCSP_CERTID *id = NULL; STACK_OF(X509_EXTENSION) *exts; int i; - BOOL ok = FALSE; BOOL rv = TRUE; const char *ocspuri; apr_uri_t uri; @@ -447,8 +476,7 @@ static BOOL stapling_renew_response(server_rec *s, modssl_ctx_t *mctx, SSL *ssl, /* Create a temporary pool to constrain memory use */ apr_pool_create(&vpool, conn->pool); - ok = apr_uri_parse(vpool, ocspuri, &uri); - if (ok != APR_SUCCESS) { + if (apr_uri_parse(vpool, ocspuri, &uri) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01939) "stapling_renew_response: Error parsing uri %s", ocspuri); @@ -487,8 +515,8 @@ static BOOL stapling_renew_response(server_rec *s, modssl_ctx_t *mctx, SSL *ssl, if (response_status == OCSP_RESPONSE_STATUS_SUCCESSFUL) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01942) "stapling_renew_response: query response received"); - stapling_check_response(s, mctx, cinf, *prsp, &ok); - if (ok == FALSE) { + stapling_check_response(s, mctx, cinf, *prsp, pok); + if (*pok == FALSE) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01943) "stapling_renew_response: error in retrieved response!"); } @@ -497,9 +525,10 @@ static BOOL stapling_renew_response(server_rec *s, modssl_ctx_t *mctx, SSL *ssl, ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01944) "stapling_renew_response: responder error %s", OCSP_response_status_str(response_status)); + *pok = FALSE; } } - if (stapling_cache_response(s, mctx, *prsp, cinf, ok, pool) == FALSE) { + if (stapling_cache_response(s, mctx, *prsp, cinf, *pok, pool) == FALSE) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01945) "stapling_renew_response: error caching response!"); } @@ -651,8 +680,8 @@ static int stapling_refresh_mutex_off(server_rec *s) } static int get_and_check_cached_response(server_rec *s, modssl_ctx_t *mctx, - OCSP_RESPONSE **rsp, certinfo *cinf, - apr_pool_t *p) + OCSP_RESPONSE **rsp, BOOL *pok, + certinfo *cinf, apr_pool_t *p) { BOOL ok; int rv; @@ -688,6 +717,7 @@ static int get_and_check_cached_response(server_rec *s, modssl_ctx_t *mctx, else if (!mctx->stapling_return_errors) { OCSP_RESPONSE_free(*rsp); *rsp = NULL; + *pok = FALSE; return SSL_TLSEXT_ERR_NOACK; } } @@ -712,6 +742,7 @@ static int stapling_cb(SSL *ssl, void *arg) certinfo *cinf = NULL; OCSP_RESPONSE *rsp = NULL; int rv; + BOOL ok = TRUE; if (sc->server->stapling_enabled != TRUE) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01950) @@ -730,7 +761,7 @@ static int stapling_cb(SSL *ssl, void *arg) ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01952) "stapling_cb: retrieved cached certificate data"); - rv = get_and_check_cached_response(s, mctx, &rsp, cinf, conn->pool); + rv = get_and_check_cached_response(s, mctx, &rsp, &ok, cinf, conn->pool); if (rv != 0) { return rv; } @@ -742,7 +773,8 @@ static int stapling_cb(SSL *ssl, void *arg) /* Maybe another request refreshed the OCSP response while this * thread waited for the mutex. Check again. */ - rv = get_and_check_cached_response(s, mctx, &rsp, cinf, conn->pool); + rv = get_and_check_cached_response(s, mctx, &rsp, &ok, cinf, + conn->pool); if (rv != 0) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "stapling_cb: error checking for cached response " @@ -760,7 +792,8 @@ static int stapling_cb(SSL *ssl, void *arg) ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "stapling_cb: still must refresh cached response " "after obtaining refresh mutex"); - rv = stapling_renew_response(s, mctx, ssl, cinf, &rsp, conn->pool); + rv = stapling_renew_response(s, mctx, ssl, cinf, &rsp, &ok, + conn->pool); stapling_refresh_mutex_off(s); if (rv == TRUE) { @@ -775,7 +808,7 @@ static int stapling_cb(SSL *ssl, void *arg) } } - if (rsp) { + if (rsp && ((ok == TRUE) || (mctx->stapling_return_errors == TRUE))) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01956) "stapling_cb: setting response"); if (!stapling_set_response(ssl, rsp)) @@ -783,7 +816,7 @@ static int stapling_cb(SSL *ssl, void *arg) return SSL_TLSEXT_ERR_OK; } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01957) - "stapling_cb: no response available"); + "stapling_cb: no suitable response available"); return SSL_TLSEXT_ERR_NOACK; diff --git a/server/core.c b/server/core.c index 37484b66..7ed6d9cc 100644 --- a/server/core.c +++ b/server/core.c @@ -191,6 +191,7 @@ static void *create_core_dir_config(apr_pool_t *a, char *dir) conf->max_reversals = AP_MAXRANGES_UNSET; conf->cgi_pass_auth = AP_CGI_PASS_AUTH_UNSET; + conf->qualify_redirect_url = AP_CORE_CONFIG_UNSET; return (void *)conf; } @@ -405,6 +406,8 @@ static void *merge_core_dir_configs(apr_pool_t *a, void *basev, void *newv) conf->cgi_pass_auth = new->cgi_pass_auth != AP_CGI_PASS_AUTH_UNSET ? new->cgi_pass_auth : base->cgi_pass_auth; + AP_CORE_MERGE_FLAG(qualify_redirect_url, conf, base, new); + return (void*)conf; } @@ -1707,6 +1710,15 @@ static const char *set_cgi_pass_auth(cmd_parms *cmd, void *d_, int flag) return NULL; } +static const char *set_qualify_redirect_url(cmd_parms *cmd, void *d_, int flag) +{ + core_dir_config *d = d_; + + d->qualify_redirect_url = flag ? AP_CORE_CONFIG_ON : AP_CORE_CONFIG_OFF; + + return NULL; +} + static const char *set_override_list(cmd_parms *cmd, void *d_, int argc, char *const argv[]) { core_dir_config *d = d_; @@ -1724,7 +1736,7 @@ static const char *set_override_list(cmd_parms *cmd, void *d_, int argc, char *c d->override_list = apr_table_make(cmd->pool, argc); - for (i=0;iserver, @@ -1753,7 +1766,7 @@ static const char *set_override_list(cmd_parms *cmd, void *d_, int argc, char *c continue; } else { - apr_table_set(d->override_list, argv[i], "1"); + apr_table_setn(d->override_list, argv[i], "1"); } } } @@ -4206,6 +4219,10 @@ AP_INIT_TAKE12("LimitInternalRecursion", set_recursion_limit, NULL, RSRC_CONF, AP_INIT_FLAG("CGIPassAuth", set_cgi_pass_auth, NULL, OR_AUTHCFG, "Controls whether HTTP authorization headers, normally hidden, will " "be passed to scripts"), +AP_INIT_FLAG("QualifyRedirectURL", set_qualify_redirect_url, NULL, OR_FILEINFO, + "Controls whether HTTP authorization headers, normally hidden, will " + "be passed to scripts"), + AP_INIT_TAKE1("ForceType", ap_set_string_slot_lower, (void *)APR_OFFSETOF(core_dir_config, mime_type), OR_FILEINFO, "a mime type that overrides other configured type"), @@ -4995,8 +5012,15 @@ static void core_dump_config(apr_pool_t *p, server_rec *s) static int core_upgrade_handler(request_rec *r) { conn_rec *c = r->connection; - const char *upgrade = apr_table_get(r->headers_in, "Upgrade"); + const char *upgrade; + if (c->master) { + /* Not possible to perform an HTTP/1.1 upgrade from a slave + * connection. */ + return DECLINED; + } + + upgrade = apr_table_get(r->headers_in, "Upgrade"); if (upgrade && *upgrade) { const char *conn = apr_table_get(r->headers_in, "Connection"); if (ap_find_token(r->pool, conn, "upgrade")) { @@ -5011,8 +5035,7 @@ static int core_upgrade_handler(request_rec *r) } if (offers && offers->nelts > 0) { - const char *protocol = ap_select_protocol(c, r, r->server, - offers); + const char *protocol = ap_select_protocol(c, r, NULL, offers); if (protocol && strcmp(protocol, ap_get_protocol(c))) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02909) "Upgrade selects '%s'", protocol); @@ -5034,6 +5057,19 @@ static int core_upgrade_handler(request_rec *r) } } } + else if (!c->keepalives) { + /* first request on a master connection, if we have protocols other + * than the current one enabled here, announce them to the + * client. If the client is already talking a protocol with requests + * on slave connections, leave it be. */ + const apr_array_header_t *upgrades; + ap_get_protocol_upgrades(c, r, NULL, 0, &upgrades); + if (upgrades && upgrades->nelts > 0) { + char *protocols = apr_array_pstrcat(r->pool, upgrades, ','); + apr_table_setn(r->headers_out, "Upgrade", protocols); + apr_table_setn(r->headers_out, "Connection", "Upgrade"); + } + } return DECLINED; } diff --git a/server/protocol.c b/server/protocol.c index fc507fa0..7fc5b096 100644 --- a/server/protocol.c +++ b/server/protocol.c @@ -561,12 +561,7 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb) unsigned int major = 1, minor = 0; /* Assume HTTP/1.0 if non-"HTTP" protocol */ char http[5]; apr_size_t len; - int num_blank_lines = 0; - int max_blank_lines = r->server->limit_req_fields; - - if (max_blank_lines <= 0) { - max_blank_lines = DEFAULT_LIMIT_REQUEST_FIELDS; - } + int num_blank_lines = DEFAULT_LIMIT_BLANK_LINES; /* Read past empty lines until we get a real request line, * a read error, the connection closes (EOF), or we timeout. @@ -613,7 +608,7 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb) r->protocol = apr_pstrdup(r->pool, "HTTP/1.0"); return 0; } - } while ((len <= 0) && (++num_blank_lines < max_blank_lines)); + } while ((len <= 0) && (--num_blank_lines >= 0)); if (APLOGrtrace5(r)) { ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, @@ -627,6 +622,13 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb) uri = ap_getword_white(r->pool, &ll); + if (!*r->method || !*uri) { + r->status = HTTP_BAD_REQUEST; + r->proto_num = HTTP_VERSION(1,0); + r->protocol = apr_pstrdup(r->pool, "HTTP/1.0"); + return 0; + } + /* Provide quick information about the request method as soon as known */ r->method_number = ap_method_number_of(r->method); @@ -635,6 +637,9 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb) } ap_parse_uri(r, uri); + if (r->status != HTTP_OK) { + return 0; + } if (ll[0]) { r->assbackwards = 0; @@ -1823,15 +1828,61 @@ AP_DECLARE(const char *) ap_get_protocol(conn_rec *c) return protocol? protocol : AP_PROTOCOL_HTTP1; } +AP_DECLARE(apr_status_t) ap_get_protocol_upgrades(conn_rec *c, request_rec *r, + server_rec *s, int report_all, + const apr_array_header_t **pupgrades) +{ + apr_pool_t *pool = r? r->pool : c->pool; + core_server_config *conf; + const char *existing; + apr_array_header_t *upgrades = NULL; + + if (!s) { + s = (r? r->server : c->base_server); + } + conf = ap_get_core_module_config(s->module_config); + + if (conf->protocols->nelts > 0) { + existing = ap_get_protocol(c); + if (conf->protocols->nelts > 1 + || !ap_array_str_contains(conf->protocols, existing)) { + int i; + + /* possibly more than one choice or one, but not the + * existing. (TODO: maybe 426 and Upgrade then?) */ + upgrades = apr_array_make(pool, conf->protocols->nelts + 1, + sizeof(char *)); + for (i = 0; i < conf->protocols->nelts; i++) { + const char *p = APR_ARRAY_IDX(conf->protocols, i, char *); + if (strcmp(existing, p)) { + /* not the one we have and possible, add in this order */ + APR_ARRAY_PUSH(upgrades, const char*) = p; + } + else if (!report_all) { + break; + } + } + } + } + + *pupgrades = upgrades; + return APR_SUCCESS; +} + AP_DECLARE(const char *) ap_select_protocol(conn_rec *c, request_rec *r, server_rec *s, const apr_array_header_t *choices) { apr_pool_t *pool = r? r->pool : c->pool; - core_server_config *conf = ap_get_core_module_config(s->module_config); + core_server_config *conf; const char *protocol = NULL, *existing; apr_array_header_t *proposals; + if (!s) { + s = (r? r->server : c->base_server); + } + conf = ap_get_core_module_config(s->module_config); + if (APLOGcdebug(c)) { const char *p = apr_array_pstrcat(pool, conf->protocols, ','); ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, @@ -1937,6 +1988,22 @@ AP_DECLARE(apr_status_t) ap_switch_protocol(conn_rec *c, request_rec *r, } } +AP_DECLARE(int) ap_is_allowed_protocol(conn_rec *c, request_rec *r, + server_rec *s, const char *protocol) +{ + core_server_config *conf; + + if (!s) { + s = (r? r->server : c->base_server); + } + conf = ap_get_core_module_config(s->module_config); + + if (conf->protocols->nelts > 0) { + return ap_array_str_contains(conf->protocols, protocol); + } + return !strcmp(AP_PROTOCOL_HTTP1, protocol); +} + AP_IMPLEMENT_HOOK_VOID(pre_read_request, (request_rec *r, conn_rec *c), diff --git a/server/scoreboard.c b/server/scoreboard.c index 9e16a2ae..6d1b3bef 100644 --- a/server/scoreboard.c +++ b/server/scoreboard.c @@ -129,14 +129,19 @@ static apr_status_t ap_cleanup_shared_mem(void *d) return APR_SUCCESS; } +#define SIZE_OF_scoreboard APR_ALIGN_DEFAULT(sizeof(scoreboard)) +#define SIZE_OF_global_score APR_ALIGN_DEFAULT(sizeof(global_score)) +#define SIZE_OF_process_score APR_ALIGN_DEFAULT(sizeof(process_score)) +#define SIZE_OF_worker_score APR_ALIGN_DEFAULT(sizeof(worker_score)) + AP_DECLARE(int) ap_calc_scoreboard_size(void) { ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit); ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit); - scoreboard_size = sizeof(global_score); - scoreboard_size += sizeof(process_score) * server_limit; - scoreboard_size += sizeof(worker_score) * server_limit * thread_limit; + scoreboard_size = SIZE_OF_global_score; + scoreboard_size += SIZE_OF_process_score * server_limit; + scoreboard_size += SIZE_OF_worker_score * server_limit * thread_limit; return scoreboard_size; } @@ -153,17 +158,17 @@ AP_DECLARE(void) ap_init_scoreboard(void *shared_score) ap_calc_scoreboard_size(); ap_scoreboard_image = - ap_calloc(1, sizeof(scoreboard) + server_limit * sizeof(worker_score *)); + ap_calloc(1, SIZE_OF_scoreboard + server_limit * sizeof(worker_score *)); more_storage = shared_score; ap_scoreboard_image->global = (global_score *)more_storage; - more_storage += sizeof(global_score); + more_storage += SIZE_OF_global_score; ap_scoreboard_image->parent = (process_score *)more_storage; - more_storage += sizeof(process_score) * server_limit; + more_storage += SIZE_OF_process_score * server_limit; ap_scoreboard_image->servers = - (worker_score **)((char*)ap_scoreboard_image + sizeof(scoreboard)); + (worker_score **)((char*)ap_scoreboard_image + SIZE_OF_scoreboard); for (i = 0; i < server_limit; i++) { ap_scoreboard_image->servers[i] = (worker_score *)more_storage; - more_storage += thread_limit * sizeof(worker_score); + more_storage += thread_limit * SIZE_OF_worker_score; } ap_assert(more_storage == (char*)shared_score + scoreboard_size); ap_scoreboard_image->global->server_limit = server_limit; @@ -305,10 +310,10 @@ int ap_create_scoreboard(apr_pool_t *p, ap_scoreboard_e sb_type) if (ap_scoreboard_image) { ap_scoreboard_image->global->restart_time = apr_time_now(); memset(ap_scoreboard_image->parent, 0, - sizeof(process_score) * server_limit); + SIZE_OF_process_score * server_limit); for (i = 0; i < server_limit; i++) { memset(ap_scoreboard_image->servers[i], 0, - sizeof(worker_score) * thread_limit); + SIZE_OF_worker_score * thread_limit); } ap_init_scoreboard(NULL); return OK; diff --git a/server/util_script.c b/server/util_script.c index 14991cd0..7ac79301 100644 --- a/server/util_script.c +++ b/server/util_script.c @@ -282,21 +282,26 @@ AP_DECLARE(void) ap_add_common_vars(request_rec *r) /* Apache custom error responses. If we have redirected set two new vars */ if (r->prev) { - /* PR#57785: reconstruct full URL here */ - apr_uri_t *uri = &r->prev->parsed_uri; - if (!uri->scheme) { - uri->scheme = (char*)ap_http_scheme(r->prev); - } - if (!uri->port) { - uri->port = ap_get_server_port(r->prev); - uri->port_str = apr_psprintf(r->pool, "%u", uri->port); - } - if (!uri->hostname) { - uri->hostname = (char*)ap_get_server_name_for_url(r->prev); + if (conf->qualify_redirect_url != AP_CORE_CONFIG_ON) { + add_unless_null(e, "REDIRECT_URL", r->prev->uri); + } + else { + /* PR#57785: reconstruct full URL here */ + apr_uri_t *uri = &r->prev->parsed_uri; + if (!uri->scheme) { + uri->scheme = (char*)ap_http_scheme(r->prev); + } + if (!uri->port) { + uri->port = ap_get_server_port(r->prev); + uri->port_str = apr_psprintf(r->pool, "%u", uri->port); + } + if (!uri->hostname) { + uri->hostname = (char*)ap_get_server_name_for_url(r->prev); + } + add_unless_null(e, "REDIRECT_URL", + apr_uri_unparse(r->pool, uri, 0)); } add_unless_null(e, "REDIRECT_QUERY_STRING", r->prev->args); - add_unless_null(e, "REDIRECT_URL", - apr_uri_unparse(r->pool, uri, 0)); } if (e != r->subprocess_env) { -- cgit v1.2.3