details:   
https://github.com/nginx/njs/commit/34b80511acfd44a5cbbbce835d7540081e5d7527
branches:  master
commit:    34b80511acfd44a5cbbbce835d7540081e5d7527
user:      Dmitry Volyntsev <xei...@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 {
_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
https://mailman.nginx.org/mailman/listinfo/nginx-devel

Reply via email to