From sorin.v.manole at gmail.com Sat Mar 1 14:41:15 2025 From: sorin.v.manole at gmail.com (Sorin Manole) Date: Sat, 1 Mar 2025 16:41:15 +0200 Subject: Development guide sentence wording Message-ID: Hello, Didn't know where to report it, just a small improvement of the development guide wording: The function ngx_event_process_posted() is called to process an event queue. It calls event handlers until the queue is not empty. This means that a posted event handler can post more events to be processed within the current event loop iteration. should probably be: The function ngx_event_process_posted() is called to process an event queue. It calls event handlers until the queue *is empty*. This means that a posted event handler can post more events to be processed within the current event loop iteration. -------------- next part -------------- An HTML attachment was scrubbed... URL: From osa at freebsd.org.ru Sat Mar 1 15:09:12 2025 From: osa at freebsd.org.ru (Sergey A. Osokin) Date: Sat, 1 Mar 2025 18:09:12 +0300 Subject: Development guide sentence wording In-Reply-To: References: Message-ID: Hi Sorin, thanks for the report! On Sat, Mar 01, 2025 at 04:41:15PM +0200, Sorin Manole wrote: > Hello, > > Didn't know where to report it, just a small improvement of the development > guide wording: > > The function ngx_event_process_posted() is called to process an event > queue. It calls event handlers until the queue is not empty. This means > that a posted event handler can post more events to be processed within the > current event loop iteration. > > should probably be: > > The function ngx_event_process_posted() is called to process an event > queue. It calls event handlers until the queue *is empty*. This means that > a posted event handler can post more events to be processed within the > current event loop iteration. We'll review and update our documentation. Thank you. -- Sergey A. Osokin From pluknet at nginx.com Tue Mar 4 15:44:14 2025 From: pluknet at nginx.com (Sergey Kandaurov) Date: Tue, 4 Mar 2025 19:44:14 +0400 Subject: Development guide sentence wording In-Reply-To: References: Message-ID: <3A2DF92D-E091-4060-A5E1-9CA883229B2F@nginx.com> > On 1 Mar 2025, at 18:41, Sorin Manole wrote: > > Hello, > > Didn't know where to report it, just a small improvement of the development guide wording: > > The function ngx_event_process_posted() is called to process an event queue. It calls event handlers until the queue is not empty. This means that a posted event handler can post more events to be processed within the current event loop iteration. > > should probably be: > > The function ngx_event_process_posted() is called to process an event queue. It calls event handlers until the queue is empty. This means that a posted event handler can post more events to be processed within the current event loop iteration. The site should be now updated, thanks for report. -- Sergey Kandaurov From noreply at nginx.com Mon Mar 10 16:33:02 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Mon, 10 Mar 2025 16:33:02 +0000 (UTC) Subject: [nginx] Slice filter: improved memory allocation error handling. Message-ID: <20250310163302.C2E72477EF@pubserv1.nginx> details: https://github.com/nginx/nginx/commit/d31305653701bd99e8e5e6aa48094599a08f9f12 branches: master commit: d31305653701bd99e8e5e6aa48094599a08f9f12 user: Sergey Kandaurov date: Thu, 27 Feb 2025 16:09:50 +0400 description: Slice filter: improved memory allocation error handling. As uncovered by recent addition in slice.t, a partially initialized context, coupled with HTTP 206 response from stub backend, might be accessed in the next slice subrequest. Found by bad memory allocator simulation. --- src/http/modules/ngx_http_slice_filter_module.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/http/modules/ngx_http_slice_filter_module.c b/src/http/modules/ngx_http_slice_filter_module.c index 3b0bef629..67dc14c82 100644 --- a/src/http/modules/ngx_http_slice_filter_module.c +++ b/src/http/modules/ngx_http_slice_filter_module.c @@ -419,13 +419,13 @@ ngx_http_slice_range_variable(ngx_http_request_t *r, return NGX_ERROR; } - ngx_http_set_ctx(r, ctx, ngx_http_slice_filter_module); - p = ngx_pnalloc(r->pool, sizeof("bytes=-") - 1 + 2 * NGX_OFF_T_LEN); if (p == NULL) { return NGX_ERROR; } + ngx_http_set_ctx(r, ctx, ngx_http_slice_filter_module); + ctx->start = slcf->size * (ngx_http_slice_get_start(r) / slcf->size); ctx->range.data = p; From zyrotnet at gmail.com Wed Mar 12 15:04:24 2025 From: zyrotnet at gmail.com (Zyrot System) Date: Wed, 12 Mar 2025 10:04:24 -0500 Subject: Use directive code from other module inside my custom ngx_http_mycontrol_module Message-ID: Hello everyone, first I want to say I already searched mailman.nginx.org for a topic relating to my question but I cannot find it. ngx_http_rewrite_module has directives rewrite, return, etc. I want to use directives code from other module inside my module so can do code reuse "DRY", for example create my own directive like: server { # important to work in this Context mycontrol * { myrewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 last; # using the core ngx_http_rewrite_module.c } mycontrol * { # optional using the original ngx_http_rewrite_module.c rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 last; } } So can avoid replicate the already functionality (I want to extend the ngx_http_rewrite_module, and others modules in my module mycontrol with more features). Any ideas ? I just want to be sure for what route to go, maybe instead I should used the PCRE library directly in ngx_http_mycontrol_module like does other modules ? Thanks for reading Note repost in: stackoverflow.com/questions/79503527/use-directive-code-from-other-module-inside-my-module-nginx -------------- next part -------------- An HTML attachment was scrubbed... URL: From teward at thomas-ward.net Sat Mar 15 21:55:40 2025 From: teward at thomas-ward.net (Thomas Ward) Date: Sat, 15 Mar 2025 17:55:40 -0400 Subject: Proposal: Change `ssl_client_certificate` to `ssl_client_ca_certificate` Message-ID: <3545d0b5-b53f-4d45-b82c-23bb593c22f2@thomas-ward.net> In line with a recent nginx mailing list thread I had with a user about how to properly secure a site with SSL/TLS Client Certificates, the user indicated that "ssl_client_certificate" is a confusing misnomer.  It implies that the certificate(s) provided are a bundle of certs that are *individual client certificates* not the Certification Authority (CA) certificate and chain that issued the certificiates. It's always annoyed me slightly that it has been "ssl_client_certificate" and has no mention of it being a CA cert. I'm guessing that's because you could theoretically use a self-signed certificate and verify it against itself, thus not needing a CA certificate, however that's not the primary use case nor is that how it's really explained in the NGINX documentation of the command. Has there been any discussion or consideration of renaming ssl_client_certificate to something that is less confusing to people new to the process, to show that this is supposed to be the CA certificate of the authority that is issuing the client certificates? Thomas From noreply at nginx.com Tue Mar 18 02:00:02 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 18 Mar 2025 02:00:02 +0000 (UTC) Subject: [njs] QuickJS: making ngx_qjs_*() functions consistent with ngx_js_*(). Message-ID: <20250318020002.D0AD748373@pubserv1.nginx> details: https://github.com/nginx/njs/commit/9010aeea7807a6398e5d5ea89794f94ca0e4cb1e branches: master commit: 9010aeea7807a6398e5d5ea89794f94ca0e4cb1e user: hongzhidao date: Mon, 17 Mar 2025 14:40:49 +0800 description: QuickJS: making ngx_qjs_*() functions consistent with ngx_js_*(). --- nginx/ngx_http_js_module.c | 71 ++++++++++++++++--------------------- nginx/ngx_js.c | 84 ++++++++++++++++++++++++-------------------- nginx/ngx_js.h | 8 ++--- nginx/ngx_js_shared_dict.c | 30 ++++------------ nginx/ngx_stream_js_module.c | 16 ++++----- 5 files changed, 91 insertions(+), 118 deletions(-) diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index 71fd92ba..ae970eb1 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -5133,7 +5133,7 @@ ngx_http_qjs_ext_internal_redirect(JSContext *cx, JSValueConst this_val, "internalRedirect cannot be called while filtering"); } - if (ngx_qjs_string(ctx->engine, argv[0], &ctx->redirect_uri) != NGX_OK) { + if (ngx_qjs_string(cx, argv[0], &ctx->redirect_uri) != NGX_OK) { return JS_EXCEPTION; } @@ -5412,7 +5412,7 @@ ngx_http_qjs_ext_return(JSContext *cx, JSValueConst this_val, ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (status < NGX_HTTP_BAD_REQUEST || !JS_IsNullOrUndefined(argv[1])) { - if (ngx_qjs_string(ctx->engine, argv[1], &body) != NGX_OK) { + if (ngx_qjs_string(cx, argv[1], &body) != NGX_OK) { return JS_ThrowOutOfMemory(cx); } @@ -5515,7 +5515,7 @@ ngx_http_qjs_ext_send(JSContext *cx, JSValueConst this_val, ll = &out; for (n = 0; n < (ngx_uint_t) argc; n++) { - if (ngx_qjs_string(ctx->engine, argv[n], &s) != NGX_OK) { + if (ngx_qjs_string(cx, argv[n], &s) != NGX_OK) { return JS_ThrowTypeError(cx, "failed to convert arg"); } @@ -5578,7 +5578,7 @@ ngx_http_qjs_ext_send_buffer(JSContext *cx, JSValueConst this_val, return JS_ThrowTypeError(cx, "cannot send buffer while not filtering"); } - if (ngx_qjs_string(ctx->engine, argv[0], &buffer) != NGX_OK) { + if (ngx_qjs_string(cx, argv[0], &buffer) != NGX_OK) { return JS_ThrowTypeError(cx, "failed get buffer arg"); } @@ -5735,7 +5735,7 @@ ngx_http_qjs_subrequest_done(ngx_http_request_t *r, void *data, ngx_int_t rc) reply = JS_DupValue(cx, ngx_qjs_arg(sctx->args[0])); } - rc = ngx_qjs_call((ngx_js_ctx_t *) ctx, event->function, &reply, 1); + rc = ngx_qjs_call(cx, event->function, &reply, 1); JS_FreeValue(cx, reply); ngx_js_del_event(ctx, event); @@ -5786,7 +5786,7 @@ ngx_http_qjs_ext_subrequest(JSContext *cx, JSValueConst this_val, "the primary request"); } - if (ngx_qjs_string(ctx->engine, argv[0], &uri) != NGX_OK) { + if (ngx_qjs_string(cx, argv[0], &uri) != NGX_OK) { return JS_ThrowTypeError(cx, "failed to convert uri arg"); } @@ -5812,7 +5812,7 @@ ngx_http_qjs_ext_subrequest(JSContext *cx, JSValueConst this_val, arg = argv[1]; if (JS_IsString(arg)) { - if (ngx_qjs_string(ctx->engine, arg, &args) != NGX_OK) { + if (ngx_qjs_string(cx, arg, &args) != NGX_OK) { return JS_ThrowTypeError(cx, "failed to convert args"); } @@ -5833,7 +5833,7 @@ ngx_http_qjs_ext_subrequest(JSContext *cx, JSValueConst this_val, } if (!JS_IsUndefined(value)) { - rc = ngx_qjs_string(ctx->engine, value, &args); + rc = ngx_qjs_string(cx, value, &args); JS_FreeValue(cx, value); if (rc != NGX_OK) { @@ -5857,7 +5857,7 @@ ngx_http_qjs_ext_subrequest(JSContext *cx, JSValueConst this_val, } if (!JS_IsUndefined(value)) { - rc = ngx_qjs_string(ctx->engine, value, &method_name); + rc = ngx_qjs_string(cx, value, &method_name); JS_FreeValue(cx, value); if (rc != NGX_OK) { @@ -5884,7 +5884,7 @@ ngx_http_qjs_ext_subrequest(JSContext *cx, JSValueConst this_val, } if (!JS_IsUndefined(value)) { - rc = ngx_qjs_string(ctx->engine, value, &body_arg); + rc = ngx_qjs_string(cx, value, &body_arg); JS_FreeValue(cx, value); if (rc != NGX_OK) { @@ -6233,7 +6233,6 @@ ngx_http_qjs_variables_set_property(JSContext *cx, JSValueConst obj, u_char *lowcase_key; ngx_str_t name, s; ngx_uint_t key; - ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; ngx_http_variable_t *v; ngx_http_variable_value_t *vv; @@ -6279,9 +6278,7 @@ ngx_http_qjs_variables_set_property(JSContext *cx, JSValueConst obj, return -1; } - ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); - - if (ngx_qjs_string(ctx->engine, value, &s) != NGX_OK) { + if (ngx_qjs_string(cx, value, &s) != NGX_OK) { return -1; } @@ -6692,15 +6689,14 @@ ngx_http_qjs_headers_out_handler(JSContext *cx, ngx_http_request_t *r, ngx_str_t *name, JSPropertyDescriptor *pdesc, JSValue *value, unsigned flags) { - u_char *p; - int64_t length; - uint32_t i; - ngx_int_t rc; - ngx_str_t s; - JSValue v; - ngx_list_part_t *part; - ngx_table_elt_t *header, *h, **ph; - ngx_http_js_ctx_t *ctx; + u_char *p; + int64_t length; + uint32_t i; + ngx_int_t rc; + ngx_str_t s; + JSValue v; + ngx_list_part_t *part; + ngx_table_elt_t *header, *h, **ph; if (flags & NJS_HEADER_GET) { return ngx_http_qjs_header_generic(cx, r, &r->headers_out.headers, NULL, @@ -6758,7 +6754,6 @@ ngx_http_qjs_headers_out_handler(JSContext *cx, ngx_http_request_t *r, } ph = &header; - ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); for (i = 0; i < (uint32_t) length; i++) { if (JS_IsArray(cx, *value)) { @@ -6768,7 +6763,7 @@ ngx_http_qjs_headers_out_handler(JSContext *cx, ngx_http_request_t *r, } } - rc = ngx_qjs_string(ctx->engine, v, &s); + rc = ngx_qjs_string(cx, v, &s); if (JS_IsArray(cx, *value)) { JS_FreeValue(cx, v); @@ -6828,15 +6823,14 @@ ngx_http_qjs_headers_out_special_handler(JSContext *cx, ngx_http_request_t *r, ngx_str_t *name, JSPropertyDescriptor *pdesc, JSValue *value, unsigned flags, ngx_table_elt_t **hh) { - u_char *p; - uint32_t length; - JSValue len, setval; - ngx_str_t s; - ngx_uint_t i, rc; - ngx_list_t *headers; - ngx_list_part_t *part; - ngx_table_elt_t *header, *h; - ngx_http_js_ctx_t *ctx; + u_char *p; + uint32_t length; + JSValue len, setval; + ngx_str_t s; + ngx_uint_t i, rc; + ngx_list_t *headers; + ngx_list_part_t *part; + ngx_table_elt_t *header, *h; if (flags & NJS_HEADER_GET) { return ngx_http_qjs_headers_out_handler(cx, r, name, pdesc, NULL, @@ -6870,9 +6864,7 @@ ngx_http_qjs_headers_out_special_handler(JSContext *cx, ngx_http_request_t *r, setval = JS_UNDEFINED; } - ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); - - rc = ngx_qjs_string(ctx->engine, setval, &s); + rc = ngx_qjs_string(cx, setval, &s); if (value != NULL && JS_IsArray(cx, *value)) { JS_FreeValue(cx, setval); @@ -7054,7 +7046,6 @@ ngx_http_qjs_headers_out_content_type(JSContext *cx, ngx_http_request_t *r, JSValue len, setval; ngx_int_t rc; ngx_str_t *hdr, s; - ngx_http_js_ctx_t *ctx; if (flags & NJS_HEADER_GET) { hdr = &r->headers_out.content_type; @@ -7108,9 +7099,7 @@ ngx_http_qjs_headers_out_content_type(JSContext *cx, ngx_http_request_t *r, setval = *value; } - ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); - - rc = ngx_qjs_string(ctx->engine, setval, &s); + rc = ngx_qjs_string(cx, setval, &s); if (JS_IsArray(cx, *value)) { JS_FreeValue(cx, setval); diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c index 383d2304..84cdcc2e 100644 --- a/nginx/ngx_js.c +++ b/nginx/ngx_js.c @@ -101,7 +101,7 @@ static void ngx_qjs_rejection_tracker(JSContext *ctx, JSValueConst promise, JSValueConst reason, JS_BOOL is_handled, void *opaque); static JSValue ngx_qjs_value(JSContext *cx, const ngx_str_t *path); -static ngx_int_t ngx_qjs_dump_obj(ngx_engine_t *e, JSValueConst val, +static ngx_int_t ngx_qjs_dump_obj(JSContext *cx, JSValueConst val, ngx_str_t *dst); static JSModuleDef *ngx_qjs_core_init(JSContext *cx, const char *name); @@ -849,7 +849,7 @@ ngx_engine_qjs_compile(ngx_js_loc_conf_t *conf, ngx_log_t *log, u_char *start, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); if (JS_IsException(code)) { - ngx_qjs_exception(engine, &text); + ngx_qjs_exception(cx, &text); ngx_log_error(NGX_LOG_EMERG, log, 0, "js compile %V", &text); return NGX_ERROR; } @@ -972,7 +972,7 @@ ngx_qjs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, void *external) rv = JS_ReadObject(cx, pc[i].code, pc[i].code_size, JS_READ_OBJ_BYTECODE); if (JS_IsException(rv)) { - ngx_qjs_exception(engine, &exception); + ngx_qjs_exception(cx, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js load module exception: %V", &exception); @@ -988,7 +988,7 @@ ngx_qjs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, void *external) rv = JS_EvalFunction(cx, rv); if (JS_IsException(rv)) { - ngx_qjs_exception(engine, &exception); + ngx_qjs_exception(cx, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js eval exception: %V", &exception); @@ -997,7 +997,7 @@ ngx_qjs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, void *external) rv = js_std_await(cx, rv); if (JS_IsException(rv)) { - ngx_qjs_exception(engine, &exception); + ngx_qjs_exception(cx, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js eval exception: %V", &exception); @@ -1042,7 +1042,7 @@ ngx_engine_qjs_call(ngx_js_ctx_t *ctx, ngx_str_t *fname, val = JS_Call(cx, fn, JS_UNDEFINED, nargs, &ngx_qjs_arg(args[0])); JS_FreeValue(cx, fn); if (JS_IsException(val)) { - ngx_qjs_exception(ctx->engine, &exception); + ngx_qjs_exception(cx, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js call exception: %V", &exception); @@ -1059,7 +1059,7 @@ ngx_engine_qjs_call(ngx_js_ctx_t *ctx, ngx_str_t *fname, rc = JS_ExecutePendingJob(rt, &cx1); if (rc <= 0) { if (rc == -1) { - ngx_qjs_exception(ctx->engine, &exception); + ngx_qjs_exception(cx, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js job exception: %V", &exception); @@ -1093,7 +1093,7 @@ static ngx_int_t ngx_engine_qjs_string(ngx_engine_t *e, njs_opaque_value_t *value, ngx_str_t *str) { - return ngx_qjs_dump_obj(e, ngx_qjs_arg(*value), str); + return ngx_qjs_dump_obj(e->u.qjs.ctx, ngx_qjs_arg(*value), str); } @@ -1150,7 +1150,7 @@ ngx_engine_qjs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx, } if (ngx_qjs_unhandled_rejection(ctx)) { - ngx_qjs_exception(ctx->engine, &exception); + ngx_qjs_exception(cx, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js unhandled rejection: %V", &exception); } @@ -1255,13 +1255,17 @@ ngx_qjs_value(JSContext *cx, const ngx_str_t *path) static ngx_int_t -ngx_qjs_dump_obj(ngx_engine_t *e, JSValueConst val, ngx_str_t *dst) +ngx_qjs_dump_obj(JSContext *cx, JSValueConst val, ngx_str_t *dst) { - size_t len, byte_offset, byte_length; - u_char *start, *p; - JSValue buffer, stack; - ngx_str_t str, stack_str; - JSContext *cx; + size_t len, byte_offset, byte_length; + u_char *start, *p; + JSValue buffer, stack; + ngx_str_t str, stack_str; + ngx_js_ctx_t *ctx; + ngx_engine_t *e; + + ctx = ngx_qjs_external_ctx(cx, JS_GetContextOpaque(cx)); + e = ctx->engine; if (JS_IsNullOrUndefined(val)) { dst->data = NULL; @@ -1350,19 +1354,20 @@ ngx_qjs_dump_obj(ngx_engine_t *e, JSValueConst val, ngx_str_t *dst) ngx_int_t -ngx_qjs_call(ngx_js_ctx_t *ctx, JSValue fn, JSValue *argv, int argc) +ngx_qjs_call(JSContext *cx, JSValue fn, JSValue *argv, int argc) { - int rc; - JSValue ret; - ngx_str_t exception; - JSRuntime *rt; - JSContext *cx, *cx1; + int rc; + JSValue ret; + ngx_str_t exception; + JSRuntime *rt; + JSContext *cx1; + ngx_js_ctx_t *ctx; - cx = ctx->engine->u.qjs.ctx; + ctx = ngx_qjs_external_ctx(cx, JS_GetContextOpaque(cx)); ret = JS_Call(cx, fn, JS_UNDEFINED, argc, argv); if (JS_IsException(ret)) { - ngx_qjs_exception(ctx->engine, &exception); + ngx_qjs_exception(cx, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js call exception: %V", &exception); @@ -1378,7 +1383,7 @@ ngx_qjs_call(ngx_js_ctx_t *ctx, JSValue fn, JSValue *argv, int argc) rc = JS_ExecutePendingJob(rt, &cx1); if (rc <= 0) { if (rc == -1) { - ngx_qjs_exception(ctx->engine, &exception); + ngx_qjs_exception(cx, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js job exception: %V", &exception); @@ -1395,16 +1400,16 @@ ngx_qjs_call(ngx_js_ctx_t *ctx, JSValue fn, JSValue *argv, int argc) ngx_int_t -ngx_qjs_exception(ngx_engine_t *e, ngx_str_t *s) +ngx_qjs_exception(JSContext *cx, ngx_str_t *s) { JSValue exception; - exception = JS_GetException(e->u.qjs.ctx); - if (ngx_qjs_dump_obj(e, exception, s) != NGX_OK) { + exception = JS_GetException(cx); + if (ngx_qjs_dump_obj(cx, exception, s) != NGX_OK) { return NGX_ERROR; } - JS_FreeValue(e->u.qjs.ctx, exception); + JS_FreeValue(cx, exception); return NGX_OK; } @@ -1431,13 +1436,17 @@ ngx_qjs_integer(JSContext *cx, JSValueConst val, ngx_int_t *n) ngx_int_t -ngx_qjs_string(ngx_engine_t *e, JSValueConst val, ngx_str_t *dst) +ngx_qjs_string(JSContext *cx, JSValueConst val, ngx_str_t *dst) { - size_t len, byte_offset, byte_length; - u_char *start; - JSValue buffer; - JSContext *cx; - const char *str; + size_t len, byte_offset, byte_length; + u_char *start; + JSValue buffer; + const char *str; + ngx_js_ctx_t *ctx; + ngx_engine_t *e; + + ctx = ngx_qjs_external_ctx(cx, JS_GetContextOpaque(cx)); + e = ctx->engine; if (JS_IsNullOrUndefined(val)) { dst->data = NULL; @@ -1512,8 +1521,7 @@ ngx_qjs_timer_handler(ngx_event_t *ev) external = JS_GetContextOpaque(cx); ctx = ngx_qjs_external_ctx(cx, external); - rc = ngx_qjs_call((ngx_js_ctx_t *) ctx, event->function, event->args, - event->nargs); + rc = ngx_qjs_call(cx, event->function, event->args, event->nargs); ngx_js_del_event(ctx, event); @@ -1740,7 +1748,6 @@ ngx_qjs_ext_log(JSContext *cx, JSValueConst this_val, int argc, char *p; uint32_t level; ngx_str_t msg; - ngx_js_ctx_t *ctx; ngx_connection_t *c; p = JS_GetContextOpaque(cx); @@ -1759,11 +1766,10 @@ ngx_qjs_ext_log(JSContext *cx, JSValueConst this_val, int argc, argv++; } - ctx = ngx_qjs_external_ctx(cx, p); c = ngx_qjs_external_connection(cx, p); for ( ; argc > 0; argc--, argv++) { - if (ngx_qjs_dump_obj(ctx->engine, argv[0], &msg) != NGX_OK) { + if (ngx_qjs_dump_obj(cx, argv[0], &msg) != NGX_OK) { return JS_EXCEPTION; } diff --git a/nginx/ngx_js.h b/nginx/ngx_js.h index 833c1490..6fac6994 100644 --- a/nginx/ngx_js.h +++ b/nginx/ngx_js.h @@ -340,11 +340,11 @@ ngx_engine_t *ngx_qjs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, void *external); void ngx_engine_qjs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *conf); -ngx_int_t ngx_qjs_call(ngx_js_ctx_t *ctx, JSValue function, - JSValue *argv, int argc); -ngx_int_t ngx_qjs_exception(ngx_engine_t *e, ngx_str_t *s); +ngx_int_t ngx_qjs_call(JSContext *cx, JSValue function, JSValue *argv, + int argc); +ngx_int_t ngx_qjs_exception(JSContext *cx, ngx_str_t *s); ngx_int_t ngx_qjs_integer(JSContext *cx, JSValueConst val, ngx_int_t *n); -ngx_int_t ngx_qjs_string(ngx_engine_t *e, JSValueConst val, ngx_str_t *str); +ngx_int_t ngx_qjs_string(JSContext *cx, JSValueConst val, ngx_str_t *str); #define ngx_qjs_prop(cx, type, start, len) \ ((type == NGX_JS_STRING) ? qjs_string_create(cx, start, len) \ diff --git a/nginx/ngx_js_shared_dict.c b/nginx/ngx_js_shared_dict.c index 6ae12d84..7f8c808e 100644 --- a/nginx/ngx_js_shared_dict.c +++ b/nginx/ngx_js_shared_dict.c @@ -2078,7 +2078,6 @@ ngx_qjs_ext_shared_dict_delete(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_str_t key; - ngx_js_ctx_t *ctx; ngx_shm_zone_t *shm_zone; shm_zone = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_SHARED_DICT); @@ -2086,9 +2085,7 @@ ngx_qjs_ext_shared_dict_delete(JSContext *cx, JSValueConst this_val, return JS_ThrowTypeError(cx, "\"this\" is not a shared dict"); } - ctx = ngx_qjs_external_ctx(cx, JS_GetContextOpaque(cx)); - - if (ngx_qjs_string(ctx->engine, argv[0], &key) != NGX_OK) { + if (ngx_qjs_string(cx, argv[0], &key) != NGX_OK) { return JS_EXCEPTION; } @@ -2124,7 +2121,6 @@ ngx_qjs_ext_shared_dict_get(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_str_t key; - ngx_js_ctx_t *ctx; ngx_shm_zone_t *shm_zone; shm_zone = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_SHARED_DICT); @@ -2132,9 +2128,7 @@ ngx_qjs_ext_shared_dict_get(JSContext *cx, JSValueConst this_val, return JS_ThrowTypeError(cx, "\"this\" is not a shared dict"); } - ctx = ngx_qjs_external_ctx(cx, JS_GetContextOpaque(cx)); - - if (ngx_qjs_string(ctx->engine, argv[0], &key) != NGX_OK) { + if (ngx_qjs_string(cx, argv[0], &key) != NGX_OK) { return JS_EXCEPTION; } @@ -2149,7 +2143,6 @@ ngx_qjs_ext_shared_dict_has(JSContext *cx, JSValueConst this_val, ngx_str_t key; ngx_msec_t now; ngx_time_t *tp; - ngx_js_ctx_t *ctx; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; ngx_js_dict_node_t *node; @@ -2159,9 +2152,7 @@ ngx_qjs_ext_shared_dict_has(JSContext *cx, JSValueConst this_val, return JS_ThrowTypeError(cx, "\"this\" is not a shared dict"); } - ctx = ngx_qjs_external_ctx(cx, JS_GetContextOpaque(cx)); - - if (ngx_qjs_string(ctx->engine, argv[0], &key) != NGX_OK) { + if (ngx_qjs_string(cx, argv[0], &key) != NGX_OK) { return JS_EXCEPTION; } @@ -2193,7 +2184,6 @@ ngx_qjs_ext_shared_dict_incr(JSContext *cx, JSValueConst this_val, double delta, init; uint32_t timeout; ngx_str_t key; - ngx_js_ctx_t *ctx; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; @@ -2208,9 +2198,7 @@ ngx_qjs_ext_shared_dict_incr(JSContext *cx, JSValueConst this_val, return JS_ThrowTypeError(cx, "shared dict is not a number dict"); } - ctx = ngx_qjs_external_ctx(cx, JS_GetContextOpaque(cx)); - - if (ngx_qjs_string(ctx->engine, argv[0], &key) != NGX_OK) { + if (ngx_qjs_string(cx, argv[0], &key) != NGX_OK) { return JS_EXCEPTION; } @@ -2464,7 +2452,6 @@ ngx_qjs_ext_shared_dict_pop(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_str_t key; - ngx_js_ctx_t *ctx; ngx_shm_zone_t *shm_zone; shm_zone = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_SHARED_DICT); @@ -2472,9 +2459,7 @@ ngx_qjs_ext_shared_dict_pop(JSContext *cx, JSValueConst this_val, return JS_ThrowTypeError(cx, "\"this\" is not a shared dict"); } - ctx = ngx_qjs_external_ctx(cx, JS_GetContextOpaque(cx)); - - if (ngx_qjs_string(ctx->engine, argv[0], &key) != NGX_OK) { + if (ngx_qjs_string(cx, argv[0], &key) != NGX_OK) { return JS_EXCEPTION; } @@ -2489,7 +2474,6 @@ ngx_qjs_ext_shared_dict_set(JSContext *cx, JSValueConst this_val, JSValue ret; uint32_t timeout; ngx_str_t key; - ngx_js_ctx_t *ctx; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; @@ -2498,9 +2482,7 @@ ngx_qjs_ext_shared_dict_set(JSContext *cx, JSValueConst this_val, return JS_ThrowTypeError(cx, "\"this\" is not a shared dict"); } - ctx = ngx_qjs_external_ctx(cx, JS_GetContextOpaque(cx)); - - if (ngx_qjs_string(ctx->engine, argv[0], &key) != NGX_OK) { + if (ngx_qjs_string(cx, argv[0], &key) != NGX_OK) { return JS_EXCEPTION; } diff --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c index 7e93b237..a7dddd04 100644 --- a/nginx/ngx_stream_js_module.c +++ b/nginx/ngx_stream_js_module.c @@ -2152,7 +2152,7 @@ ngx_stream_qjs_ext_on(JSContext *cx, JSValueConst this_val, int argc, ctx = ngx_stream_get_module_ctx(ses->session, ngx_stream_js_module); - if (ngx_qjs_string(ctx->engine, argv[0], &name) != NGX_OK) { + if (ngx_qjs_string(cx, argv[0], &name) != NGX_OK) { return JS_EXCEPTION; } @@ -2195,7 +2195,7 @@ ngx_stream_qjs_ext_off(JSContext *cx, JSValueConst this_val, int argc, ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); - if (ngx_qjs_string(ctx->engine, argv[0], &name) != NGX_OK) { + if (ngx_qjs_string(cx, argv[0], &name) != NGX_OK) { return JS_EXCEPTION; } @@ -2278,7 +2278,7 @@ ngx_stream_qjs_ext_send(JSContext *cx, JSValueConst this_val, int argc, return JS_ThrowInternalError(cx, "cannot send buffer in this handler"); } - if (ngx_qjs_string(ctx->engine, argv[0], &buffer) != NGX_OK) { + if (ngx_qjs_string(cx, argv[0], &buffer) != NGX_OK) { return JS_EXCEPTION; } @@ -2510,7 +2510,6 @@ ngx_stream_qjs_variables_set_property(JSContext *cx, JSValueConst obj, { ngx_str_t name, name_lc, val; ngx_uint_t key; - ngx_js_ctx_t *ctx; ngx_stream_session_t *s; ngx_stream_variable_t *v; ngx_stream_variable_value_t *vv; @@ -2556,9 +2555,7 @@ ngx_stream_qjs_variables_set_property(JSContext *cx, JSValueConst obj, return -1; } - ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); - - if (ngx_qjs_string(ctx->engine, value, &val) != NGX_OK) { + if (ngx_qjs_string(cx, value, &val) != NGX_OK) { return -1; } @@ -2669,14 +2666,13 @@ ngx_stream_qjs_run_event(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, JS_SetOpaque(argv[1], (void *) flags); - rc = ngx_qjs_call((ngx_js_ctx_t *) ctx, ngx_qjs_arg(event->function), - &argv[0], 2); + rc = ngx_qjs_call(cx, ngx_qjs_arg(event->function), &argv[0], 2); JS_FreeValue(cx, argv[0]); JS_FreeValue(cx, argv[1]); if (rc == NGX_ERROR) { error: - ngx_qjs_exception(ctx->engine, &exception); + ngx_qjs_exception(cx, &exception); ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %V", &exception); From noreply at nginx.com Tue Mar 18 21:46:02 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 18 Mar 2025 21:46:02 +0000 (UTC) Subject: [njs] Test262: using default prepare_args() where appropriate. Message-ID: <20250318214602.C4D8C48379@pubserv1.nginx> details: https://github.com/nginx/njs/commit/93e73cf1678038726b6f7fda927b932eb3e0d9d3 branches: master commit: 93e73cf1678038726b6f7fda927b932eb3e0d9d3 user: Dmitry Volyntsev date: Tue, 25 Feb 2025 22:35:55 -0800 description: Test262: using default prepare_args() where appropriate. --- test/buffer.t.js | 36 ++++-------------------------------- test/harness/runTsuite.js | 12 +++++++++++- test/querystring.t.mjs | 20 -------------------- test/text_decoder.t.js | 3 --- test/text_encoder.t.js | 9 --------- test/xml/saml_verify.t.mjs | 8 -------- test/zlib.t.mjs | 11 ++--------- 7 files changed, 17 insertions(+), 82 deletions(-) diff --git a/test/buffer.t.js b/test/buffer.t.js index 8e3f4ca9..01e25bed 100644 --- a/test/buffer.t.js +++ b/test/buffer.t.js @@ -3,13 +3,6 @@ includes: [compatBuffer.js, runTsuite.js, compareArray.js] flags: [async] ---*/ -function p(args, default_opts) { - let params = merge({}, default_opts); - params = merge(params, args); - - return params; -} - let alloc_tsuite = { name: "Buffer.alloc() tests", skip: () => (!has_buffer()), @@ -23,7 +16,6 @@ let alloc_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: { encoding: 'utf-8' }, tests: [ @@ -56,7 +48,6 @@ let byteLength_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: { encoding: 'utf-8' }, tests: [ @@ -95,7 +86,6 @@ let concat_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ { buffers: [ Buffer.from('abc'), @@ -122,7 +112,6 @@ let compare_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -161,7 +150,6 @@ let comparePrototype_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -212,7 +200,6 @@ let copy_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -249,7 +236,6 @@ let equals_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -285,7 +271,6 @@ let fill_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ { buf: Buffer.from('abc'), value: 0x61, expected: 'aaa' }, @@ -336,7 +321,7 @@ let from_tsuite = { return 'SUCCESS'; }, - prepare_args: p, + opts: { fmt: 'utf-8' }, tests: [ @@ -445,7 +430,6 @@ let includes_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -471,7 +455,6 @@ let indexOf_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -519,7 +502,7 @@ let isBuffer_tsuite = { return 'SUCCESS'; }, - prepare_args: p, + opts: {}, tests: [ @@ -543,7 +526,6 @@ let isEncoding_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -594,7 +576,6 @@ let lastIndexOf_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -659,7 +640,6 @@ let readXIntXX_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -719,7 +699,6 @@ let readFloat_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -748,7 +727,6 @@ let readGeneric_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -789,7 +767,6 @@ let slice_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -818,7 +795,6 @@ let subarray_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -841,7 +817,6 @@ let swap_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: { swap: 'swap16' }, tests: [ @@ -867,8 +842,8 @@ let toJSON_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, + tests: [ { value: '', expected: { type: 'Buffer', data: [] } }, { value: 'αβγ', expected: { type: 'Buffer', data: [0xCE, 0xB1, 0xCE, 0xB2, 0xCE, 0xB3] } }, @@ -893,7 +868,7 @@ let toString_tsuite = { return 'SUCCESS'; }, - prepare_args: p, + opts: { fmt: 'utf-8' }, tests: [ @@ -937,7 +912,6 @@ let write_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -981,7 +955,6 @@ let writeXIntXX_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -1022,7 +995,6 @@ let writeGeneric_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ diff --git a/test/harness/runTsuite.js b/test/harness/runTsuite.js index 1823e4a2..068a19e1 100644 --- a/test/harness/runTsuite.js +++ b/test/harness/runTsuite.js @@ -26,7 +26,10 @@ async function run(tlist) { return Promise.resolve("SKIPPED"); } - return ts.T(ts.prepare_args(t, ts.opts)); + let prepare_args = ts.prepare_args ? ts.prepare_args + : default_prepare_args; + + return ts.T(prepare_args(t, ts.opts)); } catch (e) { return Promise.reject(e); @@ -57,6 +60,13 @@ async function run(tlist) { } } +function default_prepare_args(args, default_opts) { + let params = merge({}, default_opts); + params = merge(params, args); + + return params; +} + function merge(to, from) { let r = Object.assign(Array.isArray(to) ? [] : {}, to); Object.keys(from).forEach(v => { diff --git a/test/querystring.t.mjs b/test/querystring.t.mjs index b382d10d..cb42305e 100644 --- a/test/querystring.t.mjs +++ b/test/querystring.t.mjs @@ -16,12 +16,7 @@ let escape_tsuite = { return 'SUCCESS'; }, - prepare_args: (args, default_opts) => { - let params = merge({}, default_opts); - params = merge(params, args); - return params; - }, opts: { }, tests: [ @@ -66,12 +61,7 @@ let parse_tsuite = { return 'SUCCESS'; }, - prepare_args: (args, default_opts) => { - let params = merge({}, default_opts); - params = merge(params, args); - return params; - }, opts: { }, tests: [ @@ -174,12 +164,7 @@ let stringify_tsuite = { return 'SUCCESS'; }, - prepare_args: (args, default_opts) => { - let params = merge({}, default_opts); - params = merge(params, args); - return params; - }, opts: { }, tests: [ @@ -230,12 +215,7 @@ let unescape_tsuite = { return 'SUCCESS'; }, - prepare_args: (args, default_opts) => { - let params = merge({}, default_opts); - params = merge(params, args); - return params; - }, opts: { }, tests: [ diff --git a/test/text_decoder.t.js b/test/text_decoder.t.js index 2eb879c0..a6fced2b 100644 --- a/test/text_decoder.t.js +++ b/test/text_decoder.t.js @@ -40,7 +40,6 @@ let stream_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -96,7 +95,6 @@ let fatal_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -126,7 +124,6 @@ let ignoreBOM_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ diff --git a/test/text_encoder.t.js b/test/text_encoder.t.js index e790ae37..10324bca 100644 --- a/test/text_encoder.t.js +++ b/test/text_encoder.t.js @@ -4,13 +4,6 @@ includes: [runTsuite.js, compareArray.js] flags: [async] ---*/ -function p(args, default_opts) { - let params = merge({}, default_opts); - params = merge(params, args); - - return params; -} - let encode_tsuite = { name: "TextEncoder() encode tests", T: async (params) => { @@ -33,7 +26,6 @@ let encode_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ @@ -66,7 +58,6 @@ let encodeinto_tsuite = { return 'SUCCESS'; }, - prepare_args: p, opts: {}, tests: [ diff --git a/test/xml/saml_verify.t.mjs b/test/xml/saml_verify.t.mjs index 875fbd40..d6f06a14 100644 --- a/test/xml/saml_verify.t.mjs +++ b/test/xml/saml_verify.t.mjs @@ -271,18 +271,10 @@ async function signatureSAML(signature, key_data, produce) { signedInfoC14n); } -function p(args, default_opts) { - let params = merge({}, default_opts); - params = merge(params, args); - - return params; -} - let saml_verify_tsuite = { name: "SAML verify", skip: () => (!has_njs() || !has_webcrypto() || !has_xml()), T: verify, - prepare_args: p, opts: { key: { fmt: "spki", file: "rsa.spki" }, }, diff --git a/test/zlib.t.mjs b/test/zlib.t.mjs index d972f6f8..faef1fb0 100644 --- a/test/zlib.t.mjs +++ b/test/zlib.t.mjs @@ -5,13 +5,6 @@ flags: [async] import zlib from 'zlib'; -function p(args, default_opts) { - let params = merge({}, default_opts); - params = merge(params, args); - - return params; -} - let deflateSync_tsuite = { name: "deflateSync()/deflateRawSync() tests", skip: () => !zlib.deflateRawSync, @@ -29,7 +22,7 @@ let deflateSync_tsuite = { return 'SUCCESS'; }, - prepare_args: p, + opts: { raw: true }, tests: [ @@ -72,7 +65,7 @@ let inflateSync_tsuite = { return 'SUCCESS'; }, - prepare_args: p, + opts: { raw: true }, tests: [ From noreply at nginx.com Tue Mar 18 21:46:02 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 18 Mar 2025 21:46:02 +0000 (UTC) Subject: [njs] Test262: allowing to omit empty default option argument. Message-ID: <20250318214602.C9EEC4837A@pubserv1.nginx> details: https://github.com/nginx/njs/commit/3ad475cb9a0b24fc7bc49460ede80f4ec104c3fd branches: master commit: 3ad475cb9a0b24fc7bc49460ede80f4ec104c3fd user: Dmitry Volyntsev date: Tue, 25 Feb 2025 22:44:39 -0800 description: Test262: allowing to omit empty default option argument. --- test/buffer.t.js | 37 ------------------------------------- test/fs/methods.t.mjs | 4 ---- test/harness/runTsuite.js | 2 +- test/querystring.t.mjs | 8 -------- test/text_decoder.t.js | 6 ------ test/text_encoder.t.js | 4 ---- test/webcrypto/digest.t.mjs | 1 - test/webcrypto/import.t.mjs | 4 ---- test/webcrypto/rsa_decoding.t.mjs | 1 - 9 files changed, 1 insertion(+), 66 deletions(-) diff --git a/test/buffer.t.js b/test/buffer.t.js index 01e25bed..37821104 100644 --- a/test/buffer.t.js +++ b/test/buffer.t.js @@ -86,7 +86,6 @@ let concat_tsuite = { return 'SUCCESS'; }, - opts: {}, tests: [ { buffers: [ Buffer.from('abc'), Buffer.from(new Uint8Array([0x64, 0x65, 0x66]).buffer, 1) ], @@ -112,8 +111,6 @@ let compare_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { buf1: Buffer.from('abc'), buf2: Buffer.from('abc'), expected: 0 }, { buf1: Buffer.from('abc'), @@ -150,8 +147,6 @@ let comparePrototype_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { buf: Buffer.from('abc'), target: Buffer.from('abc'), expected: 0 }, { buf: Buffer.from('abc'), @@ -200,8 +195,6 @@ let copy_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { buf: Buffer.from('abcdef'), target: Buffer.from('123456'), expected: 6, expected_buf: 'abcdef' }, @@ -236,7 +229,6 @@ let equals_tsuite = { return 'SUCCESS'; }, - opts: {}, tests: [ { buf1: Buffer.from('abc'), buf2: Buffer.from('abc'), expected: true }, @@ -271,7 +263,6 @@ let fill_tsuite = { return 'SUCCESS'; }, - opts: {}, tests: [ { buf: Buffer.from('abc'), value: 0x61, expected: 'aaa' }, { buf: Buffer.from('abc'), value: 0x61, expected: 'aaa', offset: 0, end: 3 }, @@ -430,8 +421,6 @@ let includes_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { buf: Buffer.from('abcdef'), value: 'abc', expected: true }, { buf: Buffer.from('abcdef'), value: 'def', expected: true }, @@ -455,8 +444,6 @@ let indexOf_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { buf: Buffer.from('abcdef'), value: 'abc', expected: 0 }, { buf: Buffer.from('abcdef'), value: 'def', expected: 3 }, @@ -503,8 +490,6 @@ let isBuffer_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { value: Buffer.from('α'), expected: true }, { value: new Uint8Array(10), expected: false }, @@ -526,8 +511,6 @@ let isEncoding_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { value: 'utf-8', expected: true }, { value: 'utf8', expected: true }, @@ -576,8 +559,6 @@ let lastIndexOf_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { buf: Buffer.from('abcdef'), value: 'abc', expected: 0 }, { buf: Buffer.from('abcabc'), value: 'abc', expected: 3 }, @@ -640,8 +621,6 @@ let readXIntXX_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 0, expected: [ -86,170,-17494,-21829,48042,43707,-573785174,-1430532899,3721182122,2864434397 ] }, @@ -699,8 +678,6 @@ let readFloat_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ {} ], @@ -727,8 +704,6 @@ let readGeneric_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 0, length: 1, expected: [ 170, 170, -86, -86 ] }, @@ -767,8 +742,6 @@ let slice_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { buf: Buffer.from('abcdef'), start: 1, expected: 'bcdef' }, { buf: Buffer.from('abcdef'), start: 1, end: 3, expected: 'bc' }, @@ -795,8 +768,6 @@ let subarray_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { buf: Buffer.from('abcdef'), start: 0, end: 3, expected: 'Zbc' }, { buf: Buffer.from('abcdef'), start: 1, expected: 'bcdef' }, @@ -842,8 +813,6 @@ let toJSON_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { value: '', expected: { type: 'Buffer', data: [] } }, { value: 'αβγ', expected: { type: 'Buffer', data: [0xCE, 0xB1, 0xCE, 0xB2, 0xCE, 0xB3] } }, @@ -912,8 +881,6 @@ let write_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { value: 'abc', expected: 3, expected_buf: 'abcZZZZZZZ' }, { value: 'abc', offset: 1, expected: 3, expected_buf: 'ZabcZZZZZZ' }, @@ -955,8 +922,6 @@ let writeXIntXX_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { write: 'writeInt8', value: 0xaa, exception: 'RangeError: Index out of range' }, { write: 'writeInt8', value: 0x00, offset: 3, expected: 4, expected_buf: '5a5a5a005a5a5a5a5a5a' }, @@ -995,8 +960,6 @@ let writeGeneric_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { write: 'writeUIntLE', value: 0xaa, length: 1, exception: 'RangeError: Index out of range' }, diff --git a/test/fs/methods.t.mjs b/test/fs/methods.t.mjs index 928d1682..705c1b5a 100644 --- a/test/fs/methods.t.mjs +++ b/test/fs/methods.t.mjs @@ -1008,7 +1008,6 @@ let readSync_tsuite = { skip: () => (!has_buffer()), T: read_test, prepare_args: p, - opts: {}, get tests() { return read_tests() }, }; @@ -1017,7 +1016,6 @@ let readFh_tsuite = { skip: () => (!has_buffer()), T: readFh_test, prepare_args: p, - opts: {}, get tests() { return read_tests() }, }; @@ -1213,7 +1211,6 @@ let writeSync_tsuite = { skip: () => (!has_buffer()), T: write_test, prepare_args: p, - opts: {}, get tests() { return write_tests() }, }; @@ -1222,7 +1219,6 @@ let writeFh_tsuite = { skip: () => (!has_buffer()), T: writeFh_test, prepare_args: p, - opts: {}, get tests() { return write_tests() }, }; diff --git a/test/harness/runTsuite.js b/test/harness/runTsuite.js index 068a19e1..2a324fbe 100644 --- a/test/harness/runTsuite.js +++ b/test/harness/runTsuite.js @@ -29,7 +29,7 @@ async function run(tlist) { let prepare_args = ts.prepare_args ? ts.prepare_args : default_prepare_args; - return ts.T(prepare_args(t, ts.opts)); + return ts.T(prepare_args(t, ts.opts ? ts.opts : {})); } catch (e) { return Promise.reject(e); diff --git a/test/querystring.t.mjs b/test/querystring.t.mjs index cb42305e..8ea14050 100644 --- a/test/querystring.t.mjs +++ b/test/querystring.t.mjs @@ -17,8 +17,6 @@ let escape_tsuite = { return 'SUCCESS'; }, - opts: { }, - tests: [ { value: '', expected: '' }, { value: 'baz=fuz', expected: 'baz%3Dfuz' }, @@ -62,8 +60,6 @@ let parse_tsuite = { return 'SUCCESS'; }, - opts: { }, - tests: [ { value: '', expected: {} }, { value: 'baz=fuz', expected: { baz:'fuz' } }, @@ -165,8 +161,6 @@ let stringify_tsuite = { return 'SUCCESS'; }, - opts: { }, - tests: [ { obj: {}, expected: '' }, { obj: { baz:'fuz', muz:'tax' }, expected: 'baz=fuz&muz=tax' }, @@ -216,8 +210,6 @@ let unescape_tsuite = { return 'SUCCESS'; }, - opts: { }, - tests: [ { value: '', expected: '' }, { value: 'baz%3Dfuz', expected: 'baz=fuz' }, diff --git a/test/text_decoder.t.js b/test/text_decoder.t.js index a6fced2b..afc91fc4 100644 --- a/test/text_decoder.t.js +++ b/test/text_decoder.t.js @@ -40,8 +40,6 @@ let stream_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { chunks: [new Uint8Array([0xF0, 0x9F, 0x8C, 0x9F])], expected: ['🌟'] }, @@ -95,8 +93,6 @@ let fatal_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { chunks: [new Uint8Array([0xF0, 0xA0, 0xAE, 0xB7])], expected: ['𠮷'] }, @@ -124,8 +120,6 @@ let ignoreBOM_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { value: new Uint8Array([239, 187, 191, 50]), opts: {ignoreBOM: true}, diff --git a/test/text_encoder.t.js b/test/text_encoder.t.js index 10324bca..25173cd1 100644 --- a/test/text_encoder.t.js +++ b/test/text_encoder.t.js @@ -26,8 +26,6 @@ let encode_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { value: "", expected: [] }, { value: "abc", expected: [97, 98, 99] }, @@ -58,8 +56,6 @@ let encodeinto_tsuite = { return 'SUCCESS'; }, - opts: {}, - tests: [ { value: "", dest: new Uint8Array(4), expected: [], read: 0 }, { value: "aα", dest: new Uint8Array(3), expected: [97, 206, 177], read: 2 }, diff --git a/test/webcrypto/digest.t.mjs b/test/webcrypto/digest.t.mjs index 4318fe16..f07f0346 100644 --- a/test/webcrypto/digest.t.mjs +++ b/test/webcrypto/digest.t.mjs @@ -25,7 +25,6 @@ let digest_tsuite = { skip: () => (!has_buffer() || !has_webcrypto()), T: test, prepare_args: p, - opts: { }, tests: [ { name: "XXX", data: "", diff --git a/test/webcrypto/import.t.mjs b/test/webcrypto/import.t.mjs index 1b804006..9ec0fb60 100644 --- a/test/webcrypto/import.t.mjs +++ b/test/webcrypto/import.t.mjs @@ -85,7 +85,6 @@ let aes_tsuite = { skip: () => (!has_webcrypto()), T: test, prepare_args: p, - opts: {}, tests: [ { key: { fmt: "jwk", @@ -186,7 +185,6 @@ let ec_tsuite = { skip: () => (!has_webcrypto()), T: test, prepare_args: p, - opts: {}, tests: [ { key: { fmt: "jwk", @@ -350,7 +348,6 @@ let hmac_tsuite = { skip: () => (!has_webcrypto()), T: test, prepare_args: p, - opts: {}, tests: [ { key: { fmt: "raw", @@ -465,7 +462,6 @@ let rsa_tsuite = { skip: () => (!has_webcrypto()), T: test, prepare_args: p, - opts: {}, tests: [ { key: { fmt: "jwk", diff --git a/test/webcrypto/rsa_decoding.t.mjs b/test/webcrypto/rsa_decoding.t.mjs index 08874491..179072ff 100644 --- a/test/webcrypto/rsa_decoding.t.mjs +++ b/test/webcrypto/rsa_decoding.t.mjs @@ -29,7 +29,6 @@ let rsa_tsuite = { name: "RSA-OAEP decoding", T: test, prepare_args: (v) => v, - opts: { }, tests: [ { pem: "rsa.pkcs8", src: "text.base64.rsa-oaep.enc", expected: "WAKAWAKA" }, From noreply at nginx.com Tue Mar 18 21:46:02 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 18 Mar 2025 21:46:02 +0000 (UTC) Subject: [njs] QuickJS: crypto module. Message-ID: <20250318214602.D78D34837C@pubserv1.nginx> details: https://github.com/nginx/njs/commit/d5359d17f151bf172697e2ac353377b469b64c0f branches: master commit: d5359d17f151bf172697e2ac353377b469b64c0f user: Vadim Zhestikov date: Wed, 5 Mar 2025 18:54:41 -0800 description: QuickJS: crypto module. --- auto/qjs_modules | 6 + external/qjs_crypto_module.c | 659 ++++++++++++++++++++++++++++++++++++++++ external/qjs_webcrypto_module.c | 23 -- src/qjs.c | 103 +++++++ src/qjs.h | 21 +- src/test/njs_unit_test.c | 265 ---------------- test/crypto.t.mjs | 447 +++++++++++++++++++++++++++ 7 files changed, 1235 insertions(+), 289 deletions(-) diff --git a/auto/qjs_modules b/auto/qjs_modules index d82f9d9f..1381d2c5 100644 --- a/auto/qjs_modules +++ b/auto/qjs_modules @@ -7,6 +7,12 @@ njs_module_srcs=src/qjs_buffer.c . auto/qjs_module +njs_module_name=qjs_crypto_module +njs_module_incs= +njs_module_srcs=external/qjs_crypto_module.c + +. auto/qjs_module + njs_module_name=qjs_fs_module njs_module_incs= njs_module_srcs=external/qjs_fs_module.c diff --git a/external/qjs_crypto_module.c b/external/qjs_crypto_module.c new file mode 100644 index 00000000..2b1bb26b --- /dev/null +++ b/external/qjs_crypto_module.c @@ -0,0 +1,659 @@ + +/* + * Copyright (C) Vadim Zhestkov + * Copyright (C) F5, Inc. + */ + +#include +#include "njs_hash.h" + +typedef void (*qjs_hash_init)(njs_hash_t *ctx); +typedef void (*qjs_hash_update)(njs_hash_t *ctx, const void *data, size_t size); +typedef void (*qjs_hash_final)(u_char result[32], njs_hash_t *ctx); + +typedef JSValue (*qjs_digest_encode)(JSContext *cx, const njs_str_t *src); + + +typedef struct { + njs_str_t name; + + size_t size; + qjs_hash_init init; + qjs_hash_update update; + qjs_hash_final final; +} qjs_hash_alg_t; + +typedef struct { + njs_hash_t ctx; + qjs_hash_alg_t *alg; +} qjs_digest_t; + +typedef struct { + u_char opad[64]; + njs_hash_t ctx; + qjs_hash_alg_t *alg; +} qjs_hmac_t; + + +typedef struct { + njs_str_t name; + + qjs_digest_encode encode; +} qjs_crypto_enc_t; + + +static qjs_hash_alg_t *qjs_crypto_algorithm(JSContext *cx, JSValueConst val); +static qjs_crypto_enc_t *qjs_crypto_encoding(JSContext *cx, JSValueConst val); +static JSValue qjs_buffer_digest(JSContext *cx, const njs_str_t *src); +static JSValue qjs_crypto_create_hash(JSContext *cx, JSValueConst this_val, + int argc, JSValueConst *argv); +static JSValue qjs_hash_prototype_update(JSContext *cx, JSValueConst this_val, + int argc, JSValueConst *argv, int hmac); +static JSValue qjs_hash_prototype_digest(JSContext *cx, JSValueConst this_val, + int argc, JSValueConst *argv, int hmac); +static JSValue qjs_hash_prototype_copy(JSContext *cx, JSValueConst this_val, + int argc, JSValueConst *argv); +static JSValue qjs_crypto_create_hmac(JSContext *cx, JSValueConst this_val, + int argc, JSValueConst *argv); +static void qjs_hash_finalizer(JSRuntime *rt, JSValue val); +static void qjs_hmac_finalizer(JSRuntime *rt, JSValue val); +static int qjs_crypto_module_init(JSContext *cx, JSModuleDef *m); +static JSModuleDef * qjs_crypto_init(JSContext *cx, const char *module_name); + + +static qjs_hash_alg_t qjs_hash_algorithms[] = { + + { + njs_str("md5"), + 16, + njs_md5_init, + njs_md5_update, + njs_md5_final + }, + + { + njs_str("sha1"), + 20, + njs_sha1_init, + njs_sha1_update, + njs_sha1_final + }, + + { + njs_str("sha256"), + 32, + njs_sha2_init, + njs_sha2_update, + njs_sha2_final + }, + + { + njs_null_str, + 0, + NULL, + NULL, + NULL + } + +}; + + +static qjs_crypto_enc_t qjs_encodings[] = { + + { + njs_str("buffer"), + qjs_buffer_digest + }, + + { + njs_str("hex"), + qjs_string_hex + }, + + { + njs_str("base64"), + qjs_string_base64 + }, + + { + njs_str("base64url"), + qjs_string_base64url + }, + + { + njs_null_str, + NULL + } + +}; + + +static const JSCFunctionListEntry qjs_crypto_export[] = { + JS_CFUNC_DEF("createHash", 1, qjs_crypto_create_hash), + JS_CFUNC_DEF("createHmac", 2, qjs_crypto_create_hmac), +}; + + +static const JSCFunctionListEntry qjs_hash_proto_proto[] = { + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Hash", JS_PROP_CONFIGURABLE), + JS_CFUNC_MAGIC_DEF("update", 2, qjs_hash_prototype_update, 0), + JS_CFUNC_MAGIC_DEF("digest", 1, qjs_hash_prototype_digest, 0), + JS_CFUNC_DEF("copy", 0, qjs_hash_prototype_copy), + JS_CFUNC_DEF("constructor", 1, qjs_crypto_create_hash), +}; + + +static const JSCFunctionListEntry qjs_hmac_proto_proto[] = { + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Hmac", JS_PROP_CONFIGURABLE), + JS_CFUNC_MAGIC_DEF("update", 2, qjs_hash_prototype_update, 1), + JS_CFUNC_MAGIC_DEF("digest", 1, qjs_hash_prototype_digest, 1), + JS_CFUNC_DEF("constructor", 2, qjs_crypto_create_hmac), +}; + + +static JSClassDef qjs_hash_class = { + "Hash", + .finalizer = qjs_hash_finalizer, +}; + + +static JSClassDef qjs_hmac_class = { + "Hmac", + .finalizer = qjs_hmac_finalizer, +}; + + +qjs_module_t qjs_crypto_module = { + .name = "crypto", + .init = qjs_crypto_init, +}; + + +static JSValue +qjs_crypto_create_hash(JSContext *cx, JSValueConst this_val, int argc, + JSValueConst *argv) +{ + JSValue obj; + qjs_digest_t *dgst; + qjs_hash_alg_t *alg; + + alg = qjs_crypto_algorithm(cx, argv[0]); + if (alg == NULL) { + return JS_EXCEPTION; + } + + dgst = js_malloc(cx, sizeof(qjs_digest_t)); + if (dgst == NULL) { + return JS_ThrowOutOfMemory(cx); + } + + dgst->alg = alg; + alg->init(&dgst->ctx); + + obj = JS_NewObjectClass(cx, QJS_CORE_CLASS_CRYPTO_HASH); + if (JS_IsException(obj)) { + js_free(cx, dgst); + return obj; + } + + JS_SetOpaque(obj, dgst); + + return obj; +} + + +static JSValue +qjs_hash_prototype_update(JSContext *cx, JSValueConst this_val, int argc, + JSValueConst *argv, int hmac) +{ + njs_str_t str, content; + njs_hash_t *uctx; + qjs_hmac_t *hctx; + qjs_bytes_t bytes; + qjs_digest_t *dgst; + const qjs_buffer_encoding_t *enc; + + void (*update)(njs_hash_t *ctx, const void *data, size_t size); + + if (!hmac) { + dgst = JS_GetOpaque2(cx, this_val, QJS_CORE_CLASS_CRYPTO_HASH); + if (dgst == NULL) { + return JS_ThrowTypeError(cx, "\"this\" is not a hash object"); + } + + if (dgst->alg == NULL) { + return JS_ThrowTypeError(cx, "Digest already called"); + } + + update = dgst->alg->update; + uctx = &dgst->ctx; + + } else { + hctx = JS_GetOpaque2(cx, this_val, QJS_CORE_CLASS_CRYPTO_HMAC); + if (hctx == NULL) { + return JS_ThrowTypeError(cx, "\"this\" is not a hmac object"); + } + + if (hctx->alg == NULL) { + return JS_ThrowTypeError(cx, "Digest already called"); + } + + update = hctx->alg->update; + uctx = &hctx->ctx; + } + + if (JS_IsString(argv[0])) { + enc = qjs_buffer_encoding(cx, argv[1], 1); + if (enc == NULL) { + return JS_EXCEPTION; + } + + str.start = (u_char *) JS_ToCStringLen(cx, &str.length, argv[0]); + if (str.start == NULL) { + return JS_EXCEPTION; + } + + if (enc->decode_length != NULL) { + content.length = enc->decode_length(cx, &str); + content.start = js_malloc(cx, content.length); + if (content.start == NULL) { + JS_FreeCString(cx, (const char *) str.start); + return JS_ThrowOutOfMemory(cx); + } + + if (enc->decode(cx, &str, &content) != 0) { + JS_FreeCString(cx, (const char *) str.start); + JS_FreeCString(cx, (const char *) content.start); + return JS_EXCEPTION; + } + + JS_FreeCString(cx, (const char *) str.start); + + update(uctx, content.start, content.length); + js_free(cx, content.start); + + } else { + update(uctx, str.start, str.length); + JS_FreeCString(cx, (const char *) str.start); + } + + } else if (qjs_is_typed_array(cx, argv[0])) { + if (qjs_to_bytes(cx, &bytes, argv[0]) != 0) { + return JS_EXCEPTION; + } + + update(uctx, bytes.start, bytes.length); + + } else { + return JS_ThrowTypeError(cx, + "data is not a string or Buffer-like object"); + } + + return JS_DupValue(cx, this_val); +} + + +static JSValue +qjs_hash_prototype_digest(JSContext *cx, JSValueConst this_val, int argc, + JSValueConst *argv, int hmac) +{ + njs_str_t str; + qjs_hmac_t *hctx; + qjs_digest_t *dgst; + qjs_hash_alg_t *alg; + qjs_crypto_enc_t *enc; + u_char hash1[32],digest[32]; + + if (!hmac) { + dgst = JS_GetOpaque2(cx, this_val, QJS_CORE_CLASS_CRYPTO_HASH); + if (dgst == NULL) { + return JS_ThrowTypeError(cx, "\"this\" is not a hash object"); + } + + alg = dgst->alg; + if (alg == NULL) { + return JS_ThrowTypeError(cx, "Digest already called"); + } + + dgst->alg = NULL; + + alg->final(digest, &dgst->ctx); + + } else { + hctx = JS_GetOpaque2(cx, this_val, QJS_CORE_CLASS_CRYPTO_HMAC); + if (hctx == NULL) { + return JS_ThrowTypeError(cx, "\"this\" is not a hmac object"); + } + + alg = hctx->alg; + if (alg == NULL) { + return JS_ThrowTypeError(cx, "Digest already called"); + } + + hctx->alg = NULL; + + alg->final(hash1, &hctx->ctx); + + alg->init(&hctx->ctx); + alg->update(&hctx->ctx, hctx->opad, 64); + alg->update(&hctx->ctx, hash1, alg->size); + alg->final(digest, &hctx->ctx); + } + + str.start = digest; + str.length = alg->size; + + if (argc == 0) { + return qjs_buffer_digest(cx, &str); + } + + enc = qjs_crypto_encoding(cx, argv[0]); + if (enc == NULL) { + return JS_EXCEPTION; + } + + return enc->encode(cx, &str); +} + + +static JSValue +qjs_hash_prototype_copy(JSContext *cx, JSValueConst this_val, int argc, + JSValueConst *argv) +{ + JSValue obj; + qjs_digest_t *dgst, *copy; + + dgst = JS_GetOpaque2(cx, this_val, QJS_CORE_CLASS_CRYPTO_HASH); + if (dgst == NULL) { + return JS_EXCEPTION; + } + + if (dgst->alg == NULL) { + return JS_ThrowTypeError(cx, "Digest already called"); + } + + copy = js_malloc(cx, sizeof(qjs_digest_t)); + if (copy == NULL) { + return JS_ThrowOutOfMemory(cx); + } + + memcpy(copy, dgst, sizeof(qjs_digest_t)); + + obj = JS_NewObjectClass(cx, QJS_CORE_CLASS_CRYPTO_HASH); + if (JS_IsException(obj)) { + js_free(cx, copy); + return obj; + } + + JS_SetOpaque(obj, copy); + + return obj; +} + + +static JSValue +qjs_crypto_create_hmac(JSContext *cx, JSValueConst this_val, int argc, + JSValueConst *argv) +{ + int i; + JS_BOOL key_is_string; + JSValue obj; + njs_str_t key; + qjs_hmac_t *hmac; + qjs_bytes_t bytes; + qjs_hash_alg_t *alg; + u_char digest[32], key_buf[64]; + + alg = qjs_crypto_algorithm(cx, argv[0]); + if (alg == NULL) { + return JS_EXCEPTION; + } + + key_is_string = JS_IsString(argv[1]); + if (key_is_string) { + key.start = (u_char *) JS_ToCStringLen(cx, &key.length, argv[1]); + if (key.start == NULL) { + return JS_EXCEPTION; + } + + } else if (qjs_is_typed_array(cx, argv[1])) { + if (qjs_to_bytes(cx, &bytes, argv[1]) != 0) { + return JS_EXCEPTION; + } + + key.start = bytes.start; + key.length = bytes.length; + + } else { + return JS_ThrowTypeError(cx, + "key is not a string or Buffer-like object"); + } + + hmac = js_malloc(cx, sizeof(qjs_hmac_t)); + if (hmac == NULL) { + if (key_is_string) { + JS_FreeCString(cx, (const char *) key.start); + } + + return JS_ThrowOutOfMemory(cx); + } + + hmac->alg = alg; + + if (key.length > sizeof(key_buf)) { + alg->init(&hmac->ctx); + alg->update(&hmac->ctx, key.start, key.length); + alg->final(digest, &hmac->ctx); + + memcpy(key_buf, digest, alg->size); + memset(key_buf + alg->size, 0, sizeof(key_buf) - alg->size); + + } else { + memcpy(key_buf, key.start, key.length); + memset(key_buf + key.length, 0, sizeof(key_buf) - key.length); + } + + if (key_is_string) { + JS_FreeCString(cx, (const char *) key.start); + } + + for (i = 0; i < 64; i++) { + hmac->opad[i] = key_buf[i] ^ 0x5c; + } + + for (i = 0; i < 64; i++) { + key_buf[i] ^= 0x36; + } + + alg->init(&hmac->ctx); + alg->update(&hmac->ctx, key_buf, 64); + + obj = JS_NewObjectClass(cx, QJS_CORE_CLASS_CRYPTO_HMAC); + if (JS_IsException(obj)) { + js_free(cx, hmac); + return obj; + } + + JS_SetOpaque(obj, hmac); + + return obj; +} + + +static void +qjs_hash_finalizer(JSRuntime *rt, JSValue val) +{ + qjs_digest_t *dgst; + + dgst = JS_GetOpaque(val, QJS_CORE_CLASS_CRYPTO_HASH); + if (dgst != NULL) { + js_free_rt(rt, dgst); + } +} + + +static void +qjs_hmac_finalizer(JSRuntime *rt, JSValue val) +{ + qjs_hmac_t *hmac; + + hmac = JS_GetOpaque(val, QJS_CORE_CLASS_CRYPTO_HMAC); + if (hmac != NULL) { + js_free_rt(rt, hmac); + } +} + + +static qjs_hash_alg_t * +qjs_crypto_algorithm(JSContext *cx, JSValueConst val) +{ + njs_str_t name; + qjs_hash_alg_t *a, *alg; + + name.start = (u_char *) JS_ToCStringLen(cx, &name.length, val); + if (name.start == NULL) { + JS_ThrowTypeError(cx, "algorithm must be a string"); + return NULL; + } + + alg = NULL; + + for (a = &qjs_hash_algorithms[0]; a->name.start != NULL; a++) { + if (njs_strstr_eq(&name, &a->name)) { + alg = a; + break; + } + } + + JS_FreeCString(cx, (const char *) name.start); + + if (alg == NULL) { + JS_ThrowTypeError(cx, "not supported algorithm"); + } + + return alg; +} + + +static qjs_crypto_enc_t * +qjs_crypto_encoding(JSContext *cx, JSValueConst val) +{ + njs_str_t name; + qjs_crypto_enc_t *e, *enc; + + if (JS_IsNullOrUndefined(val)) { + return &qjs_encodings[0]; + } + + name.start = (u_char *) JS_ToCStringLen(cx, &name.length, val); + if (name.start == NULL) { + return NULL; + } + + enc = NULL; + + for (e = &qjs_encodings[1]; e->name.start != NULL; e++) { + if (njs_strstr_eq(&name, &e->name)) { + enc = e; + break; + } + } + + JS_FreeCString(cx, (const char *) name.start); + + if (enc == NULL) { + JS_ThrowTypeError(cx, "Unknown digest encoding"); + } + + return enc; +} + + +static JSValue +qjs_buffer_digest(JSContext *cx, const njs_str_t *src) +{ + return qjs_buffer_create(cx, src->start, src->length); +} + + +static int +qjs_crypto_module_init(JSContext *cx, JSModuleDef *m) +{ + JSValue crypto_obj; + + crypto_obj = JS_NewObject(cx); + if (JS_IsException(crypto_obj)) { + return -1; + } + + JS_SetPropertyFunctionList(cx, crypto_obj, qjs_crypto_export, + njs_nitems(qjs_crypto_export)); + + if (JS_SetModuleExport(cx, m, "default", crypto_obj) != 0) { + return -1; + } + + return JS_SetModuleExportList(cx, m, qjs_crypto_export, + njs_nitems(qjs_crypto_export)); +} + + +static JSModuleDef * +qjs_crypto_init(JSContext *cx, const char *module_name) +{ + JSValue proto; + JSModuleDef *m; + + if (!JS_IsRegisteredClass(JS_GetRuntime(cx), + QJS_CORE_CLASS_CRYPTO_HASH)) + { + if (JS_NewClass(JS_GetRuntime(cx), QJS_CORE_CLASS_CRYPTO_HASH, + &qjs_hash_class) < 0) + { + return NULL; + } + + proto = JS_NewObject(cx); + if (JS_IsException(proto)) { + return NULL; + } + + JS_SetPropertyFunctionList(cx, proto, qjs_hash_proto_proto, + njs_nitems(qjs_hash_proto_proto)); + + JS_SetClassProto(cx, QJS_CORE_CLASS_CRYPTO_HASH, proto); + + if (JS_NewClass(JS_GetRuntime(cx), QJS_CORE_CLASS_CRYPTO_HMAC, + &qjs_hmac_class) < 0) + { + return NULL; + } + + proto = JS_NewObject(cx); + if (JS_IsException(proto)) { + return NULL; + } + + JS_SetPropertyFunctionList(cx, proto, qjs_hmac_proto_proto, + njs_nitems(qjs_hmac_proto_proto)); + + JS_SetClassProto(cx, QJS_CORE_CLASS_CRYPTO_HMAC, proto); + } + + m = JS_NewCModule(cx, module_name, qjs_crypto_module_init); + if (m == NULL) { + return NULL; + } + + if (JS_AddModuleExport(cx, m, "default") < 0) { + return NULL; + } + + if (JS_AddModuleExportList(cx, m, qjs_crypto_export, + njs_nitems(qjs_crypto_export)) != 0) + { + return NULL; + } + + return m; +} diff --git a/external/qjs_webcrypto_module.c b/external/qjs_webcrypto_module.c index 730feb6e..a1983651 100644 --- a/external/qjs_webcrypto_module.c +++ b/external/qjs_webcrypto_module.c @@ -1238,29 +1238,6 @@ fail: } -static JSValue -qjs_string_base64url(JSContext *cx, const njs_str_t *src) -{ - size_t padding; - njs_str_t dst; - u_char buf[/* qjs_base64_encoded_length(512) */ 686]; - - if (src->length == 0) { - return JS_NewStringLen(cx, "", 0); - } - - padding = src->length % 3; - padding = (4 >> padding) & 0x03; - - dst.start = buf; - dst.length = qjs_base64_encode_length(cx, src) - padding; - - qjs_base64url_encode(cx, src, &dst); - - return JS_NewStringLen(cx, (const char *) dst.start, dst.length); -} - - static JSValue qjs_export_base64url_bignum(JSContext *cx, const BIGNUM *v, size_t size) { diff --git a/src/qjs.c b/src/qjs.c index ed9a6178..15a575a2 100644 --- a/src/qjs.c +++ b/src/qjs.c @@ -1025,3 +1025,106 @@ qjs_string_create_chb(JSContext *cx, njs_chb_t *chain) return val; } + + +JSValue +qjs_string_hex(JSContext *cx, const njs_str_t *src) +{ + JSValue ret; + njs_str_t dst; + u_char buf[1024]; + + if (src->length == 0) { + return JS_NewStringLen(cx, "", 0); + } + + dst.start = buf; + dst.length = qjs_hex_encode_length(cx, src); + + if (dst.length <= sizeof(buf)) { + qjs_hex_encode(cx, src, &dst); + ret = JS_NewStringLen(cx, (const char *) dst.start, dst.length); + + } else { + dst.start = js_malloc(cx, dst.length); + if (dst.start == NULL) { + return JS_ThrowOutOfMemory(cx); + } + + qjs_hex_encode(cx, src, &dst); + ret = JS_NewStringLen(cx, (const char *) dst.start, dst.length); + js_free(cx, dst.start); + } + + return ret; +} + + +JSValue +qjs_string_base64(JSContext *cx, const njs_str_t *src) +{ + JSValue ret; + njs_str_t dst; + u_char buf[1024]; + + if (src->length == 0) { + return JS_NewStringLen(cx, "", 0); + } + + dst.start = buf; + dst.length = qjs_base64_encode_length(cx, src); + + if (dst.length <= sizeof(buf)) { + qjs_base64_encode(cx, src, &dst); + ret = JS_NewStringLen(cx, (const char *) dst.start, dst.length); + + } else { + dst.start = js_malloc(cx, dst.length); + if (dst.start == NULL) { + return JS_ThrowOutOfMemory(cx); + } + + qjs_base64_encode(cx, src, &dst); + ret = JS_NewStringLen(cx, (const char *) dst.start, dst.length); + js_free(cx, dst.start); + } + + return ret; +} + + +JSValue +qjs_string_base64url(JSContext *cx, const njs_str_t *src) +{ + size_t padding; + JSValue ret; + njs_str_t dst; + u_char buf[1024]; + + if (src->length == 0) { + return JS_NewStringLen(cx, "", 0); + } + + padding = src->length % 3; + padding = (4 >> padding) & 0x03; + + dst.start = buf; + dst.length = qjs_base64_encode_length(cx, src) - padding; + + if (dst.length <= sizeof(buf)) { + qjs_base64url_encode(cx, src, &dst); + ret = JS_NewStringLen(cx, (const char *) dst.start, dst.length); + + } else { + dst.start = js_malloc(cx, dst.length); + if (dst.start == NULL) { + return JS_ThrowOutOfMemory(cx); + } + + qjs_base64url_encode(cx, src, &dst); + ret = JS_NewStringLen(cx, (const char *) dst.start, dst.length); + js_free(cx, dst.start); + } + + return ret; +} diff --git a/src/qjs.h b/src/qjs.h index 7c560d5f..54f96dfe 100644 --- a/src/qjs.h +++ b/src/qjs.h @@ -42,7 +42,9 @@ #define QJS_CORE_CLASS_ID_FS_DIRENT (QJS_CORE_CLASS_ID_OFFSET + 5) #define QJS_CORE_CLASS_ID_FS_FILEHANDLE (QJS_CORE_CLASS_ID_OFFSET + 6) #define QJS_CORE_CLASS_ID_WEBCRYPTO_KEY (QJS_CORE_CLASS_ID_OFFSET + 7) -#define QJS_CORE_CLASS_ID_LAST (QJS_CORE_CLASS_ID_OFFSET + 8) +#define QJS_CORE_CLASS_CRYPTO_HASH (QJS_CORE_CLASS_ID_OFFSET + 8) +#define QJS_CORE_CLASS_CRYPTO_HMAC (QJS_CORE_CLASS_ID_OFFSET + 9) +#define QJS_CORE_CLASS_ID_LAST (QJS_CORE_CLASS_ID_OFFSET + 10) typedef JSModuleDef *(*qjs_addon_init_pt)(JSContext *ctx, const char *name); @@ -102,6 +104,20 @@ typedef struct { u_char *start; } qjs_bytes_t; + +njs_inline int +qjs_is_typed_array(JSContext *cx, JSValue val) +{ + JS_BOOL exception; + + val = JS_GetTypedArrayBuffer(cx, val, NULL, NULL, NULL); + exception = JS_IsException(val); + JS_FreeValue(cx, val); + + return !exception; +} + + int qjs_to_bytes(JSContext *ctx, qjs_bytes_t *data, JSValueConst value); void qjs_bytes_free(JSContext *ctx, qjs_bytes_t *data); JSValue qjs_typed_array_data(JSContext *ctx, JSValueConst value, @@ -111,6 +127,9 @@ JSValue qjs_typed_array_data(JSContext *ctx, JSValueConst value, JS_NewStringLen(ctx, (const char *) (data), len) JSValue qjs_string_create_chb(JSContext *cx, njs_chb_t *chain); +JSValue qjs_string_hex(JSContext *cx, const njs_str_t *src); +JSValue qjs_string_base64(JSContext *cx, const njs_str_t *src); +JSValue qjs_string_base64url(JSContext *cx, const njs_str_t *src); static inline JS_BOOL JS_IsNullOrUndefined(JSValueConst v) { diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 4f5e5c91..f39660f5 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -20239,265 +20239,6 @@ static njs_unit_test_t njs_fs_module_test[] = }; -static njs_unit_test_t njs_crypto_module_test[] = -{ - { njs_str("import x from 'crypto'"), - njs_str("undefined") }, - - { njs_str("import x from 'crypto' 1"), - njs_str("SyntaxError: Unexpected token \"1\" in 1") }, - - { njs_str("if (1) {import x from 'crypto'}"), - njs_str("SyntaxError: Illegal import statement in 1") }, - - { njs_str("var h = require('crypto').createHash('sha1');" - "[Object.prototype.toString.call(h), njs.dump(h),h]"), - njs_str("[object Hash],Hash {},[object Hash]") }, - - { njs_str("var h = require('crypto').createHash('sha1');" - "var Hash = h.constructor; " - "Hash('sha1').update('AB').digest('hex')"), - njs_str("06d945942aa26a61be18c3e22bf19bbca8dd2b5d") }, - - { njs_str("var hash = require('crypto').createHash.bind(undefined, 'md5');" - "['hex', 'base64', 'base64url'].map(e => {" - " var h = hash().update('AB').digest().toString(e);" - " var h2 = hash().update(Buffer.from('XABX').subarray(1,3)).digest(e);" - " var h3 = hash().update('A').update('B').digest(e);" - " if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};" - " if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};" - " return h;" - "})"), - njs_str("b86fc6b051f63d73de262d4c34e3a0a9," - "uG/GsFH2PXPeJi1MNOOgqQ==," - "uG_GsFH2PXPeJi1MNOOgqQ") }, - - { njs_str("var hash = require('crypto').createHash.bind(undefined, 'sha1');" - "['hex', 'base64', 'base64url'].map(e => {" - " var h = hash().update('4142', 'hex').digest().toString(e);" - " var h2 = hash().update(Buffer.from('XABX').subarray(1,3)).digest(e);" - " var h3 = hash().update('A').update('B').digest(e);" - " if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};" - " if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};" - " return h;" - "})"), - njs_str("06d945942aa26a61be18c3e22bf19bbca8dd2b5d," - "BtlFlCqiamG+GMPiK/GbvKjdK10=," - "BtlFlCqiamG-GMPiK_GbvKjdK10") }, - - { njs_str("var hash = require('crypto').createHash.bind(undefined, 'sha1');" - "['hex', 'base64', 'base64url'].every(e => {" - " var h = hash().digest(e);" - " var h2 = hash().update('').digest(e);" - " if (h !== h2) {throw new Error(`digest($e):$h != update('').digest($e):$h2`)};" - " return true;" - "})"), - njs_str("true") }, - - { njs_str("var hash = require('crypto').createHash.bind(undefined, 'sha1');" - "[" - " ['AB']," - " ['4142', 'hex']," - " ['QUI=', 'base64']," - " ['QUI', 'base64url']" - "].every(args => {" - " return hash().update(args[0], args[1]).digest('hex') === '06d945942aa26a61be18c3e22bf19bbca8dd2b5d';" - "})"), - njs_str("true") }, - - { njs_str("var hash = require('crypto').createHash.bind(undefined, 'sha256');" - "['hex', 'base64', 'base64url'].map(e => {" - " var h = hash().update('AB').digest().toString(e);" - " var h2 = hash().update(Buffer.from('XABX').subarray(1,3)).digest(e);" - " var h3 = hash().update('A').update('B').digest(e);" - " if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};" - " if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};" - " return h;" - "})"), - njs_str("38164fbd17603d73f696b8b4d72664d735bb6a7c88577687fd2ae33fd6964153," - "OBZPvRdgPXP2lri01yZk1zW7anyIV3aH/SrjP9aWQVM=," - "OBZPvRdgPXP2lri01yZk1zW7anyIV3aH_SrjP9aWQVM") }, - - { njs_str("const crypto = require('crypto');" - "let hash = crypto.createHash('sha256');" - "let digests = [];" - "hash.update('one');" - "digests.push(hash.copy().digest('hex'));" - "hash.update('two');" - "digests.push(hash.copy().digest('hex'));" - "hash.update('three');" - "digests.push(hash.copy().digest('hex'));" - "digests"), - njs_str("7692c3ad3540bb803c020b3aee66cd8887123234ea0c6e7143c0add73ff431ed," - "25b6746d5172ed6352966a013d93ac846e1110d5a25e8f183b5931f4688842a1," - "4592092e1061c7ea85af2aed194621cc17a2762bae33a79bf8ce33fd0168b801") }, - - { njs_str("const crypto = require('crypto');" - "let hash = crypto.createHash('sha256');" - "hash.update('one').digest();" - "hash.copy()"), - njs_str("Error: Digest already called") }, - - { njs_str("var hash = require('crypto').createHash;" - "njs.dump(['', 'abc'.repeat(100)].map(v => {" - " return ['md5', 'sha1', 'sha256'].map(h => {" - " return hash(h).update(v).digest('hex');" - " })" - "}))"), - njs_str("[['d41d8cd98f00b204e9800998ecf8427e'," - "'da39a3ee5e6b4b0d3255bfef95601890afd80709'," - "'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855']," - "['f571117acbd8153c8dc3c81b8817773a'," - "'c95466320eaae6d19ee314ae4f135b12d45ced9a'," - "'d9f5aeb06abebb3be3f38adec9a2e3b94228d52193be923eb4e24c9b56ee0930']]") }, - - { njs_str("var h = require('crypto').createHash()"), - njs_str("TypeError: algorithm must be a string") }, - - { njs_str("var h = require('crypto').createHash([])"), - njs_str("TypeError: algorithm must be a string") }, - - { njs_str("var h = require('crypto').createHash('sha512')"), - njs_str("TypeError: not supported algorithm: \"sha512\"") }, - - { njs_str("var h = require('crypto').createHash('sha1');" - "h.update()"), - njs_str("TypeError: data is not a string or Buffer-like object") }, - - { njs_str("var h = require('crypto').createHash('sha1');" - "h.update({})"), - njs_str("TypeError: data is not a string or Buffer-like object") }, - - { njs_str("var h = require('crypto').createHash('sha1');" - "h.update('A').digest('latin1')"), - njs_str("TypeError: Unknown digest encoding: \"latin1\"") }, - - { njs_str("require('crypto').createHash('sha1').digest() instanceof Buffer"), - njs_str("true") }, - - { njs_str("var h = require('crypto').createHash('sha1');" - "h.update('A').digest('hex'); h.digest('hex')"), - njs_str("Error: Digest already called") }, - - { njs_str("var h = require('crypto').createHash('sha1');" - "h.update('A').digest('hex'); h.update('B')"), - njs_str("Error: Digest already called") }, - - { njs_str("typeof require('crypto').createHash('md5')"), - njs_str("object") }, - - { njs_str("var h = require('crypto').createHmac('sha1', '');" - "[Object.prototype.toString.call(h), njs.dump(h),h]"), - njs_str("[object Hmac],Hmac {},[object Hmac]") }, - - { njs_str("var hmac = require('crypto').createHmac.bind(undefined, 'md5', '');" - "['hex', 'base64', 'base64url'].map(e => {" - " var h = hmac().update('AB').digest().toString(e);" - " var h2 = hmac().update(Buffer.from('XABX').subarray(1,3)).digest(e);" - " var h3 = hmac().update('A').update('B').digest(e);" - " if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};" - " if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};" - " return h;" - "})"), - njs_str("9e0e9e545ef63d41dfb653daecf8ebc7," - "ng6eVF72PUHftlPa7Pjrxw==," - "ng6eVF72PUHftlPa7Pjrxw") }, - - { njs_str("var hmac = require('crypto').createHmac.bind(undefined, 'sha1', '');" - "['hex', 'base64', 'base64url'].map(e => {" - " var h = hmac().update('AB').digest().toString(e);" - " var h2 = hmac().update(Buffer.from('XABX').subarray(1,3)).digest(e);" - " var h3 = hmac().update('A').update('B').digest(e);" - " if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};" - " if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};" - " return h;" - "})"), - njs_str("d32c0b6637cc2dfe4670f3fe48ef4434123c4810," - "0ywLZjfMLf5GcPP+SO9ENBI8SBA=," - "0ywLZjfMLf5GcPP-SO9ENBI8SBA") }, - - { njs_str("var hash = require('crypto').createHmac.bind(undefined, 'sha1', '');" - "[" - " ['AB']," - " ['4142', 'hex']," - " ['QUI=', 'base64']," - " ['QUI', 'base64url']" - "].every(args => {" - " return hash().update(args[0], args[1]).digest('hex') === 'd32c0b6637cc2dfe4670f3fe48ef4434123c4810';" - "})"), - njs_str("true") }, - - { njs_str("var hmac = require('crypto').createHmac.bind(undefined, 'sha256', '');" - "['hex', 'base64', 'base64url'].map(e => {" - " var h = hmac().update('AB').digest().toString(e);" - " var h2 = hmac().update(Buffer.from('AB')).digest(e);" - " var h3 = hmac().update('A').update('B').digest(e);" - " if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};" - " if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};" - " return h;" - "})"), - njs_str("d53400095496267cf02e5dbd4b0bf9fbfb5f36f311ea7d9809af5487421743e3," - "1TQACVSWJnzwLl29Swv5+/tfNvMR6n2YCa9Uh0IXQ+M=," - "1TQACVSWJnzwLl29Swv5-_tfNvMR6n2YCa9Uh0IXQ-M") }, - - { njs_str("var hmac = require('crypto').createHmac;" - "njs.dump(['', 'abc'.repeat(100)].map(v => {" - " return ['md5', 'sha1', 'sha256'].map(h => {" - " return hmac(h, Buffer.from('secret')).update(v).digest('hex');" - " })" - "}))"), - njs_str("[['5c8db03f04cec0f43bcb060023914190'," - "'25af6174a0fcecc4d346680a72b7ce644b9a88e8'," - "'f9e66e179b6747ae54108f82f8ade8b3c25d76fd30afde6c395822c530196169']," - "['91eb74a225cdd3bbfccc34396c6e3ac5'," - "'0aac71e3a813a7acc4a809cfdedb2ecba04ffc5e'," - "'8660d2d51d6f20f61d5aadfb6c43df7fd05fc2fc4967d8aec1846f3d9ec03987']]") }, - - { njs_str("var h = require('crypto').createHmac('sha1', '');" - "var Hmac = h.constructor; " - "Hmac('sha1', '').digest('hex')"), - njs_str("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d") }, - - { njs_str("require('crypto').createHmac('sha1', '').digest() instanceof Buffer"), - njs_str("true") }, - - { njs_str("var h = require('crypto').createHmac('sha256', 'A'.repeat(64));" - "h.update('AB').digest('hex')"), - njs_str("ee9dce43b12eb3e865614ad9c1a8d4fad4b6eac2b64647bd24cd192888d3f367") }, - - { njs_str("var h = require('crypto').createHmac('sha256', 'A'.repeat(100));" - "h.update('AB').digest('hex')"), - njs_str("5647b6c429701ff512f0f18232b4507065d2376ca8899a816a0a6e721bf8ddcc") }, - - { njs_str("var h = require('crypto').createHmac()"), - njs_str("TypeError: algorithm must be a string") }, - - { njs_str("var h = require('crypto').createHmac([])"), - njs_str("TypeError: algorithm must be a string") }, - - { njs_str("var h = require('crypto').createHmac('sha512', '')"), - njs_str("TypeError: not supported algorithm: \"sha512\"") }, - - { njs_str("var h = require('crypto').createHmac('sha1', [])"), - njs_str("TypeError: key is not a string or Buffer-like object") }, - - { njs_str("var h = require('crypto').createHmac('sha1', 'secret key');" - "h.update('A').digest('hex'); h.digest('hex')"), - njs_str("Error: Digest already called") }, - - { njs_str("var h = require('crypto').createHmac('sha1', 'secret key');" - "h.update('A').digest('hex'); h.update('B')"), - njs_str("Error: Digest already called") }, - - { njs_str("typeof require('crypto').createHmac('md5', 'a')"), - njs_str("object") }, - - { njs_str("var cr = require('crypto'); var h = cr.createHash('sha1');" - "h.update.call(cr.createHmac('sha1', 's'), '')"), - njs_str("TypeError: \"this\" is not a hash object") }, -}; - - #define NJS_XML_DOC "const xml = require('xml');" \ "let data = `ToveJani`;" \ "let doc = xml.parse(data);" @@ -23570,12 +23311,6 @@ static njs_test_suite_t njs_suites[] = njs_nitems(njs_fs_module_test), njs_unit_test }, - { njs_str("crypto module"), - { .repeat = 1, .unsafe = 1 }, - njs_crypto_module_test, - njs_nitems(njs_crypto_module_test), - njs_unit_test }, - { njs_str("externals"), { .externals = 1, .repeat = 1, .unsafe = 1 }, njs_externals_test, diff --git a/test/crypto.t.mjs b/test/crypto.t.mjs new file mode 100644 index 00000000..ad237d88 --- /dev/null +++ b/test/crypto.t.mjs @@ -0,0 +1,447 @@ +/*--- +includes: [runTsuite.js] +flags: [async] +---*/ + +import cr from 'crypto'; + +let createHash_tsuite = { + name: "createHash tests", + skip: () => !cr.createHash, + T: async (params) => { + var h = params.hash_value ? params.hash_value(params.hash) + : cr.createHash(params.hash); + + if (typeof h !== 'object') { + throw Error(`unexpected result "${h}" is not object`); + } + + for (let i = 0; i < params.data.length; i++) { + let args = params.data[i]; + + if (Array.isArray(args)) { + h.update(args[0], args[1]); + + } else { + h.update(args); + } + } + + let r = h.digest(params.digest); + + if (!params.digest) { + if (!(r instanceof Buffer)) { + throw Error(`unexpected result "${r}" is not Buffer`); + } + + if (r.compare(params.expected) !== 0) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + + } else { + if (typeof r !== 'string') { + throw Error(`unexpected result "${r}" is not string`); + } + + if (r !== params.expected) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + } + + return 'SUCCESS'; + }, + + tests: [ + { hash: 'md5', data: [], digest: 'hex', + expected: "d41d8cd98f00b204e9800998ecf8427e" }, + { hash: 'md5', data: [''], digest: 'hex', + expected: "d41d8cd98f00b204e9800998ecf8427e" }, + { hash: 'md5', data: [''], + expected: Buffer.from("d41d8cd98f00b204e9800998ecf8427e", "hex") }, + + { hash: 'md5', data: ['AB'], digest: 'hex', + expected: "b86fc6b051f63d73de262d4c34e3a0a9" }, + { hash: 'md5', data: ['A', 'B'], digest: 'hex', + expected: "b86fc6b051f63d73de262d4c34e3a0a9" }, + { hash: 'md5', data: [Buffer.from('XABX').subarray(1,3)], digest: 'hex', + expected: "b86fc6b051f63d73de262d4c34e3a0a9" }, + + { hash: 'md5', data: [['4142', 'hex']], digest: 'hex', + expected: "b86fc6b051f63d73de262d4c34e3a0a9" }, + { hash: 'md5', data: [['QUI=', 'base64']], digest: 'hex', + expected: "b86fc6b051f63d73de262d4c34e3a0a9" }, + { hash: 'md5', data: [['QUI', 'base64url']], digest: 'hex', + expected: "b86fc6b051f63d73de262d4c34e3a0a9" }, + + { hash: 'md5', data: ['abc'.repeat(100)], digest: 'hex', + expected: "f571117acbd8153c8dc3c81b8817773a" }, + + { hash: 'md5', data: ['AB'], digest: 'base64', + expected: "uG/GsFH2PXPeJi1MNOOgqQ==" }, + { hash: 'md5', data: ['A', 'B'], digest: 'base64', + expected: "uG/GsFH2PXPeJi1MNOOgqQ==" }, + { hash: 'md5', data: [Buffer.from('XABX').subarray(1,3)], digest: 'base64', + expected: "uG/GsFH2PXPeJi1MNOOgqQ==" }, + + { hash: 'md5', data: ['AB'], digest: 'base64url', + expected: "uG_GsFH2PXPeJi1MNOOgqQ" }, + { hash: 'md5', data: ['A', 'B'], digest: 'base64url', + expected: "uG_GsFH2PXPeJi1MNOOgqQ" }, + { hash: 'md5', data: [Buffer.from('XABX').subarray(1,3)], digest: 'base64url', + expected: "uG_GsFH2PXPeJi1MNOOgqQ" }, + + { hash: 'sha1', data: [], digest: 'hex', + expected: "da39a3ee5e6b4b0d3255bfef95601890afd80709" }, + { hash: 'sha1', data: [''], digest: 'hex', + expected: "da39a3ee5e6b4b0d3255bfef95601890afd80709" }, + { hash: 'sha1', data: [''], + expected: Buffer.from("da39a3ee5e6b4b0d3255bfef95601890afd80709", "hex") }, + + { hash: 'sha1', data: ['AB'], digest: 'hex', + expected: "06d945942aa26a61be18c3e22bf19bbca8dd2b5d" }, + { hash: 'sha1', data: ['A', 'B'], digest: 'hex', + expected: "06d945942aa26a61be18c3e22bf19bbca8dd2b5d" }, + { hash: 'sha1', data: [Buffer.from('XABX').subarray(1,3)], digest: 'hex', + expected: "06d945942aa26a61be18c3e22bf19bbca8dd2b5d" }, + + { hash: 'sha1', data: [['4142', 'hex']], digest: 'hex', + expected: "06d945942aa26a61be18c3e22bf19bbca8dd2b5d" }, + { hash: 'sha1', data: [['QUI=', 'base64']], digest: 'hex', + expected: "06d945942aa26a61be18c3e22bf19bbca8dd2b5d" }, + { hash: 'sha1', data: [['QUI', 'base64url']], digest: 'hex', + expected: "06d945942aa26a61be18c3e22bf19bbca8dd2b5d" }, + + { hash: 'sha1', data: ['abc'.repeat(100)], digest: 'hex', + expected: "c95466320eaae6d19ee314ae4f135b12d45ced9a" }, + + { hash: 'sha1', data: ['AB'], digest: 'base64', + expected: "BtlFlCqiamG+GMPiK/GbvKjdK10=" }, + { hash: 'sha1', data: ['A', 'B'], digest: 'base64', + expected: "BtlFlCqiamG+GMPiK/GbvKjdK10=" }, + { hash: 'sha1', data: [Buffer.from('XABX').subarray(1,3)], digest: 'base64', + expected: "BtlFlCqiamG+GMPiK/GbvKjdK10=" }, + + { hash: 'sha1', data: ['AB'], digest: 'base64url', + expected: "BtlFlCqiamG-GMPiK_GbvKjdK10" }, + { hash: 'sha1', data: ['A', 'B'], digest: 'base64url', + expected: "BtlFlCqiamG-GMPiK_GbvKjdK10" }, + { hash: 'sha1', data: [Buffer.from('XABX').subarray(1,3)], digest: 'base64url', + expected: "BtlFlCqiamG-GMPiK_GbvKjdK10" }, + + { hash: 'sha256', data: [], digest: 'hex', + expected: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" }, + { hash: 'sha256', data: [''], digest: 'hex', + expected: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" }, + { hash: 'sha256', data: [''], + expected: Buffer.from("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "hex") }, + + { hash: 'sha256', data: ['AB'], digest: 'hex', + expected: "38164fbd17603d73f696b8b4d72664d735bb6a7c88577687fd2ae33fd6964153" }, + { hash: 'sha256', data: ['A', 'B'], digest: 'hex', + expected: "38164fbd17603d73f696b8b4d72664d735bb6a7c88577687fd2ae33fd6964153" }, + { hash: 'sha256', data: [Buffer.from('XABX').subarray(1,3)], digest: 'hex', + expected: "38164fbd17603d73f696b8b4d72664d735bb6a7c88577687fd2ae33fd6964153" }, + + { hash: 'sha256', data: [['4142', 'hex']], digest: 'hex', + expected: "38164fbd17603d73f696b8b4d72664d735bb6a7c88577687fd2ae33fd6964153" }, + { hash: 'sha256', data: [['QUI=', 'base64']], digest: 'hex', + expected: "38164fbd17603d73f696b8b4d72664d735bb6a7c88577687fd2ae33fd6964153" }, + { hash: 'sha256', data: [['QUI', 'base64url']], digest: 'hex', + expected: "38164fbd17603d73f696b8b4d72664d735bb6a7c88577687fd2ae33fd6964153" }, + + { hash: 'sha256', data: ['abc'.repeat(100)], digest: 'hex', + expected: "d9f5aeb06abebb3be3f38adec9a2e3b94228d52193be923eb4e24c9b56ee0930" }, + + { hash: 'sha256', data: ['AB'], digest: 'base64', + expected: "OBZPvRdgPXP2lri01yZk1zW7anyIV3aH/SrjP9aWQVM=" }, + { hash: 'sha256', data: ['A', 'B'], digest: 'base64', + expected: "OBZPvRdgPXP2lri01yZk1zW7anyIV3aH/SrjP9aWQVM=" }, + { hash: 'sha256', data: [Buffer.from('XABX').subarray(1,3)], digest: 'base64', + expected: "OBZPvRdgPXP2lri01yZk1zW7anyIV3aH/SrjP9aWQVM=" }, + + { hash: 'sha256', data: ['AB'], digest: 'base64url', + expected: "OBZPvRdgPXP2lri01yZk1zW7anyIV3aH_SrjP9aWQVM" }, + { hash: 'sha256', data: ['A', 'B'], digest: 'base64url', + expected: "OBZPvRdgPXP2lri01yZk1zW7anyIV3aH_SrjP9aWQVM" }, + { hash: 'sha256', data: [Buffer.from('XABX').subarray(1,3)], digest: 'base64url', + expected: "OBZPvRdgPXP2lri01yZk1zW7anyIV3aH_SrjP9aWQVM" }, + + { hash: 'sha1', + hash_value(hash) { + var Hash = cr.createHash(hash).constructor; + return Hash(hash); + }, + data: [], digest: 'hex', + expected: "da39a3ee5e6b4b0d3255bfef95601890afd80709" }, + + { hash: 'sha1', + hash_value(hash) { + var h = cr.createHash(hash); + h.copy().digest('hex'); + return h; + }, + data: [], digest: 'hex', + expected: "da39a3ee5e6b4b0d3255bfef95601890afd80709" }, + + { hash: 'sha1', + hash_value(hash) { + var h = cr.createHash(hash); + h.digest('hex'); + return h; + }, + data: [], digest: 'hex', + exception: "TypeError: Digest already called" }, + + { hash: 'sha1', + hash_value(hash) { + var h = cr.createHash(hash); + h.digest('hex'); + h.copy(); + return h; + }, + data: [], digest: 'hex', + exception: "TypeError: Digest already called" }, + + { hash: 'sha1', + hash_value(hash) { + var h = cr.createHash(hash); + h.update('AB'); + return h; + }, + data: [], digest: 'hex', + exception: "TypeError: Digest already called" }, + + { hash: 'sha1', data: [undefined], digest: 'hex', + exception: "TypeError: data is not a string or Buffer-like object" }, + { hash: 'sha1', data: [{}], digest: 'hex', + exception: "TypeError: data is not a string or Buffer-like object" }, + + { hash: 'unknown', data: [], digest: 'hex', + exception: 'TypeError: not supported algorithm: "unknown"' }, + { hash: 'sha1', data: [], digest: 'unknown', + exception: 'TypeError: unknown digest type: "unknown"' }, +]}; + + +let createHmac_tsuite = { + name: "createHmac tests", + skip: () => !cr.createHmac, + T: async (params) => { + var h = params.hmac_value ? params.hmac_value(params.hash, params.key) + : cr.createHmac(params.hash, params.key || ''); + + if (typeof h !== 'object') { + throw Error(`unexpected result "${h}" is not object`); + } + + for (let i = 0; i < params.data.length; i++) { + let args = params.data[i]; + + if (Array.isArray(args)) { + h.update(args[0], args[1]); + + } else { + h.update(args); + } + } + + let r = h.digest(params.digest); + + if (!params.digest) { + if (!(r instanceof Buffer)) { + throw Error(`unexpected result "${r}" is not Buffer`); + } + + if (r.compare(params.expected) !== 0) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + + } else { + if (typeof r !== 'string') { + throw Error(`unexpected result "${r}" is not string`); + } + + if (r !== params.expected) { + throw Error(`unexpected output "${r}" != "${params.expected}"`); + } + } + + return 'SUCCESS'; + }, + + tests: [ + { hash: 'md5', key: '', data: [], digest: 'hex', + expected: "74e6f7298a9c2d168935f58c001bad88" }, + { hash: 'md5', key: '', data: [''], digest: 'hex', + expected: "74e6f7298a9c2d168935f58c001bad88" }, + { hash: 'md5', key: '', data: [''], + expected: Buffer.from("74e6f7298a9c2d168935f58c001bad88", "hex") }, + + { hash: 'md5', key: '', data: ['AB'], digest: 'hex', + expected: "9e0e9e545ef63d41dfb653daecf8ebc7" }, + { hash: 'md5', key: '', data: ['A', 'B'], digest: 'hex', + expected: "9e0e9e545ef63d41dfb653daecf8ebc7" }, + { hash: 'md5', key: '', data: [Buffer.from('XABX').subarray(1,3)], digest: 'hex', + expected: "9e0e9e545ef63d41dfb653daecf8ebc7" }, + + { hash: 'md5', key: '', data: [['4142', 'hex']], digest: 'hex', + expected: "9e0e9e545ef63d41dfb653daecf8ebc7" }, + { hash: 'md5', key: '', data: [['QUI=', 'base64']], digest: 'hex', + expected: "9e0e9e545ef63d41dfb653daecf8ebc7" }, + { hash: 'md5', key: '', data: [['QUI', 'base64url']], digest: 'hex', + expected: "9e0e9e545ef63d41dfb653daecf8ebc7" }, + + { hash: 'md5', key: Buffer.from('secret'), data: ['abc'.repeat(100)], digest: 'hex', + expected: "91eb74a225cdd3bbfccc34396c6e3ac5" }, + + { hash: 'md5', key: '', data: ['AB'], digest: 'base64', + expected: "ng6eVF72PUHftlPa7Pjrxw==" }, + { hash: 'md5', key: '', data: ['A', 'B'], digest: 'base64', + expected: "ng6eVF72PUHftlPa7Pjrxw==" }, + { hash: 'md5', key: '', data: [Buffer.from('XABX').subarray(1,3)], digest: 'base64', + expected: "ng6eVF72PUHftlPa7Pjrxw==" }, + + { hash: 'md5', key: '', data: ['AB'], digest: 'base64url', + expected: "ng6eVF72PUHftlPa7Pjrxw" }, + { hash: 'md5', key: '', data: ['A', 'B'], digest: 'base64url', + expected: "ng6eVF72PUHftlPa7Pjrxw" }, + { hash: 'md5', key: '', data: [Buffer.from('XABX').subarray(1,3)], digest: 'base64url', + expected: "ng6eVF72PUHftlPa7Pjrxw" }, + + { hash: 'sha1', key: '', data: [], digest: 'hex', + expected: "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d" }, + { hash: 'sha1', key: '', data: [''], digest: 'hex', + expected: "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d" }, + { hash: 'sha1', key: '', data: [''], + expected: Buffer.from("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d", "hex") }, + + { hash: 'sha1', key: '', data: ['AB'], digest: 'hex', + expected: "d32c0b6637cc2dfe4670f3fe48ef4434123c4810" }, + { hash: 'sha1', key: '', data: ['A', 'B'], digest: 'hex', + expected: "d32c0b6637cc2dfe4670f3fe48ef4434123c4810" }, + { hash: 'sha1', key: '', data: [Buffer.from('XABX').subarray(1,3)], digest: 'hex', + expected: "d32c0b6637cc2dfe4670f3fe48ef4434123c4810" }, + + { hash: 'sha1', key: '', data: [['4142', 'hex']], digest: 'hex', + expected: "d32c0b6637cc2dfe4670f3fe48ef4434123c4810" }, + { hash: 'sha1', key: '', data: [['QUI=', 'base64']], digest: 'hex', + expected: "d32c0b6637cc2dfe4670f3fe48ef4434123c4810" }, + { hash: 'sha1', key: '', data: [['QUI', 'base64url']], digest: 'hex', + expected: "d32c0b6637cc2dfe4670f3fe48ef4434123c4810" }, + + { hash: 'sha1', key: Buffer.from('secret'), data: ['abc'.repeat(100)], digest: 'hex', + expected: "0aac71e3a813a7acc4a809cfdedb2ecba04ffc5e" }, + + { hash: 'sha1', key: '', data: ['AB'], digest: 'base64', + expected: "0ywLZjfMLf5GcPP+SO9ENBI8SBA=" }, + { hash: 'sha1', key: '', data: ['A', 'B'], digest: 'base64', + expected: "0ywLZjfMLf5GcPP+SO9ENBI8SBA=" }, + { hash: 'sha1', key: '', data: [Buffer.from('XABX').subarray(1,3)], digest: 'base64', + expected: "0ywLZjfMLf5GcPP+SO9ENBI8SBA=" }, + + { hash: 'sha1', key: '', data: ['AB'], digest: 'base64url', + expected: "0ywLZjfMLf5GcPP-SO9ENBI8SBA" }, + { hash: 'sha1', key: '', data: ['A', 'B'], digest: 'base64url', + expected: "0ywLZjfMLf5GcPP-SO9ENBI8SBA" }, + { hash: 'sha1', key: '', data: [Buffer.from('XABX').subarray(1,3)], digest: 'base64url', + expected: "0ywLZjfMLf5GcPP-SO9ENBI8SBA" }, + + { hash: 'sha256', key: '', data: [], digest: 'hex', + expected: "b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad" }, + { hash: 'sha256', key: '', data: [''], digest: 'hex', + expected: "b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad" }, + { hash: 'sha256', key: '', data: [''], + expected: Buffer.from("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad", "hex") }, + + { hash: 'sha256', key: '', data: ['AB'], digest: 'hex', + expected: "d53400095496267cf02e5dbd4b0bf9fbfb5f36f311ea7d9809af5487421743e3" }, + { hash: 'sha256', key: '', data: ['A', 'B'], digest: 'hex', + expected: "d53400095496267cf02e5dbd4b0bf9fbfb5f36f311ea7d9809af5487421743e3" }, + { hash: 'sha256', key: '', data: [Buffer.from('XABX').subarray(1,3)], digest: 'hex', + expected: "d53400095496267cf02e5dbd4b0bf9fbfb5f36f311ea7d9809af5487421743e3" }, + + { hash: 'sha256', key: '', data: [['4142', 'hex']], digest: 'hex', + expected: "d53400095496267cf02e5dbd4b0bf9fbfb5f36f311ea7d9809af5487421743e3" }, + { hash: 'sha256', key: '', data: [['QUI=', 'base64']], digest: 'hex', + expected: "d53400095496267cf02e5dbd4b0bf9fbfb5f36f311ea7d9809af5487421743e3" }, + { hash: 'sha256', key: '', data: [['QUI', 'base64url']], digest: 'hex', + expected: "d53400095496267cf02e5dbd4b0bf9fbfb5f36f311ea7d9809af5487421743e3" }, + + { hash: 'sha256', key: Buffer.from('secret'), data: ['abc'.repeat(100)], digest: 'hex', + expected: "8660d2d51d6f20f61d5aadfb6c43df7fd05fc2fc4967d8aec1846f3d9ec03987" }, + + { hash: 'sha256', key: '', data: ['AB'], digest: 'base64', + expected: "1TQACVSWJnzwLl29Swv5+/tfNvMR6n2YCa9Uh0IXQ+M=" }, + { hash: 'sha256', key: '', data: ['A', 'B'], digest: 'base64', + expected: "1TQACVSWJnzwLl29Swv5+/tfNvMR6n2YCa9Uh0IXQ+M=" }, + { hash: 'sha256', key: '', data: [Buffer.from('XABX').subarray(1,3)], digest: 'base64', + expected: "1TQACVSWJnzwLl29Swv5+/tfNvMR6n2YCa9Uh0IXQ+M=" }, + + { hash: 'sha256', key: '', data: ['AB'], digest: 'base64url', + expected: "1TQACVSWJnzwLl29Swv5-_tfNvMR6n2YCa9Uh0IXQ-M" }, + { hash: 'sha256', key: '', data: ['A', 'B'], digest: 'base64url', + expected: "1TQACVSWJnzwLl29Swv5-_tfNvMR6n2YCa9Uh0IXQ-M" }, + { hash: 'sha256', key: '', data: [Buffer.from('XABX').subarray(1,3)], digest: 'base64url', + expected: "1TQACVSWJnzwLl29Swv5-_tfNvMR6n2YCa9Uh0IXQ-M" }, + + { hash: 'sha256', key: 'A'.repeat(64), data: ['AB'], digest: 'hex', + expected: "ee9dce43b12eb3e865614ad9c1a8d4fad4b6eac2b64647bd24cd192888d3f367" }, + + { hash: 'sha256', key: 'A'.repeat(100), data: ['AB'], digest: 'hex', + expected: "5647b6c429701ff512f0f18232b4507065d2376ca8899a816a0a6e721bf8ddcc" }, + + { hash: 'sha1', + hmac_value(hash, key) { + var Hmac = cr.createHmac(hash, key).constructor; + return Hmac(hash, key); + }, + key: '', data: [], digest: 'hex', + expected: "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d" }, + + { hash: 'sha1', + hmac_value(hash, key) { + var h = cr.createHmac(hash, key); + h.digest('hex'); + return h; + }, + key: '', data: [], digest: 'hex', + exception: "TypeError: Digest already called" }, + + { hash: 'sha1', + hmac_value(hash, key) { + var h = cr.createHmac(hash, key); + h.digest('hex'); + h.update('A'); + return h; + }, + key: '', data: [], digest: 'hex', + exception: "TypeError: Digest already called" }, + + { hash: 'sha1', key: '', data: [undefined], digest: 'hex', + exception: "TypeError: data is not a string or Buffer-like object" }, + { hash: 'sha1', key: '', data: [{}], digest: 'hex', + exception: "TypeError: data is not a string or Buffer-like object" }, + + { hash: 'unknown', key: '', data: [], digest: 'hex', + exception: 'TypeError: not supported algorithm: "unknown"' }, + { hash: 'sha1', key: '', data: [], digest: 'unknown', + exception: 'TypeError: unknown digest type: "unknown"' }, + + { hash: 'sha1', key: [], data: [], digest: 'hex', + exception: 'TypeError: key is not a string or Buffer-like object' }, + + { hash: 'sha1', + hmac_value(hash, key) { + var h = cr.createHash('sha1'); + h.update.call(cr.createHmac(hash, key), ''); + }, + key: '', data: [], digest: 'hex', + exception: 'TypeError: "this" is not a hash object' }, +]}; + + +run([ + createHash_tsuite, + createHmac_tsuite +]) +.then($DONE, $DONE); From noreply at nginx.com Wed Mar 19 03:17:02 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 19 Mar 2025 03:17:02 +0000 (UTC) Subject: [njs] QuickJS: added error checks in modules initialization. Message-ID: <20250319031702.313C748379@pubserv1.nginx> details: https://github.com/nginx/njs/commit/6736121eba717407356c0a7e8ac7046c7744b7e6 branches: master commit: 6736121eba717407356c0a7e8ac7046c7744b7e6 user: Vadim Zhestikov date: Tue, 18 Mar 2025 10:40:24 -0700 description: QuickJS: added error checks in modules initialization. --- external/qjs_fs_module.c | 9 ++++++++- external/qjs_query_string_module.c | 5 ++++- external/qjs_webcrypto_module.c | 9 ++++++++- external/qjs_zlib_module.c | 9 ++++++++- src/qjs_buffer.c | 9 ++++++++- 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/external/qjs_fs_module.c b/external/qjs_fs_module.c index 9d1f7687..b09d1dcd 100644 --- a/external/qjs_fs_module.c +++ b/external/qjs_fs_module.c @@ -2960,6 +2960,10 @@ qjs_fs_module_init(JSContext *cx, JSModuleDef *m) JSValue proto; proto = JS_NewObject(cx); + if (JS_IsException(proto)) { + return -1; + } + JS_SetPropertyFunctionList(cx, proto, qjs_fs_export, njs_nitems(qjs_fs_export)); @@ -3031,7 +3035,10 @@ qjs_fs_init(JSContext *cx, const char *name) return NULL; } - JS_AddModuleExport(cx, m, "default"); + if (JS_AddModuleExport(cx, m, "default") < 0) { + return NULL; + } + rc = JS_AddModuleExportList(cx, m, qjs_fs_export, njs_nitems(qjs_fs_export)); if (rc != 0) { diff --git a/external/qjs_query_string_module.c b/external/qjs_query_string_module.c index 85d2fcb3..3059ee1b 100644 --- a/external/qjs_query_string_module.c +++ b/external/qjs_query_string_module.c @@ -998,7 +998,10 @@ qjs_querystring_init(JSContext *ctx, const char *name) return NULL; } - JS_AddModuleExport(ctx, m, "default"); + if (JS_AddModuleExport(ctx, m, "default") < 0) { + return NULL; + } + rc = JS_AddModuleExportList(ctx, m, qjs_querystring_export, njs_nitems(qjs_querystring_export)); if (rc != 0) { diff --git a/external/qjs_webcrypto_module.c b/external/qjs_webcrypto_module.c index a1983651..9552ca12 100644 --- a/external/qjs_webcrypto_module.c +++ b/external/qjs_webcrypto_module.c @@ -4775,6 +4775,10 @@ qjs_webcrypto_module_init(JSContext *cx, JSModuleDef *m) JSValue proto; proto = JS_NewObject(cx); + if (JS_IsException(proto)) { + return -1; + } + JS_SetPropertyFunctionList(cx, proto, qjs_webcrypto_export, njs_nitems(qjs_webcrypto_export)); @@ -4833,7 +4837,10 @@ qjs_webcrypto_init(JSContext *cx, const char *name) return NULL; } - JS_AddModuleExport(cx, m, "default"); + if (JS_AddModuleExport(cx, m, "default") < 0) { + return NULL; + } + rc = JS_AddModuleExportList(cx, m, qjs_webcrypto_export, njs_nitems(qjs_webcrypto_export)); if (rc != 0) { diff --git a/external/qjs_zlib_module.c b/external/qjs_zlib_module.c index 2d3b2d92..5abf108a 100644 --- a/external/qjs_zlib_module.c +++ b/external/qjs_zlib_module.c @@ -463,6 +463,10 @@ qjs_zlib_module_init(JSContext *ctx, JSModuleDef *m) JSValue proto; proto = JS_NewObject(ctx); + if (JS_IsException(proto)) { + return -1; + } + JS_SetPropertyFunctionList(ctx, proto, qjs_zlib_export, njs_nitems(qjs_zlib_export)); @@ -487,7 +491,10 @@ qjs_zlib_init(JSContext *ctx, const char *name) return NULL; } - JS_AddModuleExport(ctx, m, "default"); + if (JS_AddModuleExport(ctx, m, "default") < 0) { + return NULL; + } + rc = JS_AddModuleExportList(ctx, m, qjs_zlib_export, njs_nitems(qjs_zlib_export)); if (rc != 0) { diff --git a/src/qjs_buffer.c b/src/qjs_buffer.c index 638d273c..dfe7b04d 100644 --- a/src/qjs_buffer.c +++ b/src/qjs_buffer.c @@ -2550,6 +2550,10 @@ qjs_buffer_module_init(JSContext *ctx, JSModuleDef *m) JSValue proto; proto = JS_NewObject(ctx); + if (JS_IsException(proto)) { + return -1; + } + JS_SetPropertyFunctionList(ctx, proto, qjs_buffer_export, njs_nitems(qjs_buffer_export)); @@ -2576,7 +2580,10 @@ qjs_buffer_init(JSContext *ctx, const char *name) return NULL; } - JS_AddModuleExport(ctx, m, "default"); + if (JS_AddModuleExport(ctx, m, "default") < 0) { + return NULL; + } + rc = JS_AddModuleExportList(ctx, m, qjs_buffer_export, njs_nitems(qjs_buffer_export)); if (rc != 0) { From mike.maccana at gmail.com Wed Mar 19 03:43:14 2025 From: mike.maccana at gmail.com (Mike MacCana) Date: Tue, 18 Mar 2025 23:43:14 -0400 Subject: [njs] Test262: allowing to omit empty default option argument. In-Reply-To: <20250318214602.C9EEC4837A@pubserv1.nginx> References: <20250318214602.C9EEC4837A@pubserv1.nginx> Message-ID: Unsubscribe On Tue, Mar 18, 2025 at 17:46 wrote: > details: > https://github.com/nginx/njs/commit/3ad475cb9a0b24fc7bc49460ede80f4ec104c3fd > branches: master > commit: 3ad475cb9a0b24fc7bc49460ede80f4ec104c3fd > user: Dmitry Volyntsev > date: Tue, 25 Feb 2025 22:44:39 -0800 > description: > Test262: allowing to omit empty default option argument. > > > --- > test/buffer.t.js | 37 > ------------------------------------- > test/fs/methods.t.mjs | 4 ---- > test/harness/runTsuite.js | 2 +- > test/querystring.t.mjs | 8 -------- > test/text_decoder.t.js | 6 ------ > test/text_encoder.t.js | 4 ---- > test/webcrypto/digest.t.mjs | 1 - > test/webcrypto/import.t.mjs | 4 ---- > test/webcrypto/rsa_decoding.t.mjs | 1 - > 9 files changed, 1 insertion(+), 66 deletions(-) > > diff --git a/test/buffer.t.js b/test/buffer.t.js > index 01e25bed..37821104 100644 > --- a/test/buffer.t.js > +++ b/test/buffer.t.js > @@ -86,7 +86,6 @@ let concat_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > tests: [ > { buffers: [ Buffer.from('abc'), > Buffer.from(new Uint8Array([0x64, 0x65, > 0x66]).buffer, 1) ], > @@ -112,8 +111,6 @@ let compare_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { buf1: Buffer.from('abc'), buf2: Buffer.from('abc'), expected: 0 > }, > { buf1: Buffer.from('abc'), > @@ -150,8 +147,6 @@ let comparePrototype_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { buf: Buffer.from('abc'), target: Buffer.from('abc'), expected: > 0 }, > { buf: Buffer.from('abc'), > @@ -200,8 +195,6 @@ let copy_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { buf: Buffer.from('abcdef'), target: Buffer.from('123456'), > expected: 6, expected_buf: 'abcdef' }, > @@ -236,7 +229,6 @@ let equals_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > tests: [ > > { buf1: Buffer.from('abc'), buf2: Buffer.from('abc'), expected: > true }, > @@ -271,7 +263,6 @@ let fill_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > tests: [ > { buf: Buffer.from('abc'), value: 0x61, expected: 'aaa' }, > { buf: Buffer.from('abc'), value: 0x61, expected: 'aaa', offset: > 0, end: 3 }, > @@ -430,8 +421,6 @@ let includes_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { buf: Buffer.from('abcdef'), value: 'abc', expected: true }, > { buf: Buffer.from('abcdef'), value: 'def', expected: true }, > @@ -455,8 +444,6 @@ let indexOf_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { buf: Buffer.from('abcdef'), value: 'abc', expected: 0 }, > { buf: Buffer.from('abcdef'), value: 'def', expected: 3 }, > @@ -503,8 +490,6 @@ let isBuffer_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { value: Buffer.from('α'), expected: true }, > { value: new Uint8Array(10), expected: false }, > @@ -526,8 +511,6 @@ let isEncoding_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { value: 'utf-8', expected: true }, > { value: 'utf8', expected: true }, > @@ -576,8 +559,6 @@ let lastIndexOf_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { buf: Buffer.from('abcdef'), value: 'abc', expected: 0 }, > { buf: Buffer.from('abcabc'), value: 'abc', expected: 3 }, > @@ -640,8 +621,6 @@ let readXIntXX_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: > 0, > expected: [ > -86,170,-17494,-21829,48042,43707,-573785174,-1430532899,3721182122,2864434397 > ] }, > @@ -699,8 +678,6 @@ let readFloat_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > {} > ], > @@ -727,8 +704,6 @@ let readGeneric_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: > 0, length: 1, > expected: [ 170, 170, -86, -86 ] }, > @@ -767,8 +742,6 @@ let slice_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { buf: Buffer.from('abcdef'), start: 1, expected: 'bcdef' }, > { buf: Buffer.from('abcdef'), start: 1, end: 3, expected: 'bc' }, > @@ -795,8 +768,6 @@ let subarray_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { buf: Buffer.from('abcdef'), start: 0, end: 3, expected: 'Zbc' }, > { buf: Buffer.from('abcdef'), start: 1, expected: 'bcdef' }, > @@ -842,8 +813,6 @@ let toJSON_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { value: '', expected: { type: 'Buffer', data: [] } }, > { value: 'αβγ', expected: { type: 'Buffer', data: [0xCE, 0xB1, > 0xCE, 0xB2, 0xCE, 0xB3] } }, > @@ -912,8 +881,6 @@ let write_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { value: 'abc', expected: 3, expected_buf: 'abcZZZZZZZ' }, > { value: 'abc', offset: 1, expected: 3, expected_buf: > 'ZabcZZZZZZ' }, > @@ -955,8 +922,6 @@ let writeXIntXX_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { write: 'writeInt8', value: 0xaa, exception: 'RangeError: Index > out of range' }, > { write: 'writeInt8', value: 0x00, offset: 3, expected: 4, > expected_buf: '5a5a5a005a5a5a5a5a5a' }, > @@ -995,8 +960,6 @@ let writeGeneric_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { write: 'writeUIntLE', value: 0xaa, length: 1, > exception: 'RangeError: Index out of range' }, > diff --git a/test/fs/methods.t.mjs b/test/fs/methods.t.mjs > index 928d1682..705c1b5a 100644 > --- a/test/fs/methods.t.mjs > +++ b/test/fs/methods.t.mjs > @@ -1008,7 +1008,6 @@ let readSync_tsuite = { > skip: () => (!has_buffer()), > T: read_test, > prepare_args: p, > - opts: {}, > get tests() { return read_tests() }, > }; > > @@ -1017,7 +1016,6 @@ let readFh_tsuite = { > skip: () => (!has_buffer()), > T: readFh_test, > prepare_args: p, > - opts: {}, > get tests() { return read_tests() }, > }; > > @@ -1213,7 +1211,6 @@ let writeSync_tsuite = { > skip: () => (!has_buffer()), > T: write_test, > prepare_args: p, > - opts: {}, > get tests() { return write_tests() }, > }; > > @@ -1222,7 +1219,6 @@ let writeFh_tsuite = { > skip: () => (!has_buffer()), > T: writeFh_test, > prepare_args: p, > - opts: {}, > get tests() { return write_tests() }, > }; > > diff --git a/test/harness/runTsuite.js b/test/harness/runTsuite.js > index 068a19e1..2a324fbe 100644 > --- a/test/harness/runTsuite.js > +++ b/test/harness/runTsuite.js > @@ -29,7 +29,7 @@ async function run(tlist) { > let prepare_args = ts.prepare_args ? ts.prepare_args > : default_prepare_args; > > - return ts.T(prepare_args(t, ts.opts)); > + return ts.T(prepare_args(t, ts.opts ? ts.opts : {})); > > } catch (e) { > return Promise.reject(e); > diff --git a/test/querystring.t.mjs b/test/querystring.t.mjs > index cb42305e..8ea14050 100644 > --- a/test/querystring.t.mjs > +++ b/test/querystring.t.mjs > @@ -17,8 +17,6 @@ let escape_tsuite = { > return 'SUCCESS'; > }, > > - opts: { }, > - > tests: [ > { value: '', expected: '' }, > { value: 'baz=fuz', expected: 'baz%3Dfuz' }, > @@ -62,8 +60,6 @@ let parse_tsuite = { > return 'SUCCESS'; > }, > > - opts: { }, > - > tests: [ > { value: '', expected: {} }, > { value: 'baz=fuz', expected: { baz:'fuz' } }, > @@ -165,8 +161,6 @@ let stringify_tsuite = { > return 'SUCCESS'; > }, > > - opts: { }, > - > tests: [ > { obj: {}, expected: '' }, > { obj: { baz:'fuz', muz:'tax' }, expected: 'baz=fuz&muz=tax' }, > @@ -216,8 +210,6 @@ let unescape_tsuite = { > return 'SUCCESS'; > }, > > - opts: { }, > - > tests: [ > { value: '', expected: '' }, > { value: 'baz%3Dfuz', expected: 'baz=fuz' }, > diff --git a/test/text_decoder.t.js b/test/text_decoder.t.js > index a6fced2b..afc91fc4 100644 > --- a/test/text_decoder.t.js > +++ b/test/text_decoder.t.js > @@ -40,8 +40,6 @@ let stream_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { chunks: [new Uint8Array([0xF0, 0x9F, 0x8C, 0x9F])], > expected: ['🌟'] }, > @@ -95,8 +93,6 @@ let fatal_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { chunks: [new Uint8Array([0xF0, 0xA0, 0xAE, 0xB7])], > expected: ['𠮷'] }, > @@ -124,8 +120,6 @@ let ignoreBOM_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { value: new Uint8Array([239, 187, 191, 50]), > opts: {ignoreBOM: true}, > diff --git a/test/text_encoder.t.js b/test/text_encoder.t.js > index 10324bca..25173cd1 100644 > --- a/test/text_encoder.t.js > +++ b/test/text_encoder.t.js > @@ -26,8 +26,6 @@ let encode_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { value: "", expected: [] }, > { value: "abc", expected: [97, 98, 99] }, > @@ -58,8 +56,6 @@ let encodeinto_tsuite = { > return 'SUCCESS'; > }, > > - opts: {}, > - > tests: [ > { value: "", dest: new Uint8Array(4), expected: [], read: 0 }, > { value: "aα", dest: new Uint8Array(3), expected: [97, 206, 177], > read: 2 }, > diff --git a/test/webcrypto/digest.t.mjs b/test/webcrypto/digest.t.mjs > index 4318fe16..f07f0346 100644 > --- a/test/webcrypto/digest.t.mjs > +++ b/test/webcrypto/digest.t.mjs > @@ -25,7 +25,6 @@ let digest_tsuite = { > skip: () => (!has_buffer() || !has_webcrypto()), > T: test, > prepare_args: p, > - opts: { }, > > tests: [ > { name: "XXX", data: "", > diff --git a/test/webcrypto/import.t.mjs b/test/webcrypto/import.t.mjs > index 1b804006..9ec0fb60 100644 > --- a/test/webcrypto/import.t.mjs > +++ b/test/webcrypto/import.t.mjs > @@ -85,7 +85,6 @@ let aes_tsuite = { > skip: () => (!has_webcrypto()), > T: test, > prepare_args: p, > - opts: {}, > > tests: [ > { key: { fmt: "jwk", > @@ -186,7 +185,6 @@ let ec_tsuite = { > skip: () => (!has_webcrypto()), > T: test, > prepare_args: p, > - opts: {}, > > tests: [ > { key: { fmt: "jwk", > @@ -350,7 +348,6 @@ let hmac_tsuite = { > skip: () => (!has_webcrypto()), > T: test, > prepare_args: p, > - opts: {}, > > tests: [ > { key: { fmt: "raw", > @@ -465,7 +462,6 @@ let rsa_tsuite = { > skip: () => (!has_webcrypto()), > T: test, > prepare_args: p, > - opts: {}, > > tests: [ > { key: { fmt: "jwk", > diff --git a/test/webcrypto/rsa_decoding.t.mjs > b/test/webcrypto/rsa_decoding.t.mjs > index 08874491..179072ff 100644 > --- a/test/webcrypto/rsa_decoding.t.mjs > +++ b/test/webcrypto/rsa_decoding.t.mjs > @@ -29,7 +29,6 @@ let rsa_tsuite = { > name: "RSA-OAEP decoding", > T: test, > prepare_args: (v) => v, > - opts: { }, > > tests: [ > { pem: "rsa.pkcs8", src: "text.base64.rsa-oaep.enc", expected: > "WAKAWAKA" }, > _______________________________________________ > nginx-devel mailing list > nginx-devel at nginx.org > https://mailman.nginx.org/mailman/listinfo/nginx-devel > -------------- next part -------------- An HTML attachment was scrubbed... URL: From noreply at nginx.com Wed Mar 19 05:28:02 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 19 Mar 2025 05:28:02 +0000 (UTC) Subject: [njs] QuickJS: fixed njs_qjs_object_completions(). Message-ID: <20250319052802.699394837B@pubserv1.nginx> details: https://github.com/nginx/njs/commit/f6a2a795e9df4911dde8d0c3f02dd5a83c610ca5 branches: master commit: f6a2a795e9df4911dde8d0c3f02dd5a83c610ca5 user: hongzhidao date: Wed, 19 Mar 2025 11:18:41 +0800 description: QuickJS: fixed njs_qjs_object_completions(). This commit also exposed qjs_free_prop_enum() as public. --- external/njs_shell.c | 5 ++--- external/qjs_query_string_module.c | 13 ------------- src/qjs.c | 13 +++++++++++++ src/qjs.h | 2 ++ 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/external/njs_shell.c b/external/njs_shell.c index 1228b374..aeab226c 100644 --- a/external/njs_shell.c +++ b/external/njs_shell.c @@ -2950,7 +2950,6 @@ njs_qjs_object_completions(njs_engine_t *engine, JSContext *ctx, for (n = 0; n < length; n++) { key.start = (u_char *) JS_AtomToCString(ctx, ptab[n].atom); - JS_FreeAtom(ctx, ptab[n].atom); if (njs_slow_path(key.start == NULL)) { goto fail; } @@ -2993,7 +2992,7 @@ next: JS_FreeCString(ctx, (const char *) key.start); } - js_free_rt(JS_GetRuntime(ctx), ptab); + qjs_free_prop_enum(ctx, ptab, length); prototype = JS_GetPrototype(ctx, object); if (JS_IsException(prototype)) { @@ -3017,7 +3016,7 @@ fail: } if (ptab != NULL) { - js_free_rt(JS_GetRuntime(ctx), ptab); + qjs_free_prop_enum(ctx, ptab, length); } JS_FreeValue(ctx, object); diff --git a/external/qjs_query_string_module.c b/external/qjs_query_string_module.c index 3059ee1b..42322234 100644 --- a/external/qjs_query_string_module.c +++ b/external/qjs_query_string_module.c @@ -727,19 +727,6 @@ qjs_query_string_push_array(JSContext *cx, njs_chb_t *chain, JSValue key, } -static void -qjs_free_prop_enum(JSContext *cx, JSPropertyEnum *tab, uint32_t len) -{ - uint32_t i; - - for (i = 0; i < len; i++) { - JS_FreeAtom(cx, tab[i].atom); - } - - js_free(cx, tab); -} - - static JSValue qjs_query_string_stringify_internal(JSContext *cx, JSValue obj, njs_str_t *sep, njs_str_t *eq, JSValue encoder) diff --git a/src/qjs.c b/src/qjs.c index 15a575a2..52401983 100644 --- a/src/qjs.c +++ b/src/qjs.c @@ -1027,6 +1027,19 @@ qjs_string_create_chb(JSContext *cx, njs_chb_t *chain) } +void +qjs_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len) +{ + uint32_t i; + + for(i = 0; i < len; i++) { + JS_FreeAtom(ctx, tab[i].atom); + } + + js_free(ctx, tab); +} + + JSValue qjs_string_hex(JSContext *cx, const njs_str_t *src) { diff --git a/src/qjs.h b/src/qjs.h index 54f96dfe..7c13f039 100644 --- a/src/qjs.h +++ b/src/qjs.h @@ -127,6 +127,8 @@ JSValue qjs_typed_array_data(JSContext *ctx, JSValueConst value, JS_NewStringLen(ctx, (const char *) (data), len) JSValue qjs_string_create_chb(JSContext *cx, njs_chb_t *chain); +void qjs_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len); + JSValue qjs_string_hex(JSContext *cx, const njs_str_t *src); JSValue qjs_string_base64(JSContext *cx, const njs_str_t *src); JSValue qjs_string_base64url(JSContext *cx, const njs_str_t *src); From noreply at nginx.com Wed Mar 19 07:08:02 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 19 Mar 2025 07:08:02 +0000 (UTC) Subject: [njs] QuickJS: fixed ngx_qjs_string() to handle strings containing "\0". Message-ID: <20250319070802.5C8424837C@pubserv1.nginx> details: https://github.com/nginx/njs/commit/1b7adef4d532df55d81e22ec8fe998b76de34935 branches: master commit: 1b7adef4d532df55d81e22ec8fe998b76de34935 user: hongzhidao date: Wed, 19 Mar 2025 13:47:07 +0800 description: QuickJS: fixed ngx_qjs_string() to handle strings containing "\0". --- nginx/ngx_js.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c index 84cdcc2e..c91a5530 100644 --- a/nginx/ngx_js.c +++ b/nginx/ngx_js.c @@ -1482,13 +1482,11 @@ ngx_qjs_string(JSContext *cx, JSValueConst val, ngx_str_t *dst) string: - str = JS_ToCString(cx, val); + str = JS_ToCStringLen(cx, &len, val); if (str == NULL) { return NGX_ERROR; } - len = strlen(str); - start = njs_mp_alloc(e->pool, len); if (start == NULL) { JS_FreeCString(cx, str); From noreply at nginx.com Wed Mar 19 07:47:02 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 19 Mar 2025 07:47:02 +0000 (UTC) Subject: [njs] QuickJS: calling njs_chb_destroy() in qjs_string_create_chb() internally. Message-ID: <20250319074702.9DE054837D@pubserv1.nginx> details: https://github.com/nginx/njs/commit/1d36e231ed6a317cc9d01f8223091619e89e7a65 branches: master commit: 1d36e231ed6a317cc9d01f8223091619e89e7a65 user: hongzhidao date: Tue, 18 Mar 2025 21:34:59 +0800 description: QuickJS: calling njs_chb_destroy() in qjs_string_create_chb() internally. No functional changes. --- external/qjs_query_string_module.c | 17 +++-------------- nginx/ngx_http_js_module.c | 30 ++++++++++++------------------ src/qjs.c | 2 ++ 3 files changed, 17 insertions(+), 32 deletions(-) diff --git a/external/qjs_query_string_module.c b/external/qjs_query_string_module.c index 42322234..63553a53 100644 --- a/external/qjs_query_string_module.c +++ b/external/qjs_query_string_module.c @@ -171,7 +171,6 @@ static JSValue qjs_query_string_decode(JSContext *cx, const u_char *start, size_t size) { u_char *dst; - JSValue ret; uint32_t cp; njs_chb_t chain; const u_char *p, *end; @@ -250,11 +249,7 @@ qjs_query_string_decode(JSContext *cx, const u_char *start, size_t size) } - ret = qjs_string_create_chb(cx, &chain); - - njs_chb_destroy(&chain); - - return ret; + return qjs_string_create_chb(cx, &chain); } @@ -281,8 +276,6 @@ qjs_query_string_escape(JSContext *cx, JSValueConst this_val, int argc, ret = qjs_string_create_chb(cx, &chain); - njs_chb_destroy(&chain); - JS_FreeCString(cx, (char *) str.start); return ret; @@ -733,7 +726,7 @@ qjs_query_string_stringify_internal(JSContext *cx, JSValue obj, njs_str_t *sep, { int rc; uint32_t n, length; - JSValue key, val, ret; + JSValue key, val; njs_str_t sep_val, eq_val; njs_chb_t chain; JSPropertyEnum *ptab; @@ -809,11 +802,7 @@ qjs_query_string_stringify_internal(JSContext *cx, JSValue obj, njs_str_t *sep, qjs_free_prop_enum(cx, ptab, length); } - ret = qjs_string_create_chb(cx, &chain); - - njs_chb_destroy(&chain); - - return ret; + return qjs_string_create_chb(cx, &chain); fail: diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index ae970eb1..369ae50b 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -6400,10 +6400,9 @@ ngx_http_qjs_header_generic(JSContext *cx, ngx_http_request_t *r, ngx_list_t *headers, ngx_table_elt_t **ph, ngx_str_t *name, JSPropertyDescriptor *pdesc, unsigned flags) { - int ret; u_char sep; - njs_chb_t chain; JSValue val; + njs_chb_t chain; ngx_uint_t i; ngx_list_part_t *part; ngx_table_elt_t *header, *h; @@ -6493,6 +6492,10 @@ ngx_http_qjs_header_generic(JSContext *cx, ngx_http_request_t *r, return 1; } + if (pdesc == NULL) { + return 1; + } + NJS_CHB_CTX_INIT(&chain, cx); sep = flags & NJS_HEADER_SEMICOLON ? ';' : ','; @@ -6503,24 +6506,15 @@ ngx_http_qjs_header_generic(JSContext *cx, ngx_http_request_t *r, njs_chb_append_literal(&chain, " "); } - ret = 1; - - if (pdesc != NULL) { - pdesc->flags = JS_PROP_ENUMERABLE; - pdesc->getter = JS_UNDEFINED; - pdesc->setter = JS_UNDEFINED; - pdesc->value = qjs_string_create_chb(cx, &chain); - if (JS_IsException(pdesc->value)) { - ret = -1; - goto done; - } + pdesc->flags = JS_PROP_ENUMERABLE; + pdesc->getter = JS_UNDEFINED; + pdesc->setter = JS_UNDEFINED; + pdesc->value = qjs_string_create_chb(cx, &chain); + if (JS_IsException(pdesc->value)) { + return -1; } -done: - - njs_chb_destroy(&chain); - - return ret; + return 1; } diff --git a/src/qjs.c b/src/qjs.c index 52401983..38015bb9 100644 --- a/src/qjs.c +++ b/src/qjs.c @@ -1015,6 +1015,8 @@ qjs_string_create_chb(JSContext *cx, njs_chb_t *chain) njs_str_t str; ret = njs_chb_join(chain, &str); + njs_chb_destroy(chain); + if (ret != NJS_OK) { return JS_ThrowInternalError(cx, "failed to create string"); } From noreply at nginx.com Thu Mar 20 03:43:02 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Thu, 20 Mar 2025 03:43:02 +0000 (UTC) Subject: [njs] QuickJS: using JS_AddIntrinsicBigInt() if detected. Message-ID: <20250320034302.A1165478F1@pubserv1.nginx> details: https://github.com/nginx/njs/commit/f3454f01588a6eabfc7c883577544019fbb50f76 branches: master commit: f3454f01588a6eabfc7c883577544019fbb50f76 user: Vadim Zhestikov date: Wed, 19 Mar 2025 09:12:45 -0700 description: QuickJS: using JS_AddIntrinsicBigInt() if detected. --- auto/quickjs | 23 +++++++++++++++++++++++ src/qjs.c | 2 ++ 2 files changed, 25 insertions(+) diff --git a/auto/quickjs b/auto/quickjs index 97432142..e4eecd29 100644 --- a/auto/quickjs +++ b/auto/quickjs @@ -137,6 +137,29 @@ if [ $NJS_TRY_QUICKJS = YES ]; then . auto/feature + njs_feature="QuickJS JS_AddIntrinsicBigInt()" + njs_feature_name=NJS_HAVE_QUICKJS_ADD_INTRINSIC_BIG_INT + njs_feature_test="#if defined(__GNUC__) && (__GNUC__ >= 8) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored \"-Wcast-function-type\" + #endif + + #include + + int main() { + JSRuntime *rt; + JSContext *ctx; + + rt = JS_NewRuntime(); + ctx = JS_NewContextRaw(rt); + JS_AddIntrinsicBigInt(ctx); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + return 0; + }" + + . auto/feature + njs_feature="QuickJS version" njs_feature_name=NJS_QUICKJS_VERSION njs_feature_run=value diff --git a/src/qjs.c b/src/qjs.c index 38015bb9..7763c165 100644 --- a/src/qjs.c +++ b/src/qjs.c @@ -165,7 +165,9 @@ qjs_new_context(JSRuntime *rt, qjs_module_t **addons) JS_AddIntrinsicMapSet(ctx); JS_AddIntrinsicTypedArrays(ctx); JS_AddIntrinsicPromise(ctx); +#ifdef NJS_HAVE_QUICKJS_ADD_INTRINSIC_BIG_INT JS_AddIntrinsicBigInt(ctx); +#endif JS_AddIntrinsicEval(ctx); for (module = qjs_modules; *module != NULL; module++) { From noreply at nginx.com Mon Mar 24 22:58:02 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Mon, 24 Mar 2025 22:58:02 +0000 (UTC) Subject: [njs] QuickJS: added missed JS_NewClass() for the SharedDictError class. Message-ID: <20250324225802.81FBC478F5@pubserv1.nginx> details: https://github.com/nginx/njs/commit/02ccc1168c41014bcc267fe932afd34ce63c7f55 branches: master commit: 02ccc1168c41014bcc267fe932afd34ce63c7f55 user: hongzhidao date: Mon, 24 Mar 2025 15:40:15 +0800 description: QuickJS: added missed JS_NewClass() for the SharedDictError class. --- nginx/ngx_js_shared_dict.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/nginx/ngx_js_shared_dict.c b/nginx/ngx_js_shared_dict.c index 7f8c808e..c0928088 100644 --- a/nginx/ngx_js_shared_dict.c +++ b/nginx/ngx_js_shared_dict.c @@ -465,6 +465,11 @@ static JSClassDef ngx_qjs_shared_class = { }, }; +static JSClassDef ngx_qjs_shared_dict_error_class = { + "SharedDictError", + .finalizer = NULL, +}; + qjs_module_t ngx_qjs_ngx_shared_dict_module = { .name = "shared_dict", .init = ngx_qjs_ngx_shared_dict_init, @@ -3001,6 +3006,12 @@ ngx_qjs_ngx_shared_dict_init(JSContext *cx, const char *name) return NULL; } + if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_SHARED_DICT_ERROR, + &ngx_qjs_shared_dict_error_class) < 0) + { + return NULL; + } + proto = JS_NewObject(cx); if (JS_IsException(proto)) { return NULL; From noreply at nginx.com Tue Mar 25 01:36:02 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Tue, 25 Mar 2025 01:36:02 +0000 (UTC) Subject: [njs] QuickJS: introduced qjs_promise_result(). Message-ID: <20250325013602.0C32947902@pubserv1.nginx> details: https://github.com/nginx/njs/commit/87086636eda9a967a44f8aa6b695dbfe916574a4 branches: master commit: 87086636eda9a967a44f8aa6b695dbfe916574a4 user: Dmitry Volyntsev date: Tue, 25 Mar 2025 08:39:06 +0800 description: QuickJS: introduced qjs_promise_result(). --- external/qjs_fs_module.c | 35 ++++------------------ external/qjs_webcrypto_module.c | 64 ++++++++--------------------------------- src/qjs.c | 39 +++++++++++++++++++++++++ src/qjs.h | 2 ++ 4 files changed, 58 insertions(+), 82 deletions(-) diff --git a/external/qjs_fs_module.c b/external/qjs_fs_module.c index b09d1dcd..48ae9833 100644 --- a/external/qjs_fs_module.c +++ b/external/qjs_fs_module.c @@ -2689,18 +2689,10 @@ qjs_fs_filehandle_finalizer(JSRuntime *rt, JSValue val) } -static JSValue -qjs_fs_promise_trampoline(JSContext *cx, int argc, JSValueConst *argv) -{ - return JS_Call(cx, argv[0], JS_UNDEFINED, 1, &argv[1]); -} - - static JSValue qjs_fs_result(JSContext *cx, JSValue result, int calltype, JSValue callback) { - JS_BOOL is_error; - JSValue promise, callbacks[2], arguments[2]; + JSValue promise, arguments[2]; switch (calltype) { case QJS_FS_DIRECT: @@ -2712,29 +2704,12 @@ qjs_fs_result(JSContext *cx, JSValue result, int calltype, JSValue callback) return result; case QJS_FS_PROMISE: - promise = JS_NewPromiseCapability(cx, callbacks); - if (JS_IsException(promise)) { - JS_FreeValue(cx, result); - return JS_EXCEPTION; - } - - is_error = !!JS_IsError(cx, result); - - arguments[0] = callbacks[is_error]; - arguments[1] = result; - JS_FreeValue(cx, callbacks[!is_error]); - - if (JS_EnqueueJob(cx, qjs_fs_promise_trampoline, 2, arguments) < 0) { - JS_FreeValue(cx, promise); - JS_FreeValue(cx, callbacks[is_error]); - JS_FreeValue(cx, result); - return JS_EXCEPTION; + if (JS_IsError(cx, result)) { + JS_Throw(cx, result); + return qjs_promise_result(cx, JS_EXCEPTION); } - JS_FreeValue(cx, arguments[0]); - JS_FreeValue(cx, arguments[1]); - - return promise; + return qjs_promise_result(cx, result); case QJS_FS_CALLBACK: if (JS_IsError(cx, result)) { diff --git a/external/qjs_webcrypto_module.c b/external/qjs_webcrypto_module.c index 9552ca12..9c4bb452 100644 --- a/external/qjs_webcrypto_module.c +++ b/external/qjs_webcrypto_module.c @@ -156,7 +156,6 @@ static const char *qjs_algorithm_string(qjs_webcrypto_algorithm_t *algorithm); static const char *qjs_algorithm_hash_name(qjs_webcrypto_hash_t hash); static JSValue qjs_key_usage(JSContext *cx, JSValue value, unsigned *mask); static JSValue qjs_key_ops(JSContext *cx, unsigned mask); -static JSValue qjs_webcrypto_result(JSContext *cx, JSValue result, int rc); static void qjs_webcrypto_error(JSContext *cx, const char *fmt, ...); static JSModuleDef *qjs_webcrypto_init(JSContext *cx, const char *name); @@ -547,11 +546,11 @@ qjs_webcrypto_cipher(JSContext *cx, JSValueConst this_val, } } - return qjs_webcrypto_result(cx, ret, 0); + return qjs_promise_result(cx, ret); fail: - return qjs_webcrypto_result(cx, JS_UNDEFINED, -1); + return qjs_promise_result(cx, JS_EXCEPTION); } @@ -1956,7 +1955,7 @@ free: ret = qjs_new_array_buffer(cx, k, length); } - return qjs_webcrypto_result(cx, ret, 0); + return qjs_promise_result(cx, ret); fail: @@ -1964,7 +1963,7 @@ fail: js_free(cx, k); - return qjs_webcrypto_result(cx, JS_UNDEFINED, -1); + return qjs_promise_result(cx, JS_EXCEPTION); } @@ -2170,11 +2169,11 @@ qjs_webcrypto_export_key(JSContext *cx, JSValueConst this_val, int argc, } - return qjs_webcrypto_result(cx, ret, 0); + return qjs_promise_result(cx, ret); fail: - return qjs_webcrypto_result(cx, JS_UNDEFINED, -1); + return qjs_promise_result(cx, JS_EXCEPTION); } @@ -2437,7 +2436,7 @@ qjs_webcrypto_generate_key(JSContext *cx, JSValueConst this_val, goto fail; } - return qjs_webcrypto_result(cx, obj, 0); + return qjs_promise_result(cx, obj); fail: @@ -2448,7 +2447,7 @@ fail: JS_FreeValue(cx, key); JS_FreeValue(cx, keypub); - return qjs_webcrypto_result(cx, JS_UNDEFINED, -1); + return qjs_promise_result(cx, JS_EXCEPTION); } @@ -3489,7 +3488,7 @@ qjs_webcrypto_import_key(JSContext *cx, JSValueConst this_val, int argc, break; } - return qjs_webcrypto_result(cx, key, 0); + return qjs_promise_result(cx, key); fail: @@ -3499,7 +3498,7 @@ fail: JS_FreeValue(cx, key); - return qjs_webcrypto_result(cx, JS_UNDEFINED, -1); + return qjs_promise_result(cx, JS_EXCEPTION); } @@ -3955,7 +3954,7 @@ qjs_webcrypto_sign(JSContext *cx, JSValueConst this_val, int argc, ret = JS_NewBool(cx, rc != 0); } - return qjs_webcrypto_result(cx, ret, 0); + return qjs_promise_result(cx, ret); fail: @@ -3971,7 +3970,7 @@ fail: js_free(cx, dst); } - return qjs_webcrypto_result(cx, JS_UNDEFINED, -1); + return qjs_promise_result(cx, JS_EXCEPTION); } @@ -4669,45 +4668,6 @@ qjs_cpystrn(u_char *dst, u_char *src, size_t n) } -static JSValue -qjs_webcrypto_promise_trampoline(JSContext *cx, int argc, JSValueConst *argv) -{ - return JS_Call(cx, argv[0], JS_UNDEFINED, 1, &argv[1]); -} - - -static JSValue -qjs_webcrypto_result(JSContext *cx, JSValue result, int rc) -{ - JS_BOOL is_error; - JSValue promise, callbacks[2], arguments[2]; - - promise = JS_NewPromiseCapability(cx, callbacks); - if (JS_IsException(promise)) { - JS_FreeValue(cx, result); - return JS_EXCEPTION; - } - - is_error = !!(rc != 0); - - JS_FreeValue(cx, callbacks[!is_error]); - arguments[0] = callbacks[is_error]; - arguments[1] = is_error ? JS_GetException(cx) : result; - - if (JS_EnqueueJob(cx, qjs_webcrypto_promise_trampoline, 2, arguments) < 0) { - JS_FreeValue(cx, promise); - JS_FreeValue(cx, callbacks[is_error]); - JS_FreeValue(cx, arguments[1]); - return JS_EXCEPTION; - } - - JS_FreeValue(cx, arguments[0]); - JS_FreeValue(cx, arguments[1]); - - return promise; -} - - static void qjs_webcrypto_error(JSContext *cx, const char *fmt, ...) { diff --git a/src/qjs.c b/src/qjs.c index 7763c165..a941ba71 100644 --- a/src/qjs.c +++ b/src/qjs.c @@ -1145,3 +1145,42 @@ qjs_string_base64url(JSContext *cx, const njs_str_t *src) return ret; } + + +static JSValue +qjs_promise_fill_trampoline(JSContext *cx, int argc, JSValueConst *argv) +{ + return JS_Call(cx, argv[0], JS_UNDEFINED, 1, &argv[1]); +} + + +JSValue +qjs_promise_result(JSContext *cx, JSValue result) +{ + JS_BOOL is_error; + JSValue promise, callbacks[2], arguments[2]; + + promise = JS_NewPromiseCapability(cx, callbacks); + if (JS_IsException(promise)) { + JS_FreeValue(cx, result); + return JS_EXCEPTION; + } + + is_error = JS_IsException(result); + + JS_FreeValue(cx, callbacks[!is_error]); + arguments[0] = callbacks[is_error]; + arguments[1] = is_error ? JS_GetException(cx) : result; + + if (JS_EnqueueJob(cx, qjs_promise_fill_trampoline, 2, arguments) < 0) { + JS_FreeValue(cx, promise); + JS_FreeValue(cx, callbacks[is_error]); + JS_FreeValue(cx, result); + return JS_EXCEPTION; + } + + JS_FreeValue(cx, arguments[0]); + JS_FreeValue(cx, arguments[1]); + + return promise; +} diff --git a/src/qjs.h b/src/qjs.h index 7c13f039..c7ef4de0 100644 --- a/src/qjs.h +++ b/src/qjs.h @@ -129,6 +129,8 @@ JSValue qjs_string_create_chb(JSContext *cx, njs_chb_t *chain); void qjs_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len); +JSValue qjs_promise_result(JSContext *cx, JSValue result); + JSValue qjs_string_hex(JSContext *cx, const njs_str_t *src); JSValue qjs_string_base64(JSContext *cx, const njs_str_t *src); JSValue qjs_string_base64url(JSContext *cx, const njs_str_t *src); From noreply at nginx.com Wed Mar 26 02:00:02 2025 From: noreply at nginx.com (noreply at nginx.com) Date: Wed, 26 Mar 2025 02:00:02 +0000 (UTC) Subject: [njs] QuickJS: fixed compatibility with recent change in upstream. Message-ID: <20250326020002.0A9F548389@pubserv1.nginx> details: https://github.com/nginx/njs/commit/76a5e4586edd0041c7a2e23b08cbd202aa2ae8cf branches: master commit: 76a5e4586edd0041c7a2e23b08cbd202aa2ae8cf user: Dmitry Volyntsev date: Tue, 25 Mar 2025 17:08:09 -0700 description: QuickJS: fixed compatibility with recent change in upstream. JS_VALUE_GET_OBJ(v) was made hidden in 156d981. --- src/qjs_buffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qjs_buffer.c b/src/qjs_buffer.c index dfe7b04d..48f609be 100644 --- a/src/qjs_buffer.c +++ b/src/qjs_buffer.c @@ -707,8 +707,8 @@ qjs_buffer_is_buffer(JSContext *ctx, JSValueConst this_val, proto = JS_GetPrototype(ctx, argv[0]); buffer_proto = JS_GetClassProto(ctx, QJS_CORE_CLASS_ID_BUFFER); - ret = JS_NewBool(ctx, JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT && - JS_VALUE_GET_OBJ(buffer_proto) == JS_VALUE_GET_OBJ(proto)); + ret = JS_NewBool(ctx, JS_IsObject(argv[0]) + && qjs_is_same_value(ctx, proto, buffer_proto)); JS_FreeValue(ctx, buffer_proto); JS_FreeValue(ctx, proto);