[njs] Introduced Query String module implementation.
Alexander Borisov
alexander.borisov at nginx.com
Wed Jul 22 12:22:03 UTC 2020
details: https://hg.nginx.org/njs/rev/7f5c5a425d03
branches:
changeset: 1477:7f5c5a425d03
user: Alexander Borisov <alexander.borisov at nginx.com>
date: Wed Jul 22 15:21:15 2020 +0300
description:
Introduced Query String module implementation.
In collaboration with Dmitry Volyntsev.
This closes #288 issue on GitHub.
diffstat:
auto/sources | 1 +
src/njs_builtin.c | 1 +
src/njs_main.h | 1 +
src/njs_query_string.c | 912 +++++++++++++++++++++++++++++++++++++++++++++++
src/njs_query_string.h | 12 +
src/njs_string.c | 34 -
src/njs_string.h | 34 +
src/njs_utf8.h | 12 +
src/test/njs_unit_test.c | 314 ++++++++++++++++
9 files changed, 1287 insertions(+), 34 deletions(-)
diffs (truncated from 1399 to 1000 lines):
diff -r 436d4c253d10 -r 7f5c5a425d03 auto/sources
--- a/auto/sources Wed Jul 22 15:21:14 2020 +0300
+++ b/auto/sources Wed Jul 22 15:21:15 2020 +0300
@@ -55,6 +55,7 @@ NJS_LIB_SRCS=" \
src/njs_array_buffer.c \
src/njs_typed_array.c \
src/njs_promise.c \
+ src/njs_query_string.c \
"
NJS_LIB_TEST_SRCS=" \
diff -r 436d4c253d10 -r 7f5c5a425d03 src/njs_builtin.c
--- a/src/njs_builtin.c Wed Jul 22 15:21:14 2020 +0300
+++ b/src/njs_builtin.c Wed Jul 22 15:21:15 2020 +0300
@@ -50,6 +50,7 @@ static const njs_object_init_t *njs_obj
static const njs_object_init_t *njs_module_init[] = {
&njs_fs_object_init,
&njs_crypto_object_init,
+ &njs_query_string_object_init,
NULL
};
diff -r 436d4c253d10 -r 7f5c5a425d03 src/njs_main.h
--- a/src/njs_main.h Wed Jul 22 15:21:14 2020 +0300
+++ b/src/njs_main.h Wed Jul 22 15:21:15 2020 +0300
@@ -79,6 +79,7 @@
#include <njs_fs.h>
#include <njs_crypto.h>
+#include <njs_query_string.h>
#include <njs_event.h>
#include <njs_module.h>
diff -r 436d4c253d10 -r 7f5c5a425d03 src/njs_query_string.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_query_string.c Wed Jul 22 15:21:15 2020 +0300
@@ -0,0 +1,912 @@
+
+/*
+ * Copyright (C) Alexander Borisov
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#include <njs_main.h>
+
+
+static const njs_value_t njs_escape_str = njs_string("escape");
+static const njs_value_t njs_unescape_str = njs_string("unescape");
+static const njs_value_t njs_encode_uri_str =
+ njs_long_string("encodeURIComponent");
+static const njs_value_t njs_decode_uri_str =
+ njs_long_string("decodeURIComponent");
+static const njs_value_t njs_max_keys_str = njs_string("maxKeys");
+
+
+static njs_int_t njs_query_string_escape(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t unused);
+static njs_int_t njs_query_string_unescape(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t unused);
+
+
+static njs_object_t *
+njs_query_string_object_alloc(njs_vm_t *vm)
+{
+ njs_object_t *obj;
+
+ obj = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_t));
+
+ if (njs_fast_path(obj != NULL)) {
+ njs_lvlhsh_init(&obj->hash);
+ njs_lvlhsh_init(&obj->shared_hash);
+ obj->type = NJS_OBJECT;
+ obj->shared = 0;
+ obj->extensible = 1;
+ obj->error_data = 0;
+ obj->fast_array = 0;
+
+ obj->__proto__ = NULL;
+ obj->slots = NULL;
+
+ return obj;
+ }
+
+ njs_memory_error(vm);
+
+ return NULL;
+}
+
+
+static njs_int_t
+njs_query_string_decode(njs_vm_t *vm, njs_value_t *value, const u_char *start,
+ size_t size)
+{
+ u_char *dst;
+ size_t length;
+ ssize_t str_size;
+ uint32_t cp;
+ njs_int_t ret;
+ njs_chb_t chain;
+ const u_char *p, *end;
+ njs_unicode_decode_t ctx;
+
+ static const int8_t hex[256]
+ njs_aligned(32) =
+ {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+
+ njs_chb_init(&chain, vm->mem_pool);
+ njs_utf8_decode_init(&ctx);
+
+ cp = 0;
+ length = 0;
+ ret = NJS_ERROR;
+
+ p = start;
+ end = p + size;
+
+ while (p < end) {
+ if (*p == '%' && end - p > 2 && hex[p[1]] >= 0 && hex[p[2]] >= 0) {
+ cp = njs_utf8_consume(&ctx, (hex[p[1]] << 4) | hex[p[2]]);
+ p += 3;
+
+ } else {
+ if (*p == '+') {
+ cp = ' ';
+ p++;
+
+ } else {
+ cp = njs_utf8_decode(&ctx, &p, end);
+ }
+ }
+
+ if (cp > NJS_UNICODE_MAX_CODEPOINT) {
+ if (cp == NJS_UNICODE_CONTINUE) {
+ continue;
+ }
+
+ cp = NJS_UNICODE_REPLACEMENT;
+ }
+
+ dst = njs_chb_reserve(&chain, 4);
+ if (njs_slow_path(dst == NULL)) {
+ return NJS_ERROR;
+ }
+
+ njs_chb_written(&chain, njs_utf8_encode(dst, cp) - dst);
+
+ length++;
+ }
+
+ if (njs_slow_path(cp == NJS_UNICODE_CONTINUE)) {
+ dst = njs_chb_reserve(&chain, 3);
+ if (njs_slow_path(dst == NULL)) {
+ return NJS_ERROR;
+ }
+
+ njs_chb_written(&chain,
+ njs_utf8_encode(dst, NJS_UNICODE_REPLACEMENT) - dst);
+
+ length++;
+ }
+
+ str_size = njs_chb_size(&chain);
+ if (njs_slow_path(str_size < 0)) {
+ goto failed;
+ }
+
+ dst = njs_string_alloc(vm, value, str_size, length);
+ if (njs_slow_path(dst == NULL)) {
+ goto failed;
+ }
+
+ njs_chb_join_to(&chain, dst);
+
+ ret = NJS_OK;
+
+failed:
+
+ njs_chb_destroy(&chain);
+
+ return ret;
+}
+
+
+njs_inline njs_bool_t
+njs_query_string_is_native_decoder(njs_function_t *decoder)
+{
+ return decoder->native && decoder->u.native == njs_query_string_unescape;
+}
+
+
+njs_inline njs_int_t
+njs_query_string_append(njs_vm_t *vm, njs_value_t *object, const u_char *key,
+ size_t key_size, const u_char *val, size_t val_size,
+ njs_function_t *decoder)
+{
+ uint32_t key_length, val_length;
+ njs_int_t ret;
+ njs_array_t *array;
+ njs_value_t name, value, retval;
+
+ if (njs_query_string_is_native_decoder(decoder)) {
+ ret = njs_query_string_decode(vm, &name, key, key_size);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ ret = njs_query_string_decode(vm, &value, val, val_size);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ } else {
+
+ key_length = njs_max(njs_utf8_length(key, key_size), 0);
+ ret = njs_string_new(vm, &name, key, key_size, key_length);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ if (key_size > 0) {
+ ret = njs_function_call(vm, decoder, &njs_value_undefined, &name, 1,
+ &name);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ if (!njs_is_string(&name)) {
+ njs_value_to_string(vm, &name, &name);
+ }
+ }
+
+ val_length = njs_max(njs_utf8_length(val, val_size), 0);
+ ret = njs_string_new(vm, &value, val, val_size, val_length);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ if (val_size > 0) {
+ ret = njs_function_call(vm, decoder, &njs_value_undefined, &value,
+ 1, &value);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ if (!njs_is_string(&value)) {
+ njs_value_to_string(vm, &value, &value);
+ }
+ }
+ }
+
+ ret = njs_value_property(vm, object, &name, &retval);
+
+ if (ret == NJS_OK) {
+ if (njs_is_array(&retval)) {
+ return njs_array_add(vm, njs_array(&retval), &value);
+ }
+
+ array = njs_array_alloc(vm, 1, 2, 0);
+ if (njs_slow_path(array == NULL)) {
+ return NJS_ERROR;
+ }
+
+ array->start[0] = retval;
+ array->start[1] = value;
+
+ njs_set_array(&value, array);
+ }
+
+ return njs_value_property_set(vm, object, &name, &value);
+}
+
+
+static u_char *
+njs_query_string_match(u_char *p, u_char *end, njs_str_t *v)
+{
+ size_t length;
+
+ length = v->length;
+
+ if (njs_fast_path(length == 1)) {
+ p = njs_strlchr(p, end, v->start[0]);
+
+ if (p == NULL) {
+ p = end;
+ }
+
+ return p;
+ }
+
+ while (p < (end - length)) {
+ if (memcmp(p, v->start, length) == 0) {
+ return p;
+ }
+
+ p++;
+ }
+
+ return end;
+}
+
+
+static njs_int_t
+njs_query_string_parse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused)
+{
+ size_t size;
+ u_char *end, *part, *key, *val;
+ int64_t max_keys, count;
+ njs_int_t ret;
+ njs_str_t str;
+ njs_value_t obj, value, *this, *string, *options, *arg;
+ njs_value_t val_sep, val_eq;
+ njs_object_t *object;
+ njs_function_t *decode;
+
+ njs_str_t sep = njs_str("&");
+ njs_str_t eq = njs_str("=");
+
+ count = 0;
+ decode = NULL;
+ max_keys = 1000;
+
+ object = njs_query_string_object_alloc(vm);
+ if (njs_slow_path(object == NULL)) {
+ return NJS_ERROR;
+ }
+
+ njs_set_object(&obj, object);
+
+ this = njs_arg(args, nargs, 0);
+ string = njs_arg(args, nargs, 1);
+
+ if (njs_slow_path(!njs_is_string(string)
+ || njs_string_length(string) == 0))
+ {
+ goto done;
+ }
+
+ njs_string_get(string, &str);
+
+ arg = njs_arg(args, nargs, 2);
+ if (!njs_is_null_or_undefined(arg)) {
+ ret = njs_value_to_string(vm, &val_sep, arg);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ if (njs_string_length(&val_sep) != 0) {
+ njs_string_get(&val_sep, &sep);
+ }
+ }
+
+ arg = njs_arg(args, nargs, 3);
+ if (!njs_is_null_or_undefined(arg)) {
+ ret = njs_value_to_string(vm, &val_eq, arg);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ if (njs_string_length(&val_eq) != 0) {
+ njs_string_get(&val_eq, &eq);
+ }
+ }
+
+ options = njs_arg(args, nargs, 4);
+
+ if (njs_is_object(options)) {
+ ret = njs_value_property(vm, options, njs_value_arg(&njs_max_keys_str),
+ &value);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ return ret;
+ }
+
+ ret = njs_value_to_integer(vm, &value, &max_keys);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ if (max_keys == 0) {
+ max_keys = INT64_MAX;
+ }
+
+ ret = njs_value_property(vm, options,
+ njs_value_arg(&njs_decode_uri_str), &value);
+
+ if (ret == NJS_OK) {
+ if (njs_slow_path(!njs_is_function(&value))) {
+ njs_type_error(vm,
+ "option decodeURIComponent is not a function");
+ return NJS_ERROR;
+ }
+
+ decode = njs_function(&value);
+ }
+ }
+
+ if (decode == NULL) {
+ ret = njs_value_property(vm, this, njs_value_arg(&njs_unescape_str),
+ &value);
+
+ if (ret != NJS_OK || !njs_is_function(&value)) {
+ njs_type_error(vm, "QueryString.unescape is not a function");
+ return NJS_ERROR;
+ }
+
+ decode = njs_function(&value);
+ }
+
+ key = str.start;
+ end = str.start + str.length;
+
+ do {
+ if (count++ == max_keys) {
+ break;
+ }
+
+ part = njs_query_string_match(key, end, &sep);
+
+ if (part == key) {
+ goto next;
+ }
+
+ val = njs_query_string_match(key, end, &eq);
+
+ size = val - key;
+
+ if (val != end) {
+ val += eq.length;
+ }
+
+ ret = njs_query_string_append(vm, &obj, key, size, val, part - val,
+ decode);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ next:
+
+ key = part + sep.length;
+
+ } while (key < end);
+
+done:
+
+ njs_set_object(&vm->retval, object);
+
+ return NJS_OK;
+}
+
+
+njs_inline njs_int_t
+njs_query_string_encode(njs_chb_t *chain, njs_str_t *str)
+{
+ size_t size;
+ u_char *p, *start, *end;
+
+ static const uint32_t escape[] = {
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0xfc00987d, /* 1111 1100 0000 0000 1001 1000 0111 1101 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0x78000001, /* 0111 1000 0000 0000 0000 0000 0000 0001 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */
+
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ };
+
+ if (chain->error) {
+ return NJS_ERROR;
+ }
+
+ if (str->length == 0) {
+ return 0;
+ }
+
+ p = str->start;
+ end = p + str->length;
+ size = str->length;
+
+ while (p < end) {
+ if (njs_need_escape(escape, *p++)) {
+ size += 2;
+ }
+ }
+
+ start = njs_chb_reserve(chain, size);
+ if (njs_slow_path(start == NULL)) {
+ return NJS_ERROR;
+ }
+
+ if (size == str->length) {
+ memcpy(start, str->start, str->length);
+ njs_chb_written(chain, str->length);
+ return str->length;
+ }
+
+ (void) njs_string_encode(escape, str->length, str->start, start);
+
+ njs_chb_written(chain, size);
+
+ return size;
+}
+
+
+njs_inline njs_bool_t
+njs_query_string_is_native_encoder(njs_function_t *encoder)
+{
+ return encoder->native && encoder->u.native == njs_query_string_escape;
+}
+
+
+njs_inline njs_int_t
+njs_query_string_encoder_call(njs_vm_t *vm, njs_chb_t *chain,
+ njs_function_t *encoder, njs_value_t *string)
+{
+ njs_str_t str;
+ njs_int_t ret;
+ njs_value_t retval;
+
+ if (njs_slow_path(!njs_is_string(string))) {
+ ret = njs_value_to_string(vm, string, string);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+ }
+
+ if (njs_fast_path(njs_query_string_is_native_encoder(encoder))) {
+ njs_string_get(string, &str);
+ return njs_query_string_encode(chain, &str);
+ }
+
+ ret = njs_function_call(vm, encoder, &njs_value_undefined, string, 1,
+ &retval);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ if (njs_slow_path(!njs_is_string(&retval))) {
+ ret = njs_value_to_string(vm, &retval, &retval);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+ }
+
+ njs_string_get(&retval, &str);
+
+ ret = njs_utf8_length(str.start, str.length);
+ if (ret < 0) {
+ njs_type_error(vm, "got non-UTF8 string from encoder");
+ return NJS_ERROR;
+ }
+
+ njs_chb_append_str(chain, &str);
+
+ return ret;
+}
+
+
+njs_inline njs_int_t
+njs_query_string_push(njs_vm_t *vm, njs_chb_t *chain, njs_value_t *key,
+ njs_value_t *value, njs_string_prop_t *eq, njs_function_t *encoder)
+{
+ njs_int_t ret, length;
+ njs_str_t str;
+
+ length = 0;
+
+ ret = njs_query_string_encoder_call(vm, chain, encoder, key);
+ if (njs_slow_path(ret < 0)) {
+ return NJS_ERROR;
+ }
+
+ length += ret;
+
+ if (!njs_is_string(value)) {
+ ret = njs_value_to_string(vm, value, value);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+ }
+
+ njs_string_get(value, &str);
+
+ if (str.length > 0) {
+ njs_chb_append(chain, eq->start, eq->size);
+ length += eq->length;
+
+ ret = njs_query_string_encoder_call(vm, chain, encoder, value);
+ if (njs_slow_path(ret < 0)) {
+ return NJS_ERROR;
+ }
+
+ length += ret;
+ }
+
+ return length;
+}
+
+
+static njs_int_t
+njs_query_string_stringify(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused)
+{
+ u_char *p;
+ int64_t len;
+ ssize_t size;
+ uint32_t n, i;
+ uint64_t length;
+ njs_int_t ret;
+ njs_chb_t chain;
+ njs_value_t value, retval, *string, *this, *object, *arg, *options;
+ njs_array_t *keys, *array;
+ njs_function_t *encode;
+ njs_string_prop_t sep, eq;
+
+ njs_value_t val_sep = njs_string("&");
+ njs_value_t val_eq = njs_string("=");
+
+ (void) njs_string_prop(&sep, &val_sep);
+ (void) njs_string_prop(&eq, &val_eq);
+
+ encode = NULL;
+ this = njs_arg(args, nargs, 0);
+ object = njs_arg(args, nargs, 1);
+
+ if (njs_slow_path(!njs_is_object(object))) {
+ vm->retval = njs_string_empty;
+ return NJS_OK;
+ }
+
+ arg = njs_arg(args, nargs, 2);
+ if (!njs_is_null_or_undefined(arg)) {
+ ret = njs_value_to_string(vm, arg, arg);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ if (njs_string_length(arg) > 0) {
+ (void) njs_string_prop(&sep, arg);
+ }
+ }
+
+ arg = njs_arg(args, nargs, 3);
+ if (!njs_is_null_or_undefined(arg)) {
+ ret = njs_value_to_string(vm, arg, arg);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ if (njs_string_length(arg) > 0) {
+ (void) njs_string_prop(&eq, arg);
+ }
+ }
+
+ options = njs_arg(args, nargs, 4);
+
+ if (njs_is_object(options)) {
+ ret = njs_value_property(vm, options,
+ njs_value_arg(&njs_encode_uri_str), &value);
+
+ if (ret == NJS_OK) {
+ if (njs_slow_path(!njs_is_function(&value))) {
+ njs_type_error(vm,
+ "option encodeURIComponent is not a function");
+ return NJS_ERROR;
+ }
+
+ encode = njs_function(&value);
+ }
+ }
+
+ if (encode == NULL) {
+ ret = njs_value_property(vm, this, njs_value_arg(&njs_escape_str),
+ &value);
+
+ if (ret != NJS_OK || !njs_is_function(&value)) {
+ njs_type_error(vm, "QueryString.escape is not a function");
+ return NJS_ERROR;
+ }
+
+ encode = njs_function(&value);
+ }
+
+ njs_chb_init(&chain, vm->mem_pool);
+
+ keys = njs_value_own_enumerate(vm, object, NJS_ENUM_KEYS, NJS_ENUM_STRING,
+ 1);
+ if (njs_slow_path(keys == NULL)) {
+ return NJS_ERROR;
+ }
+
+ for (n = 0, length = 0; n < keys->length; n++) {
+ string = &keys->start[n];
+
+ ret = njs_value_property(vm, object, string, &value);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ goto failed;
+ }
+
+ if (njs_is_array(&value)) {
+
+ if (njs_is_fast_array(&value)) {
+ array = njs_array(&value);
+
+ for (i = 0; i < array->length; i++) {
+ if (i != 0) {
+ njs_chb_append(&chain, sep.start, sep.size);
+ length += sep.length;
+ }
+
+ ret = njs_query_string_push(vm, &chain, string,
+ &array->start[i], &eq, encode);
+ if (njs_slow_path(ret < 0)) {
+ ret = NJS_ERROR;
+ goto failed;
+ }
+
+ length += ret;
+ }
+
+ continue;
+ }
+
+ ret = njs_object_length(vm, &value, &len);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ goto failed;
+ }
+
+ for (i = 0; i < len; i++) {
+ ret = njs_value_property_i64(vm, &value, i, &retval);
+ if (njs_slow_path(ret == NJS_ERROR)) {
+ goto failed;
+ }
+
+ if (i != 0) {
+ njs_chb_append(&chain, sep.start, sep.size);
+ length += sep.length;
+ }
+
+ ret = njs_query_string_push(vm, &chain, string, &retval, &eq,
+ encode);
+ if (njs_slow_path(ret < 0)) {
+ ret = NJS_ERROR;
+ goto failed;
+ }
+
+ length += ret;
+ }
+
+ continue;
+ }
+
+ if (n != 0) {
+ njs_chb_append(&chain, sep.start, sep.size);
+ length += sep.length;
+ }
+
+ ret = njs_query_string_push(vm, &chain, string, &value, &eq, encode);
+ if (njs_slow_path(ret < 0)) {
+ ret = NJS_ERROR;
+ goto failed;
+ }
+
+ length += ret;
+ }
+
+ size = njs_chb_size(&chain);
+ if (njs_slow_path(size < 0)) {
+ njs_memory_error(vm);
+ return NJS_ERROR;
+ }
+
+ p = njs_string_alloc(vm, &vm->retval, size, length);
+ if (njs_slow_path(p == NULL)) {
+ return NJS_ERROR;
+ }
+
+ njs_chb_join_to(&chain, p);
+
+ ret = NJS_OK;
+
+failed:
+
+ njs_chb_destroy(&chain);
+
+ return ret;
+}
+
+
+static njs_int_t
+njs_query_string_escape(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused)
+{
+ u_char *p;
+ ssize_t size, length;
+ njs_int_t ret;
+ njs_str_t str;
+ njs_chb_t chain;
+ njs_value_t *string, value;
+
+ string = njs_arg(args, nargs, 1);
+
+ if (!njs_is_string(string)) {
+ ret = njs_value_to_string(vm, &value, string);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ string = &value;
+ }
+
+ njs_string_get(string, &str);
+
+ njs_chb_init(&chain, vm->mem_pool);
+
+ length = njs_query_string_encode(&chain, &str);
+ if (njs_slow_path(length < 0)) {
+ return NJS_ERROR;
+ }
+
+ size = njs_chb_size(&chain);
+
+ p = njs_string_alloc(vm, &vm->retval, size, length);
+ if (njs_slow_path(p == NULL)) {
+ return NJS_ERROR;
+ }
+
+ njs_chb_join_to(&chain, p);
+
+ njs_chb_destroy(&chain);
+
+ return NJS_OK;
+}
+
+
+static njs_int_t
+njs_query_string_unescape(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused)
+{
+ njs_int_t ret;
+ njs_str_t str;
+ njs_value_t *string, value;
+
+ string = njs_arg(args, nargs, 1);
+
+ if (!njs_is_string(string)) {
+ ret = njs_value_to_string(vm, &value, string);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ string = &value;
+ }
+
+ njs_string_get(string, &str);
+
+ return njs_query_string_decode(vm, &vm->retval, str.start, str.length);
+}
+
+
+static const njs_object_prop_t njs_query_string_object_properties[] =
+{
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("name"),
+ .value = njs_string("querystring"),
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("parse"),
+ .value = njs_native_function(njs_query_string_parse, 4),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("stringify"),
+ .value = njs_native_function(njs_query_string_stringify, 4),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("escape"),
+ .value = njs_native_function(njs_query_string_escape, 1),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("unescape"),
+ .value = njs_native_function(njs_query_string_unescape, 1),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("decode"),
+ .value = njs_native_function(njs_query_string_parse, 4),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("encode"),
+ .value = njs_native_function(njs_query_string_stringify, 4),
+ .writable = 1,
+ .configurable = 1,
+ },
+};
+
+
+const njs_object_init_t njs_query_string_object_init = {
+ njs_query_string_object_properties,
+ njs_nitems(njs_query_string_object_properties),
+};
diff -r 436d4c253d10 -r 7f5c5a425d03 src/njs_query_string.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_query_string.h Wed Jul 22 15:21:15 2020 +0300
@@ -0,0 +1,12 @@
+
+/*
+ * Copyright (C) Alexander Borisov
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_QUERY_STRING_H_INCLUDED_
+#define _NJS_QUERY_STRING_H_INCLUDED_
+
+extern const njs_object_init_t njs_query_string_object_init;
+
+#endif /* _NJS_QUERY_STRING_H_INCLUDED_ */
diff -r 436d4c253d10 -r 7f5c5a425d03 src/njs_string.c
--- a/src/njs_string.c Wed Jul 22 15:21:14 2020 +0300
+++ b/src/njs_string.c Wed Jul 22 15:21:15 2020 +0300
@@ -4172,40 +4172,6 @@ const njs_object_init_t njs_string_inst
};
-njs_inline njs_bool_t
-njs_need_escape(const uint32_t *escape, uint32_t byte)
-{
- return ((escape[byte >> 5] & ((uint32_t) 1 << (byte & 0x1f))) != 0);
-}
-
-
-njs_inline u_char *
-njs_string_encode(const uint32_t *escape, size_t size, const u_char *src,
- u_char *dst)
-{
- uint8_t byte;
- static const u_char hex[16] = "0123456789ABCDEF";
-
- do {
- byte = *src++;
-
- if (njs_need_escape(escape, byte)) {
- *dst++ = '%';
- *dst++ = hex[byte >> 4];
- *dst++ = hex[byte & 0xf];
-
- } else {
- *dst++ = byte;
- }
-
- size--;
-
More information about the nginx-devel
mailing list