[njs] Fetch: fixed handling of Content-Length header when body is provided.
noreply at nginx.com
noreply at nginx.com
Wed Jun 18 21:12:02 UTC 2025
details: https://github.com/nginx/njs/commit/34b80511acfd44a5cbbbce835d7540081e5d7527
branches: master
commit: 34b80511acfd44a5cbbbce835d7540081e5d7527
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Mon, 16 Jun 2025 19:36:35 -0700
description:
Fetch: fixed handling of Content-Length header when body is provided.
body value length takes precedence over Content-Length from header list.
https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
Let contentLength be httpRequest’s body’s length, if httpRequest’s body
is non-null; otherwise null.
Let contentLengthHeaderValue be null.
If httpRequest’s body is null and httpRequest’s method is `POST` or
`PUT`, then set contentLengthHeaderValue to `0`.
If contentLength is non-null, then set contentLengthHeaderValue to
contentLength, serialized and isomorphic encoded.
If contentLengthHeaderValue is non-null, then append (`Content-Length`,
contentLengthHeaderValue) to httpRequest’s header list.
This fixes #930 issue in Github.
---
nginx/ngx_js_fetch.c | 21 ++++++++++++++++++++-
nginx/ngx_qjs_fetch.c | 21 ++++++++++++++++++++-
nginx/t/js_fetch.t | 23 +++++++++++++++++++++--
3 files changed, 61 insertions(+), 4 deletions(-)
diff --git a/nginx/ngx_js_fetch.c b/nginx/ngx_js_fetch.c
index 45f2dc10..faa38aab 100644
--- a/nginx/ngx_js_fetch.c
+++ b/nginx/ngx_js_fetch.c
@@ -514,6 +514,7 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
ngx_url_t u;
ngx_uint_t i;
njs_bool_t has_host;
+ ngx_str_t method;
ngx_pool_t *pool;
njs_value_t *init, *value;
ngx_js_http_t *http;
@@ -674,6 +675,13 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
continue;
}
+ if (h[i].key.len == 14
+ && ngx_strncasecmp(h[i].key.data, (u_char *) "Content-Length", 14)
+ == 0)
+ {
+ continue;
+ }
+
njs_chb_append(&http->chain, h[i].key.data, h[i].key.len);
njs_chb_append_literal(&http->chain, ": ");
njs_chb_append(&http->chain, h[i].value.data, h[i].value.len);
@@ -693,7 +701,18 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_chb_append(&http->chain, request.body.data, request.body.len);
} else {
- njs_chb_append_literal(&http->chain, CRLF);
+ method = request.method;
+
+ if ((method.len == 4
+ && (ngx_strncasecmp(method.data, (u_char *) "POST", 4) == 0))
+ || (method.len == 3
+ && ngx_strncasecmp(method.data, (u_char *) "PUT", 3) == 0))
+ {
+ njs_chb_append_literal(&http->chain, "Content-Length: 0" CRLF CRLF);
+
+ } else {
+ njs_chb_append_literal(&http->chain, CRLF);
+ }
}
if (u.addrs == NULL) {
diff --git a/nginx/ngx_qjs_fetch.c b/nginx/ngx_qjs_fetch.c
index 084162ba..5ed8fc30 100644
--- a/nginx/ngx_qjs_fetch.c
+++ b/nginx/ngx_qjs_fetch.c
@@ -241,6 +241,7 @@ ngx_qjs_ext_fetch(JSContext *cx, JSValueConst this_val, int argc,
JSValue init, value, promise;
ngx_int_t rc;
ngx_url_t u;
+ ngx_str_t method;
ngx_uint_t i;
ngx_pool_t *pool;
ngx_js_ctx_t *ctx;
@@ -410,6 +411,13 @@ ngx_qjs_ext_fetch(JSContext *cx, JSValueConst this_val, int argc,
continue;
}
+ if (h[i].key.len == 14
+ && ngx_strncasecmp(h[i].key.data, (u_char *) "Content-Length", 14)
+ == 0)
+ {
+ continue;
+ }
+
njs_chb_append(&http->chain, h[i].key.data, h[i].key.len);
njs_chb_append_literal(&http->chain, ": ");
njs_chb_append(&http->chain, h[i].value.data, h[i].value.len);
@@ -429,7 +437,18 @@ ngx_qjs_ext_fetch(JSContext *cx, JSValueConst this_val, int argc,
njs_chb_append(&http->chain, request.body.data, request.body.len);
} else {
- njs_chb_append_literal(&http->chain, CRLF);
+ method = request.method;
+
+ if ((method.len == 4
+ && (ngx_strncasecmp(method.data, (u_char *) "POST", 4) == 0))
+ || (method.len == 3
+ && ngx_strncasecmp(method.data, (u_char *) "PUT", 3) == 0))
+ {
+ njs_chb_append_literal(&http->chain, "Content-Length: 0" CRLF CRLF);
+
+ } else {
+ njs_chb_append_literal(&http->chain, CRLF);
+ }
}
if (u.addrs == NULL) {
diff --git a/nginx/t/js_fetch.t b/nginx/t/js_fetch.t
index 1c6fde77..76d9238d 100644
--- a/nginx/t/js_fetch.t
+++ b/nginx/t/js_fetch.t
@@ -64,6 +64,10 @@ http {
js_content test.body;
}
+ location /body_content_length {
+ js_content test.body_content_length;
+ }
+
location /body_special {
js_content test.body_special;
}
@@ -156,6 +160,13 @@ $t->write_file('test.js', <<EOF);
.catch(e => r.return(501, e.message))
}
+ async function body_content_length(r) {
+ let resp = await ngx.fetch(`http://127.0.0.1:$p0/loc`,
+ {headers: {'Content-Length': '100'},
+ body: "CONTENT-BODY"});
+ r.return(resp.status);
+ }
+
function property(r) {
var opts = {headers:{}};
@@ -400,12 +411,12 @@ $t->write_file('test.js', <<EOF);
export default {njs: test_njs, body, broken, broken_response, body_special,
chain, chunked_ok, chunked_fail, header, header_iter,
- host_header, multi, loc, property};
+ host_header, multi, loc, property, body_content_length };
EOF
$t->try_run('no njs.fetch');
-$t->plan(37);
+$t->plan(38);
$t->run_daemon(\&http_daemon, port(8082));
$t->waitforsocket('127.0.0.1:' . port(8082));
@@ -508,6 +519,14 @@ like(http_get('/body_special?loc=head/large&method=HEAD'),
qr/200 OK.*<empty>$/s, 'fetch head method large content-length');
}
+TODO: {
+local $TODO = 'not yet' unless has_version('0.9.1');
+
+like(http_get('/body_content_length'), qr/200 OK/s,
+ 'fetch body content-length');
+
+}
+
###############################################################################
sub has_version {
More information about the nginx-devel
mailing list