nginx/njs

ngx.fetch Content-Length header is not sent

Closed this issue · 8 comments

With the current Nginx version nginx/1.24.0 there are no headers on the server that contain a minus sign in the key.

Example, works. The header x arrives at the server:
await ngx.fetch("https://yxy/asd", { verify: false, headers: { 'x': "a" } });

Example, header x-ms does not arrive at the server:
await ngx.fetch("https://yxy/asd", { verify: false, headers: { 'x-ms': "a" } });

Hi @Masterwow3,

I cannot reproduce the problem so far, I am using the following config and js code

error_log /dev/stdout info;
daemon off;
master_process off;

events { }

http {
    js_import main from fetch.js;

    resolver 1.1.1.1;

    server {
        listen       8000;

        location / {
            js_content main.fetch;
        }
    }

    server {
        listen       8001;

        location / {
            js_content main.reply;
        }
    }
}

async function fetch(r) {
    let reply = await ngx.fetch("http://127.0.0.1:8001/",
                                { headers: { 'x-ms': "a", 'foo': 'xxx' } })

    let body = await reply.text();

    r.return(200, body);
}

function reply(r) {
    let body = [njs.version, '\n'];
    r.rawHeadersIn.forEach((header) => {
        body.push(`${header[0]}: ${header[1]}\n`);
    });

    r.return(200, body.join(''));
}

export default { fetch, reply };

To run the nginx:
nginx -c fetch.conf

curl http://127.1:8000/                            
0.7.10                                                                                                                                   
Host: 127.0.0.1                                                     
Connection: close                                                   
x-ms: a                                                                                                                                  
foo: xxx

I tried njs versions from 0.7.10 to 0.7.12 and the latest one 0.8.2. Which njs version are you using?

Hi @xeioex,
thanks for your reply, you are right, the mirror I was using swallowed the headers.

But there is still a problem. The header Content-Length cannot be set. This does not reach the server.

Log output: [info] 29#29: *3 js: [["Host","10.3.2.52"],["Connection","close"],["x-ms","123"]]
nginx/1.24.0

async function send(r) {
  await ngx.fetch("http://10.3.2.52:8082/", {
    headers: {
      "Content-Length": "0",
      "x-ms": "123"
    }
  });
  r.return(200);
}

async function log(r) {
  r.log(JSON.stringify(r.rawHeadersIn));
  r.return(200);
}


export default { send, log }
server {
  listen 8081;
  js_import njs/test.js;

  location / {
    js_content test.send;
  }
}

server {
  listen 8082;
  js_import njs/test.js;

  location / {
    js_content test.log;
  }
}

Hi @Masterwow3,

"Content-Length" and a handful of other headers are treated in a special way by nginx. For example, with "Content-Length", the value is put to r->headers_in.content_length as a number and not as a header.
Whereas both r.rawHeadersIn and r.headersIn look for ordinary headers.
In njs you can get "Content-length" by r.requestBuffer.length for the text input.

Hi @xeioex,

curl -v -H "Content-Length: 0" "http://10.3.2.52:8082/"
Arrives at nginx: [info] 29#29: *2 js: [["Host","10.3.2.52:8082"],["User-Agent","curl/7.87.0"],["Accept","*/*"],["Content-Length","0"]]
It does not arrive per NJS.

await ngx.fetch("http://10.3.2.52:8082/", {
    headers: {
      "Content-Length": "0",
      "x-ms": "123"
    }
  });

Does that mean I can't send this header via njs?

Hi @Masterwow3,

There are no special treatment of Content-Length in njs Fetch for headers object.
With this code https://gist.github.com/xeioex/f3ceec948833c254dfb51bee4b7e48eb
I get the following output:

curl http://127.1:8000/
0.8.3
Host: 127.0.0.1
x-ms: a
foo: xxx
Content-Length: 0
Connection: close

Can you confirm the njs version? Just output njs.version in JS code.

Hi @xeioex,

[info] 28#28: *1 js: NJS version: 0.7.12

Okay too bad, is it planned to support the header Content-Length in NJS?
I use the Microsoft Blob API which requires this header.

Hi @Masterwow3,

There WAS a special treatment for Content-Length and other headers in accordance with Fetch API (between 0.7.10 and 0.7.12). But this was reverted back since 0.8.0. The latest released version is 0.8.2.

Feel free to upgrade to nginx 1.25.3 and njs 0.8.2.

nginx/1.25.3
NJS version: 0.8.2
28#28: *3 js: [["Host","10.3.2.52"],["Content-Length","0"],["x-ms","123"],["Connection","close"]]

Thank you, it works.