From: Dmitry Volyntsev Date: Thu, 13 Aug 2026 00:22:55 +0000 (-0700) Subject: Fetch: preserve QuickJS header names X-Git-Tag: 1.0.1~17 X-Git-Url: https://git.kaiwu.me/%7B@url%7D?a=commitdiff_plain;h=3388e416967f3aeef3c853af2c523844f37e068c;p=njs.git Fetch: preserve QuickJS header names Previously, Headers.set() converted the header name with JS_ToCStringLen(). When a new header was added, the list retained that pointer after it was released with JS_FreeCString(), leaving the header with a dangling name. The fix is to copy the name into the request pool only when set() creates a new list entry. Replacing an existing entry requires no additional allocation, and the temporary QuickJS string can always be released after the mutation. --- diff --git a/nginx/ngx_js_fetch.c b/nginx/ngx_js_fetch.c index e2ce0320..06dff385 100644 --- a/nginx/ngx_js_fetch.c +++ b/nginx/ngx_js_fetch.c @@ -1552,7 +1552,7 @@ ngx_js_headers_append(njs_vm_t *vm, ngx_js_headers_t *headers, { ngx_js_headers_rc_t rc; - rc = ngx_js_headers_modify(headers, name, len, value, vlen, 0); + rc = ngx_js_headers_modify(NULL, headers, name, len, value, vlen, 0); return ngx_js_headers_result(vm, rc); } @@ -1990,8 +1990,8 @@ ngx_headers_js_ext_set(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return NJS_ERROR; } - rc = ngx_js_headers_modify(headers, name.start, name.length, value.start, - value.length, 1); + rc = ngx_js_headers_modify(NULL, headers, name.start, name.length, + value.start, value.length, 1); if (ngx_js_headers_result(vm, rc) != NJS_OK) { return NJS_ERROR; } diff --git a/nginx/ngx_js_http.c b/nginx/ngx_js_http.c index db8b0732..d2b7323a 100644 --- a/nginx/ngx_js_http.c +++ b/nginx/ngx_js_http.c @@ -1834,9 +1834,10 @@ ngx_js_headers_error(ngx_js_headers_rc_t rc) ngx_js_headers_rc_t -ngx_js_headers_modify(ngx_js_headers_t *headers, u_char *name, size_t len, - u_char *value, size_t vlen, njs_bool_t replace) +ngx_js_headers_modify(ngx_pool_t *pool, ngx_js_headers_t *headers, + u_char *name, size_t len, u_char *value, size_t vlen, njs_bool_t replace) { + u_char *p; ngx_uint_t i; ngx_list_part_t *part; ngx_js_tb_elt_t *first, *h, **ph; @@ -1906,6 +1907,20 @@ ngx_js_headers_modify(ngx_js_headers_t *headers, u_char *name, size_t len, while (*ph) { ph = &(*ph)->next; } } + /* + * A pool is needed for QuickJS because the provided name is freed after + * this call. + */ + if (pool != NULL) { + p = ngx_pnalloc(pool, len); + if (p == NULL) { + return NGX_JS_HEADERS_NOMEM; + } + + ngx_memcpy(p, name, len); + name = p; + } + h = ngx_list_push(&headers->header_list); if (h == NULL) { return NGX_JS_HEADERS_NOMEM; diff --git a/nginx/ngx_js_http.h b/nginx/ngx_js_http.h index fc4276d0..c3ebe15a 100644 --- a/nginx/ngx_js_http.h +++ b/nginx/ngx_js_http.h @@ -193,8 +193,9 @@ ngx_int_t ngx_js_check_header_name(u_char *name, size_t len); ngx_int_t ngx_js_check_request_line_component(u_char *value, size_t len); ngx_int_t ngx_js_check_header_value(u_char *value, size_t len); const char *ngx_js_headers_error(ngx_js_headers_rc_t rc); -ngx_js_headers_rc_t ngx_js_headers_modify(ngx_js_headers_t *headers, - u_char *name, size_t len, u_char *value, size_t vlen, njs_bool_t replace); +ngx_js_headers_rc_t ngx_js_headers_modify(ngx_pool_t *pool, + ngx_js_headers_t *headers, u_char *name, size_t len, u_char *value, + size_t vlen, njs_bool_t replace); ngx_js_headers_rc_t ngx_js_headers_remove(ngx_js_headers_t *headers, u_char *name, size_t len); njs_bool_t ngx_js_headers_has(ngx_js_headers_t *headers, u_char *name, diff --git a/nginx/ngx_qjs_fetch.c b/nginx/ngx_qjs_fetch.c index 1e6f551b..2c67f630 100644 --- a/nginx/ngx_qjs_fetch.c +++ b/nginx/ngx_qjs_fetch.c @@ -1384,7 +1384,7 @@ ngx_qjs_headers_append(JSContext *cx, ngx_js_headers_t *headers, { ngx_js_headers_rc_t rc; - rc = ngx_js_headers_modify(headers, name, len, value, vlen, 0); + rc = ngx_js_headers_modify(NULL, headers, name, len, value, vlen, 0); return ngx_qjs_headers_result(cx, rc); } @@ -1914,7 +1914,7 @@ ngx_qjs_ext_fetch_headers_set(JSContext *cx, JSValueConst this_val, return JS_EXCEPTION; } - rc = ngx_js_headers_modify(headers, name.data, name.len, value.data, + rc = ngx_js_headers_modify(pool, headers, name.data, name.len, value.data, value.len, 1); JS_FreeCString(cx, (const char *) name.data); if (ngx_qjs_headers_result(cx, rc) != NGX_OK) { diff --git a/nginx/t/js_fetch_objects.t b/nginx/t/js_fetch_objects.t index 1e39d1c4..6591a8f5 100644 --- a/nginx/t/js_fetch_objects.t +++ b/nginx/t/js_fetch_objects.t @@ -298,6 +298,21 @@ $t->write_file('test.js', < { + var h = new Headers(); + + for (var i = 0; i < 2; i++) { + h.set('x-computed-header-name-' + i, 'value-' + i); + } + + for (var i = 0; i < 2; i++) { + if (h.get('x-computed-header-name-' + i) != 'value-' + i) { + throw new Error('header name is corrupted'); + } + } + return 'OK'; }, 'OK'], ];