From 9850539bd7ab7172c5a26854d1e18ae99191ca68 Mon Sep 17 00:00:00 2001 From: Graham Leggett Date: Sat, 21 Feb 2015 23:59:05 +0000 Subject: [PATCH] core: Add expression support to ErrorDocument. Switch from a fixed sized 664 byte array per merge to a hash table. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1661448 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 3 ++ docs/log-message-tags/next-number | 2 +- docs/manual/expr.xml | 1 + docs/manual/mod/core.xml | 8 ++- include/ap_mmn.h | 3 +- include/http_core.h | 14 ++--- server/core.c | 86 ++++++++++++++++++++----------- 7 files changed, 79 insertions(+), 38 deletions(-) diff --git a/CHANGES b/CHANGES index 8452442428..d379baa035 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,9 @@ Changes with Apache 2.5.0 calls r:wsupgrade() can cause a child process crash. [Edward Lu ] + *) core: Add expression support to ErrorDocument. Switch from a fixed + sized 664 byte array per merge to a hash table. [Graham Leggett] + *) mod_ssl: Add the SSL_CLIENT_CERT_RFC4523_CEA variable, which provides a combination of certificate serialNumber and issuer as defined by CertificateExactMatch in RFC4523. [Graham Leggett] diff --git a/docs/log-message-tags/next-number b/docs/log-message-tags/next-number index e069f01702..55d9622930 100644 --- a/docs/log-message-tags/next-number +++ b/docs/log-message-tags/next-number @@ -1 +1 @@ -2830 +2831 diff --git a/docs/manual/expr.xml b/docs/manual/expr.xml index 44e35eaee9..dc6a505295 100644 --- a/docs/manual/expr.xml +++ b/docs/manual/expr.xml @@ -40,6 +40,7 @@ If ElseIf Else +ErrorDocument Alias ScriptAlias Redirect diff --git a/docs/manual/mod/core.xml b/docs/manual/mod/core.xml index 19c1e3aade..0f6351ee9c 100644 --- a/docs/manual/mod/core.xml +++ b/docs/manual/mod/core.xml @@ -1254,10 +1254,15 @@ in case of an error or a message. Apache httpd will sometimes offer additional information regarding the problem/error.

+

From 2.4.13, expression syntax can be + used inside the directive to produce dynamic strings and URLs.

+

URLs can begin with a slash (/) for local web-paths (relative to the DocumentRoot), or be a full URL which the client can resolve. Alternatively, a message - can be provided to be displayed by the browser. Examples:

+ can be provided to be displayed by the browser. Note that deciding + whether the parameter is an URL, a path or a message is performed + before any expression is parsed. Examples:

ErrorDocument 500 http://foo.example.com/cgi-bin/tester @@ -1265,6 +1270,7 @@ ErrorDocument 404 /cgi-bin/bad_urls.pl ErrorDocument 401 /subscription_info.html ErrorDocument 403 "Sorry can't allow you access today" ErrorDocument 403 Forbidden! +ErrorDocument 403 /cgi-bin/forbidden.pl?referrer=%{escape:%{HTTP_REFERER}}

Additionally, the special value default can be used diff --git a/include/ap_mmn.h b/include/ap_mmn.h index c6790aba71..f92e106d3b 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -478,6 +478,7 @@ * 20140627.10 (2.5.0-dev) Add ap_proxy_de_socketfy to mod_proxy.h * 20150121.0 (2.5.0-dev) Revert field addition from core_dir_config; r1653666 * 20150121.1 (2.5.0-dev) Add cmd_parms_struct.parent to http_config.h + * 20150121.2 (2.5.0-dev) Add response_code_exprs to http_core.h */ #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */ @@ -485,7 +486,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20150121 #endif -#define MODULE_MAGIC_NUMBER_MINOR 1 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 2 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/include/http_core.h b/include/http_core.h index 33430880cb..87481268c1 100644 --- a/include/http_core.h +++ b/include/http_core.h @@ -497,12 +497,7 @@ typedef struct { overrides_t override; allow_options_t override_opts; - /* Custom response config. These can contain text or a URL to redirect to. - * if response_code_strings is NULL then there are none in the config, - * if it's not null then it's allocated to sizeof(char*)*RESPONSE_CODES. - * This lets us do quick merges in merge_core_dir_configs(). - */ - + /* Used to be the custom response config. No longer used. */ char **response_code_strings; /* from ErrorDocument, not from * ap_custom_response() */ @@ -632,6 +627,13 @@ typedef struct { * advice */ unsigned int cgi_pass_auth : 2; + + /** Custom response config with expression support. The hash table + * contains compiled expressions keyed against the custom response + * code. + */ + apr_hash_t *response_code_exprs; + } core_dir_config; /* macro to implement off by default behaviour */ diff --git a/server/core.c b/server/core.c index 587e3751fb..44187631c8 100644 --- a/server/core.c +++ b/server/core.c @@ -260,23 +260,14 @@ static void *merge_core_dir_configs(apr_pool_t *a, void *basev, void *newv) conf->override_list = new->override_list; } - if (conf->response_code_strings == NULL) { - conf->response_code_strings = new->response_code_strings; + if (conf->response_code_exprs == NULL) { + conf->response_code_exprs = new->response_code_exprs; } - else if (new->response_code_strings != NULL) { - /* If we merge, the merge-result must have its own array - */ - conf->response_code_strings = apr_pmemdup(a, - base->response_code_strings, - sizeof(*conf->response_code_strings) * RESPONSE_CODES); - - for (i = 0; i < RESPONSE_CODES; ++i) { - if (new->response_code_strings[i] != NULL) { - conf->response_code_strings[i] = new->response_code_strings[i]; - } - } + else if (new->response_code_exprs != NULL) { + conf->response_code_exprs = apr_hash_overlay(a, + new->response_code_exprs, conf->response_code_exprs); } - /* Otherwise we simply use the base->response_code_strings array + /* Otherwise we simply use the base->response_code_exprs array */ if (new->hostname_lookups != HOSTNAME_LOOKUP_UNSET) { @@ -833,25 +824,46 @@ char *ap_response_code_string(request_rec *r, int error_index) { core_dir_config *dirconf; core_request_config *reqconf = ap_get_core_module_config(r->request_config); + const char *err; + const char *response; + void *val; + ap_expr_info_t *expr; /* check for string registered via ap_custom_response() first */ - if (reqconf->response_code_strings != NULL && - reqconf->response_code_strings[error_index] != NULL) { + if (reqconf->response_code_strings != NULL + && reqconf->response_code_strings[error_index] != NULL) { return reqconf->response_code_strings[error_index]; } /* check for string specified via ErrorDocument */ dirconf = ap_get_core_module_config(r->per_dir_config); - if (dirconf->response_code_strings == NULL) { + if (!dirconf->response_code_exprs) { + return NULL; + } + + expr = apr_hash_get(dirconf->response_code_exprs, &error_index, + sizeof(error_index)); + if (!expr) { return NULL; } - if (dirconf->response_code_strings[error_index] == &errordocument_default) { + /* special token to indicate revert back to default */ + if ((char *) expr == &errordocument_default) { return NULL; } - return dirconf->response_code_strings[error_index]; + err = NULL; + response = ap_expr_str_exec(r, expr, &err); + if (err) { + ap_log_rerror( + APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02830) "core: ErrorDocument: can't " + "evaluate require expression: %s", err); + return NULL; + } + + /* alas, duplication required as we return not-const */ + return apr_pstrdup(r->pool, response); } @@ -1566,27 +1578,43 @@ static const char *set_error_document(cmd_parms *cmd, void *conf_, "directive --- ignoring!", cmd->directive->filename, cmd->directive->line_num); } else { /* Store it... */ - if (conf->response_code_strings == NULL) { - conf->response_code_strings = - apr_pcalloc(cmd->pool, - sizeof(*conf->response_code_strings) * - RESPONSE_CODES); + if (conf->response_code_exprs == NULL) { + conf->response_code_exprs = apr_hash_make(cmd->pool); } if (strcasecmp(msg, "default") == 0) { /* special case: ErrorDocument 404 default restores the * canned server error response */ - conf->response_code_strings[index_number] = &errordocument_default; + apr_hash_set(conf->response_code_exprs, + apr_pmemdup(cmd->pool, &index_number, sizeof(index_number)), + sizeof(index_number), &errordocument_default); } else { + ap_expr_info_t *expr; + const char *expr_err = NULL; + /* hack. Prefix a " if it is a msg; as that is what * http_protocol.c relies on to distinguish between * a msg and a (local) path. */ - conf->response_code_strings[index_number] = (what == MSG) ? - apr_pstrcat(cmd->pool, "\"", msg, NULL) : - apr_pstrdup(cmd->pool, msg); + const char *response = + (what == MSG) ? apr_pstrcat(cmd->pool, "\"", msg, NULL) : + apr_pstrdup(cmd->pool, msg); + + expr = ap_expr_parse_cmd(cmd, response, AP_EXPR_FLAG_STRING_RESULT, + &expr_err, NULL); + + if (expr_err) { + return apr_pstrcat(cmd->temp_pool, + "Cannot parse expression in ErrorDocument: ", + expr_err, NULL); + } + + apr_hash_set(conf->response_code_exprs, + apr_pmemdup(cmd->pool, &index_number, sizeof(index_number)), + sizeof(index_number), expr); + } } -- 2.40.0