mirror of
https://github.com/apache/httpd.git
synced 2025-08-20 16:09:55 +00:00
mod_substitute: allow opt-in to expressions in substitution value
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1819739 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
3
CHANGES
3
CHANGES
@ -1,6 +1,9 @@
|
||||
-*- coding: utf-8 -*-
|
||||
Changes with Apache 2.5.1
|
||||
|
||||
*) mod_susbtitute: Allow expressions in the subtitution, prefixed with expr=
|
||||
[Eric Covener]
|
||||
|
||||
*) mod_md: fixed mem pool usage for auto-added server names. Added
|
||||
error logging of exact ACME response when challenges failed.
|
||||
[Stefan Eissing]
|
||||
|
@ -41,6 +41,7 @@
|
||||
<contextlist><context>directory</context>
|
||||
<context>.htaccess</context></contextlist>
|
||||
<override>FileInfo</override>
|
||||
<compatibility>"expr=" substitution values were added in 2.5.1</compatibility>
|
||||
|
||||
<usage>
|
||||
<p>The <directive>Substitute</directive> directive specifies a
|
||||
@ -69,8 +70,11 @@
|
||||
or regex of a subsequent one.</dd>
|
||||
</dl>
|
||||
|
||||
<p>The <var>substitution</var> is may contain literal text and regular
|
||||
expression backreferences</p>
|
||||
<p>The <var>substitution</var> may contain literal text and regular
|
||||
expression backreferences. If the substitution begins with the text
|
||||
<code>expr=</code> it is intepreted as an <a href="../expr.html">
|
||||
expression</a> which allows access to environment variables and
|
||||
header values. </p>
|
||||
|
||||
<example><title>Example</title>
|
||||
<highlight language="config">
|
||||
@ -111,6 +115,26 @@
|
||||
</highlight>
|
||||
</example>
|
||||
|
||||
<p> When using an <a href="../expr.html">expression</a> for the
|
||||
<var>substitution</var>, regular expression backreferences must be
|
||||
backslash ('\') escaped as illustrated in the example below:</p>
|
||||
<example><title>Expression Example</title>
|
||||
<highlight language="config">
|
||||
<Location "/">
|
||||
AddOutputFilterByType SUBSTITUTE text/html
|
||||
Substitute "s/example.com/expr=%{HTTP:HOST}/i"
|
||||
Substitute "s/Hello, (\S+)/expr=Hello from %{REQUEST_URI}, \$1,/i"
|
||||
</Location>
|
||||
</highlight>
|
||||
</example>
|
||||
|
||||
<note type="warning"><title>Expressions and caching</title>
|
||||
<p>Caution must be exercised when performing substitutions that reference
|
||||
HTTP request headers. Because this module operates after response headers
|
||||
have been sent, the <a href="../expr.html">expression parser</a> cannot add
|
||||
referenced HTTP request headers to the outgoing Vary header. </p>
|
||||
</note>
|
||||
|
||||
<p>A common use scenario for <code>mod_substitute</code> is the
|
||||
situation in which a front-end server proxies requests to a back-end
|
||||
server which returns HTML with hard-coded embedded URLs that refer
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "util_varbuf.h"
|
||||
#include "apr_buckets.h"
|
||||
#include "http_request.h"
|
||||
#include "ap_expr.h"
|
||||
#define APR_WANT_STRFUNC
|
||||
#include "apr_want.h"
|
||||
|
||||
@ -52,6 +53,7 @@ typedef struct subst_pattern_t {
|
||||
apr_size_t patlen;
|
||||
int flatten;
|
||||
const char *from;
|
||||
ap_expr_info_t* expr_replacement;
|
||||
} subst_pattern_t;
|
||||
|
||||
typedef struct {
|
||||
@ -69,6 +71,11 @@ typedef struct {
|
||||
apr_pool_t *tpool;
|
||||
} substitute_module_ctx;
|
||||
|
||||
typedef struct {
|
||||
const char **expcache;
|
||||
int *expcache_len;
|
||||
} subst_req_t;
|
||||
|
||||
static void *create_substitute_dcfg(apr_pool_t *p, char *d)
|
||||
{
|
||||
subst_dir_conf *dcfg =
|
||||
@ -137,11 +144,14 @@ static apr_status_t do_pattmatch(ap_filter_t *f, apr_bucket *inb,
|
||||
struct ap_varbuf vb;
|
||||
apr_bucket *b;
|
||||
apr_bucket *tmp_b;
|
||||
subst_pattern_t *script;
|
||||
|
||||
subst_dir_conf *cfg =
|
||||
(subst_dir_conf *) ap_get_module_config(f->r->per_dir_config,
|
||||
&substitute_module);
|
||||
subst_pattern_t *script;
|
||||
subst_req_t *rconf =
|
||||
(subst_req_t*) ap_get_module_config(f->r->request_config,
|
||||
&substitute_module);
|
||||
|
||||
APR_BRIGADE_INSERT_TAIL(mybb, inb);
|
||||
ap_varbuf_init(pool, &vb, 0);
|
||||
@ -155,6 +165,16 @@ static apr_status_t do_pattmatch(ap_filter_t *f, apr_bucket *inb,
|
||||
force_quick = 1;
|
||||
}
|
||||
for (i = 0; i < cfg->patterns->nelts; i++) {
|
||||
const char *replacement = script->replacement;
|
||||
apr_size_t replen = script->replen;
|
||||
if (script->expr_replacement) {
|
||||
if (!rconf) {
|
||||
rconf = apr_pcalloc(f->r->pool, sizeof(*rconf));
|
||||
rconf->expcache = apr_pcalloc(f->r->pool, sizeof(const char*) * cfg->patterns->nelts);
|
||||
rconf->expcache_len = apr_pcalloc(f->r->pool, sizeof(int) * cfg->patterns->nelts);
|
||||
ap_set_module_config(f->r->request_config, &substitute_module, rconf);
|
||||
}
|
||||
}
|
||||
for (b = APR_BRIGADE_FIRST(mybb);
|
||||
b != APR_BRIGADE_SENTINEL(mybb);
|
||||
b = APR_BUCKET_NEXT(b)) {
|
||||
@ -187,12 +207,26 @@ static apr_status_t do_pattmatch(ap_filter_t *f, apr_bucket *inb,
|
||||
* line length reaches max_line_length.
|
||||
*/
|
||||
apr_size_t space_left = cfg->max_line_length;
|
||||
apr_size_t repl_len = strlen(script->replacement);
|
||||
while ((repl = apr_strmatch(script->pattern, buff, bytes)))
|
||||
{
|
||||
|
||||
if (!have_match && script->expr_replacement) {
|
||||
if (!rconf->expcache[i]) {
|
||||
const char *err = NULL;
|
||||
rconf->expcache[i] = ap_expr_str_exec(f->r, script->expr_replacement, &err);
|
||||
if (err) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, "error evaluating expression: %s", err);
|
||||
return APR_EINVAL;
|
||||
}
|
||||
rconf->expcache_len[i] = strlen(rconf->expcache[i]);
|
||||
}
|
||||
replacement = rconf->expcache[i];
|
||||
replen = rconf->expcache_len[i];
|
||||
}
|
||||
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
|
||||
"Matching found, result: '%s'",
|
||||
script->replacement);
|
||||
replacement);
|
||||
have_match = 1;
|
||||
/* get offset into buff for pattern */
|
||||
len = (apr_size_t) (repl - buff);
|
||||
@ -205,10 +239,10 @@ static apr_status_t do_pattmatch(ap_filter_t *f, apr_bucket *inb,
|
||||
* are constanting allocing space and copying
|
||||
* strings.
|
||||
*/
|
||||
if (vb.strlen + len + repl_len > cfg->max_line_length)
|
||||
if (vb.strlen + len + replen > cfg->max_line_length)
|
||||
return APR_ENOMEM;
|
||||
ap_varbuf_strmemcat(&vb, buff, len);
|
||||
ap_varbuf_strmemcat(&vb, script->replacement, repl_len);
|
||||
ap_varbuf_strmemcat(&vb, replacement, replen);
|
||||
}
|
||||
else {
|
||||
/*
|
||||
@ -217,9 +251,9 @@ static apr_status_t do_pattmatch(ap_filter_t *f, apr_bucket *inb,
|
||||
* Check if we still have space for this string and
|
||||
* the replacement string.
|
||||
*/
|
||||
if (space_left < len + repl_len)
|
||||
if (space_left < len + replen)
|
||||
return APR_ENOMEM;
|
||||
space_left -= len + repl_len;
|
||||
space_left -= len + replen;
|
||||
/*
|
||||
* We now split off the string before the match
|
||||
* as its own bucket, then isolate the matched
|
||||
@ -230,8 +264,8 @@ static apr_status_t do_pattmatch(ap_filter_t *f, apr_bucket *inb,
|
||||
* Finally, we create a bucket that contains the
|
||||
* replacement...
|
||||
*/
|
||||
tmp_b = apr_bucket_transient_create(script->replacement,
|
||||
script->replen,
|
||||
tmp_b = apr_bucket_transient_create(replacement,
|
||||
replen,
|
||||
f->r->connection->bucket_alloc);
|
||||
/* ... and insert it */
|
||||
APR_BUCKET_INSERT_BEFORE(b, tmp_b);
|
||||
@ -282,6 +316,20 @@ static apr_status_t do_pattmatch(ap_filter_t *f, apr_bucket *inb,
|
||||
while (!ap_regexec_len(script->regexp, pos, left,
|
||||
AP_MAX_REG_MATCH, regm, 0)) {
|
||||
apr_status_t rv;
|
||||
if (!have_match && script->expr_replacement) {
|
||||
if (!rconf->expcache[i]) {
|
||||
const char *err = NULL;
|
||||
rconf->expcache[i] = ap_expr_str_exec(f->r, script->expr_replacement, &err);
|
||||
if (err) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, "error evaluating expression: %s", err);
|
||||
return APR_EGENERAL;
|
||||
}
|
||||
rconf->expcache_len[i] = strlen(rconf->expcache[i]);
|
||||
}
|
||||
replacement = rconf->expcache[i];
|
||||
replen = rconf->expcache_len[i];
|
||||
}
|
||||
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, f->r,
|
||||
"Matching found");
|
||||
have_match = 1;
|
||||
@ -296,7 +344,7 @@ static apr_status_t do_pattmatch(ap_filter_t *f, apr_bucket *inb,
|
||||
if (regm[0].rm_so > 0)
|
||||
ap_varbuf_strmemcat(&vb, pos, regm[0].rm_so);
|
||||
/* add replacement string, last argument is unsigned! */
|
||||
rv = ap_varbuf_regsub(&vb, script->replacement, pos,
|
||||
rv = ap_varbuf_regsub(&vb, replacement, pos,
|
||||
AP_MAX_REG_MATCH, regm,
|
||||
cfg->max_line_length - vb.strlen);
|
||||
if (rv != APR_SUCCESS)
|
||||
@ -311,7 +359,7 @@ static apr_status_t do_pattmatch(ap_filter_t *f, apr_bucket *inb,
|
||||
return APR_ENOMEM;
|
||||
space_left -= regm[0].rm_so;
|
||||
rv = ap_pregsub_ex(pool, &repl,
|
||||
script->replacement, pos,
|
||||
replacement, pos,
|
||||
AP_MAX_REG_MATCH, regm,
|
||||
space_left);
|
||||
if (rv != APR_SUCCESS)
|
||||
@ -692,6 +740,18 @@ static const char *set_pattern(cmd_parms *cmd, void *cfg, const char *line)
|
||||
|
||||
nscript->replacement = to;
|
||||
nscript->replen = strlen(to);
|
||||
|
||||
if (!strncasecmp(to, "expr=", 5)) {
|
||||
const char *err;
|
||||
nscript->expr_replacement = ap_expr_parse_cmd(cmd, to+5,
|
||||
AP_EXPR_FLAG_STRING_RESULT,
|
||||
&err, NULL);
|
||||
if (err) {
|
||||
return apr_pstrcat(cmd->pool,
|
||||
"Can't parse value expression : ", err, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
nscript->flatten = flatten;
|
||||
|
||||
return NULL;
|
||||
|
Reference in New Issue
Block a user