]> granicus.if.org Git - apache/commitdiff
Merge r1780328, r1780329, r1781329, r1782164, r1782166, r1782193, r1778350, r1781329...
authorJim Jagielski <jim@apache.org>
Fri, 24 Mar 2017 13:43:12 +0000 (13:43 +0000)
committerJim Jagielski <jim@apache.org>
Fri, 24 Mar 2017 13:43:12 +0000 (13:43 +0000)
Adjust as needed

debugging

add ProxyFCGISetEnvIf

Logging update

Follow up to r1782164: fix typo (closing double-quote).

Remove trailing whitespace : no functional change

PR60576: php-fpm broken w/ per-dir rewrites

Attempt to dig out of well-meaning fixes for generic fcgi backends
that negatively affected some FPM configs.

Adds ProxyFCGIBackendType

add ProxyFCGISetEnvIf

trailing whitespace

Allow final admin-level fine-tuning

mod_proxy_fcgi: fix spelling in APLOG_INFO message

mod_proxy_fcgi: allow setting empty variables in ProxyFCGISetEnvIf

support unsetting vars

ProxyFCGISetEnvIf: reject invalid invocations, streamline unsets

Neither of the following makes sense:
- ProxyFCGISetEnvIf cond !VARIABLE value
- ProxyFCGISetEnvIf cond !

Error out in these cases. Also, don't execute the (unused) replacement
expression when unsetting.

drop the longhand version

Submitted by: jim, covener, jim, ylavic, jim, covener, covener, jim, jim, jchampion, jchampion, covener, jchampion, covener
Reviewed by: jim, covener, jchampion

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1788445 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
STATUS
docs/manual/mod/mod_proxy_fcgi.xml
modules/mappers/mod_actions.c
modules/proxy/mod_proxy_fcgi.c

diff --git a/CHANGES b/CHANGES
index b4761769132cb0396178b29c4cf617da3ef2cd9d..ccab19ea512e29899945604a55bcaf3631c8a771 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,21 @@
 
 Changes with Apache 2.4.26
 
+  *) mod_proxy_fcgi: Add ProxyFCGISetEnvIf to fixup CGI environment
+     variables just before invoking the FastCGI. [Eric Covener,
+     Jacob Champion]
+
+  *) mod_proxy: Allow the per-request environment variable "no-proxy" to
+     be used as an alternative to ProxyPass /path !. This is primarily
+     to set exceptions for ProxyPass specified in <Location> context.
+    Use SetEnvIf, not SetEnv. [Eric Covener]
+
+  *) mod_proxy_fcgi: Return to 2.4.20-and-earlier behavior of leaving
+     a "proxy:fcgi://" prefix in the SCRIPT_FILENAME environment variable by
+     default.  Add ProxyFCGIBackendType to allow the type of backend to be
+     specified so these kinds of fixups can be restored without impacting
+     FPM. PR60576 [Eric Covener, Jim Jagielski]
+
   *) mod_ssl: work around leaks on (graceful) restart. [Yann Ylavic]
 
   *) mod_ssl: Add support for OpenSSL 1.1.0. [Rainer Jung]
diff --git a/STATUS b/STATUS
index 82ce4f94dd89249a4cac2f547f9e44bcf334b573..015316bdcdc94ff967051152c205864f075caf08 100644 (file)
--- a/STATUS
+++ b/STATUS
@@ -137,31 +137,6 @@ PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
      2.4.x patch: http://people.apache.org/~covener/patches/ebcdic-interim.diff
      +1: covener, wrowe, ylavic (by inspection)
 
-  *) PR 60576: 2.4.21 broke PHP-FPM with the patch to strip the bogus "proxy://"
-     prefix from SCRIPT_FILENAME. We need to revert to the previous behavior
-     ASAP to avoid any further hurdles with FCGI implementations while we figure
-     this out.
-     trunk patch: http://svn.apache.org/r1780328
-                  http://svn.apache.org/r1780329
-                  http://svn.apache.org/r1781329
-                  http://svn.apache.org/r1782164
-                  http://svn.apache.org/r1782166
-                  http://svn.apache.org/r1782193
-                  http://svn.apache.org/r1778350
-                  http://svn.apache.org/r1781329
-                  http://svn.apache.org/r1782194
-                  http://svn.apache.org/r1782323
-                  http://svn.apache.org/r1782418
-                  http://svn.apache.org/r1782419
-                  http://svn.apache.org/r1782482
-                  http://svn.apache.org/r1782532
-                  http://svn.apache.org/r1788040
-     2.4.x patch: http://home.apache.org/~jim/patches/mod_proxy_fcgi-v3.patch
-                 + http://svn.apache.org/r1788040
-     +1: jim, covener, jchampion
-     jchampion: holding off on promotion for an answer to jailletc36's ML
-                question regarding `ProxyFCGIBackendType PHP-FPM`
-     covener: added http://svn.apache.org/r1788040
 
 PATCHES PROPOSED TO BACKPORT FROM TRUNK:
   [ New proposals should be added at the end of the list ]
index fa5f27da284bee96e2db5db9687b0c05d136447c..9281d48cf144a128c96e55eea22aaca73c31fb78 100644 (file)
@@ -195,4 +195,118 @@ ProxyPass "/myapp/" "balancer://myappcluster/"
     </dl>
 </section>
 
+<directivesynopsis>
+<name>ProxyFCGIBackendType</name>
+<description>Specify the type of backend FastCGI application</description>
+<syntax>ProxyFCGIBackendType FPM|GENERIC</syntax>
+<default>ProxyFCGIBackendType FPM</default>
+<contextlist><context>server config</context>
+<context>virtual host</context><context>directory</context>
+<context>.htaccess</context></contextlist>
+<compatibility>Available in version 2.4.26 and later</compatibility>
+
+<usage>
+<p>This directive allows the type of backend FastCGI application to be
+specified. Some FastCGI servers, such as PHP-FPM,  use historical quirks of
+environment variables to identify the type of proxy server being used.  Set
+this directive to "GENERIC" if your non PHP-FPM application has trouble
+interpreting environment variables such as SCRIPT_FILENAME or PATH_TRANSLATED
+as set by the server.</p>
+
+<p>One example of values that change based on the setting of this directive is
+SCRIPT_FILENAME. When using <module>mod_proxy_fcgi</module> historically,
+SCRIPT_FILENAME was prefixed with the string "proxy:fcgi://". This variable is
+what some generic FastCGI applications would read as their script input, but
+PHP-FPM would strip the prefix then remember it was talking to Apache.  In
+2.4.21 through 2.4.25, this prefix was automatically stripped by the server,
+breaking the ability of PHP-FPM to detect and interoperate with Apache in some
+scenarios.</p>
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
+<name>ProxyFCGISetEnvIf</name>
+<description>Allow variables sent to FastCGI servers to be fixed up</description>
+<syntax>ProxyFCGISetEnvIf <var>conditional-expression</var>
+    [!]<var>environment-variable-name</var>
+    [<var>value-expression</var>]</syntax>
+<contextlist><context>server config</context>
+<context>virtual host</context><context>directory</context>
+<context>.htaccess</context></contextlist>
+<compatibility>Available in version 2.4.26 and later</compatibility>
+
+<usage>
+<p>Just before passing a request to the configured FastCGI server, the core of
+the web server sets a number of environment variables based on details of the
+current request. FastCGI programs often uses these environment variables
+as inputs that determine what underlying scripts they will process, or what
+output they directly produce.</p>
+<p>Examples of noteworthy environment variables are:</p>
+<ul>
+  <li>SCRIPT_NAME</li>
+  <li>SCRIPT_FILENAME</li>
+  <li>REQUEST_URI</li>
+  <li>PATH_INFO</li>
+  <li>PATH_TRANSLATED</li>
+</ul>
+
+<p>This directive allows the environment variables above, or any others of
+interest, to be overridden.  This directive is evaluated after the initial
+values for these variables are set, so they can be used as input into both
+the condition expressions and value expressions.</p>
+<p>Parameter syntax:</p>
+<dl>
+<dt>conditional-expression</dt>
+<dd>Specifies an expression that controls whether the environment variable that
+   follows will be modified.  For information on the expression syntax, see
+   the examples that follow or the full specification at the
+   <a href="../expr.html">ap_expr</a> documentation.
+   </dd>
+<dt>environment-variable-name</dt>
+<dd> Specifies the CGI environment variable to change,
+   such as PATH_INFO. If preceded by an exclamation point, the variable 
+   will be unset.</dd>
+<dt>value-expression</dt>
+<dd>Specifies the replacement value for the preceding environment variable.
+   Backreferences, such as "$1", can be included from regular expression
+   captures in <var>conditional-expression</var>. If omitted, the variable is
+   set (or overridden) to an empty string &mdash; but see the Note below.</dd>
+</dl>
+
+<example>
+   <highlight language="config">
+# A basic, unconditional override
+ProxyFCGISetEnvIf "true" PATH_INFO "/example"
+
+# Use an environment variable in the value
+ProxyFCGISetEnvIf "true" PATH_INFO "%{reqenv:SCRIPT_NAME}"
+
+# Use captures in the conditions and backreferences in the replacement
+ProxyFCGISetEnvIf "reqenv('PATH_TRANSLATED') =~ m|(/.*prefix)(\d+)(.*)|" PATH_TRANSLATED "$1$3"
+   </highlight>
+</example>
+
+<note><title>Note: Unset vs. Empty</title>
+  The following will unset <code>VARIABLE</code>, preventing it from being sent
+  to the FastCGI server:
+
+    <highlight language="config">ProxyFCGISetEnvIf true !VARIABLE</highlight>
+
+  Whereas the following will erase any existing <em>value</em> of
+  <code>VARIABLE</code> (by setting it to the empty string), but the empty
+  <code>VARIABLE</code> will still be sent to the server:
+
+    <highlight language="config">ProxyFCGISetEnvIf true VARIABLE</highlight>
+
+  The CGI/1.1 specification
+  <a href="https://tools.ietf.org/html/rfc3875#section-4.1">does not
+  distinguish</a> between a variable with an empty value and a variable that
+  does not exist. However, many CGI and FastCGI implementations distinguish (or
+  allow scripts to distinguish) between the two. The choice of which to use is
+  dependent upon your implementation and your reason for modifying the variable.
+</note>
+
+</usage>
+</directivesynopsis>
+
 </modulesynopsis>
index 2a67a2742ae0a2152824d41174168519f51d27dc..ac9c3b7428fbceefdfe6e74b7d93f91e67f6e32c 100644 (file)
@@ -186,7 +186,8 @@ static int action_handler(request_rec *r)
         ap_field_noparam(r->pool, r->content_type);
 
     if (action && (t = apr_table_get(conf->action_types, action))) {
-        if (*t++ == '0' && r->finfo.filetype == APR_NOFILE) {
+        int virtual = (*t++ == '0' ? 0 : 1);
+        if (!virtual && r->finfo.filetype == APR_NOFILE) {
             ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00652)
                           "File does not exist: %s", r->filename);
             return HTTP_NOT_FOUND;
@@ -197,6 +198,9 @@ static int action_handler(request_rec *r)
          * (will be REDIRECT_HANDLER there)
          */
         apr_table_setn(r->subprocess_env, "HANDLER", action);
+        if (virtual) {
+            apr_table_setn(r->notes, "virtual_script", "1");
+        }
     }
 
     if (script == NULL)
index 3dc74dff9349fbc3666d077b1135217dfec2a3cc..a268556e30200b414a82dfaa81b4f4673ad6f991 100644 (file)
 #include "mod_proxy.h"
 #include "util_fcgi.h"
 #include "util_script.h"
+#include "ap_expr.h"
 
 module AP_MODULE_DECLARE_DATA proxy_fcgi_module;
 
+typedef struct {
+    ap_expr_info_t *cond;
+    ap_expr_info_t *subst;
+    const char *envname;
+} sei_entry;
+
 typedef struct {
     int need_dirwalk;
 } fcgi_req_config_t;
 
+/* We will assume FPM, but still differentiate */
+typedef enum {
+    BACKEND_DEFAULT_UNKNOWN = 0,
+    BACKEND_FPM,
+    BACKEND_GENERIC,
+} fcgi_backend_t;
+
+
+#define FCGI_MAY_BE_FPM(dconf)                              \
+        (dconf &&                                           \
+        ((dconf->backend_type == BACKEND_DEFAULT_UNKNOWN) || \
+        (dconf->backend_type == BACKEND_FPM)))
+
+typedef struct {
+    fcgi_backend_t backend_type;
+    apr_array_header_t *env_fixups;
+} fcgi_dirconf_t;
+
 /*
  * Canonicalise http-like URLs.
  * scheme is the scheme for the URL
@@ -39,7 +64,7 @@ static int proxy_fcgi_canon(request_rec *r, char *url)
     fcgi_req_config_t *rconf = NULL;
     const char *pathinfo_type = NULL;
 
-    if (strncasecmp(url, "fcgi:", 5) == 0) {
+    if (ap_cstr_casecmpn(url, "fcgi:", 5) == 0) {
         url += 5;
     }
     else {
@@ -133,6 +158,49 @@ static int proxy_fcgi_canon(request_rec *r, char *url)
     return OK;
 }
 
+
+/*
+  ProxyFCGISetEnvIf "reqenv('PATH_INFO') =~ m#/foo(\d+)\.php$#" COVENV1 "$1"
+  ProxyFCGISetEnvIf "reqenv('PATH_INFO') =~ m#/foo(\d+)\.php$#" PATH_INFO "/foo.php"
+  ProxyFCGISetEnvIf "reqenv('PATH_TRANSLATED') =~ m#(/.*foo)(\d+)(.*)#" PATH_TRANSLATED "$1$3"
+*/
+static void fix_cgivars(request_rec *r, fcgi_dirconf_t *dconf)
+{
+    sei_entry *entries;
+    const char *err, *src;
+    int i = 0, rc = 0;
+    ap_regmatch_t regm[AP_MAX_REG_MATCH];
+
+    entries = (sei_entry *) dconf->env_fixups->elts;
+    for (i = 0; i < dconf->env_fixups->nelts; i++) {
+        sei_entry *entry = &entries[i];
+
+        if (entry->envname[0] == '!') {
+            apr_table_unset(r->subprocess_env, entry->envname+1);
+        }
+        else if (0 < (rc = ap_expr_exec_re(r, entry->cond, AP_MAX_REG_MATCH, regm, &src, &err)))  {
+            const char *val = ap_expr_str_exec_re(r, entry->subst, AP_MAX_REG_MATCH, regm, &src, &err);
+            if (err) {
+                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03514)
+                              "Error evaluating expression for replacement of %s: '%s'",
+                               entry->envname, err);
+                continue;
+            }
+            if (APLOGrtrace4(r)) {
+                const char *oldval = apr_table_get(r->subprocess_env, entry->envname);
+                ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
+                              "fix_cgivars: override %s from '%s' to '%s'",
+                              entry->envname, oldval, val);
+
+            }
+            apr_table_setn(r->subprocess_env, entry->envname, val);
+        }
+        else {
+            ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, "fix_cgivars: Condition returned %d", rc);
+        }
+    }
+}
+
 /* Wrapper for apr_socket_sendv that handles updating the worker stats. */
 static apr_status_t send_data(proxy_conn_rec *conn,
                               struct iovec *vec,
@@ -253,7 +321,9 @@ static apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r,
     apr_status_t rv;
     apr_size_t avail_len, len, required_len;
     int next_elem, starting_elem;
+    int fpm = 0;
     fcgi_req_config_t *rconf = ap_get_module_config(r->request_config, &proxy_fcgi_module);
+    fcgi_dirconf_t *dconf = ap_get_module_config(r->per_dir_config, &proxy_fcgi_module);
 
     if (rconf) {
        if (rconf->need_dirwalk) {
@@ -268,15 +338,24 @@ static apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r,
         if (!strncmp(r->filename, "proxy:balancer://", 17)) {
             newfname = apr_pstrdup(r->pool, r->filename+17);
         }
-        else if (!strncmp(r->filename, "proxy:fcgi://", 13)) {
-            newfname = apr_pstrdup(r->pool, r->filename+13);
-        }
-        /* Query string in environment only */
-        if (newfname && r->args && *r->args) {
-            char *qs = strrchr(newfname, '?');
-            if (qs && !strcmp(qs+1, r->args)) {
-                *qs = '\0';
+
+        if (!FCGI_MAY_BE_FPM(dconf))  {
+            if (!strncmp(r->filename, "proxy:fcgi://", 13)) {
+                /* If we strip this under FPM, and any internal redirect occurs
+                 * on PATH_INFO, FPM may use PATH_TRANSLATED instead of
+                 * SCRIPT_FILENAME (a la mod_fastcgi + Action).
+                 */
+                newfname = apr_pstrdup(r->pool, r->filename+13);
+            }
+            /* Query string in environment only */
+            if (newfname && r->args && *r->args) {
+                char *qs = strrchr(newfname, '?');
+                if (qs && !strcmp(qs+1, r->args)) {
+                    *qs = '\0';
+                }
             }
+        } else {
+            fpm = 1;
         }
 
         if (newfname) {
@@ -285,11 +364,43 @@ static apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r,
         }
     }
 
+#if 0
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(09999)
+                  "r->filename: %s", (r->filename ? r->filename : "nil"));
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(09999)
+                  "r->uri: %s", (r->uri ? r->uri : "nil"));
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(09999)
+                  "r->path_info: %s", (r->path_info ? r->path_info : "nil"));
+#endif
+
     ap_add_common_vars(r);
     ap_add_cgi_vars(r);
 
+    if (fpm || apr_table_get(r->notes, "virtual_script")) {
+        /*
+         * Adjust SCRIPT_NAME, PATH_INFO and PATH_TRANSLATED for PHP-FPM
+         * TODO: Right now, PATH_INFO and PATH_TRANSLATED look OK...
+         */
+        const char *pend;
+        const char *script_name = apr_table_get(r->subprocess_env, "SCRIPT_NAME");
+        pend = script_name + strlen(script_name);
+        if (r->path_info && *r->path_info) {
+            pend = script_name + ap_find_path_info(script_name, r->path_info) - 1;
+        }
+        while (pend != script_name && *pend != '/') {
+            pend--;
+        }
+        apr_table_setn(r->subprocess_env, "SCRIPT_NAME", pend);
+        ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
+                      "fpm:virtual_script: Modified SCRIPT_NAME to: %s",
+                      pend);
+    }
+
     /* XXX are there any FastCGI specific env vars we need to send? */
 
+    /* Give admins final option to fine-tune env vars */
+    fix_cgivars(r, dconf);
+
     /* XXX mod_cgi/mod_cgid use ap_create_environment here, which fills in
      *     the TZ value specially.  We could use that, but it would mean
      *     parsing the key/value pairs back OUT of the allocated env array,
@@ -915,7 +1026,7 @@ static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker,
                   "url: %s proxyname: %s proxyport: %d",
                   url, proxyname, proxyport);
 
-    if (strncasecmp(url, "fcgi:", 5) != 0) {
+    if (ap_cstr_casecmpn(url, "fcgi:", 5) != 0) {
         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01077) "declining URL %s", url);
         return DECLINED;
     }
@@ -976,18 +1087,120 @@ cleanup:
     return status;
 }
 
+static void *fcgi_create_dconf(apr_pool_t *p, char *path)
+{
+    fcgi_dirconf_t *a;
+
+    a = (fcgi_dirconf_t *)apr_pcalloc(p, sizeof(fcgi_dirconf_t));
+    a->backend_type = BACKEND_DEFAULT_UNKNOWN;
+    a->env_fixups = apr_array_make(p, 20, sizeof(sei_entry));
+
+    return a;
+}
+
+static void *fcgi_merge_dconf(apr_pool_t *p, void *basev, void *overridesv)
+{
+    fcgi_dirconf_t *a, *base, *over;
+
+    a     = (fcgi_dirconf_t *)apr_pcalloc(p, sizeof(fcgi_dirconf_t));
+    base  = (fcgi_dirconf_t *)basev;
+    over  = (fcgi_dirconf_t *)overridesv;
+
+    a->backend_type = (over->backend_type != BACKEND_DEFAULT_UNKNOWN)
+                      ? over->backend_type
+                      : base->backend_type;
+    a->env_fixups = apr_array_append(p, base->env_fixups, over->env_fixups);
+    return a;
+}
+
+static const char *cmd_servertype(cmd_parms *cmd, void *in_dconf,
+                                   const char *val)
+{
+    fcgi_dirconf_t *dconf = in_dconf;
+
+    if (!strcasecmp(val, "GENERIC")) {
+       dconf->backend_type = BACKEND_GENERIC;
+    }
+    else if (!strcasecmp(val, "FPM")) {
+       dconf->backend_type = BACKEND_FPM;
+    }
+    else {
+        return "ProxyFCGIBackendType requires one of the following arguments: "
+               "'GENERIC', 'FPM'";
+    }
+
+    return NULL;
+}
+
+
+static const char *cmd_setenv(cmd_parms *cmd, void *in_dconf,
+                              const char *arg1, const char *arg2,
+                              const char *arg3)
+{
+    fcgi_dirconf_t *dconf = in_dconf;
+    const char *err;
+    sei_entry *new;
+    const char *envvar = arg2;
+
+    new = apr_array_push(dconf->env_fixups);
+    new->cond = ap_expr_parse_cmd(cmd, arg1, 0, &err, NULL);
+    if (err) {
+        return apr_psprintf(cmd->pool, "Could not parse expression \"%s\": %s",
+                            arg1, err);
+    }
+
+    if (envvar[0] == '!') {
+        /* Unset mode. */
+        if (arg3) {
+            return apr_psprintf(cmd->pool, "Third argument (\"%s\") is not "
+                                "allowed when using ProxyFCGISetEnvIf's unset "
+                                "mode (%s)", arg3, envvar);
+        }
+        else if (!envvar[1]) {
+            /* i.e. someone tried to give us a name of just "!" */
+            return "ProxyFCGISetEnvIf: \"!\" is not a valid variable name";
+        }
+
+        new->subst = NULL;
+    }
+    else {
+        /* Set mode. */
+        if (!arg3) {
+            /* A missing expr-value should be treated as empty. */
+            arg3 = "";
+        }
+
+        new->subst = ap_expr_parse_cmd(cmd, arg3, AP_EXPR_FLAG_STRING_RESULT, &err, NULL);
+        if (err) {
+            return apr_psprintf(cmd->pool, "Could not parse expression \"%s\": %s",
+                                arg3, err);
+        }
+    }
+
+    new->envname = envvar;
+
+    return NULL;
+}
 static void register_hooks(apr_pool_t *p)
 {
     proxy_hook_scheme_handler(proxy_fcgi_handler, NULL, NULL, APR_HOOK_FIRST);
     proxy_hook_canon_handler(proxy_fcgi_canon, NULL, NULL, APR_HOOK_FIRST);
 }
 
+static const command_rec command_table[] = {
+    AP_INIT_TAKE1("ProxyFCGIBackendType", cmd_servertype, NULL, OR_FILEINFO,
+                  "Specify the type of FastCGI server: 'Generic', 'FPM'"),
+    AP_INIT_TAKE23("ProxyFCGISetEnvIf", cmd_setenv, NULL, OR_FILEINFO,
+                  "expr-condition env-name expr-value"),
+    { NULL }
+};
+
 AP_DECLARE_MODULE(proxy_fcgi) = {
     STANDARD20_MODULE_STUFF,
-    NULL,                       /* create per-directory config structure */
-    NULL,                       /* merge per-directory config structures */
+    fcgi_create_dconf,          /* create per-directory config structure */
+    fcgi_merge_dconf,           /* merge per-directory config structures */
     NULL,                       /* create per-server config structure */
     NULL,                       /* merge per-server config structures */
-    NULL,                       /* command apr_table_t */
+    command_table,              /* command apr_table_t */
     register_hooks              /* register hooks */
 };