[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
parent
04d387e37f
commit
7ea2d40734
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
5
src/h2.c
5
src/h2.c
|
@ -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;
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue