diff options
author | Zhidao HONG <z.hong@f5.com> | 2023-08-09 14:37:16 +0800 |
---|---|---|
committer | Zhidao HONG <z.hong@f5.com> | 2023-08-09 14:37:16 +0800 |
commit | a28bef097cd63da3bbb375ef0283e65be41a88ff (patch) | |
tree | 8553e74c050a9747bdba928cbaec82ee6d15cb5d | |
parent | 9f04d6db630a22658581cbe7a9d28ecdb13d8de9 (diff) | |
download | unit-a28bef097cd63da3bbb375ef0283e65be41a88ff.tar.gz unit-a28bef097cd63da3bbb375ef0283e65be41a88ff.tar.bz2 |
HTTP: controlling response headers support.
-rw-r--r-- | auto/sources | 1 | ||||
-rw-r--r-- | docs/changes.xml | 6 | ||||
-rw-r--r-- | src/nxt_conf_validation.c | 37 | ||||
-rw-r--r-- | src/nxt_http.h | 6 | ||||
-rw-r--r-- | src/nxt_http_request.c | 6 | ||||
-rw-r--r-- | src/nxt_http_route.c | 12 | ||||
-rw-r--r-- | src/nxt_http_set_headers.c | 176 | ||||
-rw-r--r-- | src/nxt_http_variables.c | 2 |
8 files changed, 245 insertions, 1 deletions
diff --git a/auto/sources b/auto/sources index f4a7170a..6ee4d87b 100644 --- a/auto/sources +++ b/auto/sources @@ -93,6 +93,7 @@ NXT_LIB_SRCS=" \ src/nxt_http_route.c \ src/nxt_http_route_addr.c \ src/nxt_http_rewrite.c \ + src/nxt_http_set_headers.c \ src/nxt_http_return.c \ src/nxt_http_static.c \ src/nxt_http_proxy.c \ diff --git a/docs/changes.xml b/docs/changes.xml index b2c70376..40d60249 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -40,6 +40,12 @@ njs updated to 0.8.0. <change type="feature"> <para> +ability to control HTTP response headers. +</para> +</change> + +<change type="feature"> +<para> ability to get the HTTP response header variables. </para> </change> diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 8c75a9fe..09f5598e 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -167,6 +167,8 @@ static nxt_int_t nxt_conf_vldt_match_addrs(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_match_addr(nxt_conf_validation_t *vldt, nxt_conf_value_t *value); +static nxt_int_t nxt_conf_vldt_response_header(nxt_conf_validation_t *vldt, + nxt_str_t *name, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_forwarded(nxt_conf_validation_t *vldt, @@ -688,6 +690,12 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_action_common_members[] = { .name = nxt_string("rewrite"), .type = NXT_CONF_VLDT_STRING, }, + { + .name = nxt_string("response_headers"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = nxt_conf_vldt_object_iterator, + .u.object = nxt_conf_vldt_response_header, + }, NXT_CONF_VLDT_END }; @@ -2448,6 +2456,35 @@ nxt_conf_vldt_object_conf_commands(nxt_conf_validation_t *vldt, static nxt_int_t +nxt_conf_vldt_response_header(nxt_conf_validation_t *vldt, nxt_str_t *name, + nxt_conf_value_t *value) +{ + nxt_uint_t type; + + static nxt_str_t content_length = nxt_string("Content-Length"); + + if (name->length == 0) { + return nxt_conf_vldt_error(vldt, "The response header name " + "must not be empty."); + } + + if (nxt_strstr_eq(name, &content_length)) { + return nxt_conf_vldt_error(vldt, "The \"Content-Length\" response " + "header value is not supported"); + } + + type = nxt_conf_type(value); + + if (type == NXT_CONF_STRING || type == NXT_CONF_NULL) { + return NXT_OK; + } + + return nxt_conf_vldt_error(vldt, "The \"%V\" response header value " + "must either be a string or a null", name); +} + + +static nxt_int_t nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) { diff --git a/src/nxt_http.h b/src/nxt_http.h index fc47faa8..e812bd0d 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -228,6 +228,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 *set_headers; nxt_conf_value_t *pass; nxt_conf_value_t *ret; nxt_conf_value_t *location; @@ -256,6 +257,7 @@ struct nxt_http_action_s { } u; nxt_tstr_t *rewrite; + nxt_array_t *set_headers; /* of nxt_http_field_t */ nxt_http_action_t *fallback; }; @@ -385,6 +387,10 @@ 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_int_t nxt_http_set_headers_init(nxt_router_conf_t *rtcf, + nxt_http_action_t *action, nxt_http_action_conf_t *acf); +nxt_int_t nxt_http_set_headers(nxt_http_request_t *r); + nxt_int_t nxt_http_return_init(nxt_router_conf_t *rtcf, nxt_http_action_t *action, nxt_http_action_conf_t *acf); diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index 1ede9ffa..e532baff 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -630,9 +630,15 @@ nxt_http_request_header_send(nxt_task_t *task, nxt_http_request_t *r, nxt_work_handler_t body_handler, void *data) { u_char *p, *end, *server_string; + nxt_int_t ret; nxt_http_field_t *server, *date, *content_length; nxt_socket_conf_t *skcf; + ret = nxt_http_set_headers(r); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } + /* * TODO: "Server", "Date", and "Content-Length" processing should be moved * to the last header filter. diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index 8fd6801d..4a64d5c1 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -584,6 +584,11 @@ static nxt_conf_map_t nxt_http_route_action_conf[] = { offsetof(nxt_http_action_conf_t, rewrite) }, { + nxt_string("response_headers"), + NXT_CONF_MAP_PTR, + offsetof(nxt_http_action_conf_t, set_headers) + }, + { nxt_string("pass"), NXT_CONF_MAP_PTR, offsetof(nxt_http_action_conf_t, pass) @@ -671,6 +676,13 @@ nxt_http_action_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, } } + if (acf.set_headers != NULL) { + ret = nxt_http_set_headers_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); } diff --git a/src/nxt_http_set_headers.c b/src/nxt_http_set_headers.c new file mode 100644 index 00000000..25dd7478 --- /dev/null +++ b/src/nxt_http_set_headers.c @@ -0,0 +1,176 @@ + +/* + * Copyright (C) Zhidao HONG + * Copyright (C) NGINX, Inc. + */ + +#include <nxt_router.h> +#include <nxt_http.h> + + +typedef struct { + nxt_str_t name; + nxt_tstr_t *value; +} nxt_http_header_val_t; + + +nxt_int_t +nxt_http_set_headers_init(nxt_router_conf_t *rtcf, nxt_http_action_t *action, + nxt_http_action_conf_t *acf) + { + uint32_t next; + nxt_str_t str, name; + nxt_array_t *headers; + nxt_conf_value_t *value; + nxt_http_header_val_t *hv; + + headers = nxt_array_create(rtcf->mem_pool, 4, + sizeof(nxt_http_header_val_t)); + if (nxt_slow_path(headers == NULL)) { + return NXT_ERROR; + } + + action->set_headers = headers; + + next = 0; + + for ( ;; ) { + value = nxt_conf_next_object_member(acf->set_headers, &name, &next); + if (value == NULL) { + break; + } + + hv = nxt_array_zero_add(headers); + if (nxt_slow_path(hv == NULL)) { + return NXT_ERROR; + } + + hv->name.length = name.length; + + hv->name.start = nxt_mp_nget(rtcf->mem_pool, name.length); + if (nxt_slow_path(hv->name.start == NULL)) { + return NXT_ERROR; + } + + nxt_memcpy(hv->name.start, name.start, name.length); + + if (nxt_conf_type(value) == NXT_CONF_STRING) { + nxt_conf_get_string(value, &str); + + hv->value = nxt_tstr_compile(rtcf->tstr_state, &str, 0); + if (nxt_slow_path(hv->value == NULL)) { + return NXT_ERROR; + } + } + } + + return NXT_OK; +} + + +static nxt_http_field_t * +nxt_http_resp_header_find(nxt_http_request_t *r, u_char *name, size_t length) +{ + nxt_http_field_t *f; + + nxt_list_each(f, r->resp.fields) { + + if (f->skip) { + continue; + } + + if (length == f->name_length + && nxt_memcasecmp(name, f->name, f->name_length) == 0) + { + return f; + } + + } nxt_list_loop; + + return NULL; +} + + +nxt_int_t +nxt_http_set_headers(nxt_http_request_t *r) +{ + nxt_int_t ret; + nxt_uint_t i, n; + nxt_str_t *value; + nxt_http_field_t *f; + nxt_router_conf_t *rtcf; + nxt_http_action_t *action; + nxt_http_header_val_t *hv, *header; + + action = r->action; + + if (action == NULL || action->set_headers == NULL) { + return NXT_OK; + } + + if ((r->status < NXT_HTTP_OK || r->status >= NXT_HTTP_BAD_REQUEST)) { + return NXT_OK; + } + + rtcf = r->conf->socket_conf->router_conf; + + header = action->set_headers->elts; + n = action->set_headers->nelts; + + value = nxt_mp_zalloc(r->mem_pool, sizeof(nxt_str_t) * n); + if (nxt_slow_path(value == NULL)) { + return NXT_ERROR; + } + + for (i = 0; i < n; i++) { + hv = &header[i]; + + if (hv->value == NULL) { + continue; + } + + if (nxt_tstr_is_const(hv->value)) { + nxt_tstr_str(hv->value, &value[i]); + + } else { + 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(&r->task, r->tstr_query, hv->value, &value[i]); + + if (nxt_slow_path(nxt_tstr_query_failed(r->tstr_query))) { + return NXT_ERROR; + } + } + } + + for (i = 0; i < n; i++) { + hv = &header[i]; + + f = nxt_http_resp_header_find(r, hv->name.start, hv->name.length); + + if (value[i].start != NULL) { + + if (f == NULL) { + f = nxt_list_zero_add(r->resp.fields); + if (nxt_slow_path(f == NULL)) { + return NXT_ERROR; + } + + f->name = hv->name.start; + f->name_length = hv->name.length; + } + + f->value = value[i].start; + f->value_length = value[i].length; + + } else if (f != NULL) { + f->skip = 1; + } + } + + return NXT_OK; +} diff --git a/src/nxt_http_variables.c b/src/nxt_http_variables.c index 04e24a4b..46594a6b 100644 --- a/src/nxt_http_variables.c +++ b/src/nxt_http_variables.c @@ -137,7 +137,7 @@ nxt_http_unknown_var_ref(nxt_tstr_state_t *state, nxt_var_ref_t *ref, if (nxt_str_start(name, "response_header_", 16)) { ref->handler = nxt_http_var_response_header; - ref->cacheable = 1; + ref->cacheable = 0; str.start = name->start + 16; str.length = name->length - 16; |