diff options
-rw-r--r-- | auto/help | 2 | ||||
-rw-r--r-- | auto/options | 10 | ||||
-rw-r--r-- | auto/pcre | 56 | ||||
-rw-r--r-- | auto/sources | 11 | ||||
-rwxr-xr-x | configure | 6 | ||||
-rw-r--r-- | src/nxt_conf_validation.c | 35 | ||||
-rw-r--r-- | src/nxt_http.h | 6 | ||||
-rw-r--r-- | src/nxt_http_route.c | 99 | ||||
-rw-r--r-- | src/nxt_pcre.c | 135 | ||||
-rw-r--r-- | src/nxt_pcre2.c | 161 | ||||
-rw-r--r-- | src/nxt_regex.h | 41 | ||||
-rw-r--r-- | src/nxt_runtime.c | 4 | ||||
-rw-r--r-- | test/test_routing.py | 42 |
13 files changed, 572 insertions, 36 deletions
@@ -35,6 +35,8 @@ cat << END --no-ipv6 disable IPv6 support --no-unix-sockets disable Unix domain sockets support + --no-regex disable regular expression support + --no-pcre2 force using PCRE library --openssl enable OpenSSL library usage diff --git a/auto/options b/auto/options index d315b227..b6007bc2 100644 --- a/auto/options +++ b/auto/options @@ -16,8 +16,11 @@ NXT_DEBUG=NO NXT_INET6=YES NXT_UNIX_DOMAIN=YES -NXT_REGEX=NO -NXT_PCRE=NO +NXT_PCRE_CFLAGS= +NXT_PCRE_LIB= + +NXT_REGEX=YES +NXT_TRY_PCRE2=YES NXT_TLS=NO NXT_OPENSSL=NO @@ -73,7 +76,8 @@ do --no-ipv6) NXT_INET6=NO ;; --no-unix-sockets) NXT_UNIX_DOMAIN=NO ;; - --pcre) NXT_PCRE=YES ;; + --no-regex) NXT_REGEX=NO ;; + --no-pcre2) NXT_TRY_PCRE2=NO ;; --openssl) NXT_OPENSSL=YES ;; --gnutls) NXT_GNUTLS=YES ;; @@ -3,15 +3,41 @@ # Copyright (C) NGINX, Inc. -NXT_REGEX=NO -NXT_PCRE_CFLAGS= -NXT_PCRE_LIB= +nxt_found=no +NXT_HAVE_PCRE2=NO +if [ $NXT_TRY_PCRE2 = YES ]; then + if /bin/sh -c "(pcre2-config --version)" >> $NXT_AUTOCONF_ERR 2>&1; then -if [ $NXT_PCRE = YES ]; then + NXT_PCRE_CFLAGS=`pcre2-config --cflags` + NXT_PCRE_LIB=`pcre2-config --libs8` - nxt_found=no + nxt_feature="PCRE2 library" + nxt_feature_name=NXT_HAVE_PCRE2 + nxt_feature_run=no + nxt_feature_incs="-DPCRE2_CODE_UNIT_WIDTH=8 $NXT_PCRE_CFLAGS" + nxt_feature_libs=$NXT_PCRE_LIB + nxt_feature_test="#include <pcre2.h> + + int main(void) { + pcre2_code *re; + + re = pcre2_compile((PCRE2_SPTR)\"\", + PCRE2_ZERO_TERMINATED, 0, + NULL, NULL, NULL); + return (re == NULL); + }" + + . auto/feature + + if [ $nxt_found = yes ]; then + NXT_HAVE_PCRE2=YES + $echo " + PCRE2 version: `pcre2-config --version`" + fi + fi +fi +if [ $nxt_found = no ]; then if /bin/sh -c "(pcre-config --version)" >> $NXT_AUTOCONF_ERR 2>&1; then NXT_PCRE_CFLAGS=`pcre-config --cflags` @@ -33,17 +59,19 @@ if [ $NXT_PCRE = YES ]; then return 0; }" . auto/feature - fi - if [ $nxt_found = no ]; then - $echo - $echo $0: error: no PCRE library found. - $echo - exit 1; + if [ $nxt_found = yes ]; then + $echo " + PCRE version: `pcre-config --version`" + fi fi +fi - NXT_REGEX=YES - nxt_have=NXT_REGEX . auto/have +if [ $nxt_found = yes ]; then + nxt_have=NXT_HAVE_REGEX . auto/have - $echo " + PCRE version: `pcre-config --version`" +else + $echo + $echo $0: error: no PCRE library found. + $echo + exit 1; fi diff --git a/auto/sources b/auto/sources index e44dc4bb..01fec6c1 100644 --- a/auto/sources +++ b/auto/sources @@ -128,6 +128,9 @@ NXT_LIB_GNUTLS_SRCS="src/nxt_gnutls.c" NXT_LIB_CYASSL_SRCS="src/nxt_cyassl.c" NXT_LIB_POLARSSL_SRCS="src/nxt_polarssl.c" +NXT_LIB_PCRE_SRCS="src/nxt_pcre.c" +NXT_LIB_PCRE2_SRCS="src/nxt_pcre2.c" + NXT_LIB_EPOLL_SRCS="src/nxt_epoll_engine.c" NXT_LIB_KQUEUE_SRCS="src/nxt_kqueue_engine.c" NXT_LIB_EVENTPORT_SRCS="src/nxt_eventport_engine.c" @@ -211,6 +214,14 @@ if [ $NXT_POLARSSL = YES ]; then fi +if [ "$NXT_REGEX" = "YES" ]; then + if [ "$NXT_HAVE_PCRE2" = "YES" ]; then + NXT_LIB_SRCS="$NXT_LIB_SRCS $NXT_LIB_PCRE2_SRCS" + else + NXT_LIB_SRCS="$NXT_LIB_SRCS $NXT_LIB_PCRE_SRCS" + fi +fi + if [ "$NXT_HAVE_EPOLL" = "YES" -o "$NXT_TEST_BUILD_EPOLL" = "YES" ]; then NXT_LIB_SRCS="$NXT_LIB_SRCS $NXT_LIB_EPOLL_SRCS" fi @@ -127,7 +127,11 @@ NXT_LIBRT= . auto/unix . auto/os/conf . auto/ssltls -. auto/pcre + +if [ $NXT_REGEX = YES ]; then + . auto/pcre +fi + . auto/isolation . auto/capability diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index f4e7c358..acb2e3de 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -11,6 +11,7 @@ #include <nxt_http.h> #include <nxt_sockaddr.h> #include <nxt_http_route_addr.h> +#include <nxt_regex.h> typedef enum { @@ -1504,8 +1505,12 @@ static nxt_int_t nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) { - nxt_str_t pattern; - nxt_uint_t i, first, last; + nxt_str_t pattern; + nxt_uint_t i, first, last; +#if (NXT_HAVE_REGEX) + nxt_regex_t *re; + nxt_regex_err_t err; +#endif if (nxt_conf_type(value) != NXT_CONF_STRING) { return nxt_conf_vldt_error(vldt, "The \"match\" patterns for \"host\", " @@ -1519,6 +1524,32 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt, } first = (pattern.start[0] == '!'); + + if (first < pattern.length && pattern.start[first] == '~') { +#if (NXT_HAVE_REGEX) + pattern.start += first + 1; + pattern.length -= first + 1; + + re = nxt_regex_compile(vldt->pool, &pattern, &err); + if (nxt_slow_path(re == NULL)) { + if (err.offset < pattern.length) { + return nxt_conf_vldt_error(vldt, "Invalid regular expression: " + "%s at offset %d", + err.msg, err.offset); + } + + return nxt_conf_vldt_error(vldt, "Invalid regular expression: %s", + err.msg); + } + + return NXT_OK; +#else + return nxt_conf_vldt_error(vldt, "Unit is built without support of " + "regular expressions: \"--no-regex\" " + "./configure option was set."); +#endif + } + last = pattern.length - 1; for (i = first; i < last; i++) { diff --git a/src/nxt_http.h b/src/nxt_http.h index 08181520..1418be95 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -7,6 +7,8 @@ #ifndef _NXT_HTTP_H_INCLUDED_ #define _NXT_HTTP_H_INCLUDED_ +#include <nxt_regex.h> + typedef enum { NXT_HTTP_UNSET = -1, @@ -168,6 +170,10 @@ struct nxt_http_request_s { void *req_rpc_data; +#if (NXT_HAVE_REGEX) + nxt_regex_match_t *regex_match; +#endif + nxt_http_peer_t *peer; nxt_buf_t *last; diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index ae91076a..9aaa708e 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -8,6 +8,7 @@ #include <nxt_http.h> #include <nxt_sockaddr.h> #include <nxt_http_route_addr.h> +#include <nxt_regex.h> typedef enum { @@ -76,12 +77,20 @@ typedef struct { typedef struct { + union { + nxt_array_t *pattern_slices; +#if (NXT_HAVE_REGEX) + nxt_regex_t *regex; +#endif + } u; uint32_t min_length; - nxt_array_t *pattern_slices; uint8_t case_sensitive; /* 1 bit */ uint8_t negative; /* 1 bit */ uint8_t any; /* 1 bit */ +#if (NXT_HAVE_REGEX) + uint8_t regex; /* 1 bit */ +#endif } nxt_http_route_pattern_t; @@ -1060,24 +1069,24 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, nxt_str_t test, tmp; nxt_int_t ret; nxt_array_t *slices; +#if (NXT_HAVE_REGEX) + nxt_regex_t *re; + nxt_regex_err_t err; +#endif nxt_http_route_pattern_type_t type; - nxt_http_route_pattern_slice_t *slice; type = NXT_HTTP_ROUTE_PATTERN_EXACT; nxt_conf_get_string(cv, &test); - slices = nxt_array_create(mp, 1, sizeof(nxt_http_route_pattern_slice_t)); - if (nxt_slow_path(slices == NULL)) { - return NXT_ERROR; - } - - pattern->pattern_slices = slices; - + pattern->u.pattern_slices = NULL; pattern->negative = 0; pattern->any = 1; pattern->min_length = 0; +#if (NXT_HAVE_REGEX) + pattern->regex = 0; +#endif if (test.length != 0 && test.start[0] == '!') { test.start++; @@ -1087,6 +1096,41 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, pattern->any = 0; } + if (test.length > 0 && test.start[0] == '~') { +#if (NXT_HAVE_REGEX) + test.start++; + test.length--; + + re = nxt_regex_compile(mp, &test, &err); + if (nxt_slow_path(re == NULL)) { + if (err.offset < test.length) { + nxt_alert(task, "nxt_regex_compile(%V) failed: %s at offset %d", + &test, err.msg, (int) err.offset); + return NXT_ERROR; + } + + nxt_alert(task, "nxt_regex_compile(%V) failed %s", &test, err.msg); + + return NXT_ERROR; + } + + pattern->u.regex = re; + pattern->regex = 1; + + return NXT_OK; + +#else + return NXT_ERROR; +#endif + } + + slices = nxt_array_create(mp, 1, sizeof(nxt_http_route_pattern_slice_t)); + if (nxt_slow_path(slices == NULL)) { + return NXT_ERROR; + } + + pattern->u.pattern_slices = slices; + if (test.length == 0) { slice = nxt_array_add(slices); if (nxt_slow_path(slice == NULL)) { @@ -1980,6 +2024,9 @@ nxt_http_route_header(nxt_http_request_t *r, nxt_http_route_rule_t *rule) } ret = nxt_http_route_test_rule(r, rule, f->value, f->value_length); + if (nxt_slow_path(ret == NXT_ERROR)) { + return NXT_ERROR; + } if (ret == 0) { return ret; @@ -2155,7 +2202,7 @@ static nxt_int_t nxt_http_route_test_argument(nxt_http_request_t *r, nxt_http_route_rule_t *rule, nxt_array_t *array) { - nxt_bool_t ret; + nxt_int_t ret; nxt_http_name_value_t *nv, *end; ret = 0; @@ -2171,6 +2218,10 @@ nxt_http_route_test_argument(nxt_http_request_t *r, { ret = nxt_http_route_test_rule(r, rule, nv->value, nv->value_length); + if (nxt_slow_path(ret == NXT_ERROR)) { + return NXT_ERROR; + } + if (ret == 0) { break; } @@ -2189,7 +2240,7 @@ nxt_http_route_scheme(nxt_http_request_t *r, nxt_http_route_rule_t *rule) nxt_bool_t tls, https; nxt_http_route_pattern_slice_t *pattern_slice; - pattern_slice = rule->pattern[0].pattern_slices->elts; + pattern_slice = rule->pattern[0].u.pattern_slices->elts; https = (pattern_slice->length == nxt_length("https")); tls = (r->tls != NULL); @@ -2337,7 +2388,7 @@ static nxt_int_t nxt_http_route_test_cookie(nxt_http_request_t *r, nxt_http_route_rule_t *rule, nxt_array_t *array) { - nxt_bool_t ret; + nxt_int_t ret; nxt_http_name_value_t *nv, *end; ret = 0; @@ -2353,6 +2404,10 @@ nxt_http_route_test_cookie(nxt_http_request_t *r, { ret = nxt_http_route_test_rule(r, rule, nv->value, nv->value_length); + if (nxt_slow_path(ret == NXT_ERROR)) { + return NXT_ERROR; + } + if (ret == 0) { break; } @@ -2378,6 +2433,9 @@ nxt_http_route_test_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule, while (pattern < end) { ret = nxt_http_route_pattern(r, pattern, start, length); + if (nxt_slow_path(ret == NXT_ERROR)) { + return NXT_ERROR; + } /* nxt_http_route_pattern() returns either 1 or 0. */ ret ^= pattern->negative; @@ -2403,11 +2461,26 @@ nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern, nxt_array_t *pattern_slices; nxt_http_route_pattern_slice_t *pattern_slice; +#if (NXT_HAVE_REGEX) + if (pattern->regex) { + if (r->regex_match == NULL) { + r->regex_match = nxt_regex_match_create(r->mem_pool, 0); + if (nxt_slow_path(r->regex_match == NULL)) { + return NXT_ERROR; + } + } + + return nxt_regex_match(pattern->u.regex, start, length, r->regex_match); + } +#endif + if (length < pattern->min_length) { return 0; } - pattern_slices = pattern->pattern_slices; + nxt_assert(pattern->u.pattern_slices != NULL); + + pattern_slices = pattern->u.pattern_slices; pattern_slice = pattern_slices->elts; end = start + length; diff --git a/src/nxt_pcre.c b/src/nxt_pcre.c new file mode 100644 index 00000000..737fc7cf --- /dev/null +++ b/src/nxt_pcre.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) Axel Duch + * Copyright (C) NGINX, Inc. + */ + +#include <nxt_main.h> +#include <nxt_regex.h> +#include <pcre.h> + + +struct nxt_regex_s { + pcre *code; + pcre_extra *extra; + nxt_str_t pattern; +}; + +struct nxt_regex_match_s { + int ovecsize; + int ovec[]; +}; + + +static void *nxt_pcre_malloc(size_t size); +static void nxt_pcre_free(void *p); + +static nxt_mp_t *nxt_pcre_mp; + + +nxt_regex_t * +nxt_regex_compile(nxt_mp_t *mp, nxt_str_t *source, nxt_regex_err_t *err) +{ + int erroffset; + char *pattern; + void *saved_malloc, *saved_free; + nxt_regex_t *re; + + err->offset = source->length; + + re = nxt_mp_get(mp, sizeof(nxt_regex_t) + source->length + 1); + if (nxt_slow_path(re == NULL)) { + err->msg = "memory allocation failed"; + return NULL; + } + + pattern = nxt_pointer_to(re, sizeof(nxt_regex_t)); + + nxt_memcpy(pattern, source->start, source->length); + pattern[source->length] = '\0'; + + re->pattern.length = source->length; + re->pattern.start = (u_char *) pattern; + + saved_malloc = pcre_malloc; + saved_free = pcre_free; + + pcre_malloc = nxt_pcre_malloc; + pcre_free = nxt_pcre_free; + nxt_pcre_mp = mp; + + re->code = pcre_compile(pattern, 0, &err->msg, &erroffset, NULL); + if (nxt_fast_path(re->code != NULL)) { +#if 0 + re->extra = pcre_study(re->code, PCRE_STUDY_JIT_COMPILE, &err->msg); + if (nxt_slow_path(re->extra == NULL && err->msg != NULL)) { + nxt_log_warn(thr->log, "pcre_study(%V) failed: %s", source, err->msg); + } +#else + re->extra = NULL; +#endif + + } else { + err->offset = erroffset; + re = NULL; + } + + pcre_malloc = saved_malloc; + pcre_free = saved_free; + + return re; +} + + +static void* +nxt_pcre_malloc(size_t size) +{ + if (nxt_slow_path(nxt_pcre_mp == NULL)) { + nxt_thread_log_alert("pcre_malloc(%uz) called without memory pool", + size); + return NULL; + } + + nxt_thread_log_debug("pcre_malloc(%uz), pool %p", size, nxt_pcre_mp); + + return nxt_mp_get(nxt_pcre_mp, size); +} + + +static void +nxt_pcre_free(void *p) +{ +} + + +nxt_regex_match_t * +nxt_regex_match_create(nxt_mp_t *mp, size_t size) +{ + nxt_regex_match_t *match; + + match = nxt_mp_get(mp, sizeof(nxt_regex_match_t) + sizeof(int) * size); + if (nxt_fast_path(match != NULL)) { + match->ovecsize = size; + } + + return match; +} + + +nxt_int_t +nxt_regex_match(nxt_regex_t *re, u_char *subject, size_t length, + nxt_regex_match_t *match) +{ + int ret; + + ret = pcre_exec(re->code, re->extra, (const char *) subject, length, 0, 0, + match->ovec, match->ovecsize); + if (nxt_slow_path(ret < PCRE_ERROR_NOMATCH)) { + nxt_thread_log_error(NXT_LOG_ERR, + "pcre_exec() failed: %d on \"%*s\" using \"%V\"", + ret, length, subject, &re->pattern); + + return NXT_ERROR; + } + + return (ret != PCRE_ERROR_NOMATCH); +} diff --git a/src/nxt_pcre2.c b/src/nxt_pcre2.c new file mode 100644 index 00000000..22c4d2d4 --- /dev/null +++ b/src/nxt_pcre2.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) Axel Duch + * Copyright (C) NGINX, Inc. + */ + +#include <nxt_main.h> +#include <nxt_regex.h> + +#define PCRE2_CODE_UNIT_WIDTH 8 +#include <pcre2.h> + + +static void *nxt_pcre2_malloc(PCRE2_SIZE size, void *memory_data); +static void nxt_pcre2_free(void *p, void *memory_data); + + +struct nxt_regex_s { + pcre2_code *code; + nxt_str_t pattern; +}; + + +nxt_regex_t * +nxt_regex_compile(nxt_mp_t *mp, nxt_str_t *source, nxt_regex_err_t *err) +{ + int errcode; + nxt_int_t ret; + PCRE2_SIZE erroffset; + nxt_regex_t *re; + pcre2_general_context *general_ctx; + pcre2_compile_context *compile_ctx; + + static const u_char alloc_error[] = "memory allocation failed"; + + general_ctx = pcre2_general_context_create(nxt_pcre2_malloc, + nxt_pcre2_free, mp); + if (nxt_slow_path(general_ctx == NULL)) { + goto alloc_fail; + } + + compile_ctx = pcre2_compile_context_create(general_ctx); + if (nxt_slow_path(compile_ctx == NULL)) { + goto alloc_fail; + } + + re = nxt_mp_get(mp, sizeof(nxt_regex_t)); + if (nxt_slow_path(re == NULL)) { + goto alloc_fail; + } + + if (nxt_slow_path(nxt_str_dup(mp, &re->pattern, source) == NULL)) { + goto alloc_fail; + } + + re->code = pcre2_compile((PCRE2_SPTR) source->start, source->length, 0, + &errcode, &erroffset, compile_ctx); + if (nxt_slow_path(re->code == NULL)) { + err->offset = erroffset; + + ret = pcre2_get_error_message(errcode, (PCRE2_UCHAR *) err->msg, + ERR_BUF_SIZE); + if (ret < 0) { + (void) nxt_sprintf(err->msg, err->msg + ERR_BUF_SIZE, + "compilation failed with unknown " + "error code: %d%Z", errcode); + } + + return NULL; + } + +#if 0 + errcode = pcre2_jit_compile(re, PCRE2_JIT_COMPLETE); + if (nxt_slow_path(errcode != 0 && errcode != PCRE2_ERROR_JIT_BADOPTION)) { + ret = pcre2_get_error_message(errcode, (PCRE2_UCHAR *) err->msg, + ERR_BUF_SIZE); + if (ret < 0) { + (void) nxt_sprintf(err->msg, err->msg + ERR_BUF_SIZE, + "JIT compilation failed with unknown " + "error code: %d%Z", errcode); + } + + return NULL; + } +#endif + + return re; + +alloc_fail: + + err->offset = source->length; + nxt_memcpy(err->msg, alloc_error, sizeof(alloc_error)); + + return NULL; +} + + +static void * +nxt_pcre2_malloc(PCRE2_SIZE size, void *mp) +{ + return nxt_mp_get(mp, size); +} + + +static void +nxt_pcre2_free(void *p, void *mp) +{ +} + + +nxt_regex_match_t * +nxt_regex_match_create(nxt_mp_t *mp, size_t size) +{ + nxt_regex_match_t *match; + pcre2_general_context *ctx; + + ctx = pcre2_general_context_create(nxt_pcre2_malloc, nxt_pcre2_free, mp); + if (nxt_slow_path(ctx == NULL)) { + nxt_thread_log_alert("pcre2_general_context_create() failed"); + return NULL; + } + + match = pcre2_match_data_create(size, ctx); + if (nxt_slow_path(match == NULL)) { + nxt_thread_log_alert("pcre2_match_data_create(%uz) failed", size); + return NULL; + } + + return match; +} + + +nxt_int_t +nxt_regex_match(nxt_regex_t *re, u_char *subject, size_t length, + nxt_regex_match_t *match) +{ + nxt_int_t ret; + PCRE2_UCHAR errptr[ERR_BUF_SIZE]; + + ret = pcre2_match(re->code, (PCRE2_SPTR) subject, length, 0, 0, match, + NULL); + + if (nxt_slow_path(ret < PCRE2_ERROR_NOMATCH)) { + + if (pcre2_get_error_message(ret, errptr, ERR_BUF_SIZE) < 0) { + nxt_thread_log_error(NXT_LOG_ERR, + "pcre2_match() failed: %d on \"%*s\" " + "using \"%V\"", ret, subject, length, subject, + &re->pattern); + + } else { + nxt_thread_log_error(NXT_LOG_ERR, + "pcre2_match() failed: %s (%d) on \"%*s\" " + "using \"%V\"", errptr, ret, length, subject, + &re->pattern); + } + + return NXT_ERROR; + } + + return (ret != PCRE2_ERROR_NOMATCH); +} diff --git a/src/nxt_regex.h b/src/nxt_regex.h new file mode 100644 index 00000000..a24c50f8 --- /dev/null +++ b/src/nxt_regex.h @@ -0,0 +1,41 @@ + +/* + * Copyright (C) Axel Duch + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_REGEX_H_INCLUDED_ +#define _NXT_REGEX_H_INCLUDED_ + +#if (NXT_HAVE_REGEX) + +typedef struct nxt_regex_s nxt_regex_t; + + #if (NXT_HAVE_PCRE2) +typedef void nxt_regex_match_t; +#else +typedef struct nxt_regex_match_s nxt_regex_match_t; +#endif + +typedef struct { + size_t offset; + +#if (NXT_HAVE_PCRE2) +#define ERR_BUF_SIZE 256 + u_char msg[ERR_BUF_SIZE]; +#else + const char *msg; +#endif +} nxt_regex_err_t; + + +NXT_EXPORT void nxt_regex_init(void); +NXT_EXPORT nxt_regex_t *nxt_regex_compile(nxt_mp_t *mp, nxt_str_t *source, + nxt_regex_err_t *err); +NXT_EXPORT nxt_regex_match_t *nxt_regex_match_create(nxt_mp_t *mp, size_t size); +NXT_EXPORT nxt_int_t nxt_regex_match(nxt_regex_t *re, u_char *subject, + size_t length, nxt_regex_match_t *match); + +#endif /* NXT_HAVE_REGEX */ + +#endif /* _NXT_REGEX_H_INCLUDED_ */ diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c index 44970b34..d9d986da 100644 --- a/src/nxt_runtime.c +++ b/src/nxt_runtime.c @@ -10,6 +10,7 @@ #include <nxt_port.h> #include <nxt_main_process.h> #include <nxt_router.h> +#include <nxt_regex.h> static nxt_int_t nxt_runtime_inherited_listen_sockets(nxt_task_t *task, @@ -702,9 +703,6 @@ nxt_runtime_thread_pool_destroy(nxt_task_t *task, nxt_runtime_t *rt, static void nxt_runtime_thread_pool_init(void) { -#if (NXT_REGEX) - nxt_regex_init(0); -#endif } diff --git a/test/test_routing.py b/test/test_routing.py index 9e0f52dc..83852273 100644 --- a/test/test_routing.py +++ b/test/test_routing.py @@ -230,6 +230,48 @@ class TestRouting(TestApplicationProto): assert self.get(url='/aBCaBbc')['status'] == 200 assert self.get(url='/ABc')['status'] == 404 + def test_routes_empty_regex(self): + self.route_match({"uri":"~"}) + assert self.get(url='/')['status'] == 200, 'empty regexp' + assert self.get(url='/anything')['status'] == 200, '/anything' + + self.route_match({"uri":"!~"}) + assert self.get(url='/')['status'] == 404, 'empty regexp 2' + assert self.get(url='/nothing')['status'] == 404, '/nothing' + + def test_routes_bad_regex(self): + assert 'error' in self.route( + {"match": {"uri": "~/bl[ah"}, "action": {"return": 200}} + ), 'bad regex' + + status = self.route( + {"match": {"uri": "~(?R)?z"}, "action": {"return": 200}} + ) + if 'error' not in status: + assert self.get(url='/nothing_z')['status'] == 500, '/nothing_z' + + status = self.route( + {"match": {"uri": "~((?1)?z)"}, "action": {"return": 200}} + ) + if 'error' not in status: + assert self.get(url='/nothing_z')['status'] == 500, '/nothing_z' + + def test_routes_match_regex_case_sensitive(self): + self.route_match({"uri": "~/bl[ah]"}) + + assert self.get(url='/rlah')['status'] == 404, '/rlah' + assert self.get(url='/blah')['status'] == 200, '/blah' + assert self.get(url='/blh')['status'] == 200, '/blh' + assert self.get(url='/BLAH')['status'] == 404, '/BLAH' + + def test_routes_match_regex_negative_case_sensitive(self): + self.route_match({"uri": "!~/bl[ah]"}) + + assert self.get(url='/rlah')['status'] == 200, '/rlah' + assert self.get(url='/blah')['status'] == 404, '/blah' + assert self.get(url='/blh')['status'] == 404, '/blh' + assert self.get(url='/BLAH')['status'] == 200, '/BLAH' + def test_routes_pass_encode(self): def check_pass(path, name): assert 'success' in self.conf( |