Browse Source

[multiple] PCRE w/ PCRE_STUDY_JIT_COMPILE (fixes #2361)

enabled by default
disable using server.feature-flags += ("server.pcre_jit" => "disable")

Available since pcre-8.20 (2011), and improved in pcre-8.32 (2012),
PCRE_STUDY_JIT_COMPILE can greatly speed up repeated execution of PCRE
patterns.  (https://zherczeg.github.io/sljit/pcre.html)

lighttpd continues to use pcre_exec() instead of pcre_jit_exec(),
even though doing so does not realize all of the performance increase
potentially available with PCRE_STUDY_JIT_COMPILE and pcre_jit_exec().

pcre_jit_exec() is available with PCRE 8.32 and later, if PCRE is
compiled with --enable-jit, but lighttpd does not currently use
pcre_jit_exec() since the PCRE library might not have been compiled
with --enable-jit (though this could be solved with a weak symbol).
Similarly, lighttpd does not currently configure the pcre_jit_stack.

(Using pcre_jit_exec() may be revisited in the future.)

x-ref:
  "add support for pcre JIT"
  https://redmine.lighttpd.net/issues/2361
master
Glenn Strauss 9 months ago
parent
commit
7b9c5adda1
  1. 7
      src/configfile.c
  2. 2
      src/configfile.h
  3. 48
      src/data_config.c
  4. 17
      src/keyvalue.c
  5. 2
      src/keyvalue.h
  6. 19
      src/mod_redirect.c
  7. 8
      src/mod_rewrite.c
  8. 8
      src/t/test_keyvalue.c

7
src/configfile.c

@ -241,11 +241,16 @@ static void config_burl_normalize_cond (server * const srv) {
}
static int config_pcre_keyvalue (server * const srv) {
const int pcre_jit =
!srv->srvconf.feature_flags
|| config_plugin_value_tobool(
array_get_element_klen(srv->srvconf.feature_flags,
CONST_STR_LEN("server.pcre_jit")), 1);
for (uint32_t i = 0; i < srv->config_context->used; ++i) {
data_config * const dc = (data_config *)srv->config_context->data[i];
if (dc->cond != CONFIG_COND_NOMATCH && dc->cond != CONFIG_COND_MATCH)
continue;
if (!data_config_pcre_compile(dc))
if (!data_config_pcre_compile(dc, pcre_jit, srv->errh))
return 0;
}

2
src/configfile.h

@ -49,7 +49,7 @@ __attribute_cold__
data_config *data_config_init(void);
__attribute_cold__
int data_config_pcre_compile(data_config *dc);
int data_config_pcre_compile(data_config *dc, int pcre_jit, log_error_st *errh);
/*struct cond_cache_t;*/ /* declaration */ /*(moved to plugin_config.h)*/
/*int data_config_pcre_exec(const data_config *dc, struct cond_cache_t *cache, buffer *b);*/

48
src/data_config.c

@ -9,6 +9,10 @@
#ifdef HAVE_PCRE_H
#include <pcre.h>
#ifndef PCRE_STUDY_JIT_COMPILE
#define PCRE_STUDY_JIT_COMPILE 0
#define pcre_free_study(x) pcre_free(x)
#endif
#endif
__attribute_cold__
@ -39,7 +43,7 @@ static void data_config_free(data_unset *d) {
free(ds->string.ptr);
#ifdef HAVE_PCRE_H
if (ds->regex) pcre_free(ds->regex);
if (ds->regex_study) pcre_free(ds->regex_study);
if (ds->regex_study) pcre_free_study(ds->regex_study);
#endif
free(d);
@ -145,45 +149,51 @@ data_config *data_config_init(void) {
return ds;
}
int data_config_pcre_compile(data_config *dc) {
#include "log.h"
int data_config_pcre_compile(data_config * const dc, const int pcre_jit, log_error_st * const errh) {
#ifdef HAVE_PCRE_H
/* (use fprintf() on error, as this is called from configparser.y) */
const char *errptr;
int erroff, captures;
if (dc->regex) pcre_free(dc->regex);
if (dc->regex_study) pcre_free(dc->regex_study);
dc->regex = pcre_compile(dc->string.ptr, 0, &errptr, &erroff, NULL);
if (NULL == dc->regex) {
fprintf(stderr, "parsing regex failed: %s -> %s at offset %d\n",
dc->string.ptr, errptr, erroff);
log_error(errh, __FILE__, __LINE__,
"parsing regex failed: %s -> %s at offset %d\n",
dc->string.ptr, errptr, erroff);
return 0;
}
dc->regex_study = pcre_study(dc->regex, 0, &errptr);
const int study_options = pcre_jit ? PCRE_STUDY_JIT_COMPILE : 0;
dc->regex_study = pcre_study(dc->regex, study_options, &errptr);
if (NULL == dc->regex_study && errptr != NULL) {
fprintf(stderr, "studying regex failed: %s -> %s\n",
dc->string.ptr, errptr);
log_error(errh, __FILE__, __LINE__,
"studying regex failed: %s -> %s\n",
dc->string.ptr, errptr);
return 0;
}
erroff = pcre_fullinfo(dc->regex, dc->regex_study, PCRE_INFO_CAPTURECOUNT,
&captures);
if (0 != erroff) {
fprintf(stderr, "getting capture count for regex failed: %s\n",
dc->string.ptr);
log_error(errh, __FILE__, __LINE__,
"getting capture count for regex failed: %s\n",
dc->string.ptr);
return 0;
} else if (captures > 9) {
fprintf(stderr, "Too many captures in regex, use (?:...) instead of (...): %s\n",
dc->string.ptr);
}
else if (captures > 9) {
log_error(errh, __FILE__, __LINE__,
"Too many captures in regex, use (?:...) instead of (...): %s\n",
dc->string.ptr);
return 0;
}
return 1;
#else
fprintf(stderr, "can't handle '%s' as you compiled without pcre support. \n"
"(perhaps just a missing pcre-devel package ?) \n",
dc->comp_key);
UNUSED(pcre_jit);
log_error(errh, __FILE__, __LINE__,
"can't handle '%s' as you compiled without pcre support. \n"
"(perhaps just a missing pcre-devel package ?) \n",
dc->comp_key);
return 0;
#endif
}

17
src/keyvalue.c

@ -17,6 +17,10 @@
#ifdef HAVE_PCRE_H
#include <pcre.h>
#ifndef PCRE_STUDY_JIT_COMPILE
#define PCRE_STUDY_JIT_COMPILE 0
#define pcre_free_study(x) pcre_free(x)
#endif
#endif
typedef struct pcre_keyvalue {
@ -36,7 +40,7 @@ pcre_keyvalue_buffer *pcre_keyvalue_buffer_init(void) {
return kvb;
}
int pcre_keyvalue_buffer_append(log_error_st *errh, pcre_keyvalue_buffer *kvb, const buffer *key, const buffer *value) {
int pcre_keyvalue_buffer_append(log_error_st *errh, pcre_keyvalue_buffer *kvb, const buffer *key, const buffer *value, const int pcre_jit) {
#ifdef HAVE_PCRE_H
const char *errptr;
int erroff;
@ -62,8 +66,12 @@ int pcre_keyvalue_buffer_append(log_error_st *errh, pcre_keyvalue_buffer *kvb, c
return 0;
}
if (NULL == (kv->key_extra = pcre_study(kv->key, 0, &errptr)) &&
errptr != NULL) {
const int study_options = pcre_jit ? PCRE_STUDY_JIT_COMPILE : 0;
if (NULL == (kv->key_extra = pcre_study(kv->key, study_options, &errptr))
&& errptr != NULL) {
log_error(errh, __FILE__, __LINE__,
"studying regex failed: %s -> %s\n",
key->ptr, errptr);
return 0;
}
#else
@ -75,6 +83,7 @@ int pcre_keyvalue_buffer_append(log_error_st *errh, pcre_keyvalue_buffer *kvb, c
UNUSED(kvb);
UNUSED(key);
UNUSED(value);
UNUSED(pcre_jit);
#endif
return 1;
@ -85,7 +94,7 @@ void pcre_keyvalue_buffer_free(pcre_keyvalue_buffer *kvb) {
for (uint32_t i = 0; i < kvb->used; ++i) {
pcre_keyvalue * const kv = kvb->kv+i;
if (kv->key) pcre_free(kv->key);
if (kv->key_extra) pcre_free(kv->key_extra);
if (kv->key_extra) pcre_free_study(kv->key_extra);
/*free (kv->value.ptr);*//*(see pcre_keyvalue_buffer_append)*/
}

2
src/keyvalue.h

@ -27,7 +27,7 @@ __attribute_cold__
pcre_keyvalue_buffer *pcre_keyvalue_buffer_init(void);
__attribute_cold__
int pcre_keyvalue_buffer_append(log_error_st *errh, pcre_keyvalue_buffer *kvb, const buffer *key, const buffer *value);
int pcre_keyvalue_buffer_append(log_error_st *errh, pcre_keyvalue_buffer *kvb, const buffer *key, const buffer *value, int pcre_jit);
__attribute_cold__
void pcre_keyvalue_buffer_free(pcre_keyvalue_buffer *kvb);

19
src/mod_redirect.c

@ -76,9 +76,13 @@ 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) {
pcre_keyvalue_buffer * const redirect = pcre_keyvalue_buffer_init();
redirect->x0 = (unsigned short)condidx;
log_error_st * const errh = srv->errh;
const int pcre_jit =
!srv->srvconf.feature_flags
|| config_plugin_value_tobool(
array_get_element_klen(srv->srvconf.feature_flags,
CONST_STR_LEN("server.pcre_jit")), 1);
pcre_keyvalue_buffer * const kvb = pcre_keyvalue_buffer_init();
kvb->x0 = (unsigned short)condidx;
buffer * const tb = srv->tmp_buf;
for (uint32_t j = 0; j < a->used; ++j) {
data_string *ds = (data_string *)a->data[j];
@ -86,14 +90,15 @@ static pcre_keyvalue_buffer * mod_redirect_parse_list(server *srv, const array *
pcre_keyvalue_burl_normalize_key(&ds->key, tb);
pcre_keyvalue_burl_normalize_value(&ds->value, tb);
}
if (!pcre_keyvalue_buffer_append(errh, redirect, &ds->key, &ds->value)){
log_error(errh, __FILE__, __LINE__,
if (!pcre_keyvalue_buffer_append(srv->errh, kvb, &ds->key, &ds->value,
pcre_jit)) {
log_error(srv->errh, __FILE__, __LINE__,
"pcre-compile failed for %s", ds->key.ptr);
pcre_keyvalue_buffer_free(redirect);
pcre_keyvalue_buffer_free(kvb);
return NULL;
}
}
return redirect;
return kvb;
}
SETDEFAULTS_FUNC(mod_redirect_set_defaults) {

8
src/mod_rewrite.c

@ -94,6 +94,11 @@ static void mod_rewrite_patch_config(request_st * const r, plugin_data * const p
}
static pcre_keyvalue_buffer * mod_rewrite_parse_list(server *srv, const array *a, pcre_keyvalue_buffer *kvb, const int condidx) {
const int pcre_jit =
!srv->srvconf.feature_flags
|| config_plugin_value_tobool(
array_get_element_klen(srv->srvconf.feature_flags,
CONST_STR_LEN("server.pcre_jit")), 1);
int allocated = 0;
if (NULL == kvb) {
allocated = 1;
@ -108,7 +113,8 @@ static pcre_keyvalue_buffer * mod_rewrite_parse_list(server *srv, const array *a
pcre_keyvalue_burl_normalize_key(&ds->key, tb);
pcre_keyvalue_burl_normalize_value(&ds->value, tb);
}
if (!pcre_keyvalue_buffer_append(srv->errh, kvb, &ds->key, &ds->value)){
if (!pcre_keyvalue_buffer_append(srv->errh, kvb, &ds->key, &ds->value,
pcre_jit)) {
log_error(srv->errh, __FILE__, __LINE__,
"pcre-compile failed for %s", ds->key.ptr);
if (allocated) pcre_keyvalue_buffer_free(kvb);

8
src/t/test_keyvalue.c

@ -29,10 +29,10 @@ static pcre_keyvalue_buffer * test_keyvalue_test_kvb_init (void) {
{ "/?file=$1&$2", sizeof("/?file=$1&$2"), 0 }
};
assert(pcre_keyvalue_buffer_append(errh, kvb, kvstr+0, kvstr+1));
assert(pcre_keyvalue_buffer_append(errh, kvb, kvstr+2, kvstr+3));
assert(pcre_keyvalue_buffer_append(errh, kvb, kvstr+4, kvstr+5));
assert(pcre_keyvalue_buffer_append(errh, kvb, kvstr+6, kvstr+7));
assert(pcre_keyvalue_buffer_append(errh, kvb, kvstr+0, kvstr+1, 1));
assert(pcre_keyvalue_buffer_append(errh, kvb, kvstr+2, kvstr+3, 1));
assert(pcre_keyvalue_buffer_append(errh, kvb, kvstr+4, kvstr+5, 1));
assert(pcre_keyvalue_buffer_append(errh, kvb, kvstr+6, kvstr+7, 1));
log_error_st_free(errh);

Loading…
Cancel
Save