[core] save config regex captures only if used

save config regex captures separately only if used by url.redirect
or url.rewrite replacement directives within the condition
(or for conditions containing directives from any other module
 which calls config_capture() for its directives during init)

keep pointer to match data (cond_match_t *) in r->cond_match[]
rather than cond_match_t to reduce data copying in h2_init_stream().
h2_init_stream() copies the results for already-evaluated conditions
to avoid re-evaluating connection-level conditions for each and every
stream.  When conditions are reset, then the pointer in r->cond_match[]
is updated when the condition is re-evaluated.  (This all assumes that
HTTP/2 connection-level conditions are not unset or re-evaluated once
HTTP/2 streams begin.)
personal/stbuehler/tests-path
Glenn Strauss 2021-11-19 15:50:02 -05:00
parent 04d387e37f
commit 7ea2d40734
11 changed files with 94 additions and 36 deletions

View File

@ -146,6 +146,7 @@ typedef struct {
struct server {
void *plugin_slots;
array *config_context;
int config_captures;
struct fdevents *ev;
int (* network_backend_write)(int fd, chunkqueue *cq, off_t max_bytes, log_error_st *errh);

View File

@ -39,6 +39,13 @@ void config_get_config_cond_info(config_cond_info * const cfginfo, uint32_t idx)
cfginfo->comp_key = dc->comp_key;
}
int config_capture(server *srv, int idx) {
data_config * const dc = (data_config *)config_reference.data[idx];
return (dc->capture_idx)
? dc->capture_idx
: (dc->capture_idx = ++srv->config_captures);
}
int config_feature_bool (const server *srv, const char *feature, int default_value) {
return srv->srvconf.feature_flags
? config_plugin_value_tobool(
@ -543,7 +550,8 @@ static cond_result_t config_check_cond_nocache(request_st * const r, const data_
break;
case CONFIG_COND_NOMATCH:
case CONFIG_COND_MATCH: {
cond_match_t *cond_match = r->cond_match + dc->context_ndx;
cond_match_t * const cond_match =
r->cond_match[dc->capture_idx] = r->cond_match_data+dc->capture_idx;
match = (dc->cond == CONFIG_COND_MATCH);
match ^= (data_config_pcre_exec(dc, cache, l, cond_match) > 0);
break;
@ -596,8 +604,6 @@ static void config_cond_clear_node(cond_cache_t * const cond_cache, const data_c
/**
* reset the config-cache for a named item
*
* if the item is COND_LAST_ELEMENT we reset all items
*/
void config_cond_cache_reset_item(request_st * const r, comp_key_t item) {
cond_cache_t * const cond_cache = r->cond_cache;

View File

@ -1228,6 +1228,21 @@ int config_finalize(server *srv, const buffer *default_server_tag) {
return 0;
}
/* adjust cond_match_data list size if regex config conditions present */
if (srv->config_context->used > 1) {
const data_config *dcrx = NULL;
for (uint32_t i = 1; i < srv->config_context->used; ++i) {
const data_config * const dc =
(data_config *)srv->config_context->data[i];
if ((dc->cond==CONFIG_COND_MATCH || dc->cond==CONFIG_COND_NOMATCH)
&& 0 == dc->capture_idx) {
dcrx = dc;
break;
}
}
if (dcrx || srv->config_captures) ++srv->config_captures;
}
return 1;
}

View File

@ -37,6 +37,7 @@ struct data_config {
void *regex;
struct pcre_extra *regex_study;
#endif
int capture_idx;
int ext;
buffer comp_tag;
const char *comp_key;

View File

@ -2571,8 +2571,9 @@ h2_init_stream (request_st * const h2r, connection * const con)
r->conditional_is_valid = h2r->conditional_is_valid;
memcpy(r->cond_cache, h2r->cond_cache, used * sizeof(cond_cache_t));
#ifdef HAVE_PCRE_H
if (used > 1) /*(save 128b per con if no conditions)*/
memcpy(r->cond_match, h2r->cond_match, used * sizeof(cond_match_t));
if (srv->config_captures > 1)
memcpy(r->cond_match, h2r->cond_match,
srv->config_captures * sizeof(cond_match_t));
#endif
/*(see request_config_reset() and request_reset_ex())*/
r->server_name = h2r->server_name;

View File

@ -19,8 +19,9 @@ typedef struct pcre_keyvalue_ctx {
typedef struct {
struct pcre_keyvalue *kv;
uint32_t used;
uint16_t x0;
uint16_t x1;
int x0;
int x1;
int cfgidx;
} pcre_keyvalue_buffer;
__attribute_cold__

View File

@ -78,14 +78,23 @@ static void mod_redirect_patch_config(request_st * const r, plugin_data * const
static pcre_keyvalue_buffer * mod_redirect_parse_list(server *srv, const array *a, const int condidx) {
const int pcre_jit = config_feature_bool(srv, "server.pcre_jit", 1);
pcre_keyvalue_buffer * const kvb = pcre_keyvalue_buffer_init();
kvb->x0 = (unsigned short)condidx;
kvb->cfgidx = condidx;
buffer * const tb = srv->tmp_buf;
int percent = 0;
for (uint32_t j = 0; j < a->used; ++j) {
data_string *ds = (data_string *)a->data[j];
if (srv->srvconf.http_url_normalize) {
pcre_keyvalue_burl_normalize_key(&ds->key, tb);
pcre_keyvalue_burl_normalize_value(&ds->value, tb);
}
for (const char *s = ds->value.ptr; (s = strchr(s, '%')); ++s) {
if (s[1] == '%')
++s;
else if (light_isdigit(s[1]) || s[1] == '{') {
percent |= 1;
break;
}
}
if (!pcre_keyvalue_buffer_append(srv->errh, kvb, &ds->key, &ds->value,
pcre_jit)) {
log_error(srv->errh, __FILE__, __LINE__,
@ -94,6 +103,8 @@ static pcre_keyvalue_buffer * mod_redirect_parse_list(server *srv, const array *
return NULL;
}
}
if (percent)
kvb->x0 = config_capture(srv, condidx);
return kvb;
}
@ -157,10 +168,10 @@ URIHANDLER_FUNC(mod_redirect_uri_handler) {
if (!p->conf.redirect || !p->conf.redirect->used) return HANDLER_GO_ON;
ctx.cache = NULL;
if (p->conf.redirect->x0) { /*(p->conf.redirect->x0 is context_idx)*/
if (p->conf.redirect->x0) { /*(p->conf.redirect->x0 is capture_idx)*/
ctx.cond_match_count =
r->cond_cache[p->conf.redirect->x0].patterncount;
ctx.cache = r->cond_match + p->conf.redirect->x0;
r->cond_cache[p->conf.redirect->cfgidx].patterncount;
ctx.cache = r->cond_match[p->conf.redirect->x0];
}
ctx.burl = &burl;
burl.scheme = &r->uri.scheme;

View File

@ -99,16 +99,25 @@ static pcre_keyvalue_buffer * mod_rewrite_parse_list(server *srv, const array *a
if (NULL == kvb) {
allocated = 1;
kvb = pcre_keyvalue_buffer_init();
kvb->x0 = (unsigned short)condidx;
kvb->cfgidx = condidx;
}
buffer * const tb = srv->tmp_buf;
int percent = 0;
for (uint32_t j = 0; j < a->used; ++j) {
data_string *ds = (data_string *)a->data[j];
if (srv->srvconf.http_url_normalize) {
pcre_keyvalue_burl_normalize_key(&ds->key, tb);
pcre_keyvalue_burl_normalize_value(&ds->value, tb);
}
for (const char *s = ds->value.ptr; (s = strchr(s, '%')); ++s) {
if (s[1] == '%')
++s;
else if (light_isdigit(s[1]) || s[1] == '{') {
percent |= 1;
break;
}
}
if (!pcre_keyvalue_buffer_append(srv->errh, kvb, &ds->key, &ds->value,
pcre_jit)) {
log_error(srv->errh, __FILE__, __LINE__,
@ -117,6 +126,8 @@ static pcre_keyvalue_buffer * mod_rewrite_parse_list(server *srv, const array *a
return NULL;
}
}
if (percent && 0 == kvb->x0)
kvb->x0 = config_capture(srv, condidx);
return kvb;
}
@ -207,7 +218,7 @@ SETDEFAULTS_FUNC(mod_rewrite_set_defaults) {
kvb = cpv->v.v;
}
if (kvb) kvb->x1 = (unsigned short)kvb->used; /* repeat_idx */
if (kvb) kvb->x1 = (int)kvb->used; /* repeat_idx */
if ((cpv = rewrite_repeat)) {
cpv->v.v = mod_rewrite_parse_list(srv, cpv->v.a, kvb, condidx);
@ -223,7 +234,7 @@ SETDEFAULTS_FUNC(mod_rewrite_set_defaults) {
kvb_NF = cpv->v.v;
}
if (kvb_NF) kvb_NF->x1 = (unsigned short)kvb_NF->used; /* repeat_idx */
if (kvb_NF) kvb_NF->x1 = (int)kvb_NF->used; /* repeat_idx */
if ((cpv = rewrite_repeat_NF)) {
cpv->v.v = mod_rewrite_parse_list(srv, cpv->v.a, kvb_NF, condidx);
@ -243,6 +254,23 @@ SETDEFAULTS_FUNC(mod_rewrite_set_defaults) {
return HANDLER_GO_ON;
}
__attribute_cold__
static handler_t process_rewrite_rules_loop_error(request_st * const r, const int cfgidx) {
if (0 != cfgidx) {
config_cond_info cfginfo;
config_get_config_cond_info(&cfginfo, (uint32_t)cfgidx);
log_error(r->conf.errh, __FILE__, __LINE__,
"ENDLESS LOOP IN rewrite-rule DETECTED ... aborting request, "
"perhaps you want to use url.rewrite-once instead of "
"url.rewrite-repeat (%s)", cfginfo.comp_key);
}
else
log_error(r->conf.errh, __FILE__, __LINE__,
"ENDLESS LOOP IN rewrite-rule DETECTED ... aborting request");
return HANDLER_ERROR;
}
URIHANDLER_FUNC(mod_rewrite_con_reset) {
r->plugin_ctx[((plugin_data *)p_d)->id] = NULL;
return HANDLER_GO_ON;
@ -255,31 +283,17 @@ static handler_t process_rewrite_rules(request_st * const r, plugin_data *p, con
if (r->plugin_ctx[p->id]) {
uintptr_t * const hctx = (uintptr_t *)(r->plugin_ctx + p->id);
if (((++*hctx) & 0x1FF) > 100) {
if (0 != kvb->x0) {
config_cond_info cfginfo;
config_get_config_cond_info(&cfginfo, kvb->x0);
log_error(r->conf.errh, __FILE__, __LINE__,
"ENDLESS LOOP IN rewrite-rule DETECTED ... aborting request, "
"perhaps you want to use url.rewrite-once instead of "
"url.rewrite-repeat (%s)", cfginfo.comp_key);
return HANDLER_ERROR;
}
log_error(r->conf.errh, __FILE__, __LINE__,
"ENDLESS LOOP IN rewrite-rule DETECTED ... aborting request");
return HANDLER_ERROR;
return process_rewrite_rules_loop_error(r, kvb->cfgidx);
}
if (*hctx & REWRITE_STATE_FINISHED) return HANDLER_GO_ON;
}
ctx.cache = NULL;
if (kvb->x0) { /*(kvb->x0 is context_idx)*/
if (kvb->x0) { /*(kvb->x0 is capture_idx)*/
ctx.cond_match_count =
r->cond_cache[kvb->x0].patterncount;
ctx.cache = r->cond_match + kvb->x0;
r->cond_cache[kvb->cfgidx].patterncount;
ctx.cache = r->cond_match[kvb->x0];
}
ctx.burl = &burl;
burl.scheme = &r->uri.scheme;

View File

@ -49,6 +49,9 @@ typedef struct {
__attribute_cold__
void config_get_config_cond_info(config_cond_info *cfginfo, uint32_t idx);
__attribute_cold__
int config_capture(server *srv, int idx);
__attribute_cold__
void config_init(server *srv);

View File

@ -61,10 +61,11 @@ request_init_data (request_st * const r, connection * const con, server * const
force_assert(NULL != r->cond_cache);
#ifdef HAVE_PCRE_H
if (srv->config_context->used > 1) {/*(save 128b per con if no conditions)*/
r->cond_match =
calloc(srv->config_context->used, sizeof(cond_match_t));
if (srv->config_captures) {/*(save 128b per con if no regex conditions)*/
r->cond_match = calloc(srv->config_captures, sizeof(cond_match_t *));
force_assert(NULL != r->cond_match);
r->cond_match_data = calloc(srv->config_captures, sizeof(cond_match_t));
force_assert(NULL != r->cond_match_data);
}
#endif
@ -224,7 +225,10 @@ request_free_data (request_st * const r)
free(r->plugin_ctx);
free(r->cond_cache);
#ifdef HAVE_PCRE_H
free(r->cond_match);
free(r->cond_match_data);
#endif
/* note: r is not zeroed here and r is not freed here */
}

View File

@ -138,7 +138,8 @@ struct request_st {
/* config conditions (internal) */
uint32_t conditional_is_valid;
struct cond_cache_t *cond_cache;
struct cond_match_t *cond_match;
struct cond_match_t **cond_match;
struct cond_match_t *cond_match_data;
request_config conf;