[njs] Added "zlib" module.

Dmitry Volyntsev xeioex at nginx.com
Wed Mar 29 04:38:51 UTC 2023


details:   https://hg.nginx.org/njs/rev/5e7fc8efebdc
branches:  
changeset: 2077:5e7fc8efebdc
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Mon Mar 27 22:41:27 2023 -0700
description:
Added "zlib" module.

    - zlib.deflateRawSync(string|buffer, options?) compresses data using
        deflate, and do not append a zlib header, returns Buffer.
    - zlib.deflateSync(string|buffer, options?) compresses data using
        deflate, returns Buffer.
    - zlib.inflateRawSync(string|buffer) decompresses a raw deflate
        stream, returns Buffer.
    - zlib.inflateSync(string|buffer) decompresses a deflate stream,
        return Buffer.

diffstat:

 auto/help                  |    4 +
 auto/modules               |    8 +
 auto/options               |    2 +
 auto/summary               |    4 +
 auto/zlib                  |   61 ++++
 configure                  |    1 +
 external/njs_zlib_module.c |  566 +++++++++++++++++++++++++++++++++++++++++++++
 nginx/config               |    5 +-
 nginx/config.make          |    2 +-
 nginx/ngx_js.c             |    2 +
 src/test/njs_unit_test.c   |   79 ++++++
 11 files changed, 731 insertions(+), 3 deletions(-)

diffs (867 lines):

diff -r ec007866a53b -r 5e7fc8efebdc auto/help
--- a/auto/help	Wed Mar 22 15:22:37 2023 +0200
+++ b/auto/help	Mon Mar 27 22:41:27 2023 -0700
@@ -39,6 +39,10 @@ default: "$NJS_LD_OPT"
                             enabled libxml2 dependant code is not built as a
                             part of libnjs.a.
 
+  --no-zlib                 disabled zlib discovery. When this option is
+                            enabled zlib dependant code is not built as a
+                            part of libnjs.a.
+
   --address-sanitizer=YES   enables build with address sanitizer, \
 default: "$NJS_ADDRESS_SANITIZER"
   --addr2line=YES           enables native function symbolization, \
diff -r ec007866a53b -r 5e7fc8efebdc auto/modules
--- a/auto/modules	Wed Mar 22 15:22:37 2023 +0200
+++ b/auto/modules	Mon Mar 27 22:41:27 2023 -0700
@@ -29,6 +29,14 @@ if [ $NJS_LIBXML2 = YES -a $NJS_HAVE_LIB
 	. auto/module
 fi
 
+if [ $NJS_ZLIB = YES -a $NJS_HAVE_ZLIB = YES ]; then
+	njs_module_name=njs_zlib_module
+	njs_module_incs=
+	njs_module_srcs=external/njs_zlib_module.c
+
+	. auto/module
+fi
+
 njs_module_name=njs_fs_module
 njs_module_incs=
 njs_module_srcs=external/njs_fs_module.c
diff -r ec007866a53b -r 5e7fc8efebdc auto/options
--- a/auto/options	Wed Mar 22 15:22:37 2023 +0200
+++ b/auto/options	Mon Mar 27 22:41:27 2023 -0700
@@ -17,6 +17,7 @@ NJS_TEST262=YES
 
 NJS_OPENSSL=YES
 NJS_LIBXML2=YES
+NJS_ZLIB=YES
 
 NJS_PCRE=YES
 NJS_TRY_PCRE2=YES
@@ -50,6 +51,7 @@ do
 
         --no-openssl)                    NJS_OPENSSL=NO                      ;;
         --no-libxml2)                    NJS_LIBXML2=NO                      ;;
+        --no-zlib)                       NJS_ZLIB=NO                         ;;
 
         --no-pcre)                       NJS_PCRE=NO                         ;;
         --no-pcre2)                      NJS_TRY_PCRE2=NO                    ;;
diff -r ec007866a53b -r 5e7fc8efebdc auto/summary
--- a/auto/summary	Wed Mar 22 15:22:37 2023 +0200
+++ b/auto/summary	Mon Mar 27 22:41:27 2023 -0700
@@ -26,6 +26,10 @@ if [ $NJS_HAVE_LIBXML2 = YES ]; then
   echo " + using libxml2 library: $NJS_LIBXML2_LIB"
 fi
 
+if [ $NJS_HAVE_ZLIB = YES ]; then
+  echo " + using zlib library: $NJS_ZLIB_LIB"
+fi
+
 if [ $NJS_HAVE_COMPUTED_GOTO = YES ]; then
   echo " + using computed goto"
 fi
diff -r ec007866a53b -r 5e7fc8efebdc auto/zlib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/auto/zlib	Mon Mar 27 22:41:27 2023 -0700
@@ -0,0 +1,61 @@
+
+# Copyright (C) Dmitry Volyntsev
+# Copyright (C) NGINX, Inc.
+
+NJS_ZLIB_LIB=
+NJS_HAVE_ZLIB=NO
+
+if [ $NJS_ZLIB = YES ]; then
+    njs_found=no
+    njs_feature_name=NJS_HAVE_ZLIB
+    njs_feature_run=no
+    njs_feature_test="#include <zlib.h>
+
+                      int main() {
+                          int       rc;
+                          z_stream  z;
+
+                          rc = deflate(&z, Z_NO_FLUSH);
+
+                          return (rc == Z_OK) ? 0 : 1;
+                      }"
+
+
+    if /bin/sh -c "(pkg-config zlib --exists)" >> $NJS_AUTOCONF_ERR 2>&1; then
+
+        # pkg-config
+
+        njs_feature="zlib via pkg-config"
+        njs_feature_incs=`pkg-config zlib --cflags | sed -n -e 's/.*-I *\([^ ][^ ]*\).*/\1/p'`
+        njs_feature_libs=`pkg-config zlib --libs`
+
+        . auto/feature
+    fi
+
+    if [ $njs_found = no ]; then
+
+        njs_feature="zlib"
+        njs_feature_libs="-lz"
+
+        . auto/feature
+    fi
+
+    if [ $njs_found = yes ]; then
+        njs_feature="zlib version"
+        njs_feature_name=NJS_ZLIB_VERSION
+        njs_feature_run=value
+        njs_feature_test="#include <stdio.h>
+                          #include <zlib.h>
+
+                          int main() {
+                              printf(\"\\\"%s\\\"\", zlibVersion());
+                              return 0;
+                          }"
+        . auto/feature
+
+        NJS_HAVE_ZLIB=YES
+        NJS_ZLIB_LIB="$njs_feature_libs"
+        NJS_LIB_INCS="$NJS_LIB_INCS $njs_feature_incs"
+        NJS_LIB_AUX_LIBS="$NJS_LIB_AUX_LIBS $njs_feature_libs"
+    fi
+fi
diff -r ec007866a53b -r 5e7fc8efebdc configure
--- a/configure	Wed Mar 22 15:22:37 2023 +0200
+++ b/configure	Mon Mar 27 22:41:27 2023 -0700
@@ -52,6 +52,7 @@ NJS_LIB_AUX_LIBS=
 . auto/readline
 . auto/openssl
 . auto/libxml2
+. auto/zlib
 . auto/libbfd
 . auto/link
 
diff -r ec007866a53b -r 5e7fc8efebdc external/njs_zlib_module.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/external/njs_zlib_module.c	Mon Mar 27 22:41:27 2023 -0700
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#include <njs.h>
+#include <string.h>
+#include <zlib.h>
+
+#define NJS_ZLIB_CHUNK_SIZE  1024
+
+static njs_int_t njs_zlib_ext_deflate(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused);
+static njs_int_t njs_zlib_ext_inflate(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused);
+njs_int_t njs_zlib_contant(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 njs_zlib_init(njs_vm_t *vm);
+static void *njs_zlib_alloc(void *opaque, u_int items, u_int size);
+static void njs_zlib_free(void *opaque, void *address);
+
+
+static njs_external_t  njs_ext_zlib_constants[] = {
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("Z_NO_COMPRESSION"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_zlib_contant,
+            .magic32 = Z_NO_COMPRESSION,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("Z_BEST_SPEED"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_zlib_contant,
+            .magic32 = Z_BEST_SPEED,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("Z_BEST_COMPRESSION"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_zlib_contant,
+            .magic32 = Z_BEST_COMPRESSION,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("Z_FILTERED"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_zlib_contant,
+            .magic32 = Z_FILTERED,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("Z_HUFFMAN_ONLY"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_zlib_contant,
+            .magic32 = Z_HUFFMAN_ONLY,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("Z_RLE"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_zlib_contant,
+            .magic32 = Z_RLE,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("Z_FIXED"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_zlib_contant,
+            .magic32 = Z_FIXED,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("Z_DEFAULT_STRATEGY"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_zlib_contant,
+            .magic32 = Z_DEFAULT_STRATEGY,
+        }
+    },
+
+};
+
+
+static njs_external_t  njs_ext_zlib[] = {
+
+    {
+        .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
+        .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
+        .u.property = {
+            .value = "zlib",
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("deflateRawSync"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_zlib_ext_deflate,
+            .magic8 = 1,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("deflateSync"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_zlib_ext_deflate,
+            .magic8 = 0,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("inflateRawSync"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_zlib_ext_inflate,
+            .magic8 = 1,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("inflateSync"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_zlib_ext_inflate,
+            .magic8 = 0,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_OBJECT,
+        .name.string = njs_str("constants"),
+        .writable = 1,
+        .configurable = 1,
+        .u.object = {
+            .properties = njs_ext_zlib_constants,
+            .nproperties = njs_nitems(njs_ext_zlib_constants),
+        }
+    },
+
+};
+
+
+njs_module_t  njs_zlib_module = {
+    .name = njs_str("zlib"),
+    .init = njs_zlib_init,
+};
+
+
+static njs_int_t
+njs_zlib_ext_deflate(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t raw)
+{
+    int                 rc, level, mem_level, strategy, window_bits;
+    u_char              *buffer;
+    size_t              chunk_size;
+    ssize_t             size;
+    njs_chb_t           chain;
+    z_stream            stream;
+    njs_int_t           ret;
+    njs_str_t           data, dictionary;
+    njs_value_t         *options, *value;
+    njs_opaque_value_t  lvalue;
+
+    static const njs_str_t chunk_size_key = njs_str("chunkSize");
+    static const njs_str_t dict_key = njs_str("dictionary");
+    static const njs_str_t level_key = njs_str("level");
+    static const njs_str_t mem_level_key = njs_str("memLevel");
+    static const njs_str_t strategy_key = njs_str("strategy");
+    static const njs_str_t window_bits_key = njs_str("windowBits");
+
+    ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 1));
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    chunk_size = NJS_ZLIB_CHUNK_SIZE;
+    dictionary.start = NULL;
+    mem_level = 8;
+    level = Z_DEFAULT_COMPRESSION;
+    strategy = Z_DEFAULT_STRATEGY;
+    window_bits = raw ? -MAX_WBITS : MAX_WBITS;
+
+    options = njs_arg(args, nargs, 2);
+
+    if (njs_value_is_object(options)) {
+        value = njs_vm_object_prop(vm, options, &chunk_size_key, &lvalue);
+        if (value != NULL) {
+            chunk_size = njs_value_number(value);
+
+            if (njs_slow_path(chunk_size < 64)) {
+                njs_vm_error(vm, "chunkSize must be >= 64");
+                return NJS_ERROR;
+            }
+        }
+
+        value = njs_vm_object_prop(vm, options, &level_key, &lvalue);
+        if (value != NULL) {
+            level = njs_value_number(value);
+
+            if (njs_slow_path(level < Z_DEFAULT_COMPRESSION
+                              || level > Z_BEST_COMPRESSION))
+            {
+                njs_vm_error(vm, "level must be in the range %d..%d",
+                             Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION);
+                return NJS_ERROR;
+            }
+        }
+
+        value = njs_vm_object_prop(vm, options, &window_bits_key, &lvalue);
+        if (value != NULL) {
+            window_bits = njs_value_number(value);
+
+            if (raw) {
+                if (njs_slow_path(window_bits < -15 || window_bits > -9)) {
+                    njs_vm_error(vm, "windowBits must be in the range -15..-9");
+                    return NJS_ERROR;
+                }
+
+            } else {
+                if (njs_slow_path(window_bits < 9 || window_bits > 15)) {
+                    njs_vm_error(vm, "windowBits must be in the range 9..15");
+                    return NJS_ERROR;
+                }
+            }
+        }
+
+        value = njs_vm_object_prop(vm, options, &mem_level_key, &lvalue);
+        if (value != NULL) {
+            mem_level = njs_value_number(value);
+
+            if (njs_slow_path(mem_level < 1 || mem_level > 9)) {
+                njs_vm_error(vm, "memLevel must be in the range 0..9");
+                return NJS_ERROR;
+            }
+        }
+
+        value = njs_vm_object_prop(vm, options, &strategy_key, &lvalue);
+        if (value != NULL) {
+            strategy = njs_value_number(value);
+
+            switch (strategy) {
+            case Z_FILTERED:
+            case Z_HUFFMAN_ONLY:
+            case Z_RLE:
+            case Z_FIXED:
+            case Z_DEFAULT_STRATEGY:
+                break;
+
+            default:
+                njs_vm_error(vm, "unknown strategy: %d", strategy);
+                return NJS_ERROR;
+            }
+        }
+
+        value = njs_vm_object_prop(vm, options, &dict_key, &lvalue);
+        if (value != NULL) {
+            ret = njs_vm_value_to_bytes(vm, &dictionary, value);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return NJS_ERROR;
+            }
+        }
+    }
+
+    stream.next_in = data.start;
+    stream.avail_in = data.length;
+
+    stream.zalloc = njs_zlib_alloc;
+    stream.zfree = njs_zlib_free;
+    stream.opaque = njs_vm_memory_pool(vm);
+
+    rc = deflateInit2(&stream, level, Z_DEFLATED, window_bits, mem_level,
+                      strategy);
+    if (njs_slow_path(rc != Z_OK)) {
+        njs_vm_error(vm, "deflateInit2() failed");
+        return NJS_ERROR;
+    }
+
+    if (dictionary.start != NULL) {
+        rc = deflateSetDictionary(&stream, dictionary.start, dictionary.length);
+        if (njs_slow_path(rc != Z_OK)) {
+            njs_vm_error(vm, "deflateSetDictionary() failed");
+            return NJS_ERROR;
+        }
+    }
+
+    njs_chb_init(&chain, njs_vm_memory_pool(vm));
+
+    do {
+        stream.next_out = njs_chb_reserve(&chain, chunk_size);
+        if (njs_slow_path(stream.next_out == NULL)) {
+            njs_vm_memory_error(vm);
+            goto fail;
+        }
+
+        stream.avail_out = chunk_size;
+
+        rc = deflate(&stream, Z_FINISH);
+        if (njs_slow_path(rc < 0)) {
+            njs_vm_error(vm, "failed to deflate the data: %s", stream.msg);
+            goto fail;
+        }
+
+        njs_chb_written(&chain, chunk_size - stream.avail_out);
+
+    } while (stream.avail_out == 0);
+
+    deflateEnd(&stream);
+
+    size = njs_chb_size(&chain);
+    if (njs_slow_path(size < 0)) {
+        njs_vm_memory_error(vm);
+        return NJS_ERROR;
+    }
+
+    buffer = njs_mp_alloc(njs_vm_memory_pool(vm), size);
+    if (njs_slow_path(buffer == NULL)) {
+        return NJS_ERROR;
+    }
+
+    njs_chb_join_to(&chain, buffer);
+
+    njs_chb_destroy(&chain);
+
+    return njs_vm_value_buffer_set(vm, njs_vm_retval(vm), buffer, size);
+
+fail:
+
+    deflateEnd(&stream);
+    njs_chb_destroy(&chain);
+
+    return NJS_ERROR;
+}
+
+
+static njs_int_t
+njs_zlib_ext_inflate(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t raw)
+{
+    int                 rc, window_bits;
+    u_char              *buffer;
+    size_t              chunk_size;
+    ssize_t             size;
+    njs_chb_t           chain;
+    z_stream            stream;
+    njs_int_t           ret;
+    njs_str_t           data, dictionary;
+    njs_value_t         *options, *value;
+    njs_opaque_value_t  lvalue;
+
+    static const njs_str_t chunk_size_key = njs_str("chunkSize");
+    static const njs_str_t dict_key = njs_str("dictionary");
+    static const njs_str_t window_bits_key = njs_str("windowBits");
+
+    ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 1));
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    chunk_size = NJS_ZLIB_CHUNK_SIZE;
+    dictionary.start = NULL;
+    window_bits = raw ? -MAX_WBITS : MAX_WBITS;
+
+    options = njs_arg(args, nargs, 2);
+
+    if (njs_value_is_object(options)) {
+        value = njs_vm_object_prop(vm, options, &chunk_size_key, &lvalue);
+        if (value != NULL) {
+            chunk_size = njs_value_number(value);
+
+            if (njs_slow_path(chunk_size < 64)) {
+                njs_vm_error(vm, "chunkSize must be >= 64");
+                return NJS_ERROR;
+            }
+        }
+
+        value = njs_vm_object_prop(vm, options, &window_bits_key, &lvalue);
+        if (value != NULL) {
+            window_bits = njs_value_number(value);
+
+            if (raw) {
+                if (njs_slow_path(window_bits < -15 || window_bits > -8)) {
+                    njs_vm_error(vm, "windowBits must be in the range -15..-8");
+                    return NJS_ERROR;
+                }
+
+            } else {
+                if (njs_slow_path(window_bits < 8 || window_bits > 15)) {
+                    njs_vm_error(vm, "windowBits must be in the range 8..15");
+                    return NJS_ERROR;
+                }
+            }
+        }
+
+        value = njs_vm_object_prop(vm, options, &dict_key, &lvalue);
+        if (value != NULL) {
+            ret = njs_vm_value_to_bytes(vm, &dictionary, value);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return NJS_ERROR;
+            }
+        }
+    }
+
+    stream.next_in = data.start;
+    stream.avail_in = data.length;
+
+    stream.zalloc = njs_zlib_alloc;
+    stream.zfree = njs_zlib_free;
+    stream.opaque = njs_vm_memory_pool(vm);
+
+    rc = inflateInit2(&stream, window_bits);
+    if (njs_slow_path(rc != Z_OK)) {
+        njs_vm_error(vm, "inflateInit2() failed");
+        return NJS_ERROR;
+    }
+
+    if (dictionary.start != NULL) {
+        rc = inflateSetDictionary(&stream, dictionary.start, dictionary.length);
+        if (njs_slow_path(rc != Z_OK)) {
+            njs_vm_error(vm, "deflateSetDictionary() failed");
+            return NJS_ERROR;
+        }
+    }
+
+    njs_chb_init(&chain, njs_vm_memory_pool(vm));
+
+    while (stream.avail_in > 0) {
+        stream.next_out = njs_chb_reserve(&chain, chunk_size);
+        if (njs_slow_path(stream.next_out == NULL)) {
+            njs_vm_memory_error(vm);
+            goto fail;
+        }
+
+        stream.avail_out = chunk_size;
+
+        rc = inflate(&stream, Z_NO_FLUSH);
+        if (njs_slow_path(rc < 0)) {
+            njs_vm_error(vm, "failed to inflate the compressed data: %s",
+                         stream.msg);
+            goto fail;
+        }
+
+        if (rc == Z_NEED_DICT) {
+            njs_vm_error(vm, "failed to inflate, dictionary is required");
+            goto fail;
+        }
+
+        njs_chb_written(&chain, chunk_size - stream.avail_out);
+    }
+
+    rc = inflateEnd(&stream);
+    if (njs_slow_path(rc != Z_OK)) {
+        njs_vm_error(vm, "failed to end the inflate stream");
+        return NJS_ERROR;
+    }
+
+    size = njs_chb_size(&chain);
+    if (njs_slow_path(size < 0)) {
+        njs_vm_memory_error(vm);
+        return NJS_ERROR;
+    }
+
+    buffer = njs_mp_alloc(njs_vm_memory_pool(vm), size);
+    if (njs_slow_path(buffer == NULL)) {
+        return NJS_ERROR;
+    }
+
+    njs_chb_join_to(&chain, buffer);
+
+    njs_chb_destroy(&chain);
+
+    return njs_vm_value_buffer_set(vm, njs_vm_retval(vm), buffer, size);
+
+fail:
+
+    inflateEnd(&stream);
+    njs_chb_destroy(&chain);
+
+    return NJS_ERROR;
+}
+
+
+njs_int_t
+njs_zlib_contant(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
+{
+    njs_value_number_set(retval,  njs_vm_prop_magic32(prop));
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_zlib_init(njs_vm_t *vm)
+{
+    njs_int_t           ret, proto_id;
+    njs_mod_t           *module;
+    njs_opaque_value_t  value;
+
+    proto_id = njs_vm_external_prototype(vm, njs_ext_zlib,
+                                         njs_nitems(njs_ext_zlib));
+    if (njs_slow_path(proto_id < 0)) {
+        return NJS_ERROR;
+    }
+
+    ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    module = njs_vm_add_module(vm, &njs_str_value("zlib"),
+                               njs_value_arg(&value));
+    if (njs_slow_path(module == NULL)) {
+        return NJS_ERROR;
+    }
+
+    return NJS_OK;
+}
+
+
+static void *
+njs_zlib_alloc(void *opaque, u_int items, u_int size)
+{
+    return njs_mp_alloc(opaque, items * size);
+}
+
+
+static void
+njs_zlib_free(void *opaque, void *address)
+{
+    /* Do nothing. */
+}
+
diff -r ec007866a53b -r 5e7fc8efebdc nginx/config
--- a/nginx/config	Wed Mar 22 15:22:37 2023 +0200
+++ b/nginx/config	Mon Mar 27 22:41:27 2023 -0700
@@ -6,6 +6,7 @@ NJS_SRCS="$ngx_addon_dir/ngx_js.c \
     $ngx_addon_dir/ngx_js_fetch.c \
     $ngx_addon_dir/ngx_js_regex.c \
     $ngx_addon_dir/../external/njs_webcrypto_module.c
+    $ngx_addon_dir/../external/njs_zlib_module.c
     $ngx_addon_dir/../external/njs_xml_module.c"
 
 if [ $HTTP != NO ]; then
@@ -14,7 +15,7 @@ if [ $HTTP != NO ]; then
     ngx_module_incs="$ngx_addon_dir/../src $ngx_addon_dir/../build"
     ngx_module_deps="$ngx_addon_dir/../build/libnjs.a $NJS_DEPS"
     ngx_module_srcs="$ngx_addon_dir/ngx_http_js_module.c $NJS_SRCS"
-    ngx_module_libs="PCRE OPENSSL LIBXSLT $ngx_addon_dir/../build/libnjs.a -lm"
+    ngx_module_libs="PCRE OPENSSL ZLIB LIBXSLT $ngx_addon_dir/../build/libnjs.a -lm"
 
     . auto/module
 
@@ -29,7 +30,7 @@ if [ $STREAM != NO ]; then
     ngx_module_incs="$ngx_addon_dir/../src $ngx_addon_dir/../build"
     ngx_module_deps="$ngx_addon_dir/../build/libnjs.a $NJS_DEPS"
     ngx_module_srcs="$ngx_addon_dir/ngx_stream_js_module.c $NJS_SRCS"
-    ngx_module_libs="PCRE OPENSSL LIBXSLT $ngx_addon_dir/../build/libnjs.a -lm"
+    ngx_module_libs="PCRE OPENSSL ZLIB LIBXSLT $ngx_addon_dir/../build/libnjs.a -lm"
 
     . auto/module
 fi
diff -r ec007866a53b -r 5e7fc8efebdc nginx/config.make
--- a/nginx/config.make	Wed Mar 22 15:22:37 2023 +0200
+++ b/nginx/config.make	Mon Mar 27 22:41:27 2023 -0700
@@ -3,7 +3,7 @@ cat << END                              
 $ngx_addon_dir/../build/libnjs.a: $NGX_MAKEFILE
 	cd $ngx_addon_dir/.. \\
 	&& if [ -f build/Makefile ]; then \$(MAKE) clean; fi \\
-	&& CFLAGS="\$(CFLAGS)" CC="\$(CC)" ./configure --no-openssl --no-libxml2 --no-pcre \\
+	&& CFLAGS="\$(CFLAGS)" CC="\$(CC)" ./configure --no-openssl --no-libxml2 --no-zlib --no-pcre \\
 	&& \$(MAKE) libnjs
 
 END
diff -r ec007866a53b -r 5e7fc8efebdc nginx/ngx_js.c
--- a/nginx/ngx_js.c	Wed Mar 22 15:22:37 2023 +0200
+++ b/nginx/ngx_js.c	Mon Mar 27 22:41:27 2023 -0700
@@ -19,6 +19,7 @@ static void ngx_js_cleanup_vm(void *data
 
 extern njs_module_t  njs_webcrypto_module;
 extern njs_module_t  njs_xml_module;
+extern njs_module_t  njs_zlib_module;
 
 
 static njs_external_t  ngx_js_ext_core[] = {
@@ -89,6 +90,7 @@ static njs_external_t  ngx_js_ext_core[]
 njs_module_t *njs_js_addon_modules[] = {
     &njs_webcrypto_module,
     &njs_xml_module,
+    &njs_zlib_module,
     NULL,
 };
 
diff -r ec007866a53b -r 5e7fc8efebdc src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Wed Mar 22 15:22:37 2023 +0200
+++ b/src/test/njs_unit_test.c	Mon Mar 27 22:41:27 2023 -0700
@@ -22153,6 +22153,74 @@ static njs_unit_test_t  njs_xml_test[] =
 };
 
 
+static njs_unit_test_t  njs_zlib_test[] =
+{
+    { njs_str("const zlib = require('zlib');"
+              "['C3f0dgQA', 'O7fx3KZzmwE=']"
+              ".map(v => zlib.inflateRawSync(Buffer.from(v, 'base64')).toString())"),
+      njs_str("WAKA,αβγ") },
+
+    { njs_str("const zlib = require('zlib');"
+              "['eJwLd/R2BAAC+gEl', 'eJw7t/HcpnObAQ/sBIE=']"
+              ".map(v => zlib.inflateSync(Buffer.from(v, 'base64')).toString())"),
+      njs_str("WAKA,αβγ") },
+
+    { njs_str("const zlib = require('zlib');"
+              "['WAKA', 'αβγ']"
+              ".map(v => zlib.deflateRawSync(v).toString('base64'))"),
+      njs_str("C3f0dgQA,O7fx3KZzmwE=") },
+
+    { njs_str("const zlib = require('zlib');"
+              "['WAKA', 'αβγ']"
+              ".map(v => zlib.deflateRawSync(v, {dictionary: Buffer.from('WAKA')}).toString('base64'))"),
+      njs_str("CwdiAA==,O7fx3KZzmwE=") },
+
+    { njs_str("const zlib = require('zlib');"
+              "['WAKA', 'αβγ']"
+              ".map(v => zlib.deflateRawSync(v, {level: zlib.constants.Z_NO_COMPRESSION}).toString('base64'))"),
+      njs_str("AQQA+/9XQUtB,AQYA+f/Osc6yzrM=") },
+
+    { njs_str("const zlib = require('zlib');"
+              "[zlib.constants.Z_FIXED,  zlib.constants.Z_RLE]"
+              ".map(v => zlib.deflateRawSync('WAKA'.repeat(10), {strategy: v}).toString('base64'))"),
+      njs_str("C3f0dgwnAgMA,BcExAQAAAMKgbNwLYP8mwmQymUwmk8lkcg==") },
+
+    { njs_str("const zlib = require('zlib');"
+              "[1, 8]"
+              ".map(v => zlib.deflateRawSync('WAKA'.repeat(35),"
+              "                              {strategy: zlib.constants.Z_RLE, memLevel: v})"
+              "          .toString('base64'))"),
+      njs_str("BMExAQAAAMKgbNwLYP8mwmQymUwmk8lkMplMJpPJZDKZTCaTyWQymUwmk+lzDHf0dgx39HYMd/R2BAA=,"
+              "BcExAQAAAMKgbNwLYP8mwmQymUwmk8lkMplMJpPJZDKZTCaTyWQymUwmk8lkMjk=") },
+
+    { njs_str("const zlib = require('zlib');"
+              "['WAKA', 'αβγ']"
+              ".map(v => zlib.deflateSync(v).toString('base64'))"),
+      njs_str("eJwLd/R2BAAC+gEl,eJw7t/HcpnObAQ/sBIE=") },
+
+    { njs_str("const zlib = require('zlib');"
+              "['WAKA'.repeat(1024), 'αβγ'.repeat(1024)]"
+              ".map(v => [v, zlib.deflateRawSync(v).toString('base64')])"
+              ".every(pair => pair[0] == zlib.inflateRawSync(Buffer.from(pair[1], 'base64')).toString())"),
+      njs_str("true") },
+
+    { njs_str("const zlib = require('zlib');"
+              "['WAKA'.repeat(1024), 'αβγ'.repeat(1024)]"
+              ".map(v => [v, zlib.deflateRawSync(v, {chunkSize:64}).toString('base64')])"
+              ".every(pair => pair[0] == zlib.inflateRawSync(Buffer.from(pair[1], 'base64'),"
+              "                                              {chunkSize:64}).toString())"),
+      njs_str("true") },
+
+    { njs_str("const zlib = require('zlib');"
+              "['WAKA', 'αβγ']"
+              ".map(v => [v, zlib.deflateRawSync(v, {dictionary: Buffer.from('WAKA')}).toString('base64')])"
+              ".every(pair => pair[0] == zlib.inflateRawSync(Buffer.from(pair[1], 'base64'),"
+              "                                              {dictionary: Buffer.from('WAKA')}).toString())"),
+      njs_str("true") },
+
+};
+
+
 static njs_unit_test_t  njs_module_test[] =
 {
     { njs_str("function f(){return 2}; var f; f()"),
@@ -24994,6 +25062,17 @@ static njs_test_suite_t  njs_suites[] =
       njs_nitems(njs_xml_test),
       njs_unit_test },
 
+    {
+#if (NJS_HAVE_ZLIB && !NJS_HAVE_MEMORY_SANITIZER)
+        njs_str("zlib"),
+#else
+        njs_str(""),
+#endif
+      { .externals = 1, .repeat = 1, .unsafe = 1 },
+      njs_zlib_test,
+      njs_nitems(njs_zlib_test),
+      njs_unit_test },
+
     { njs_str("module"),
       { .repeat = 1, .module = 1, .unsafe = 1 },
       njs_module_test,


More information about the nginx-devel mailing list