]> granicus.if.org Git - apache/commitdiff
- Add <ElseIf> and <Else> to complement <If> sections. These are both easier
authorStefan Fritsch <sf@apache.org>
Sun, 5 Jun 2011 18:15:02 +0000 (18:15 +0000)
committerStefan Fritsch <sf@apache.org>
Sun, 5 Jun 2011 18:15:02 +0000 (18:15 +0000)
  to use and more efficient than using several <If> sections.
- Update <If> documentation a bit.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1132469 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
docs/manual/expr.xml
docs/manual/mod/core.xml
include/ap_mmn.h
include/http_core.h
server/core.c
server/request.c

diff --git a/CHANGES b/CHANGES
index eea1479c00aa1ccb36e3b487b48a15cbdaaa5218..515b0a78a9692ceb01a4970fb27ef696a746a3bd 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,9 @@
 
 Changes with Apache 2.3.13
 
+  *) core: Add <ElseIf> and <Else> to complement <If> sections.
+     [Stefan Fritsch]
+
   *) mod_ext_filter: Remove DebugLevel option in favor of per-module loglevel.
      [Stefan Fritsch]
 
index bc86b793ab13afca65d21f8d82cd0e8e43f04ee0..cd382bfc892c095d294135374543a0022c5b6541 100644 (file)
@@ -38,7 +38,9 @@
     </p>
   </summary>
 
-<seealso><directive module="core">If</directive></seealso>
+<seealso><directive module="core" type="section">If</directive></seealso>
+<seealso><directive module="core" type="section">ElseIf</directive></seealso>
+<seealso><directive module="core" type="section">Else</directive></seealso>
 <seealso><directive module="mod_rewrite">RewriteCond</directive></seealso>
 <seealso><directive module="mod_setenvif">SetEnvIfExpr</directive></seealso>
 <seealso><directive module="mod_filter">FilterProvider</directive></seealso>
index a219feccd6fafffb11483df64cd2e0938f95d772..0263701fca4cc74cf13278a2951c1528ec2f9fdd 100644 (file)
@@ -801,6 +801,102 @@ from the web</description>
 Locations</a></seealso>
 </directivesynopsis>
 
+<directivesynopsis type="section">
+<name>Else</name>
+<description>Contains directives that apply only if the condition of a
+previous <directive type="section" module="core">If</directive> or
+<directive type="section" module="core">ElseIf</directive> section is not
+satisfied by a request at runtime</description>
+<syntax>&lt;Else&gt; ... &lt;/Else&gt;</syntax>
+<contextlist><context>server config</context><context>virtual host</context>
+<context>directory</context><context>.htaccess</context>
+</contextlist>
+<override>All</override>
+
+<usage>
+    <p>The <directive type="section">Else</directive> applies the enclosed
+    directives if and only if the most recent
+    <directive type="section">If</directive> or
+    <directive type="section">ElseIf</directive> section
+    in the same scope has not been applied.
+    For example: In </p>
+
+    <example>
+        &lt;If "-z req('Host')"&gt;<br/>
+        ...<br/>
+        &lt;/If&gt;<br/>
+        &lt;Else&gt;<br/>
+        ...<br/>
+        &lt;/Else&gt;<br/>
+    </example>
+
+    <p> The <directive type="section">If</directive> would match HTTP/1.0
+        requests without a <var>Host:</var> header and the
+        <directive type="section">Else</directive> would match requests
+        with a <var>Host:</var> header.</p>
+
+</usage>
+<seealso><directive type="section" module="core">If</directive></seealso>
+<seealso><directive type="section" module="core">ElseIf</directive></seealso>
+<seealso><a href="../sections.html">How &lt;Directory&gt;, &lt;Location&gt;,
+    &lt;Files&gt; sections work</a> for an explanation of how these
+    different sections are combined when a request is received.
+    <directive type="section">If</directive>,
+    <directive type="section">ElseIf</directive>, and
+    <directive type="section">Else</directive> are applied last.</seealso>
+</directivesynopsis>
+
+<directivesynopsis type="section">
+<name>ElseIf</name>
+<description>Contains directives that apply only if a condition is satisfied
+by a request at runtime while the condition of a previous
+<directive type="section" module="core">If</directive> or
+<directive type="section">ElseIf</directive> section is not
+satisfied</description>
+<syntax>&lt;ElseIf <var>expression</var>&gt; ... &lt;/ElseIf&gt;</syntax>
+<contextlist><context>server config</context><context>virtual host</context>
+<context>directory</context><context>.htaccess</context>
+</contextlist>
+<override>All</override>
+
+<usage>
+    <p>The <directive type="section">ElseIf</directive> applies the enclosed
+    directives if and only if both the given condition evaluates to true and
+    the most recent <directive type="section">If</directive> or
+    <directive type="section">ElseIf</directive> section in the same scope has
+    not been applied.  For example: In </p>
+
+    <example>
+        &lt;If "-R '10.1.0.0/16'"&gt;<br/>
+        ...<br/>
+        &lt;/If&gt;<br/>
+        &lt;ElseIf "-R '10.0.0.0/8'"&gt;<br/>
+        ...<br/>
+        &lt;/ElseIf&gt;<br/>
+        &lt;Else&gt;<br/>
+        ...<br/>
+        &lt;/Else&gt;<br/>
+    </example>
+
+    <p>The <directive type="section">ElseIf</directive> would match if
+    the remote address of a request belongs to the subnet 10.0.0.0/8 but
+    not to the subnet 10.1.0.0/16.</p>
+
+</usage>
+<seealso><a href="../expr.html">Expressions in Apache HTTP Server</a>,
+for a complete reference and more examples.</seealso>
+<seealso><directive type="section" module="core">If</directive></seealso>
+<seealso><directive type="section" module="core">Else</directive></seealso>
+<seealso><a href="../sections.html">How &lt;Directory&gt;, &lt;Location&gt;,
+    &lt;Files&gt; sections work</a> for an explanation of how these
+    different sections are combined when a request is received.
+    <directive type="section">If</directive>,
+    <directive type="section">ElseIf</directive>, and
+    <directive type="section">Else</directive> are applied last.</seealso>
+</directivesynopsis>
+
+
+
 <directivesynopsis>
 <name>EnableMMAP</name>
 <description>Use memory-mapping to read files during delivery</description>
@@ -1603,31 +1699,38 @@ satisfied by a request at runtime</description>
     For example:</p>
 
     <example>
-        &lt;If "$req{Host} = ''"&gt;
+        &lt;If "-z req('Host')"&gt;
     </example>
 
-    <p>would match HTTP/1.0 requests without a <var>Host:</var> header.</p>
-
-    <p>You may compare the value of any variable in the request headers
-    ($req), response headers ($resp) or environment ($env) in your
-    expression.</p>
-
-    <p>Apart from <code>=</code>, <code>If</code> can use the <code>IN</code>
-    operator to compare if the expression is in a given range:</p>
+    <p>would match HTTP/1.0 requests without a <var>Host:</var> header.
+    Expressions may contain various shell-like operators for string
+    comparison (<code>=</code>, <code>!=</code>, <code>&lt;</code>, ...),
+    integer comparison (<code>-eq</code>, <code>-ne</code>, ...),
+    and others (<code>-n</code>, <code>-z</code>, <code>-f</code>, ...).
+    It is also possible to use regular expressions, </p>
 
     <example>
-        &lt;If %{REQUEST_METHOD} IN GET,HEAD,OPTIONS&gt;
+        &lt;If "%{QUERY_STRING =~ /(delete|commit)=.*?elem/"&gt;
     </example>
 
+    <p>shell-like pattern matches and many other operations. These operations
+    can be done on request headers (<code>req</code>), environment variables
+    (<code>env</code>), and a large number of other properties. The full
+    documentation is available in <a href="../expr.html">Expressions in
+    Apache HTTP Server</a>.</p>
+
 </usage>
 
 <seealso><a href="../expr.html">Expressions in Apache HTTP Server</a>,
 for a complete reference and more examples.</seealso>
+<seealso><directive type="section" module="core">ElseIf</directive></seealso>
+<seealso><directive type="section" module="core">Else</directive></seealso>
 <seealso><a href="../sections.html">How &lt;Directory&gt;, &lt;Location&gt;,
     &lt;Files&gt; sections work</a> for an explanation of how these
     different sections are combined when a request is received.
-    <directive type="section">If</directive> has the same precedence
-    and usage as <directive type="section">Files</directive></seealso>
+    <directive type="section">If</directive>,
+    <directive type="section">ElseIf</directive>, and
+    <directive type="section">Else</directive> are applied last.</seealso>
 </directivesynopsis>
 
 <directivesynopsis type="section">
index a3afd13f7b80c4fec91f40b560ee28106e84a32f..c35b0f093f94f891be754ad5ac4d13e6e17f52a4 100644 (file)
  * 20110329.6 (2.3.13-dev) Add AP_EXPR_FLAGS_RESTRICTED, ap_expr_eval_ctx_t->data,
  *                         ap_expr_exec_ctx()
  * 20110604.0 (2.3.13-dev) Make ap_rputs() inline
+ * 20110605.0 (2.3.13-dev) add core_dir_config->condition_ifelse, change return
+ *                         type of ap_add_if_conf()
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */
 
 #ifndef MODULE_MAGIC_NUMBER_MAJOR
-#define MODULE_MAGIC_NUMBER_MAJOR 20110604
+#define MODULE_MAGIC_NUMBER_MAJOR 20110605
 #endif
 #define MODULE_MAGIC_NUMBER_MINOR 0                    /* 0...n */
 
index 4d7a7e35826e2f5b3da06fc4cb9b258e461d378f..c32a86540c47ed06f553cbebcd58f2bd9a5e636b 100644 (file)
@@ -538,6 +538,11 @@ typedef struct {
                                              * pitched indiscriminately */
     unsigned int decode_encoded_slashes : 1; /* whether to decode encoded slashes in URLs */
 
+#define AP_CONDITION_IF        1
+#define AP_CONDITION_ELSE      2
+#define AP_CONDITION_ELSEIF    (AP_CONDITION_ELSE|AP_CONDITION_IF)
+    unsigned int condition_ifelse : 2; /* is this an <If>, <ElseIf>, or <Else> */
+
     ap_expr_info_t *condition;   /* Conditionally merge <If> sections */
 
     /** per-dir log config */
@@ -602,7 +607,7 @@ void ap_core_reorder_directories(apr_pool_t *, server_rec *);
 AP_CORE_DECLARE(void) ap_add_per_dir_conf(server_rec *s, void *dir_config);
 AP_CORE_DECLARE(void) ap_add_per_url_conf(server_rec *s, void *url_config);
 AP_CORE_DECLARE(void) ap_add_file_conf(apr_pool_t *p, core_dir_config *conf, void *url_config);
-AP_CORE_DECLARE(void) ap_add_if_conf(apr_pool_t *p, core_dir_config *conf, void *url_config);
+AP_CORE_DECLARE(const char *) ap_add_if_conf(apr_pool_t *p, core_dir_config *conf, void *url_config);
 AP_CORE_DECLARE_NONSTD(const char *) ap_limit_section(cmd_parms *cmd, void *dummy, const char *arg);
 
 /* Core filters; not exported. */
index 32b42c08f3b7d4d995ba1ffdf91432dcab5fb67a..6566bef1fb09a2f1c8720a9df7606c649344b1a2 100644 (file)
@@ -535,16 +535,35 @@ AP_CORE_DECLARE(void) ap_add_file_conf(apr_pool_t *p, core_dir_config *conf,
     *new_space = url_config;
 }
 
-AP_CORE_DECLARE(void) ap_add_if_conf(apr_pool_t *p, core_dir_config *conf,
-                                     void *url_config)
+AP_CORE_DECLARE(const char *) ap_add_if_conf(apr_pool_t *p,
+                                             core_dir_config *conf,
+                                             void *if_config)
 {
     void **new_space;
+    core_dir_config *new = ap_get_module_config(if_config, &core_module);
 
-    if (!conf->sec_if)
+    if (!conf->sec_if) {
         conf->sec_if = apr_array_make(p, 2, sizeof(ap_conf_vector_t *));
+    }
+    if (new->condition_ifelse & AP_CONDITION_ELSE) {
+        int have_if = 0;
+        if (conf->sec_if->nelts > 0) {
+            core_dir_config *last;
+            ap_conf_vector_t *lastelt = APR_ARRAY_IDX(conf->sec_if,
+                                                      conf->sec_if->nelts - 1,
+                                                      ap_conf_vector_t *);
+            last = ap_get_module_config(lastelt, &core_module);
+            if (last->condition_ifelse & AP_CONDITION_IF)
+                have_if = 1;
+        }
+        if (!have_if)
+            return "<Else> or <ElseIf> section without previous <If> or "
+                   "<ElseIf> section in same scope";
+    }
 
     new_space = (void **)apr_array_push(conf->sec_if);
-    *new_space = url_config;
+    *new_space = if_config;
+    return NULL;
 }
 
 
@@ -2182,6 +2201,11 @@ static const char *filesection(cmd_parms *cmd, void *mconfig, const char *arg)
 
     return NULL;
 }
+
+#define COND_IF      ((void *)1)
+#define COND_ELSE    ((void *)2)
+#define COND_ELSEIF  ((void *)3)
+
 static const char *ifsection(cmd_parms *cmd, void *mconfig, const char *arg)
 {
     const char *errmsg;
@@ -2190,7 +2214,7 @@ static const char *ifsection(cmd_parms *cmd, void *mconfig, const char *arg)
     char *old_path = cmd->path;
     core_dir_config *conf;
     const command_rec *thiscmd = cmd->cmd;
-    ap_conf_vector_t *new_file_conf = ap_create_per_dir_config(cmd->pool);
+    ap_conf_vector_t *new_if_conf = ap_create_per_dir_config(cmd->pool);
     const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
     const char *condition;
     const char *expr_err;
@@ -2205,27 +2229,41 @@ static const char *ifsection(cmd_parms *cmd, void *mconfig, const char *arg)
 
     arg = apr_pstrndup(cmd->temp_pool, arg, endp - arg);
 
-    if (!arg[0]) {
-        return missing_container_arg(cmd);
-    }
 
-    condition = ap_getword_conf(cmd->pool, &arg);
     /* Only if not an .htaccess file */
     if (!old_path) {
         cmd->override = OR_ALL|ACCESS_CONF;
     }
 
     /* initialize our config and fetch it */
-    conf = ap_set_config_vectors(cmd->server, new_file_conf, cmd->path,
+    conf = ap_set_config_vectors(cmd->server, new_if_conf, cmd->path,
                                  &core_module, cmd->pool);
 
-    conf->condition = ap_expr_parse_cmd(cmd, condition, &expr_err, NULL);
-    if (expr_err) {
-        return apr_psprintf(cmd->pool, "Cannot parse condition clause: %s", expr_err);
+    if (cmd->cmd->cmd_data == COND_IF)
+        conf->condition_ifelse = AP_CONDITION_IF;
+    else if (cmd->cmd->cmd_data == COND_ELSEIF)
+        conf->condition_ifelse = AP_CONDITION_ELSEIF;
+    else if (cmd->cmd->cmd_data == COND_ELSE)
+        conf->condition_ifelse = AP_CONDITION_ELSE;
+    else
+        ap_assert(0);
+
+    if (conf->condition_ifelse == AP_CONDITION_ELSE) {
+        if (arg[0])
+            return "<Else> does not take an argument";
+    }
+    else {
+        if (!arg[0])
+            return missing_container_arg(cmd);
+        condition = ap_getword_conf(cmd->pool, &arg);
+        conf->condition = ap_expr_parse_cmd(cmd, condition, &expr_err, NULL);
+        if (expr_err)
+            return apr_psprintf(cmd->pool, "Cannot parse condition clause: %s",
+                                expr_err);
+        conf->condition->module_index = APLOG_MODULE_INDEX;
     }
-    conf->condition->module_index = APLOG_MODULE_INDEX;
 
-    errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_file_conf);
+    errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_if_conf);
     if (errmsg != NULL)
         return errmsg;
 
@@ -2233,7 +2271,9 @@ static const char *ifsection(cmd_parms *cmd, void *mconfig, const char *arg)
     conf->d_is_fnmatch = 0;
     conf->r = NULL;
 
-    ap_add_if_conf(cmd->pool, (core_dir_config *)mconfig, new_file_conf);
+    errmsg = ap_add_if_conf(cmd->pool, (core_dir_config *)mconfig, new_if_conf);
+    if (errmsg != NULL)
+        return errmsg;
 
     if (*arg != '\0') {
         return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
@@ -3659,7 +3699,11 @@ AP_INIT_TAKE1("UnDefine", unset_define, NULL, EXEC_ON_READ|ACCESS_CONF|RSRC_CONF
               "Undefine the existence of a variable. Undo a Define."),
 AP_INIT_RAW_ARGS("Error", generate_error, NULL, OR_ALL,
                  "Generate error message from within configuration"),
-AP_INIT_RAW_ARGS("<If", ifsection, NULL, OR_ALL,
+AP_INIT_RAW_ARGS("<If", ifsection, COND_IF, OR_ALL,
+  "Container for directives to be conditionally applied"),
+AP_INIT_RAW_ARGS("<ElseIf", ifsection, COND_ELSEIF, OR_ALL,
+  "Container for directives to be conditionally applied"),
+AP_INIT_RAW_ARGS("<Else", ifsection, COND_ELSE, OR_ALL,
   "Container for directives to be conditionally applied"),
 
 /* Old resource config file commands */
index b0ec847361e1394ca4beff6d4d7cd759f08f9d88..5a77a0f6a267060374ab1ff521618c0220fb1d7b 100644 (file)
@@ -1628,6 +1628,7 @@ AP_DECLARE(int) ap_if_walk(request_rec *r)
     int sec_idx;
     int matches;
     int cached_matches;
+    int prev_result = -1;
     walk_walked_t *last_walk;
 
     if (dconf->sec_if) {
@@ -1659,13 +1660,27 @@ AP_DECLARE(int) ap_if_walk(request_rec *r)
         int rc;
         entry_core = ap_get_module_config(sec_ent[sec_idx], &core_module);
 
-        rc = ap_expr_exec(r, entry_core->condition, &err);
-        if (rc <= 0) {
-            if (rc < 0)
-                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                              "Failed to evaluate <If > condition: %s",
-                              err);
-            continue;
+        AP_DEBUG_ASSERT(entry_core->condition_ifelse != 0);
+        if (entry_core->condition_ifelse & AP_CONDITION_ELSE) {
+            AP_DEBUG_ASSERT(prev_result != -1);
+            if (prev_result == 1)
+                continue;
+        }
+
+        if (entry_core->condition_ifelse & AP_CONDITION_IF) {
+            rc = ap_expr_exec(r, entry_core->condition, &err);
+            if (rc <= 0) {
+                if (rc < 0)
+                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                                  "Failed to evaluate <If > condition: %s",
+                                  err);
+                prev_result = 0;
+                continue;
+            }
+            prev_result = 1;
+        }
+        else {
+            prev_result = -1;
         }
 
         /* If we merged this same section last time, reuse it