diff options
author | Dmitry Volyntsev <xeioex@nginx.com> | 2022-05-31 09:26:47 -0700 |
---|---|---|
committer | Dmitry Volyntsev <xeioex@nginx.com> | 2022-05-31 09:26:47 -0700 |
commit | 9e1eed12e39989be82392a3f7d90fa7d7d2f2fd0 (patch) | |
tree | 56d42ba50b0c7414c583bd0ade0993a27d2aa582 /nginx/ngx_http_js_module.c | |
parent | 2b30b4ffe044b44de94f129245e3981fe3363656 (diff) | |
download | njs-9e1eed12e39989be82392a3f7d90fa7d7d2f2fd0.tar.gz njs-9e1eed12e39989be82392a3f7d90fa7d7d2f2fd0.zip |
HTTP: adapting to changes in nginx header structures.
Diffstat (limited to 'nginx/ngx_http_js_module.c')
-rw-r--r-- | nginx/ngx_http_js_module.c | 617 |
1 files changed, 617 insertions, 0 deletions
diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index ce20e71f..206d5a73 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -12,6 +12,11 @@ #include "ngx_js.h" +#define NJS_HEADER_SEMICOLON 0x1 +#define NJS_HEADER_SINGLE 0x2 +#define NJS_HEADER_ARRAY 0x4 + + typedef struct { njs_vm_t *vm; ngx_array_t *imports; @@ -84,10 +89,17 @@ typedef struct { typedef struct { njs_str_t name; +#if defined(nginx_version) && (nginx_version >= 1023000) + unsigned flags; + njs_int_t (*handler)(njs_vm_t *vm, ngx_http_request_t *r, + unsigned flags, njs_str_t *name, + njs_value_t *setval, njs_value_t *retval); +#else njs_int_t (*handler)(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); +#endif } ngx_http_js_header_t; @@ -107,14 +119,17 @@ static void ngx_http_js_cleanup_vm(void *data); static njs_int_t ngx_http_js_ext_keys_header(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys, ngx_list_t *headers); +#if defined(nginx_version) && (nginx_version < 1023000) static ngx_table_elt_t *ngx_http_js_get_header(ngx_list_part_t *part, u_char *data, size_t len); +#endif static njs_int_t ngx_http_js_ext_raw_header(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_ext_header_out(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); +#if defined(nginx_version) && (nginx_version < 1023000) static njs_int_t ngx_http_js_header_single(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); @@ -136,6 +151,7 @@ static njs_int_t ngx_http_js_content_encoding(njs_vm_t *vm, static njs_int_t ngx_http_js_content_type(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); +#endif static njs_int_t ngx_http_js_ext_keys_header_out(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys); static njs_int_t ngx_http_js_ext_status(njs_vm_t *vm, njs_object_prop_t *prop, @@ -169,6 +185,7 @@ static njs_int_t ngx_http_js_ext_get_request_body(njs_vm_t *vm, static njs_int_t ngx_http_js_ext_header_in(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); +#if defined(nginx_version) && (nginx_version < 1023000) static njs_int_t ngx_http_js_header_cookie(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); @@ -179,6 +196,7 @@ static njs_int_t ngx_http_js_header_x_forwarded_for(njs_vm_t *vm, #endif static njs_int_t ngx_http_js_header_in_array(njs_vm_t *vm, ngx_http_request_t *r, ngx_array_t *array, u_char sep, njs_value_t *retval); +#endif static njs_int_t ngx_http_js_ext_keys_header_in(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys); static njs_int_t ngx_http_js_ext_get_arg(njs_vm_t *vm, @@ -203,6 +221,28 @@ static njs_int_t ngx_http_js_ext_get_response_body(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); +#if defined(nginx_version) && (nginx_version >= 1023000) +static njs_int_t ngx_http_js_header_in(njs_vm_t *vm, ngx_http_request_t *r, + unsigned flags, njs_str_t *name, njs_value_t *retval); +static njs_int_t ngx_http_js_header_out(njs_vm_t *vm, ngx_http_request_t *r, + unsigned flags, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); +static njs_int_t ngx_http_js_content_encoding(njs_vm_t *vm, + ngx_http_request_t *r, unsigned flags, njs_str_t *name, + njs_value_t *setval, njs_value_t *retval); +static njs_int_t ngx_http_js_content_length(njs_vm_t *vm, ngx_http_request_t *r, + unsigned flags, njs_str_t *name, njs_value_t *setval, + njs_value_t *retval); +static njs_int_t ngx_http_js_content_type(njs_vm_t *vm, ngx_http_request_t *r, + unsigned flags, njs_str_t *name, njs_value_t *setval, + njs_value_t *retval); +static njs_int_t ngx_http_js_header_out_special(njs_vm_t *vm, + ngx_http_request_t *r, njs_str_t *v, njs_value_t *setval, + njs_value_t *retval, ngx_table_elt_t **hh); +static njs_int_t ngx_http_js_header_generic(njs_vm_t *vm, + ngx_http_request_t *r, ngx_list_t *headers, ngx_table_elt_t **ph, + unsigned flags, njs_str_t *name, njs_value_t *retval); +#endif + static njs_host_event_t ngx_http_js_set_timer(njs_external_ptr_t external, uint64_t delay, njs_vm_event_t vm_event); static void ngx_http_js_clear_timer(njs_external_ptr_t external, @@ -1319,6 +1359,7 @@ ngx_http_js_ext_keys_header(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys, } +#if defined(nginx_version) && (nginx_version < 1023000) static ngx_table_elt_t * ngx_http_js_get_header(ngx_list_part_t *part, u_char *data, size_t len) { @@ -1352,6 +1393,7 @@ ngx_http_js_get_header(ngx_list_part_t *part, u_char *data, size_t len) return NULL; } +#endif static njs_int_t @@ -1446,6 +1488,7 @@ ngx_http_js_ext_header_out(njs_vm_t *vm, njs_object_prop_t *prop, ngx_http_js_header_t *h; static ngx_http_js_header_t headers_out[] = { +#if defined(nginx_version) && (nginx_version < 1023000) { njs_str("Age"), ngx_http_js_header_single }, { njs_str("Content-Type"), ngx_http_js_content_type }, { njs_str("Content-Length"), ngx_http_js_content_length }, @@ -1457,6 +1500,19 @@ ngx_http_js_ext_header_out(njs_vm_t *vm, njs_object_prop_t *prop, { njs_str("Set-Cookie"), ngx_http_js_header_array }, { njs_str("Retry-After"), ngx_http_js_header_single }, { njs_str(""), ngx_http_js_header_generic }, +#else + { njs_str("Age"), NJS_HEADER_SINGLE, ngx_http_js_header_out }, + { njs_str("Content-Encoding"), 0, ngx_http_js_content_encoding }, + { njs_str("Content-Length"), 0, ngx_http_js_content_length }, + { njs_str("Content-Type"), 0, ngx_http_js_content_type }, + { njs_str("Etag"), NJS_HEADER_SINGLE, ngx_http_js_header_out }, + { njs_str("Expires"), NJS_HEADER_SINGLE, ngx_http_js_header_out }, + { njs_str("Last-Modified"), NJS_HEADER_SINGLE, ngx_http_js_header_out }, + { njs_str("Location"), NJS_HEADER_SINGLE, ngx_http_js_header_out }, + { njs_str("Set-Cookie"), NJS_HEADER_ARRAY, ngx_http_js_header_out }, + { njs_str("Retry-After"), NJS_HEADER_SINGLE, ngx_http_js_header_out }, + { njs_str(""), 0, ngx_http_js_header_out }, +#endif }; r = njs_vm_external(vm, ngx_http_js_request_proto_id, value); @@ -1485,10 +1541,15 @@ ngx_http_js_ext_header_out(njs_vm_t *vm, njs_object_prop_t *prop, } } +#if defined(nginx_version) && (nginx_version < 1023000) return h->handler(vm, r, &r->headers_out.headers, &name, setval, retval); +#else + return h->handler(vm, r, h->flags, &name, setval, retval); +#endif } +#if defined(nginx_version) && (nginx_version < 1023000) static njs_int_t ngx_http_js_header_single(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, @@ -1940,6 +2001,7 @@ ngx_http_js_content_type(njs_vm_t *vm, ngx_http_request_t *r, return NJS_OK; } +#endif static njs_int_t @@ -2545,6 +2607,7 @@ done: } +#if defined(nginx_version) && (nginx_version < 1023000) static njs_int_t ngx_http_js_ext_header_in(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) @@ -2672,6 +2735,59 @@ ngx_http_js_header_in_array(njs_vm_t *vm, ngx_http_request_t *r, return NJS_OK; } +#else +static njs_int_t +ngx_http_js_ext_header_in(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *unused, njs_value_t *retval) +{ + unsigned flags; + njs_int_t rc; + njs_str_t name, *h; + ngx_http_request_t *r; + + static njs_str_t single_headers_in[] = { + njs_str("Content-Type"), + njs_str("ETag"), + njs_str("From"), + njs_str("Max-Forwards"), + njs_str("Referer"), + njs_str("Proxy-Authorization"), + njs_str("User-Agent"), + njs_str(""), + }; + + r = njs_vm_external(vm, ngx_http_js_request_proto_id, value); + if (r == NULL) { + if (retval != NULL) { + njs_value_undefined_set(retval); + } + + return NJS_DECLINED; + } + + rc = njs_vm_prop_name(vm, prop, &name); + if (rc != NJS_OK) { + if (retval != NULL) { + njs_value_undefined_set(retval); + } + + return NJS_DECLINED; + } + + flags = 0; + + for (h = single_headers_in; h->length > 0; h++) { + if (h->length == name.length + && ngx_strncasecmp(h->start, name.start, name.length) == 0) + { + flags |= NJS_HEADER_SINGLE; + break; + } + } + + return ngx_http_js_header_in(vm, r, flags, &name, retval); +} +#endif static njs_int_t @@ -3392,6 +3508,507 @@ ngx_http_js_ext_get_response_body(njs_vm_t *vm, njs_object_prop_t *prop, } +#if defined(nginx_version) && (nginx_version >= 1023000) +static njs_int_t +ngx_http_js_header_in(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, + njs_str_t *name, njs_value_t *retval) +{ + u_char *lowcase_key; + ngx_uint_t hash; + ngx_table_elt_t **ph; + ngx_http_header_t *hh; + ngx_http_core_main_conf_t *cmcf; + + if (retval == NULL) { + return NJS_OK; + } + + /* look up hashed headers */ + + lowcase_key = ngx_pnalloc(r->pool, name->length); + if (lowcase_key == NULL) { + njs_vm_memory_error(vm); + return NJS_ERROR; + } + + hash = ngx_hash_strlow(lowcase_key, name->start, name->length); + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + hh = ngx_hash_find(&cmcf->headers_in_hash, hash, lowcase_key, + name->length); + + ph = NULL; + + if (hh) { + if (hh->offset == offsetof(ngx_http_headers_in_t, cookie)) { + flags |= NJS_HEADER_SEMICOLON; + } + + ph = (ngx_table_elt_t **) ((char *) &r->headers_in + hh->offset); + } + + return ngx_http_js_header_generic(vm, r, &r->headers_in.headers, ph, flags, + name, retval); +} + + +static njs_int_t +ngx_http_js_header_out(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, + njs_str_t *name, njs_value_t *setval, njs_value_t *retval) +{ + u_char *p; + int64_t length; + njs_value_t *array; + njs_int_t rc; + njs_str_t s; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *header, *h, **ph; + njs_opaque_value_t lvalue; + + if (retval != NULL && setval == NULL) { + return ngx_http_js_header_generic(vm, r, &r->headers_out.headers, NULL, + flags, name, retval); + + } + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + h = &header[i]; + + if (h->hash == 0 + || h->key.len != name->length + || ngx_strncasecmp(h->key.data, name->start, name->length) != 0) + { + continue; + } + + h->hash = 0; + h->next = NULL; + } + + if (retval == NULL) { + return NJS_OK; + } + + if (njs_value_is_array(setval)) { + array = setval; + + rc = njs_vm_array_length(vm, array, &length); + if (rc != NJS_OK) { + return NJS_ERROR; + } + + if (length == 0) { + return NJS_OK; + } + + } else { + array = NULL; + length = 1; + } + + ph = &header; + + for (i = 0; i < (ngx_uint_t) length; i++) { + if (array != NULL) { + setval = njs_vm_array_prop(vm, array, i, &lvalue); + } + + if (ngx_js_string(vm, setval, &s) != NGX_OK) { + return NJS_ERROR; + } + + if (s.length == 0) { + continue; + } + + h = ngx_list_push(&r->headers_out.headers); + if (h == NULL) { + njs_vm_memory_error(vm); + return NJS_ERROR; + } + + p = ngx_pnalloc(r->pool, name->length); + if (p == NULL) { + h->hash = 0; + njs_vm_memory_error(vm); + return NJS_ERROR; + } + + ngx_memcpy(p, name->start, name->length); + + h->key.data = p; + h->key.len = name->length; + + p = ngx_pnalloc(r->pool, s.length); + if (p == NULL) { + h->hash = 0; + njs_vm_memory_error(vm); + return NJS_ERROR; + } + + ngx_memcpy(p, s.start, s.length); + + h->value.data = p; + h->value.len = s.length; + h->hash = 1; + + *ph = h; + ph = &h->next; + } + + *ph = NULL; + + return NJS_OK; +} + + +static njs_int_t +ngx_http_js_content_encoding(njs_vm_t *vm, ngx_http_request_t *r, + unsigned flags, njs_str_t *v, njs_value_t *setval, njs_value_t *retval) +{ + njs_int_t rc; + ngx_table_elt_t *h; + + rc = ngx_http_js_header_out_special(vm, r, v, setval, retval, &h); + if (rc != NJS_OK) { + return NJS_ERROR; + } + + if (setval != NULL || retval == NULL) { + r->headers_out.content_encoding = h; + } + + return NJS_OK; +} + + +static njs_int_t +ngx_http_js_content_length(njs_vm_t *vm, ngx_http_request_t *r, + unsigned flags, njs_str_t *v, njs_value_t *setval, njs_value_t *retval) +{ + u_char *p, *start; + njs_int_t rc; + ngx_int_t n; + ngx_table_elt_t *h; + u_char content_len[NGX_OFF_T_LEN]; + + if (retval != NULL && setval == NULL) { + if (r->headers_out.content_length == NULL + && r->headers_out.content_length_n >= 0) + { + p = ngx_sprintf(content_len, "%O", r->headers_out.content_length_n); + + start = njs_vm_value_string_alloc(vm, retval, p - content_len); + if (start == NULL) { + return NJS_ERROR; + } + + ngx_memcpy(start, content_len, p - content_len); + + return NJS_OK; + } + } + + rc = ngx_http_js_header_out_special(vm, r, v, setval, retval, &h); + if (rc != NJS_OK) { + return NJS_ERROR; + } + + if (setval != NULL || retval == NULL) { + if (h != NULL) { + n = ngx_atoi(h->value.data, h->value.len); + if (n == NGX_ERROR) { + h->hash = 0; + njs_vm_error(vm, "failed converting argument " + "to positive integer"); + return NJS_ERROR; + } + + r->headers_out.content_length = h; + r->headers_out.content_length_n = n; + + } else { + ngx_http_clear_content_length(r); + } + } + + return NJS_OK; +} + + +static njs_int_t +ngx_http_js_content_type(njs_vm_t *vm, ngx_http_request_t *r, + unsigned flags, njs_str_t *v, njs_value_t *setval, njs_value_t *retval) +{ + int64_t length; + njs_int_t rc; + njs_str_t s; + ngx_str_t *hdr; + njs_opaque_value_t lvalue; + + if (retval != NULL && setval == NULL) { + hdr = &r->headers_out.content_type; + return njs_vm_value_string_set(vm, retval, hdr->data, hdr->len); + } + + if (setval != NULL && njs_value_is_array(setval)) { + rc = njs_vm_array_length(vm, setval, &length); + if (rc != NJS_OK) { + return NJS_ERROR; + } + + setval = njs_vm_array_prop(vm, setval, length - 1, &lvalue); + } + + if (ngx_js_string(vm, setval, &s) != NGX_OK) { + return NJS_ERROR; + } + + r->headers_out.content_type.len = s.length; + r->headers_out.content_type_len = r->headers_out.content_type.len; + r->headers_out.content_type.data = s.start; + r->headers_out.content_type_lowcase = NULL; + + return NJS_OK; +} + + +static njs_int_t +ngx_http_js_header_out_special(njs_vm_t *vm, ngx_http_request_t *r, + njs_str_t *v, njs_value_t *setval, njs_value_t *retval, + ngx_table_elt_t **hh) +{ + u_char *p; + int64_t length; + njs_int_t rc; + njs_str_t s; + ngx_uint_t i; + ngx_list_t *headers; + ngx_list_part_t *part; + ngx_table_elt_t *header, *h; + njs_opaque_value_t lvalue; + + headers = &r->headers_out.headers; + + if (retval != NULL && setval == NULL) { + return ngx_http_js_header_out(vm, r, NJS_HEADER_SINGLE, v, setval, + retval); + } + + if (setval != NULL && njs_value_is_array(setval)) { + rc = njs_vm_array_length(vm, setval, &length); + if (rc != NJS_OK) { + return NJS_ERROR; + } + + setval = njs_vm_array_prop(vm, setval, length - 1, &lvalue); + } + + if (ngx_js_string(vm, setval, &s) != NGX_OK) { + return NJS_ERROR; + } + + h = NULL; + part = &headers->part; + header = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + h = &header[i]; + + if (h->hash == 0) { + continue; + } + + if (h->key.len == v->length + && ngx_strncasecmp(h->key.data, v->start, v->length) == 0) + { + break; + } + } + + if (h != NULL && s.length == 0) { + h->hash = 0; + h = NULL; + } + + if (h == NULL && s.length != 0) { + h = ngx_list_push(headers); + if (h == NULL) { + njs_vm_memory_error(vm); + return NJS_ERROR; + } + + p = ngx_pnalloc(r->pool, v->length); + if (p == NULL) { + h->hash = 0; + njs_vm_memory_error(vm); + return NJS_ERROR; + } + + ngx_memcpy(p, v->start, v->length); + + h->key.data = p; + h->key.len = v->length; + } + + if (h != NULL) { + p = ngx_pnalloc(r->pool, s.length); + if (p == NULL) { + h->hash = 0; + njs_vm_memory_error(vm); + return NJS_ERROR; + } + + ngx_memcpy(p, s.start, s.length); + + h->value.data = p; + h->value.len = s.length; + h->hash = 1; + } + + if (hh != NULL) { + *hh = h; + } + + return NJS_OK; +} + + +static njs_int_t +ngx_http_js_header_generic(njs_vm_t *vm, ngx_http_request_t *r, + ngx_list_t *headers, ngx_table_elt_t **ph, unsigned flags, njs_str_t *name, + njs_value_t *retval) +{ + u_char *p, sep; + ssize_t size; + njs_int_t rc; + ngx_uint_t i; + njs_value_t *value; + ngx_list_part_t *part; + ngx_table_elt_t *header, *h; + + if (ph == NULL) { + /* iterate over all headers */ + + ph = &header; + part = &headers->part; + h = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + h = part->elts; + i = 0; + } + + if (h[i].hash == 0 + || name->length != h[i].key.len + || ngx_strncasecmp(name->start, h[i].key.data, name->length) + != 0) + { + continue; + } + + *ph = &h[i]; + ph = &h[i].next; + } + + *ph = NULL; + ph = &header; + } + + if (*ph == NULL) { + njs_value_undefined_set(retval); + return NJS_DECLINED; + } + + if (flags & NJS_HEADER_ARRAY) { + rc = njs_vm_array_alloc(vm, retval, 4); + if (rc != NJS_OK) { + return NJS_ERROR; + } + + for (h = *ph; h; h = h->next) { + value = njs_vm_array_push(vm, retval); + if (value == NULL) { + return NJS_ERROR; + } + + rc = njs_vm_value_string_set(vm, value, h->value.data, + h->value.len); + if (rc != NJS_OK) { + return NJS_ERROR; + } + } + + return NJS_OK; + } + + if ((*ph)->next == NULL || flags & NJS_HEADER_SINGLE) { + return njs_vm_value_string_set(vm, retval, (*ph)->value.data, + (*ph)->value.len); + } + + size = - (ssize_t) njs_length("; "); + + for (h = *ph; h; h = h->next) { + size += h->value.len + njs_length("; "); + } + + p = njs_vm_value_string_alloc(vm, retval, size); + if (p == NULL) { + return NJS_ERROR; + } + + sep = flags & NJS_HEADER_SEMICOLON ? ';' : ','; + + for (h = *ph; h; h = h->next) { + p = ngx_copy(p, h->value.data, h->value.len); + + if (h->next == NULL) { + break; + } + + *p++ = sep; *p++ = ' '; + } + + return NJS_OK; +} +#endif + + static njs_host_event_t ngx_http_js_set_timer(njs_external_ptr_t external, uint64_t delay, njs_vm_event_t vm_event) |