restify / restify/node-restify

ERR_STREAM_WRITE_AFTER_END when using gzipResponse with serveStaticFiles

Open
#1,915 3 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
10.7k
Forks
975
Avg merge
1d 6h
Merged PRs (30d)
5

Description

I am getting ERR_STREAM_WRITE_AFTER_END when requesting a static file (through serveStaticFiles) with gzip compression.

e.g:

Error [ERR_STREAM_WRITE_AFTER_END]: write after end                                                                                                                                            
    at new NodeError (internal/errors.js:322:7)                                                                                                                                                
    at Gzip.Writable.write (internal/streams/writable.js:292:11)                                                                                                                               
    at flush (webpack-internal:///../../node_modules/.pnpm/restify@8.6.1/node_modules/restify/lib/response.js:892:13)                                                                          
    at ServerResponse.__send (webpack-internal:///../../node_modules/.pnpm/restify@8.6.1/node_modules/restify/lib/response.js:546:24)                                                          
    at ServerResponse.send (webpack-internal:///../../node_modules/.pnpm/restify@8.6.1/node_modules/restify/lib/response.js:321:21)                                                            
    at Server._finishReqResCycle (webpack-internal:///../../node_modules/.pnpm/restify@8.6.1/node_modules/restify/lib/server.js:1398:13)                                                       
    at Server._afterRoute (webpack-internal:///../../node_modules/.pnpm/restify@8.6.1/node_modules/restify/lib/server.js:1105:10)                                                              
    at afterRouter (webpack-internal:///../../node_modules/.pnpm/restify@8.6.1/node_modules/restify/lib/server.js:1075:18)                                                                     
    at nextTick (webpack-internal:///../../node_modules/.pnpm/restify@8.6.1/node_modules/restify/lib/chain.js:116:24)                                                                          
    at processTicksAndRejections (internal/process/task_queues.js:77:11)        
  • Used appropriate template for the issue type
  • Searched both open and closed issues for duplicates of this issue
  • Title adequately and concisely reflects the feature or the bug

Restify Version: 8.6.1
Node.js Version: 14.18.1

Expected behaviour

Serve the files, gzipped, without error.

Actual behaviour

ERR_STREAM_WRITE_AFTER_END error is raised, crashing the application.

Repro case

I am getting ERR_STREAM_WRITE_AFTER_END in the following scenario:

  1. gzipResponse is in place
  2. serveStaticFiles in in place
  3. A request is made for a static file with the header: Accept-Encoding: gzip
  4. The file is binary. (Text files seem to work fine!)

Strangely, the following scenarios work as expected:

  • requests for static files without the header
  • requests for non-static files with the header (they are gzipped correctly)

Cause

The error seems to be produced by: https://github.com/restify/node-restify/blob/8.x/lib/server.js#L1389

i.e. Restify believes that no response has been written by the end of the handler chain, when in fact it has.
This is because the res.__flushed flag is set to false in this scenario.

I'm not sure when this flag should be getting set and why it is not set only with this specific combination of middleware.

I can also see the problem with another middleware (apollo-server-restify). It seems to interact with res in a simple way. i.e:

        res.setHeader('Content-Type', 'application/json');
        res.write(gqlResponse);
        res.end();
        next();

So I wonder if this problem might be present when using gzipResponse with any middleware that bypasses res.send. Then again, I can't see anything special about res.send either.

Workaround

I am able to work around the problem by manually setting this flag. i.e:

  const markFlushed = (req, res, next) => {
    res._flushed = true;
    next();
  }
  const servePublicFiles = [ restify.plugins.serveStaticFiles('some/path/'), markFlushed ];

  httpd.head(publicPaths, servePublicFiles);
  httpd.get(publicPaths, servePublicFiles);

Are you willing and able to fix this?

Unknown. (I've not been able to pin down the exact cause so far.)

Does anyone have any ideas what could be causing this?

Apologies if this is being caused by something strange in my set-up.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the binary static-file request with Accept-Encoding: gzip, then trace res.__flushed through lib/server.js around line 1389 and the gzipResponse/serveStaticFiles flow. Done means binary static files are served gzipped without ERR_STREAM_WRITE_AFTER_END, while uncompressed and non-static requests continue to work.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.