summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorAxel Duch <axel.duch@nginx.com>2020-11-17 15:03:30 +0000
committerAxel Duch <axel.duch@nginx.com>2020-11-17 15:03:30 +0000
commite3af18834d7cc32734cba7532d8864bb343b416b (patch)
tree578c68e31396926abff818cdaf6cb83116157511 /src
parent2a381a82a6e1bc2bd5d2f43a08fce50a1994f2e8 (diff)
downloadunit-e3af18834d7cc32734cba7532d8864bb343b416b.tar.gz
unit-e3af18834d7cc32734cba7532d8864bb343b416b.tar.bz2
Router: matching regular expressions support.
Diffstat (limited to 'src')
-rw-r--r--src/nxt_conf_validation.c35
-rw-r--r--src/nxt_http.h6
-rw-r--r--src/nxt_http_route.c99
-rw-r--r--src/nxt_pcre.c135
-rw-r--r--src/nxt_pcre2.c161
-rw-r--r--src/nxt_regex.h41
-rw-r--r--src/nxt_runtime.c4
7 files changed, 463 insertions, 18 deletions
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
}