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:
Eric Covener
2017-12-31 16:09:44 +00:00
parent 36197b5e74
commit 531f038c4f
3 changed files with 100 additions and 13 deletions

View File

@ -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]

View File

@ -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">
&lt;Location "/"&gt;
AddOutputFilterByType SUBSTITUTE text/html
Substitute "s/example.com/expr=%{HTTP:HOST}/i"
Substitute "s/Hello, (\S+)/expr=Hello from %{REQUEST_URI}, \$1,/i"
&lt;/Location&gt;
</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

View File

@ -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;