Router: matching regular expressions support.
This commit is contained in:
@@ -35,6 +35,8 @@ cat << END
|
|||||||
|
|
||||||
--no-ipv6 disable IPv6 support
|
--no-ipv6 disable IPv6 support
|
||||||
--no-unix-sockets disable Unix domain sockets 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
|
--openssl enable OpenSSL library usage
|
||||||
|
|
||||||
|
|||||||
10
auto/options
10
auto/options
@@ -16,8 +16,11 @@ NXT_DEBUG=NO
|
|||||||
NXT_INET6=YES
|
NXT_INET6=YES
|
||||||
NXT_UNIX_DOMAIN=YES
|
NXT_UNIX_DOMAIN=YES
|
||||||
|
|
||||||
NXT_REGEX=NO
|
NXT_PCRE_CFLAGS=
|
||||||
NXT_PCRE=NO
|
NXT_PCRE_LIB=
|
||||||
|
|
||||||
|
NXT_REGEX=YES
|
||||||
|
NXT_TRY_PCRE2=YES
|
||||||
|
|
||||||
NXT_TLS=NO
|
NXT_TLS=NO
|
||||||
NXT_OPENSSL=NO
|
NXT_OPENSSL=NO
|
||||||
@@ -73,7 +76,8 @@ do
|
|||||||
--no-ipv6) NXT_INET6=NO ;;
|
--no-ipv6) NXT_INET6=NO ;;
|
||||||
--no-unix-sockets) NXT_UNIX_DOMAIN=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 ;;
|
--openssl) NXT_OPENSSL=YES ;;
|
||||||
--gnutls) NXT_GNUTLS=YES ;;
|
--gnutls) NXT_GNUTLS=YES ;;
|
||||||
|
|||||||
54
auto/pcre
54
auto/pcre
@@ -3,15 +3,41 @@
|
|||||||
# Copyright (C) NGINX, Inc.
|
# Copyright (C) NGINX, Inc.
|
||||||
|
|
||||||
|
|
||||||
NXT_REGEX=NO
|
nxt_found=no
|
||||||
NXT_PCRE_CFLAGS=
|
NXT_HAVE_PCRE2=NO
|
||||||
NXT_PCRE_LIB=
|
|
||||||
|
|
||||||
|
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
|
if /bin/sh -c "(pcre-config --version)" >> $NXT_AUTOCONF_ERR 2>&1; then
|
||||||
|
|
||||||
NXT_PCRE_CFLAGS=`pcre-config --cflags`
|
NXT_PCRE_CFLAGS=`pcre-config --cflags`
|
||||||
@@ -33,17 +59,19 @@ if [ $NXT_PCRE = YES ]; then
|
|||||||
return 0;
|
return 0;
|
||||||
}"
|
}"
|
||||||
. auto/feature
|
. auto/feature
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $nxt_found = no ]; then
|
if [ $nxt_found = yes ]; then
|
||||||
|
$echo " + PCRE version: `pcre-config --version`"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $nxt_found = yes ]; then
|
||||||
|
nxt_have=NXT_HAVE_REGEX . auto/have
|
||||||
|
|
||||||
|
else
|
||||||
$echo
|
$echo
|
||||||
$echo $0: error: no PCRE library found.
|
$echo $0: error: no PCRE library found.
|
||||||
$echo
|
$echo
|
||||||
exit 1;
|
exit 1;
|
||||||
fi
|
|
||||||
|
|
||||||
NXT_REGEX=YES
|
|
||||||
nxt_have=NXT_REGEX . auto/have
|
|
||||||
|
|
||||||
$echo " + PCRE version: `pcre-config --version`"
|
|
||||||
fi
|
fi
|
||||||
|
|||||||
11
auto/sources
11
auto/sources
@@ -128,6 +128,9 @@ NXT_LIB_GNUTLS_SRCS="src/nxt_gnutls.c"
|
|||||||
NXT_LIB_CYASSL_SRCS="src/nxt_cyassl.c"
|
NXT_LIB_CYASSL_SRCS="src/nxt_cyassl.c"
|
||||||
NXT_LIB_POLARSSL_SRCS="src/nxt_polarssl.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_EPOLL_SRCS="src/nxt_epoll_engine.c"
|
||||||
NXT_LIB_KQUEUE_SRCS="src/nxt_kqueue_engine.c"
|
NXT_LIB_KQUEUE_SRCS="src/nxt_kqueue_engine.c"
|
||||||
NXT_LIB_EVENTPORT_SRCS="src/nxt_eventport_engine.c"
|
NXT_LIB_EVENTPORT_SRCS="src/nxt_eventport_engine.c"
|
||||||
@@ -211,6 +214,14 @@ if [ $NXT_POLARSSL = YES ]; then
|
|||||||
fi
|
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
|
if [ "$NXT_HAVE_EPOLL" = "YES" -o "$NXT_TEST_BUILD_EPOLL" = "YES" ]; then
|
||||||
NXT_LIB_SRCS="$NXT_LIB_SRCS $NXT_LIB_EPOLL_SRCS"
|
NXT_LIB_SRCS="$NXT_LIB_SRCS $NXT_LIB_EPOLL_SRCS"
|
||||||
fi
|
fi
|
||||||
|
|||||||
6
configure
vendored
6
configure
vendored
@@ -127,7 +127,11 @@ NXT_LIBRT=
|
|||||||
. auto/unix
|
. auto/unix
|
||||||
. auto/os/conf
|
. auto/os/conf
|
||||||
. auto/ssltls
|
. auto/ssltls
|
||||||
. auto/pcre
|
|
||||||
|
if [ $NXT_REGEX = YES ]; then
|
||||||
|
. auto/pcre
|
||||||
|
fi
|
||||||
|
|
||||||
. auto/isolation
|
. auto/isolation
|
||||||
. auto/capability
|
. auto/capability
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <nxt_http.h>
|
#include <nxt_http.h>
|
||||||
#include <nxt_sockaddr.h>
|
#include <nxt_sockaddr.h>
|
||||||
#include <nxt_http_route_addr.h>
|
#include <nxt_http_route_addr.h>
|
||||||
|
#include <nxt_regex.h>
|
||||||
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -1506,6 +1507,10 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt,
|
|||||||
{
|
{
|
||||||
nxt_str_t pattern;
|
nxt_str_t pattern;
|
||||||
nxt_uint_t i, first, last;
|
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) {
|
if (nxt_conf_type(value) != NXT_CONF_STRING) {
|
||||||
return nxt_conf_vldt_error(vldt, "The \"match\" patterns for \"host\", "
|
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] == '!');
|
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;
|
last = pattern.length - 1;
|
||||||
|
|
||||||
for (i = first; i < last; i++) {
|
for (i = first; i < last; i++) {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#ifndef _NXT_HTTP_H_INCLUDED_
|
#ifndef _NXT_HTTP_H_INCLUDED_
|
||||||
#define _NXT_HTTP_H_INCLUDED_
|
#define _NXT_HTTP_H_INCLUDED_
|
||||||
|
|
||||||
|
#include <nxt_regex.h>
|
||||||
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
NXT_HTTP_UNSET = -1,
|
NXT_HTTP_UNSET = -1,
|
||||||
@@ -168,6 +170,10 @@ struct nxt_http_request_s {
|
|||||||
|
|
||||||
void *req_rpc_data;
|
void *req_rpc_data;
|
||||||
|
|
||||||
|
#if (NXT_HAVE_REGEX)
|
||||||
|
nxt_regex_match_t *regex_match;
|
||||||
|
#endif
|
||||||
|
|
||||||
nxt_http_peer_t *peer;
|
nxt_http_peer_t *peer;
|
||||||
nxt_buf_t *last;
|
nxt_buf_t *last;
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <nxt_http.h>
|
#include <nxt_http.h>
|
||||||
#include <nxt_sockaddr.h>
|
#include <nxt_sockaddr.h>
|
||||||
#include <nxt_http_route_addr.h>
|
#include <nxt_http_route_addr.h>
|
||||||
|
#include <nxt_regex.h>
|
||||||
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -76,12 +77,20 @@ typedef struct {
|
|||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t min_length;
|
union {
|
||||||
nxt_array_t *pattern_slices;
|
nxt_array_t *pattern_slices;
|
||||||
|
#if (NXT_HAVE_REGEX)
|
||||||
|
nxt_regex_t *regex;
|
||||||
|
#endif
|
||||||
|
} u;
|
||||||
|
uint32_t min_length;
|
||||||
|
|
||||||
uint8_t case_sensitive; /* 1 bit */
|
uint8_t case_sensitive; /* 1 bit */
|
||||||
uint8_t negative; /* 1 bit */
|
uint8_t negative; /* 1 bit */
|
||||||
uint8_t any; /* 1 bit */
|
uint8_t any; /* 1 bit */
|
||||||
|
#if (NXT_HAVE_REGEX)
|
||||||
|
uint8_t regex; /* 1 bit */
|
||||||
|
#endif
|
||||||
} nxt_http_route_pattern_t;
|
} 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_str_t test, tmp;
|
||||||
nxt_int_t ret;
|
nxt_int_t ret;
|
||||||
nxt_array_t *slices;
|
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_type_t type;
|
||||||
|
|
||||||
nxt_http_route_pattern_slice_t *slice;
|
nxt_http_route_pattern_slice_t *slice;
|
||||||
|
|
||||||
type = NXT_HTTP_ROUTE_PATTERN_EXACT;
|
type = NXT_HTTP_ROUTE_PATTERN_EXACT;
|
||||||
|
|
||||||
nxt_conf_get_string(cv, &test);
|
nxt_conf_get_string(cv, &test);
|
||||||
|
|
||||||
slices = nxt_array_create(mp, 1, sizeof(nxt_http_route_pattern_slice_t));
|
pattern->u.pattern_slices = NULL;
|
||||||
if (nxt_slow_path(slices == NULL)) {
|
|
||||||
return NXT_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
pattern->pattern_slices = slices;
|
|
||||||
|
|
||||||
pattern->negative = 0;
|
pattern->negative = 0;
|
||||||
pattern->any = 1;
|
pattern->any = 1;
|
||||||
pattern->min_length = 0;
|
pattern->min_length = 0;
|
||||||
|
#if (NXT_HAVE_REGEX)
|
||||||
|
pattern->regex = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (test.length != 0 && test.start[0] == '!') {
|
if (test.length != 0 && test.start[0] == '!') {
|
||||||
test.start++;
|
test.start++;
|
||||||
@@ -1087,6 +1096,41 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp,
|
|||||||
pattern->any = 0;
|
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) {
|
if (test.length == 0) {
|
||||||
slice = nxt_array_add(slices);
|
slice = nxt_array_add(slices);
|
||||||
if (nxt_slow_path(slice == NULL)) {
|
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);
|
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) {
|
if (ret == 0) {
|
||||||
return ret;
|
return ret;
|
||||||
@@ -2155,7 +2202,7 @@ static nxt_int_t
|
|||||||
nxt_http_route_test_argument(nxt_http_request_t *r,
|
nxt_http_route_test_argument(nxt_http_request_t *r,
|
||||||
nxt_http_route_rule_t *rule, nxt_array_t *array)
|
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;
|
nxt_http_name_value_t *nv, *end;
|
||||||
|
|
||||||
ret = 0;
|
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,
|
ret = nxt_http_route_test_rule(r, rule, nv->value,
|
||||||
nv->value_length);
|
nv->value_length);
|
||||||
|
if (nxt_slow_path(ret == NXT_ERROR)) {
|
||||||
|
return NXT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
break;
|
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_bool_t tls, https;
|
||||||
nxt_http_route_pattern_slice_t *pattern_slice;
|
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"));
|
https = (pattern_slice->length == nxt_length("https"));
|
||||||
tls = (r->tls != NULL);
|
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_test_cookie(nxt_http_request_t *r,
|
||||||
nxt_http_route_rule_t *rule, nxt_array_t *array)
|
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;
|
nxt_http_name_value_t *nv, *end;
|
||||||
|
|
||||||
ret = 0;
|
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,
|
ret = nxt_http_route_test_rule(r, rule, nv->value,
|
||||||
nv->value_length);
|
nv->value_length);
|
||||||
|
if (nxt_slow_path(ret == NXT_ERROR)) {
|
||||||
|
return NXT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2378,6 +2433,9 @@ nxt_http_route_test_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule,
|
|||||||
|
|
||||||
while (pattern < end) {
|
while (pattern < end) {
|
||||||
ret = nxt_http_route_pattern(r, pattern, start, length);
|
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. */
|
/* nxt_http_route_pattern() returns either 1 or 0. */
|
||||||
ret ^= pattern->negative;
|
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_array_t *pattern_slices;
|
||||||
nxt_http_route_pattern_slice_t *pattern_slice;
|
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) {
|
if (length < pattern->min_length) {
|
||||||
return 0;
|
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;
|
pattern_slice = pattern_slices->elts;
|
||||||
end = start + length;
|
end = start + length;
|
||||||
|
|
||||||
|
|||||||
135
src/nxt_pcre.c
Normal file
135
src/nxt_pcre.c
Normal file
@@ -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);
|
||||||
|
}
|
||||||
161
src/nxt_pcre2.c
Normal file
161
src/nxt_pcre2.c
Normal file
@@ -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);
|
||||||
|
}
|
||||||
41
src/nxt_regex.h
Normal file
41
src/nxt_regex.h
Normal file
@@ -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_ */
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <nxt_port.h>
|
#include <nxt_port.h>
|
||||||
#include <nxt_main_process.h>
|
#include <nxt_main_process.h>
|
||||||
#include <nxt_router.h>
|
#include <nxt_router.h>
|
||||||
|
#include <nxt_regex.h>
|
||||||
|
|
||||||
|
|
||||||
static nxt_int_t nxt_runtime_inherited_listen_sockets(nxt_task_t *task,
|
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
|
static void
|
||||||
nxt_runtime_thread_pool_init(void)
|
nxt_runtime_thread_pool_init(void)
|
||||||
{
|
{
|
||||||
#if (NXT_REGEX)
|
|
||||||
nxt_regex_init(0);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -230,6 +230,48 @@ class TestRouting(TestApplicationProto):
|
|||||||
assert self.get(url='/aBCaBbc')['status'] == 200
|
assert self.get(url='/aBCaBbc')['status'] == 200
|
||||||
assert self.get(url='/ABc')['status'] == 404
|
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 test_routes_pass_encode(self):
|
||||||
def check_pass(path, name):
|
def check_pass(path, name):
|
||||||
assert 'success' in self.conf(
|
assert 'success' in self.conf(
|
||||||
|
|||||||
Reference in New Issue
Block a user