Nginx does not return response body when using js_body_filter njs directive

1.2k views Asked by At

The problem is that when using js_body_filter directive nginx does not return response. In fact this is sample code which can be found in docs and examples. What am I missing?

Below all configuration together with Dockerfile on which this can be tested:

docker build -t test .
docker run -p 18080:8080 test
curl localhost:18080/index.html -v

Response from curl:

*   Trying 127.0.0.1:18080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 18080 (#0)
> GET /index.html HTTP/1.1
> Host: localhost:18080
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.21.1
< Date: Wed, 22 Sep 2021 16:01:05 GMT
< Content-Type: text/html
< Content-Length: 5
< Last-Modified: Wed, 22 Sep 2021 15:55:24 GMT
< Connection: keep-alive
< ETag: "614b51ec-5"
< Expires: Wed, 22 Sep 2021 16:01:04 GMT
< Cache-Control: no-cache
< Accept-Ranges: bytes
<
* transfer closed with 5 bytes remaining to read
* Closing connection 0
curl: (18) transfer closed with 5 bytes remaining to read

default.conf

js_import envsubst from conf.d/envsubst.js;

server {
  listen 8080;
  location / {
    proxy_buffering off;
    js_body_filter envsubst.process;
    root /etc/nginx/www;
    index  index.html index.htm;
    try_files $uri $uri/ /index.html;
    expires -1;
  }

  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
    root /etc/nginx/www;
  }
}

server {
  listen 8085;
  location /health {
    access_log off;
    return 200 "";
  }
}

envsubst.js

var res = "";

function process(r, data, flags) {
    res += data;
    if (flags.last) {
        r.sendBuffer(res.toLowerCase(), flags);
    }
}

export default { process };

nginx.conf


worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /tmp/nginx.pid;

load_module modules/ngx_http_js_module.so;

events {
    worker_connections  1024;
}


http {
    proxy_temp_path /tmp/proxy_temp;
    client_body_temp_path /tmp/client_temp;
    fastcgi_temp_path /tmp/fastcgi_temp;
    uwsgi_temp_path /tmp/uwsgi_temp;
    scgi_temp_path /tmp/scgi_temp;

    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

index.html

test

Dockerfile

FROM nginxinc/nginx-unprivileged:1.21.1-alpine

COPY nginx.conf /etc/nginx/nginx.conf
COPY default.conf /etc/nginx/conf.d/default.conf
COPY envsubst.js /etc/nginx/conf.d/envsubst.js

RUN  mkdir /etc/nginx/www 
COPY index.html /etc/nginx/www/index.html

1

There are 1 answers

0
bigbear3001 On

I got the same issue and fixed it something like this:

var res = "";

function process(r, data, flags) {
    res += data;
    if (flags.last) {
        if (res.length == 0 && r.variables.request_filename && r.headersOut["Content-Length"] != 0) {
            // running this filter on files causes empty responses
            // if we have a filename we read it and send it to the client
            var fs = require('fs');
            res = fs.readFileSync(r.variables.request_filename);
        }
        r.sendBuffer(res.toLowerCase(), flags);
    }
}

export default { process };

As noted above it seems that using js_body_filter without any ..._pass the data from the file never gets read into memory. So in this fix if the filter didn't get any data and there is a request_filename set and the headers indicate that we are expected to send a non zero length body, we read the file ourselves.