HTTP: added basic URI rewrite.

This commit introduced the basic URI rewrite. It allows users to change request URI. Note the "rewrite" option ignores the contained query if any and the query from the request is preserverd.
An example:
"routes": [
    {
        "match": {
            "uri": "/v1/test"
        },
        "action": {
            "return": 200
        }
    },
    {
        "action": {
            "rewrite": "/v1$uri",
            "pass": "routes"
        }
    }
]

Reviewed-by: Alejandro Colomar <alx@nginx.com>
This commit is contained in:
Zhidao HONG 2023-04-20 23:20:41 +08:00
parent 8843e30e82
commit 14d6d97bac
11 changed files with 167 additions and 11 deletions

View file

@ -92,6 +92,7 @@ NXT_LIB_SRCS=" \
src/nxt_http_error.c \
src/nxt_http_route.c \
src/nxt_http_route_addr.c \
src/nxt_http_rewrite.c \
src/nxt_http_return.c \
src/nxt_http_static.c \
src/nxt_http_proxy.c \

View file

@ -32,6 +32,12 @@ NGINX Unit updated to 1.30.0.
date="" time=""
packager="Nginx Packaging &lt;nginx-packaging@f5.com&gt;">
<change type="feature">
<para>
basic URI rewrite support.
</para>
</change>
<change type="feature">
<para>
added conditional logging of route selection for HTTP requests.

View file

@ -669,6 +669,16 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = {
};
static nxt_conf_vldt_object_t nxt_conf_vldt_action_common_members[] = {
{
.name = nxt_string("rewrite"),
.type = NXT_CONF_VLDT_STRING,
},
NXT_CONF_VLDT_END
};
static nxt_conf_vldt_object_t nxt_conf_vldt_pass_action_members[] = {
{
.name = nxt_string("pass"),
@ -677,7 +687,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_pass_action_members[] = {
.flags = NXT_CONF_VLDT_TSTR,
},
NXT_CONF_VLDT_END
NXT_CONF_VLDT_NEXT(nxt_conf_vldt_action_common_members)
};
@ -692,7 +702,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_return_action_members[] = {
.flags = NXT_CONF_VLDT_TSTR,
},
NXT_CONF_VLDT_END
NXT_CONF_VLDT_NEXT(nxt_conf_vldt_action_common_members)
};
@ -736,7 +746,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_share_action_members[] = {
#endif
},
NXT_CONF_VLDT_END
NXT_CONF_VLDT_NEXT(nxt_conf_vldt_action_common_members)
};
@ -747,7 +757,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_proxy_action_members[] = {
.validator = nxt_conf_vldt_proxy,
},
NXT_CONF_VLDT_END
NXT_CONF_VLDT_NEXT(nxt_conf_vldt_action_common_members)
};

View file

@ -226,6 +226,7 @@ typedef struct nxt_http_route_addr_rule_s nxt_http_route_addr_rule_t;
typedef struct {
nxt_conf_value_t *rewrite;
nxt_conf_value_t *pass;
nxt_conf_value_t *ret;
nxt_conf_value_t *location;
@ -253,6 +254,7 @@ struct nxt_http_action_s {
nxt_str_t *pass;
} u;
nxt_tstr_t *rewrite;
nxt_http_action_t *fallback;
};
@ -378,6 +380,11 @@ nxt_int_t nxt_upstreams_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_int_t nxt_upstreams_joint_create(nxt_router_temp_conf_t *tmcf,
nxt_upstream_t ***upstream_joint);
nxt_int_t nxt_http_rewrite_init(nxt_router_conf_t *rtcf,
nxt_http_action_t *action, nxt_http_action_conf_t *acf);
nxt_int_t nxt_http_rewrite(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_action_t *action);
nxt_int_t nxt_http_return_init(nxt_router_conf_t *rtcf,
nxt_http_action_t *action, nxt_http_action_conf_t *acf);

View file

@ -19,8 +19,6 @@ static u_char *nxt_http_lookup_field_end(u_char *p, const u_char *end);
static nxt_int_t nxt_http_parse_field_end(nxt_http_request_parse_t *rp,
u_char **pos, const u_char *end);
static nxt_int_t nxt_http_parse_complex_target(nxt_http_request_parse_t *rp);
static nxt_int_t nxt_http_field_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
static nxt_int_t nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq,
@ -854,7 +852,7 @@ static const uint8_t nxt_http_normal[32] nxt_aligned(32) = {
};
static nxt_int_t
nxt_int_t
nxt_http_parse_complex_target(nxt_http_request_parse_t *rp)
{
u_char *p, *u, c, ch, high, *args;

View file

@ -127,6 +127,7 @@ nxt_uint_t nxt_http_fields_hash_collisions(nxt_lvlhsh_t *hash,
nxt_int_t nxt_http_fields_process(nxt_list_t *fields, nxt_lvlhsh_t *hash,
void *ctx);
nxt_int_t nxt_http_parse_complex_target(nxt_http_request_parse_t *rp);
nxt_buf_t *nxt_http_chunk_parse(nxt_task_t *task, nxt_http_chunk_parse_t *hcp,
nxt_buf_t *in);

View file

@ -555,9 +555,18 @@ void
nxt_http_request_action(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_action_t *action)
{
nxt_int_t ret;
if (nxt_fast_path(action != NULL)) {
do {
if (action->rewrite != NULL) {
ret = nxt_http_rewrite(task, r, action);
if (nxt_slow_path(ret != NXT_OK)) {
break;
}
}
action = action->handler(task, r, action);
if (action == NULL) {

104
src/nxt_http_rewrite.c Normal file
View file

@ -0,0 +1,104 @@
/*
* Copyright (C) Zhidao HONG
* Copyright (C) NGINX, Inc.
*/
#include <nxt_router.h>
#include <nxt_http.h>
nxt_int_t
nxt_http_rewrite_init(nxt_router_conf_t *rtcf, nxt_http_action_t *action,
nxt_http_action_conf_t *acf)
{
nxt_str_t str;
nxt_conf_get_string(acf->rewrite, &str);
action->rewrite = nxt_tstr_compile(rtcf->tstr_state, &str, 0);
if (nxt_slow_path(action->rewrite == NULL)) {
return NXT_ERROR;
}
return NXT_OK;
}
nxt_int_t
nxt_http_rewrite(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_action_t *action)
{
u_char *p;
nxt_int_t ret;
nxt_str_t str, encoded_path, target;
nxt_router_conf_t *rtcf;
nxt_http_request_parse_t rp;
if (nxt_tstr_is_const(action->rewrite)) {
nxt_tstr_str(action->rewrite, &str);
} else {
rtcf = r->conf->socket_conf->router_conf;
ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state,
&r->tstr_cache, r, r->mem_pool);
if (nxt_slow_path(ret != NXT_OK)) {
return NXT_ERROR;
}
nxt_tstr_query(task, r->tstr_query, action->rewrite, &str);
if (nxt_slow_path(nxt_tstr_query_failed(r->tstr_query))) {
return NXT_ERROR;
}
}
nxt_memzero(&rp, sizeof(nxt_http_request_parse_t));
rp.mem_pool = r->mem_pool;
rp.target_start = str.start;
rp.target_end = str.start + str.length;
ret = nxt_http_parse_complex_target(&rp);
if (nxt_slow_path(ret != NXT_OK)) {
return NXT_ERROR;
}
p = (rp.args.length > 0) ? rp.args.start - 1 : rp.target_end;
encoded_path.start = rp.target_start;
encoded_path.length = p - encoded_path.start;
if (r->args->length == 0) {
r->target = encoded_path;
} else {
target.length = encoded_path.length + 1 + r->args->length;
target.start = nxt_mp_alloc(r->mem_pool, target.length);
if (target.start == NULL) {
return NXT_ERROR;
}
p = nxt_cpymem(target.start, encoded_path.start, encoded_path.length);
*p++ = '?';
nxt_memcpy(p, r->args->start, r->args->length);
r->target = target;
}
r->path = nxt_mp_alloc(r->mem_pool, sizeof(nxt_str_t));
if (nxt_slow_path(r->path == NULL)) {
return NXT_ERROR;
}
*r->path = rp.path;
if (nxt_slow_path(r->log_route)) {
nxt_log(task, NXT_LOG_NOTICE, "URI rewritten to \"%V\"", &r->target);
}
return NXT_OK;
}

View file

@ -578,6 +578,11 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
static nxt_conf_map_t nxt_http_route_action_conf[] = {
{
nxt_string("rewrite"),
NXT_CONF_MAP_PTR,
offsetof(nxt_http_action_conf_t, rewrite)
},
{
nxt_string("pass"),
NXT_CONF_MAP_PTR,
@ -659,6 +664,13 @@ nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
rtcf = tmcf->router_conf;
mp = rtcf->mem_pool;
if (acf.rewrite != NULL) {
ret = nxt_http_rewrite_init(rtcf, action, &acf);
if (nxt_slow_path(ret != NXT_OK)) {
return ret;
}
}
if (acf.ret != NULL) {
return nxt_http_return_init(rtcf, action, &acf);
}
@ -1312,8 +1324,8 @@ nxt_http_pass_var(nxt_task_t *task, nxt_http_request_t *r,
goto fail;
}
action = nxt_mp_get(r->mem_pool,
sizeof(nxt_http_action_t) + sizeof(nxt_str_t));
action = nxt_mp_zget(r->mem_pool,
sizeof(nxt_http_action_t) + sizeof(nxt_str_t));
if (nxt_slow_path(action == NULL)) {
goto fail;
}
@ -1496,7 +1508,7 @@ nxt_http_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
rtcf = tmcf->router_conf;
mp = rtcf->mem_pool;
action = nxt_mp_alloc(mp, sizeof(nxt_http_action_t));
action = nxt_mp_zalloc(mp, sizeof(nxt_http_action_t));
if (nxt_slow_path(action == NULL)) {
return NULL;
}
@ -1525,7 +1537,7 @@ nxt_http_pass_application(nxt_task_t *task, nxt_router_conf_t *rtcf,
{
nxt_http_action_t *action;
action = nxt_mp_alloc(rtcf->mem_pool, sizeof(nxt_http_action_t));
action = nxt_mp_zalloc(rtcf->mem_pool, sizeof(nxt_http_action_t));
if (nxt_slow_path(action == NULL)) {
return NULL;
}

View file

@ -296,6 +296,13 @@ nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query, nxt_tstr_t *tstr,
}
nxt_bool_t
nxt_tstr_query_failed(nxt_tstr_query_t *query)
{
return query->failed;
}
void
nxt_tstr_query_resolve(nxt_task_t *task, nxt_tstr_query_t *query, void *data,
nxt_work_handler_t ready, nxt_work_handler_t error)

View file

@ -52,6 +52,7 @@ nxt_int_t nxt_tstr_query_init(nxt_tstr_query_t **query_p,
nxt_mp_t *mp);
void nxt_tstr_query(nxt_task_t *task, nxt_tstr_query_t *query, nxt_tstr_t *tstr,
nxt_str_t *val);
nxt_bool_t nxt_tstr_query_failed(nxt_tstr_query_t *query);
void nxt_tstr_query_resolve(nxt_task_t *task, nxt_tstr_query_t *query,
void *data, nxt_work_handler_t ready, nxt_work_handler_t error);
void nxt_tstr_query_handle(nxt_task_t *task, nxt_tstr_query_t *query,